From d4af6d9ddbbe7deef3b15fb4515a91331e7ed459 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Mon, 5 Aug 2024 09:02:07 +0200 Subject: [PATCH] Add TowerRun module --- TowerRun/build.gradle.kts | 67 ++++ TowerRun/src/TowerRun.properties | 36 ++ TowerRun/src/TowerRun_de.properties | 35 ++ .../src/de/steamwar/towerrun/TowerRun.java | 84 +++++ .../towerrun/commands/StartCommand.java | 51 +++ .../de/steamwar/towerrun/config/Config.java | 58 ++++ .../steamwar/towerrun/config/WorldConfig.java | 323 ++++++++++++++++++ .../towerrun/countdowns/Countdown.java | 80 +++++ .../towerrun/countdowns/EndCountdown.java | 67 ++++ .../towerrun/countdowns/GameCountdown.java | 67 ++++ .../towerrun/countdowns/LobbyCountdown.java | 71 ++++ .../steamwar/towerrun/game/TowerRunGame.java | 184 ++++++++++ .../towerrun/game/TowerRunPlayer.java | 64 ++++ .../towerrun/generator/TowerGenerator.java | 207 +++++++++++ .../towerrun/listener/GlobalListener.java | 67 ++++ .../towerrun/listener/IngameListener.java | 215 ++++++++++++ .../towerrun/listener/LobbyListener.java | 81 +++++ .../towerrun/listener/NotLobbyListener.java | 46 +++ .../de/steamwar/towerrun/state/GameState.java | 59 ++++ .../state/GameStateBukkitListener.java | 44 +++ .../towerrun/state/GameStateListener.java | 29 ++ .../state/GameStateToggleListener.java | 48 +++ .../steamwar/towerrun/state/GameStates.java | 34 ++ .../FirstOutsideWincondition.java | 35 ++ .../LastOutsideWincondition.java | 53 +++ .../LastRemainingWincondition.java | 58 ++++ .../winconditions/OutsideWincondition.java | 73 ++++ .../towerrun/winconditions/WinCondition.java | 54 +++ settings.gradle.kts | 4 +- 29 files changed, 2293 insertions(+), 1 deletion(-) create mode 100644 TowerRun/build.gradle.kts create mode 100644 TowerRun/src/TowerRun.properties create mode 100644 TowerRun/src/TowerRun_de.properties create mode 100644 TowerRun/src/de/steamwar/towerrun/TowerRun.java create mode 100644 TowerRun/src/de/steamwar/towerrun/commands/StartCommand.java create mode 100644 TowerRun/src/de/steamwar/towerrun/config/Config.java create mode 100644 TowerRun/src/de/steamwar/towerrun/config/WorldConfig.java create mode 100644 TowerRun/src/de/steamwar/towerrun/countdowns/Countdown.java create mode 100644 TowerRun/src/de/steamwar/towerrun/countdowns/EndCountdown.java create mode 100644 TowerRun/src/de/steamwar/towerrun/countdowns/GameCountdown.java create mode 100644 TowerRun/src/de/steamwar/towerrun/countdowns/LobbyCountdown.java create mode 100644 TowerRun/src/de/steamwar/towerrun/game/TowerRunGame.java create mode 100644 TowerRun/src/de/steamwar/towerrun/game/TowerRunPlayer.java create mode 100644 TowerRun/src/de/steamwar/towerrun/generator/TowerGenerator.java create mode 100644 TowerRun/src/de/steamwar/towerrun/listener/GlobalListener.java create mode 100644 TowerRun/src/de/steamwar/towerrun/listener/IngameListener.java create mode 100644 TowerRun/src/de/steamwar/towerrun/listener/LobbyListener.java create mode 100644 TowerRun/src/de/steamwar/towerrun/listener/NotLobbyListener.java create mode 100644 TowerRun/src/de/steamwar/towerrun/state/GameState.java create mode 100644 TowerRun/src/de/steamwar/towerrun/state/GameStateBukkitListener.java create mode 100644 TowerRun/src/de/steamwar/towerrun/state/GameStateListener.java create mode 100644 TowerRun/src/de/steamwar/towerrun/state/GameStateToggleListener.java create mode 100644 TowerRun/src/de/steamwar/towerrun/state/GameStates.java create mode 100644 TowerRun/src/de/steamwar/towerrun/winconditions/FirstOutsideWincondition.java create mode 100644 TowerRun/src/de/steamwar/towerrun/winconditions/LastOutsideWincondition.java create mode 100644 TowerRun/src/de/steamwar/towerrun/winconditions/LastRemainingWincondition.java create mode 100644 TowerRun/src/de/steamwar/towerrun/winconditions/OutsideWincondition.java create mode 100644 TowerRun/src/de/steamwar/towerrun/winconditions/WinCondition.java diff --git a/TowerRun/build.gradle.kts b/TowerRun/build.gradle.kts new file mode 100644 index 00000000..fcecdfeb --- /dev/null +++ b/TowerRun/build.gradle.kts @@ -0,0 +1,67 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 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 . + */ + +plugins { + id("java") + id("base") + + id("com.github.johnrengelman.shadow") +} + +group = "de.steamwar" +version = "" + +tasks.compileJava { + options.encoding = "UTF-8" +} + +tasks.build { + finalizedBy(tasks.shadowJar) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +sourceSets { + main { + java { + srcDirs("src/") + } + resources { + srcDirs("src/") + exclude("**/*.java", "**/*.kt") + } + } +} + +dependencies { + compileOnly("org.projectlombok:lombok:1.18.32") + annotationProcessor("org.projectlombok:lombok:1.18.32") + compileOnly("org.spigotmc:plugin-annotations:1.2.3-SNAPSHOT") + annotationProcessor("org.spigotmc:plugin-annotations:1.2.3-SNAPSHOT") + + compileOnly(project(":SpigotCore")) + + compileOnly("de.steamwar:spigot:1.19") + compileOnly("de.steamwar:worldedit:1.15") + + compileOnly("org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT") +} diff --git a/TowerRun/src/TowerRun.properties b/TowerRun/src/TowerRun.properties new file mode 100644 index 00000000..88c2ecd9 --- /dev/null +++ b/TowerRun/src/TowerRun.properties @@ -0,0 +1,36 @@ +# +# This file is a part of the SteamWar software. +# +# Copyright (C) 2023 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 . +# + +PREFIX=§eTowerRun§8»§r + +PLAYER_DIED=§c{0} §7died§8! +PLAYER_ESCAPE=§a{0} §7escaped§8! +GAME_START=§aThe game has started§8! +GAME_WIN=§a{0} §7has won the game§8! +GAME_STARTING=§7Starting: §e{0}s§8! +GAME_WAITING=§7Waiting for players§8... +SERVER_STOPPING=§cThe server stops in §e{0}s§8! +SERVER_RESET=§cThe server will be reset in §e{0}s§8! +KEY_NAME=§eKey +KEY_FOUND=§a{0} §7found a key§8! +GAME_TIE=§aThe game ended in a tie§8! +GAME_TIME=§a{0}:{1} +CATCH_UP_WARNING=§4!! §cYou should catch up §4!! + +COMMAND_START=§aThe game will start soon§8! \ No newline at end of file diff --git a/TowerRun/src/TowerRun_de.properties b/TowerRun/src/TowerRun_de.properties new file mode 100644 index 00000000..2c88c3b6 --- /dev/null +++ b/TowerRun/src/TowerRun_de.properties @@ -0,0 +1,35 @@ +# +# This file is a part of the SteamWar software. +# +# Copyright (C) 2023 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 . +# + +PLAYER_DIED=§c{0} §7ist gestorben§8! +PLAYER_ESCAPE=§a{0} §7ist entkommen§8! +GAME_START=§aDas Spiel beginnt§8! +GAME_WIN=§a{0} §7hat das Spiel gewonnen§8! +GAME_STARTING=§7Das Spiel startet in §e{0}s§8! +GAME_WAITING=§7Warte auf weitere Spieler§8... +SERVER_STOPPING=§cDer Server stoppt in §e{0}s§8! +SERVER_RESET=§cDer Server wird in §e{0}s §czurückgesetzt§8! +GAME_TIE=§7Keiner hat gewonnen§8! + +KEY_NAME=§eSchlüssel +KEY_FOUND=§a{0} §7hat einen Schlüssel gefunden§8! + +CATCH_UP_WARNING=§4!! §cDu solltest aufholen §4!! + +COMMAND_START=§7Das Spiel startet bald§8! \ No newline at end of file diff --git a/TowerRun/src/de/steamwar/towerrun/TowerRun.java b/TowerRun/src/de/steamwar/towerrun/TowerRun.java new file mode 100644 index 00000000..c0717b2f --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/TowerRun.java @@ -0,0 +1,84 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun; + +import de.steamwar.message.Message; +import de.steamwar.towerrun.commands.StartCommand; +import de.steamwar.towerrun.config.WorldConfig; +import de.steamwar.towerrun.countdowns.EndCountdown; +import de.steamwar.towerrun.countdowns.GameCountdown; +import de.steamwar.towerrun.countdowns.LobbyCountdown; +import de.steamwar.towerrun.game.TowerRunGame; +import de.steamwar.towerrun.generator.TowerGenerator; +import de.steamwar.towerrun.listener.GlobalListener; +import de.steamwar.towerrun.listener.IngameListener; +import de.steamwar.towerrun.listener.LobbyListener; +import de.steamwar.towerrun.listener.NotLobbyListener; +import lombok.Getter; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.plugin.java.annotation.dependency.Dependency; +import org.bukkit.plugin.java.annotation.plugin.ApiVersion; +import org.bukkit.plugin.java.annotation.plugin.Description; +import org.bukkit.plugin.java.annotation.plugin.Plugin; +import org.bukkit.plugin.java.annotation.plugin.author.Author; + +@Plugin(name = "TowerRun", version = "1.0.0") +@Dependency("SpigotCore") +@Author("YoyoNow") +@Author("Chaoscaot") +@Description("SteamWar TowerRun Plugin") +@ApiVersion(ApiVersion.Target.v1_19) +public class TowerRun extends JavaPlugin { + + @Getter + private static TowerRun instance; + + @Getter + private static Message message; + + @Getter + private static TowerGenerator towerGenerator = null; + + @Getter + private static GameCountdown gameCountdown; + + + @Override + public void onEnable() { + instance = this; + message = new Message("TowerRun", getClassLoader()); + + if (WorldConfig.TOWER_GENERATOR_CONFIG != null) { + towerGenerator = new TowerGenerator(WorldConfig.TOWER_GENERATOR_CONFIG); + } + + new LobbyListener(); + new IngameListener(); + new GlobalListener(); + new LobbyListener(); + new NotLobbyListener(); + final LobbyCountdown lobbyCountdown = new LobbyCountdown(); + new EndCountdown(lobbyCountdown); + new StartCommand(lobbyCountdown); + gameCountdown = new GameCountdown(); + + TowerRunGame.reset(); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/commands/StartCommand.java b/TowerRun/src/de/steamwar/towerrun/commands/StartCommand.java new file mode 100644 index 00000000..ac93fd85 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/commands/StartCommand.java @@ -0,0 +1,51 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.commands; + +import de.steamwar.command.SWCommand; +import de.steamwar.command.TypeValidator; +import de.steamwar.sql.SteamwarUser; +import de.steamwar.sql.UserGroup; +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.countdowns.LobbyCountdown; +import org.bukkit.entity.Player; + +public class StartCommand extends SWCommand { + private final LobbyCountdown countdown; + + public StartCommand(LobbyCountdown countdown) { + super("start", "skip"); + this.countdown = countdown; + } + + @Register + public void command(@Validator Player player) { + if (countdown.getTime() > 10) { + countdown.setOverride(true); + countdown.setTime(10); + } + TowerRun.getMessage().send("COMMAND_START", player); + } + + @ClassValidator(value = Player.class, local = true) + public TypeValidator playerValidator() { + return (commandSender, player, messageSender) -> SteamwarUser.get(player.getUniqueId()).getUserGroup() != UserGroup.Member; + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/config/Config.java b/TowerRun/src/de/steamwar/towerrun/config/Config.java new file mode 100644 index 00000000..7890e785 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/config/Config.java @@ -0,0 +1,58 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.config; + +import de.steamwar.towerrun.TowerRun; +import lombok.experimental.UtilityClass; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.util.EnumSet; +import java.util.Set; +import java.util.stream.Collectors; + +@UtilityClass +public class Config { + public static final int MIN_PLAYERS; + public static final int LOBBY_TIMER; + public static final Set DESTROYABLE_BLOCKS; + public static final int GAME_TIMER; + public static final int GAME_ESCAPE_TIMER; + + static { + File configFile = new File(TowerRun.getInstance().getDataFolder(), "config.yml"); + if (!configFile.exists()) { + TowerRun.getInstance().getLogger().severe("Config not found!"); + Bukkit.shutdown(); + } + + ConfigurationSection config = YamlConfiguration.loadConfiguration(configFile); + + MIN_PLAYERS = config.getInt("minPlayers"); + LOBBY_TIMER = config.getInt("lobbyTimer"); + GAME_TIMER = config.getInt("gameTimer", 20 * 60); + GAME_ESCAPE_TIMER = config.getInt("gameEscapeTimer", 60); + DESTROYABLE_BLOCKS = EnumSet.copyOf(config.getStringList("destroyable").stream().map(Material::valueOf).collect(Collectors.toSet())); + } + +} diff --git a/TowerRun/src/de/steamwar/towerrun/config/WorldConfig.java b/TowerRun/src/de/steamwar/towerrun/config/WorldConfig.java new file mode 100644 index 00000000..fafdd453 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/config/WorldConfig.java @@ -0,0 +1,323 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.config; + +import de.steamwar.sql.SchematicType; +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.winconditions.FirstOutsideWincondition; +import de.steamwar.towerrun.winconditions.LastOutsideWincondition; +import de.steamwar.towerrun.winconditions.LastRemainingWincondition; +import de.steamwar.towerrun.winconditions.WinCondition; +import lombok.Getter; +import lombok.experimental.UtilityClass; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.util.Vector; + +import java.io.File; +import java.util.*; + +@UtilityClass +public class WorldConfig { + + public static final Region[] REGIONS; + public static final Location[] DOORS; + public static final Location[] KEYS; + public static final int LAVA_Y; + public static final int LAVE_SPACE; + public static final Location SPAWN; + public static final int ESCAPE_HEIGHT; + public static final Location MIN_TOWER; + public static final Location MAX_TOWER; + public static final List ACTIVE_WINCONDITIONS; + public static final int MAP_MIN_X; + public static final int MAP_MIN_Z; + public static final int MAP_MAX_X; + public static final int MAP_MAX_Z; + public static final Map MELTING_TIMES; + public static final TowerGeneratorConfig TOWER_GENERATOR_CONFIG; + + public static final List WINCONDITIONS = new ArrayList<>(); + + static { + WINCONDITIONS.add(new LastRemainingWincondition()); + WINCONDITIONS.add(new LastOutsideWincondition()); + WINCONDITIONS.add(new FirstOutsideWincondition()); + } + + private static Location parseLocation(ConfigurationSection section) { + Location loc = new Location( + Bukkit.getWorlds().get(0), + section.getDouble("x"), + section.getDouble("y"), + section.getDouble("z") + ); + if (section.contains("yaw")) { + loc.setYaw((float) section.getDouble("yaw")); + } + if (section.contains("pitch")) { + loc.setPitch((float) section.getDouble("pitch")); + } + return loc; + } + + static { + File configFile = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "config.yml"); + if (!configFile.exists()) { + TowerRun.getInstance().getLogger().severe("World config not found!"); + Bukkit.shutdown(); + } + + ConfigurationSection config = YamlConfiguration.loadConfiguration(configFile); + ConfigurationSection tower = config.getConfigurationSection("tower"); + + List regions = tower.getConfigurationSection("regions").getKeys(false).stream() + .map(tower.getConfigurationSection("regions")::getConfigurationSection) + .toList(); + REGIONS = new Region[regions.size()]; + for (int i = 0; i < regions.size(); i++) { + REGIONS[i] = new Region(regions.get(i)); + } + + if (REGIONS.length == 1) { + MIN_TOWER = REGIONS[0].min; + MAX_TOWER = REGIONS[0].max; + } else { + MIN_TOWER = new Location( + Bukkit.getWorlds().get(0), + Math.min(REGIONS[0].min.getX(), REGIONS[1].min.getX()), + 0, + Math.min(REGIONS[0].min.getZ(), REGIONS[1].min.getZ()) + ); + MAX_TOWER = new Location( + Bukkit.getWorlds().get(0), + Math.max(REGIONS[0].max.getX(), REGIONS[1].max.getX()), + 255, + Math.max(REGIONS[0].max.getZ(), REGIONS[1].max.getZ()) + ); + if (REGIONS.length > 2) { + for (int i = 2; i < REGIONS.length; i++) { + MIN_TOWER.setX(Math.min(MIN_TOWER.getX(), REGIONS[i].min.getX())); + MIN_TOWER.setZ(Math.min(MIN_TOWER.getZ(), REGIONS[i].min.getZ())); + MAX_TOWER.setX(Math.max(MAX_TOWER.getX(), REGIONS[i].max.getX())); + MAX_TOWER.setZ(Math.max(MAX_TOWER.getZ(), REGIONS[i].max.getZ())); + } + } + } + + ESCAPE_HEIGHT = tower.getInt("escapeHeight"); + SPAWN = parseLocation(tower.getConfigurationSection("spawn")); + ConfigurationSection doorSection = tower.getConfigurationSection("doors"); + if (doorSection != null) { + List doors = doorSection.getKeys(false).stream() + .map(tower.getConfigurationSection("doors")::getConfigurationSection) + .toList(); + DOORS = new Location[doors.size()]; + for (int i = 0; i < doors.size(); i++) { + DOORS[i] = parseLocation(doors.get(i)); + } + } else { + DOORS = new Location[0]; + } + + ConfigurationSection keysSection = tower.getConfigurationSection("keys"); + if (keysSection != null) { + List keys = keysSection.getKeys(false).stream() + .map(tower.getConfigurationSection("keys")::getConfigurationSection) + .toList(); + KEYS = new Location[keys.size()]; + for (int i = 0; i < keys.size(); i++) { + KEYS[i] = parseLocation(keys.get(i)); + } + } else { + KEYS = new Location[0]; + } + + LAVA_Y = tower.getInt("lavaY"); + LAVE_SPACE = tower.getInt("laveSpace"); + + MAP_MIN_X = config.getInt("minX"); + MAP_MIN_Z = config.getInt("minZ"); + MAP_MAX_X = config.getInt("maxX"); + MAP_MAX_Z = config.getInt("maxZ"); + + ConfigurationSection meltingBlocksSection = tower.getConfigurationSection("meltingBlocks"); + if (meltingBlocksSection != null) { + Map meltingTimes = new HashMap<>(); + meltingBlocksSection.getKeys(false).forEach(s -> { + meltingTimes.put(Material.valueOf(s), meltingBlocksSection.getInt(s)); + }); + MELTING_TIMES = Collections.unmodifiableMap(meltingTimes); + } else { + MELTING_TIMES = Collections.emptyMap(); + } + + ACTIVE_WINCONDITIONS = config.getStringList("winconditions"); + WINCONDITIONS.stream().filter(winCondition -> ACTIVE_WINCONDITIONS.contains(winCondition.getName())).forEach(winCondition -> winCondition.setActive(true)); + + ConfigurationSection towerGeneratorSection = config.getConfigurationSection("towerGenerator"); + if (towerGeneratorSection == null) { + TOWER_GENERATOR_CONFIG = null; + } else { + TOWER_GENERATOR_CONFIG = new TowerGeneratorConfig(towerGeneratorSection); + } + } + + @Getter + public static final class Region { + public final Location min; + public final Location max; + + public Region(ConfigurationSection section) { + min = new Location( + Bukkit.getWorlds().get(0), + section.getDouble("minX"), + 0, + section.getDouble("minZ") + ); + max = new Location( + Bukkit.getWorlds().get(0), + section.getDouble("maxX"), + 255, + section.getDouble("maxZ") + ); + } + + public boolean contains(Vector vector) { + return vector.getX() >= min.getX() && vector.getX() <= max.getX() + && vector.getZ() >= min.getZ() && vector.getZ() <= max.getZ(); + } + } + + @Getter + public static final class TowerGeneratorConfig { + public final int x; + public final int y; + public final int z; + public final int minHeight; + public final int maxHeight; + public final int minNoBombFloors; + public final int maxNoBombFloors; + public final double tntChance; + public final int minBombs; + public final int maxBombs; + public final double keyChance; + public final int minNoKeyFloors; + public final int maxNoKeyFloors; + public final SchematicType schematicType; + public final TowerGeneratorFillRegion[] fillRegions; + public final Region[] tntRegions; + public final TowerGeneratorDoorBlock[] doorBlocks; + + public TowerGeneratorConfig(ConfigurationSection section) { + x = section.getInt("x"); + y = section.getInt("y"); + z = section.getInt("z"); + minHeight = section.getInt("minHeight"); + maxHeight = section.getInt("maxHeight"); + minNoBombFloors = section.getInt("minNoBombFloors"); + maxNoBombFloors = section.getInt("maxNoBombFloors"); + minBombs = section.getInt("minBombs"); + maxBombs = section.getInt("maxBombs"); + tntChance = section.getDouble("tntChance"); + keyChance = section.getDouble("keyChance"); + minNoKeyFloors = section.getInt("minNoKeyFloors"); + maxNoKeyFloors = section.getInt("maxNoKeyFloors"); + schematicType = SchematicType.fromDB(section.getString("schematicType")); + + ConfigurationSection fillRegionsSection = section.getConfigurationSection("fillRegions"); + if (fillRegionsSection != null) { + List fillRegions = fillRegionsSection.getKeys(false).stream() + .map(fillRegionsSection::getConfigurationSection) + .toList(); + + this.fillRegions = new TowerGeneratorFillRegion[fillRegions.size()]; + for (int i = 0; i < fillRegions.size(); i++) { + this.fillRegions[i] = new TowerGeneratorFillRegion(fillRegions.get(i)); + } + } else { + fillRegions = new TowerGeneratorFillRegion[0]; + } + + ConfigurationSection tntRegionsSection = section.getConfigurationSection("tntRegions"); + if (tntRegionsSection != null) { + List tntRegions = tntRegionsSection.getKeys(false).stream() + .map(tntRegionsSection::getConfigurationSection) + .toList(); + + this.tntRegions = new Region[tntRegions.size()]; + for (int i = 0; i < tntRegions.size(); i++) { + this.tntRegions[i] = new Region(tntRegions.get(i)); + } + } else { + tntRegions = new Region[0]; + } + + ConfigurationSection doorBlocksSection = section.getConfigurationSection("doorBlocks"); + if (doorBlocksSection != null) { + List doorBlocks = doorBlocksSection.getKeys(false).stream() + .map(doorBlocksSection::getConfigurationSection) + .toList(); + + this.doorBlocks = new TowerGeneratorDoorBlock[doorBlocks.size()]; + for (int i = 0; i < doorBlocks.size(); i++) { + this.doorBlocks[i] = new TowerGeneratorDoorBlock(doorBlocks.get(i)); + } + } else { + doorBlocks = new TowerGeneratorDoorBlock[0]; + } + } + } + + @Getter + public static final class TowerGeneratorFillRegion { + private final int minX; + private final int minZ; + private final int maxX; + private final int maxZ; + private final double percentage; + private final Material material; + + public TowerGeneratorFillRegion(ConfigurationSection section) { + minX = section.getInt("minX"); + minZ = section.getInt("minZ"); + maxX = section.getInt("maxX"); + maxZ = section.getInt("maxZ"); + percentage = section.getDouble("percentage"); + material = Material.valueOf(section.getString("material")); + } + } + + @Getter + public static final class TowerGeneratorDoorBlock { + private final int x; + private final int dy; + private final int z; + + public TowerGeneratorDoorBlock(ConfigurationSection section) { + x = section.getInt("x"); + dy = section.getInt("dy"); + z = section.getInt("z"); + } + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/countdowns/Countdown.java b/TowerRun/src/de/steamwar/towerrun/countdowns/Countdown.java new file mode 100644 index 00000000..727d19ea --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/countdowns/Countdown.java @@ -0,0 +1,80 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.countdowns; + +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.state.GameStateToggleListener; +import de.steamwar.towerrun.state.GameStates; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitTask; + +import java.util.EnumSet; + +public abstract class Countdown extends GameStateToggleListener { + + @Setter + @Getter + protected int time = defaultTime(); + protected BukkitTask task; + + protected Countdown(EnumSet enabledStates) { + super(enabledStates); + } + + int defaultTime() { + return 0; + } + void timerEnd() {} + boolean timerShouldCancel() { + return true; + } + void run() {} + void timerStart() {} + void timerReset() {} + + private void timer() { + if (timerShouldCancel()) { + time = defaultTime(); + timerReset(); + } else { + if (time == defaultTime()) { + timerStart(); + } + if (time == 0) { + timerEnd(); + return; + } + run(); + time--; + } + } + + @Override + public void enable() { + task = Bukkit.getScheduler().runTaskTimer(TowerRun.getInstance(), this::timer, 0, 20); + } + + @Override + public void disable() { + task.cancel(); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/countdowns/EndCountdown.java b/TowerRun/src/de/steamwar/towerrun/countdowns/EndCountdown.java new file mode 100644 index 00000000..95aa3675 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/countdowns/EndCountdown.java @@ -0,0 +1,67 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.countdowns; + +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.game.TowerRunGame; +import de.steamwar.towerrun.state.GameStates; +import org.bukkit.Bukkit; +import org.bukkit.Sound; + +import java.util.EnumSet; +import java.util.Objects; + +public class EndCountdown extends Countdown { + + private final LobbyCountdown lobbyCountdown; + + private static final boolean RESETS = Objects.requireNonNull(Bukkit.getWorlds().get(0).getWorldFolder().list((dir, name) -> name.equals("backup"))).length > 0; + + public EndCountdown(LobbyCountdown lobbyCountdown) { + super(EnumSet.of(GameStates.ENDING)); + this.lobbyCountdown = lobbyCountdown; + } + + @Override + int defaultTime() { + return 10; + } + + @Override + void timerEnd() { + Bukkit.getOnlinePlayers().forEach(player -> player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1, 1)); + if (RESETS) { + TowerRunGame.reset(); + lobbyCountdown.setTime(lobbyCountdown.defaultTime()); + } else { + Bukkit.shutdown(); + } + } + + @Override + boolean timerShouldCancel() { + return false; + } + + @Override + void run() { + TowerRun.getMessage().broadcastActionbar(RESETS ? "SERVER_RESET" : "SERVER_STOPPING", time); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/countdowns/GameCountdown.java b/TowerRun/src/de/steamwar/towerrun/countdowns/GameCountdown.java new file mode 100644 index 00000000..b56c1f3b --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/countdowns/GameCountdown.java @@ -0,0 +1,67 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.countdowns; + +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.config.Config; +import de.steamwar.towerrun.game.TowerRunGame; +import de.steamwar.towerrun.state.GameStates; + +import java.util.EnumSet; + +public class GameCountdown extends Countdown { + + public GameCountdown() { + super(EnumSet.of(GameStates.INGAME)); + } + + @Override + int defaultTime() { + return Config.GAME_TIMER; + } + + @Override + void timerEnd() { + if (TowerRunGame.PLAYERS_ESCAPED.isEmpty()) { + TowerRunGame.tie(); + } else { + TowerRunGame.win(TowerRunGame.PLAYERS_ESCAPED.get(TowerRunGame.PLAYERS_ESCAPED.size() - 1)); + } + } + + @Override + boolean timerShouldCancel() { + return false; + } + + @Override + void run() { + int timeMinutes = Math.floorDiv(time, 60); + int timeSeconds = time % 60; + + TowerRun.getMessage().broadcastActionbar("GAME_TIME", String.format("%02d", timeMinutes), String.format("%02d", timeSeconds)); + } + + @Override + public void disable() { + super.disable(); + setTime(defaultTime()); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/countdowns/LobbyCountdown.java b/TowerRun/src/de/steamwar/towerrun/countdowns/LobbyCountdown.java new file mode 100644 index 00000000..40808f1e --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/countdowns/LobbyCountdown.java @@ -0,0 +1,71 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.countdowns; + +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.config.Config; +import de.steamwar.towerrun.game.TowerRunGame; +import de.steamwar.towerrun.state.GameStates; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.Bukkit; +import org.bukkit.Sound; + +import java.util.EnumSet; + +@Getter +public class LobbyCountdown extends Countdown { + + @Setter + private boolean override = false; + + public LobbyCountdown() { + super(EnumSet.of(GameStates.LOBBY)); + } + + @Override + int defaultTime() { + return Config.LOBBY_TIMER; + } + + @Override + void timerEnd() { + TowerRunGame.prepareTowerOrStart(); + override = false; + } + + @Override + boolean timerShouldCancel() { + return !override && Bukkit.getOnlinePlayers().size() < Config.MIN_PLAYERS; + } + + @Override + void run() { + TowerRun.getMessage().broadcastActionbar("GAME_STARTING", time); + if (time < 10) { + Bukkit.getOnlinePlayers().forEach(player -> player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1, 1)); + } + } + + @Override + void timerReset() { + TowerRun.getMessage().broadcastActionbar("GAME_WAITING"); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/game/TowerRunGame.java b/TowerRun/src/de/steamwar/towerrun/game/TowerRunGame.java new file mode 100644 index 00000000..2d71ef1d --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/game/TowerRunGame.java @@ -0,0 +1,184 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.game; + +import de.steamwar.core.CraftbukkitWrapper; +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.config.WorldConfig; +import de.steamwar.towerrun.state.GameState; +import de.steamwar.towerrun.state.GameStates; +import lombok.experimental.UtilityClass; +import net.minecraft.world.level.chunk.Chunk; +import org.bukkit.*; +import org.bukkit.craftbukkit.v1_19_R2.CraftWorld; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.ObjIntConsumer; + +@UtilityClass +public class TowerRunGame { + public static final List PLAYERS_ALIVE = new ArrayList<>(); + public static final List PLAYERS_ESCAPED = new ArrayList<>(); + private static final World world = Bukkit.getWorlds().get(0); + + public static boolean isAlive(TowerRunPlayer player) { + return PLAYERS_ALIVE.contains(player); + } + + public static void prepareTowerOrStart() { + if (GameState.getCurrentState() == GameStates.LOBBY) { + GameState.nextState(); + if (TowerRun.getTowerGenerator() == null) { + start(); + return; + } + + TowerRun.getTowerGenerator().generate(); + } else { + throw new IllegalStateException("Game is already running!"); + } + } + + public static void start() { + if (GameState.getCurrentState() == GameStates.GENERATING_TOWER) { + PLAYERS_ALIVE.addAll(TowerRunPlayer.getAll()); + PLAYERS_ALIVE.forEach(p -> { + p.reset(); + p.player().setGameMode(GameMode.SURVIVAL); + }); + GameState.nextState(); + generateLava(); + TowerRun.getMessage().broadcast("GAME_START"); + + for (Location door : WorldConfig.DOORS) { + door.getBlock().setType(Material.AIR); + door.clone().add(0, 1, 0).getBlock().setType(Material.AIR); + } + } else { + throw new IllegalStateException("Game is already running!"); + } + } + + private static void generateLava() { + for (int x = WorldConfig.MIN_TOWER.getBlockX(); x < WorldConfig.MAX_TOWER.getBlockX(); x += WorldConfig.LAVE_SPACE) { + for (int z = WorldConfig.MIN_TOWER.getBlockZ(); z < WorldConfig.MAX_TOWER.getBlockZ(); z += WorldConfig.LAVE_SPACE) { + Vector pos = new Vector(x, 0, z); + if (Arrays.stream(WorldConfig.REGIONS).anyMatch(region -> region.contains(pos))) { + int offset = WorldConfig.LAVA_Y; + if (TowerRun.getTowerGenerator() != null) { + offset += TowerRun.getTowerGenerator().getHeight(); + } + WorldConfig.MIN_TOWER.getWorld().getBlockAt(x, offset, z).setType(Material.LAVA, true); + WorldConfig.MIN_TOWER.getWorld().getBlockAt(x, offset - 1, z).setType(Material.BEDROCK, true); + } + } + } + } + + public static void remove(TowerRunPlayer towerRunPlayer) { + PLAYERS_ALIVE.remove(towerRunPlayer); + PLAYERS_ESCAPED.remove(towerRunPlayer); + } + + public static void tie() { + Bukkit.getOnlinePlayers().forEach(player -> player.setGameMode(GameMode.SPECTATOR)); + PLAYERS_ALIVE.clear(); + Bukkit.getOnlinePlayers().forEach(player -> player.sendTitle(TowerRun.getMessage().parse("GAME_TIE", player), "", 10, 70, 20)); + GameState.nextState(); + } + + public static void win(TowerRunPlayer tPlayer) { + Bukkit.getOnlinePlayers().forEach(player -> player.setGameMode(GameMode.SPECTATOR)); + PLAYERS_ALIVE.clear(); + tPlayer.player().setGameMode(GameMode.SPECTATOR); + TowerRun.getMessage().broadcast("GAME_WIN", tPlayer.player().getName()); + Bukkit.getOnlinePlayers().forEach(player -> { + player.sendTitle(TowerRun.getMessage().parse("GAME_WIN", player, tPlayer.player().getName()), "", 10, 70, 20); + player.playSound(player.getLocation(), Sound.ENTITY_ENDER_DRAGON_DEATH, 1, 1); + }); + GameState.nextState(); + } + + public static void reset() { + PLAYERS_ALIVE.clear(); + PLAYERS_ESCAPED.clear(); + resetWorld(); + GameState.reset(); + Bukkit.getOnlinePlayers().forEach(player -> { + if (TowerRun.getTowerGenerator() != null) { + player.setGameMode(GameMode.SPECTATOR); + } else { + player.setGameMode(GameMode.SURVIVAL); + } + player.teleport(WorldConfig.SPAWN); + }); + } + + private static double posToChunk(int pos) { + return pos / 16.0; + } + + private static int getMinChunkX() { + return (int) Math.floor(posToChunk(WorldConfig.MAP_MIN_X)); + } + + private static int getMaxChunkX() { + return (int) Math.ceil(posToChunk(WorldConfig.MAP_MAX_X)); + } + + private static int getMinChunkZ() { + return (int) Math.floor(posToChunk(WorldConfig.MAP_MIN_Z)); + } + + private static int getMaxChunkZ() { + return (int) Math.ceil(posToChunk(WorldConfig.MAP_MAX_Z)); + } + + private static void forEachChunk(ObjIntConsumer executor) { + for (int x = getMinChunkX(); x <= getMaxChunkX(); x++) + for (int z = getMinChunkZ(); z <= getMaxChunkZ(); z++) + executor.accept(x, z); + } + + private static void resetWorld() { + world.getEntities().stream().filter(entity -> entity.getType() != EntityType.PLAYER).forEach(Entity::remove); + + World backup = new WorldCreator(world.getName() + "/backup").createWorld(); + assert backup != null; + forEachChunk((x, z) -> resetChunk(backup, x, z)); + Bukkit.unloadWorld(backup, false); + } + + private static void resetChunk(World backup, int x, int z) { + Chunk chunk = ((CraftWorld) world).getHandle().d(x, z); + Chunk backupChunk = ((CraftWorld) backup).getHandle().d(x, z); + + System.arraycopy(backupChunk.d(), 0, chunk.d(), 0, chunk.d().length); + + for (Player p : Bukkit.getOnlinePlayers()) + CraftbukkitWrapper.impl.sendChunk(p, x, z); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/game/TowerRunPlayer.java b/TowerRun/src/de/steamwar/towerrun/game/TowerRunPlayer.java new file mode 100644 index 00000000..e7b12973 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/game/TowerRunPlayer.java @@ -0,0 +1,64 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.game; + +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.config.WorldConfig; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public record TowerRunPlayer(Player player) { + private static final Map players = new HashMap<>(); + + public static TowerRunPlayer get(Player player) { + return players.computeIfAbsent(player, TowerRunPlayer::new); + } + + public static void remove(Player player) { + players.remove(player); + } + + public static Collection getAll() { + return players.values(); + } + + public void reset() { + player.getInventory().clear(); + player.updateInventory(); + player.setHealth(20); + if (TowerRun.getTowerGenerator() != null) { + player.teleport(TowerRun.getTowerGenerator().getSpawn()); + } else { + player.teleport(WorldConfig.SPAWN); + } + player.setVelocity(new Vector(0, 0, 0)); + } + + @Override + public String toString() { + return "TowerRunPlayer{" + + "player=" + player.getName() + + '}'; + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/generator/TowerGenerator.java b/TowerRun/src/de/steamwar/towerrun/generator/TowerGenerator.java new file mode 100644 index 00000000..4f379547 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/generator/TowerGenerator.java @@ -0,0 +1,207 @@ +package de.steamwar.towerrun.generator; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.session.ClipboardHolder; +import de.steamwar.sql.SchematicData; +import de.steamwar.sql.SchematicNode; +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.config.WorldConfig; +import de.steamwar.towerrun.game.TowerRunGame; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.*; +import org.bukkit.block.data.Bisected; +import org.bukkit.block.data.type.Door; +import org.bukkit.scheduler.BukkitRunnable; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Random; + +public class TowerGenerator { + + private static final Random random = new Random(); + private final WorldConfig.TowerGeneratorConfig config; + private final List allSchematics; + private final Clipboard roof; + + @Getter + private int height; + + @Getter + private Location spawn; + + @Getter + private List keys = new ArrayList<>(); + + public TowerGenerator(WorldConfig.TowerGeneratorConfig config) { + File file = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "Roof.schem"); + if (!file.exists()) { + Bukkit.shutdown(); + throw new SecurityException("TowerRun schematic not found"); + } + roof = loadSchematic(file); + + this.config = config; + allSchematics = SchematicNode.getAllSchematicsOfType(config.schematicType); + spawn = WorldConfig.SPAWN; + } + + private Clipboard loadSchematic(File file) { + Clipboard clipboard; + try (ClipboardReader reader = Objects.requireNonNull(ClipboardFormats.findByFile(file)).getReader(new FileInputStream(file))) { + clipboard = reader.read(); + } catch (NullPointerException | IOException e) { + Bukkit.shutdown(); + throw new SecurityException("TowerRun schematic not found", e); + } + return clipboard; + } + + public void generate() { + new BukkitRunnable() { + int height = random.nextInt(config.maxHeight - config.minHeight) + config.minHeight; + int y = TowerGenerator.this.config.y; + int noBombFloors; + int noKeyFloors; + + { + TowerGenerator.this.height = 0; + noBombFloors = random.nextInt(config.maxNoBombFloors - config.minNoBombFloors) + config.minNoBombFloors; + noKeyFloors = random.nextInt(config.maxNoKeyFloors - config.minNoKeyFloors) + config.minNoKeyFloors; + keys.clear(); + } + + @Override + public void run() { + if (height > 0) { + SchematicNode schematicNode = allSchematics.get(random.nextInt(allSchematics.size())); + SchematicData schematicData = new SchematicData(schematicNode); + int currentY; + int width; + int depth; + try { + Clipboard clipboard = schematicData.load(); + try (EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(Bukkit.getWorlds().get(0)), -1)) { + ClipboardHolder ch = new ClipboardHolder(clipboard); + Operations.completeBlindly(ch.createPaste(e).to(BlockVector3.at(config.x, y, config.z)).build()); + } + width = clipboard.getDimensions().getX(); + depth = clipboard.getDimensions().getZ(); + currentY = y; + y += clipboard.getDimensions().getY(); + height -= clipboard.getDimensions().getY(); + TowerGenerator.this.height += clipboard.getDimensions().getY(); + } catch (IOException e) { + allSchematics.remove(schematicNode); + return; + } + spawn = WorldConfig.SPAWN.clone().add(0, y, 0); + + List chestBlocks = new ArrayList<>(); + for (int x = config.x; x < config.x + width; x++) { + for (int z = config.z; z < config.z + depth; z++) { + for (int y = currentY; y < this.y; y++) { + Block block = Bukkit.getWorlds().get(0).getBlockAt(x, y, z); + BlockState blockState = block.getState(); + if (blockState instanceof Chest) { + chestBlocks.add((Container) blockState); + } + } + } + } + + noBombFloors--; + if (noBombFloors < 0 && random.nextDouble() < config.tntChance) { + noBombFloors = random.nextInt(config.maxNoBombFloors - config.minNoBombFloors) + config.minNoBombFloors; + int bombCount = random.nextInt(config.maxBombs - config.minBombs) + config.minBombs; + for (int i = 0; i < bombCount; i++) { + WorldConfig.Region region = config.tntRegions[random.nextInt(config.tntRegions.length)]; + int x = random.nextInt(region.max.getBlockX() - region.min.getBlockX()) + region.min.getBlockX(); + int z = random.nextInt(region.max.getBlockZ() - region.min.getBlockZ()) + region.min.getBlockZ(); + int y = currentY + 1; + Bukkit.getWorlds().get(0).getBlockAt(x, y, z).setType(Material.TNT, true); + } + } + + noKeyFloors--; + if (!chestBlocks.isEmpty() && noKeyFloors < 0 && random.nextDouble() < config.keyChance) { + noKeyFloors = random.nextInt(config.maxNoKeyFloors - config.minNoKeyFloors) + config.minNoKeyFloors; + Container container = chestBlocks.get(random.nextInt(chestBlocks.size())); + keys.add(container.getLocation()); + + for (WorldConfig.TowerGeneratorDoorBlock doorBlock : config.doorBlocks) { + int x = doorBlock.getX(); + int y = currentY + doorBlock.getDy(); + int z = doorBlock.getZ(); + Block block = Bukkit.getWorlds().get(0).getBlockAt(x, y, z); + for (int i = 0; i < 5; i++) { + if (block.getType().isAir()) { + block.setType(Material.IRON_BLOCK, false); + block = block.getRelative(0, 1, 0); + } else { + break; + } + } + + Bukkit.getScheduler().runTaskLater(TowerRun.getInstance(), () -> { + Block door = Bukkit.getWorlds().get(0).getBlockAt(x, y, z); + door.setType(Material.IRON_DOOR, false); + door = door.getRelative(0, 1, 0); + door.setType(Material.IRON_DOOR, false); + }, 10); + Bukkit.getScheduler().runTaskLater(TowerRun.getInstance(), () -> { + Block door = Bukkit.getWorlds().get(0).getBlockAt(x, y, z); + Door doorState = (Door) door.getBlockData(); + doorState.setFacing(BlockFace.EAST); + door.setBlockData(doorState, false); + + door = door.getRelative(0, 1, 0); + doorState = (Door) door.getBlockData(); + doorState.setFacing(BlockFace.EAST); + doorState.setHalf(Bisected.Half.TOP); + door.setBlockData(doorState, false); + }, 20); + } + } + + for (WorldConfig.TowerGeneratorFillRegion fillRegion : config.fillRegions) { + for (int x = fillRegion.getMinX(); x < fillRegion.getMaxX(); x++) { + for (int z = fillRegion.getMinZ(); z < fillRegion.getMaxZ(); z++) { + for (int y = currentY; y < this.y; y++) { + Block block = Bukkit.getWorlds().get(0).getBlockAt(x, y, z); + if (!block.getType().isAir()) { + continue; + } + if (random.nextDouble() < fillRegion.getPercentage()) { + block.setType(fillRegion.getMaterial(), true); + } + } + } + } + } + } else { + cancel(); + try (EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(Bukkit.getWorlds().get(0)), -1)) { + ClipboardHolder ch = new ClipboardHolder(roof); + Operations.completeBlindly(ch.createPaste(e).to(BlockVector3.at(config.x, y, config.z)).build()); + } + TowerRunGame.start(); + } + } + }.runTaskTimer(TowerRun.getInstance(), 0, 10); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/listener/GlobalListener.java b/TowerRun/src/de/steamwar/towerrun/listener/GlobalListener.java new file mode 100644 index 00000000..353adc20 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/listener/GlobalListener.java @@ -0,0 +1,67 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.listener; + +import de.steamwar.towerrun.config.WorldConfig; +import de.steamwar.towerrun.game.TowerRunGame; +import de.steamwar.towerrun.game.TowerRunPlayer; +import de.steamwar.towerrun.state.GameStateBukkitListener; +import de.steamwar.towerrun.state.GameStates; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.EnumSet; + +public class GlobalListener extends GameStateBukkitListener { + public GlobalListener() { + super(EnumSet.allOf(GameStates.class)); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + TowerRunPlayer.get(event.getPlayer()); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + if (TowerRunGame.isAlive(TowerRunPlayer.get(event.getPlayer()))) { + TowerRunGame.remove(TowerRunPlayer.get(event.getPlayer())); + } + TowerRunPlayer.remove(event.getPlayer()); + + if(Bukkit.getOnlinePlayers().isEmpty() || (Bukkit.getOnlinePlayers().size() == 1 && Bukkit.getOnlinePlayers().stream().allMatch(player -> player == event.getPlayer()))) { + Bukkit.shutdown(); + } + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Location to = event.getTo(); + if (to.getX() > WorldConfig.MAP_MIN_X && to.getX() < WorldConfig.MAP_MAX_X && to.getZ() > WorldConfig.MAP_MIN_Z && to.getZ() < WorldConfig.MAP_MAX_Z && to.getY() > WorldConfig.ESCAPE_HEIGHT - 20) { + return; + } + + event.setCancelled(true); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/listener/IngameListener.java b/TowerRun/src/de/steamwar/towerrun/listener/IngameListener.java new file mode 100644 index 00000000..ea07eb04 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/listener/IngameListener.java @@ -0,0 +1,215 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.listener; + +import de.steamwar.inventory.SWItem; +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.config.Config; +import de.steamwar.towerrun.config.WorldConfig; +import de.steamwar.towerrun.game.TowerRunGame; +import de.steamwar.towerrun.state.GameStateBukkitListener; +import de.steamwar.towerrun.state.GameStates; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.entity.EntityType; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.ItemSpawnEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Stream; + +public class IngameListener extends GameStateBukkitListener { + + private int time = 0; + private final Map> blocksToMelt = new HashMap<>(); + private BukkitRunnable blocksToMeltRunnable; + private BukkitRunnable antiCampRunnable; + + public IngameListener() { + super(EnumSet.of(GameStates.INGAME)); + } + + @Override + public void enable() { + super.enable(); + blocksToMeltRunnable = new BukkitRunnable() { + @Override + public void run() { + List blocks = blocksToMelt.get(time); + time++; + if (blocks == null) { + return; + } + blocks.forEach(block -> { + if (!WorldConfig.MELTING_TIMES.containsKey(block.getType())) return; + block.setType(Material.AIR); + block.getWorld().playSound(block.getLocation(), Sound.BLOCK_FIRE_EXTINGUISH, 0.1F, 1); + }); + } + }; + blocksToMeltRunnable.runTaskTimer(TowerRun.getInstance(), 0, 1); + + antiCampRunnable = new BukkitRunnable() { + @Override + public void run() { + double minY = TowerRunGame.PLAYERS_ALIVE.stream() + .map(p -> p.player().getLocation().getY()) + .min(Comparator.comparing(Function.identity())) + .orElse(0.0); + + TowerRunGame.PLAYERS_ALIVE.forEach(towerRunPlayer -> { + if (towerRunPlayer.player().getLocation().getY() - minY > 20) { + towerRunPlayer.player().sendTitle("§a", TowerRun.getMessage().parse("CATCH_UP_WARNING", towerRunPlayer.player()), 5, 30, 5); + } + if (towerRunPlayer.player().getLocation().getY() - minY > 30) { + towerRunPlayer.player().damage(1.0); + } + }); + } + }; + antiCampRunnable.runTaskTimer(TowerRun.getInstance(), 100, 100); + } + + @Override + public void disable() { + super.disable(); + blocksToMeltRunnable.cancel(); + blocksToMeltRunnable = null; + blocksToMelt.clear(); + time = 0; + + antiCampRunnable.cancel(); + antiCampRunnable = null; + } + + @EventHandler + public void onPlayerDeath(PlayerDeathEvent event) { + event.setDeathMessage(null); + Bukkit.getScheduler().runTaskLater(TowerRun.getInstance(), () -> { + if (TowerRun.getTowerGenerator() != null) { + event.getEntity().teleport(TowerRun.getTowerGenerator().getSpawn()); + } else { + event.getEntity().teleport(WorldConfig.SPAWN); + } + }, 5 + ); + event.getEntity().setGameMode(GameMode.SPECTATOR); + Bukkit.getOnlinePlayers().forEach(player -> { + player.sendTitle("", TowerRun.getMessage().parse("PLAYER_DIED", player, event.getEntity().getPlayer().getName()), 10, 70, 20); + player.playSound(player.getLocation(), Sound.ENTITY_WITHER_DEATH, 1, 1); + }); + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + event.setDropItems(false); + if (!Config.DESTROYABLE_BLOCKS.contains(event.getBlock().getType())) { + event.setCancelled(true); + } + } + + @EventHandler + public void onKeyUse(PlayerInteractEvent event) { + if (!event.hasItem()) return; + if (event.getItem().getType() != Material.LEVER) return; + event.setCancelled(true); + if (!event.hasBlock()) return; + if (event.getClickedBlock().getType() != Material.IRON_DOOR) return; + event.getPlayer().getInventory().setItemInMainHand(null); + event.getClickedBlock().breakNaturally(); + } + + @EventHandler + public void onItemSpawn(ItemSpawnEvent event) { + event.setCancelled(true); + } + + @EventHandler + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (event.getDamager().getType() == EntityType.PLAYER) { + event.setCancelled(true); + } + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.getPlayer().getGameMode() != GameMode.SURVIVAL) { + return; + } + + if (event.getAction() != Action.RIGHT_CLICK_BLOCK || !event.hasBlock()) { + return; + } + + if (event.getClickedBlock().getType() != Material.CHEST) { + return; + } + + Stream locations = TowerRun.getTowerGenerator() == null ? Arrays.stream(WorldConfig.KEYS) : TowerRun.getTowerGenerator().getKeys().stream(); + if (locations.noneMatch(location -> location.equals(event.getClickedBlock().getLocation()))) { + event.getClickedBlock().setType(Material.AIR); + return; + } + + event.getPlayer().getInventory().addItem(new SWItem(Material.LEVER, TowerRun.getMessage().parse("KEY_NAME", event.getPlayer())).getItemStack()); + + event.getClickedBlock().setType(Material.ENDER_CHEST); + event.getPlayer().playSound(event.getPlayer().getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1, 1); + TowerRun.getMessage().broadcast("KEY_FOUND", event.getPlayer().getName()); + } + + @EventHandler + public void onBlockPhysics(BlockPhysicsEvent event) { + if (event.getSourceBlock().getType() != Material.LAVA) { + return; + } + Block block = event.getSourceBlock(); + shouldMelt(block.getRelative(0, 1, 0)); + shouldMelt(block.getRelative(0, -1, 0)); + shouldMelt(block.getRelative(1, 0, 0)); + shouldMelt(block.getRelative(-1, 0, 0)); + shouldMelt(block.getRelative(0, 0, 1)); + shouldMelt(block.getRelative(0, 0, -1)); + } + + private void shouldMelt(Block block) { + int meltingTime = WorldConfig.MELTING_TIMES.getOrDefault(block.getType(), -1); + if (meltingTime == -1) { + return; + } + blocksToMelt.computeIfAbsent(time + meltingTime * 20, integer -> new ArrayList<>()).add(block); + } + + @EventHandler + public void onEntityRegainHealth(EntityRegainHealthEvent event) { + if (event.getEntityType() == EntityType.PLAYER) { + event.setCancelled(true); + } + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/listener/LobbyListener.java b/TowerRun/src/de/steamwar/towerrun/listener/LobbyListener.java new file mode 100644 index 00000000..eb2cb157 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/listener/LobbyListener.java @@ -0,0 +1,81 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.listener; + +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.config.WorldConfig; +import de.steamwar.towerrun.state.GameStateBukkitListener; +import de.steamwar.towerrun.state.GameStates; +import org.bukkit.GameMode; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; + +import java.util.EnumSet; + +public class LobbyListener extends GameStateBukkitListener { + public LobbyListener() { + super(EnumSet.of(GameStates.LOBBY)); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + if (TowerRun.getTowerGenerator() != null) { + player.teleport(TowerRun.getTowerGenerator().getSpawn()); + player.setGameMode(GameMode.SPECTATOR); + } else { + player.teleport(WorldConfig.SPAWN); + player.setGameMode(GameMode.SURVIVAL); + } + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + if (TowerRun.getTowerGenerator() != null) { + return; + } + if (event.getTo().getY() < WorldConfig.SPAWN.getY() - 10) { + event.getPlayer().teleport(WorldConfig.SPAWN); + } + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + event.setCancelled(true); + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + event.setCancelled(true); + } + + @EventHandler + public void onEntityDamage(EntityDamageEvent event) { + if (event.getEntityType() == EntityType.PLAYER) { + event.setCancelled(true); + } + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/listener/NotLobbyListener.java b/TowerRun/src/de/steamwar/towerrun/listener/NotLobbyListener.java new file mode 100644 index 00000000..4d3525e5 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/listener/NotLobbyListener.java @@ -0,0 +1,46 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.listener; + +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.config.WorldConfig; +import de.steamwar.towerrun.state.GameStateBukkitListener; +import de.steamwar.towerrun.state.GameStates; +import org.bukkit.GameMode; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; + +import java.util.EnumSet; + +public class NotLobbyListener extends GameStateBukkitListener { + public NotLobbyListener() { + super(EnumSet.complementOf(EnumSet.of(GameStates.LOBBY))); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + event.getPlayer().setGameMode(GameMode.SPECTATOR); + if (TowerRun.getTowerGenerator() != null) { + event.getPlayer().teleport(TowerRun.getTowerGenerator().getSpawn()); + } else { + event.getPlayer().teleport(WorldConfig.SPAWN); + } + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/state/GameState.java b/TowerRun/src/de/steamwar/towerrun/state/GameState.java new file mode 100644 index 00000000..3c3a55e0 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/state/GameState.java @@ -0,0 +1,59 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.state; + +import lombok.Getter; +import lombok.experimental.UtilityClass; + +import java.util.ArrayList; +import java.util.List; + +@UtilityClass +public class GameState { + @Getter + private static GameStates currentState = GameStates.LOBBY; + private static final List gameStateListeners = new ArrayList<>(); + + public static void addGameStateListener(GameStateListener gameStateListener) { + gameStateListeners.add(gameStateListener); + } + + public static void nextState() { + final GameStates oldState = currentState; + currentState = currentState.getNextState(); + gameStateChanges(oldState, currentState); + } + + static void setState(final GameStates newState) { + final GameStates oldState = currentState; + currentState = newState; + gameStateChanges(oldState, currentState); + } + + public static void reset() { + final GameStates oldState = currentState; + currentState = GameStates.LOBBY; + gameStateChanges(oldState, currentState); + } + + private static void gameStateChanges(GameStates oldState, GameStates newState) { + gameStateListeners.forEach(gameStateListener -> gameStateListener.onGameStateChange(oldState, newState)); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/state/GameStateBukkitListener.java b/TowerRun/src/de/steamwar/towerrun/state/GameStateBukkitListener.java new file mode 100644 index 00000000..bd259f63 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/state/GameStateBukkitListener.java @@ -0,0 +1,44 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.state; + +import de.steamwar.towerrun.TowerRun; +import org.bukkit.Bukkit; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; + +import java.util.EnumSet; + +public abstract class GameStateBukkitListener extends GameStateToggleListener implements Listener { + + protected GameStateBukkitListener(EnumSet enabledStates) { + super(enabledStates); + } + + @Override + public void enable() { + Bukkit.getPluginManager().registerEvents(this, TowerRun.getInstance()); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/state/GameStateListener.java b/TowerRun/src/de/steamwar/towerrun/state/GameStateListener.java new file mode 100644 index 00000000..d4dcd639 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/state/GameStateListener.java @@ -0,0 +1,29 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.state; + +public abstract class GameStateListener { + + protected GameStateListener() { + GameState.addGameStateListener(this); + } + + public abstract void onGameStateChange(GameStates oldState, GameStates newState); +} diff --git a/TowerRun/src/de/steamwar/towerrun/state/GameStateToggleListener.java b/TowerRun/src/de/steamwar/towerrun/state/GameStateToggleListener.java new file mode 100644 index 00000000..c8ba5534 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/state/GameStateToggleListener.java @@ -0,0 +1,48 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.state; + +import java.util.EnumSet; + +public abstract class GameStateToggleListener extends GameStateListener { + + private final EnumSet enabledStates; + + protected GameStateToggleListener(EnumSet enabledStates) { + super(); + this.enabledStates = enabledStates; + if (enabledStates.contains(GameState.getCurrentState())) { + enable(); + } + } + + public abstract void enable(); + + public abstract void disable(); + + @Override + public void onGameStateChange(GameStates oldState, GameStates newState) { + if (enabledStates.contains(newState) && !enabledStates.contains(oldState)) { + enable(); + } else if (!enabledStates.contains(newState) && enabledStates.contains(oldState)) { + disable(); + } + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/state/GameStates.java b/TowerRun/src/de/steamwar/towerrun/state/GameStates.java new file mode 100644 index 00000000..347e9814 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/state/GameStates.java @@ -0,0 +1,34 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.state; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum GameStates { + ENDING(null), + INGAME(ENDING), + GENERATING_TOWER(INGAME), + LOBBY(GENERATING_TOWER); + + private final GameStates nextState; +} diff --git a/TowerRun/src/de/steamwar/towerrun/winconditions/FirstOutsideWincondition.java b/TowerRun/src/de/steamwar/towerrun/winconditions/FirstOutsideWincondition.java new file mode 100644 index 00000000..f2d753fa --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/winconditions/FirstOutsideWincondition.java @@ -0,0 +1,35 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.winconditions; + +import de.steamwar.towerrun.game.TowerRunGame; +import de.steamwar.towerrun.game.TowerRunPlayer; +import org.bukkit.entity.Player; + +public class FirstOutsideWincondition extends OutsideWincondition { + public FirstOutsideWincondition() { + super("FIRST_OUTSIDE"); + } + + @Override + public void onPlayerOutside(Player player) { + TowerRunGame.win(TowerRunPlayer.get(player)); + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/winconditions/LastOutsideWincondition.java b/TowerRun/src/de/steamwar/towerrun/winconditions/LastOutsideWincondition.java new file mode 100644 index 00000000..260ed0fb --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/winconditions/LastOutsideWincondition.java @@ -0,0 +1,53 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.winconditions; + +import de.steamwar.towerrun.game.TowerRunGame; +import de.steamwar.towerrun.game.TowerRunPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.PlayerDeathEvent; + +public class LastOutsideWincondition extends OutsideWincondition { + public LastOutsideWincondition() { + super("LAST_OUTSIDE"); + } + + @Override + public void onPlayerOutside(Player player) { + TowerRunPlayer towerRunPlayer = TowerRunPlayer.get(player); + outside(towerRunPlayer); + if (TowerRunGame.PLAYERS_ALIVE.isEmpty()) { + TowerRunGame.win(towerRunPlayer); + } + } + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerDeath(PlayerDeathEvent event) { + if (TowerRunGame.PLAYERS_ALIVE.isEmpty()) { + if (TowerRunGame.PLAYERS_ESCAPED.isEmpty()) { + TowerRunGame.tie(); + return; + } + TowerRunGame.win(TowerRunGame.PLAYERS_ESCAPED.get(TowerRunGame.PLAYERS_ESCAPED.size() - 1)); + } + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/winconditions/LastRemainingWincondition.java b/TowerRun/src/de/steamwar/towerrun/winconditions/LastRemainingWincondition.java new file mode 100644 index 00000000..c66818d1 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/winconditions/LastRemainingWincondition.java @@ -0,0 +1,58 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.winconditions; + +import de.steamwar.towerrun.game.TowerRunGame; +import de.steamwar.towerrun.game.TowerRunPlayer; +import org.bukkit.GameMode; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class LastRemainingWincondition extends WinCondition { + + public LastRemainingWincondition() { + super("LAST_REMAINING"); + } + + @EventHandler + public void onPlayerDeath(PlayerDeathEvent event) { + TowerRunPlayer tPlayer = TowerRunPlayer.get(event.getEntity()); + TowerRunGame.PLAYERS_ALIVE.remove(tPlayer); + TowerRunGame.PLAYERS_ESCAPED.remove(tPlayer); + tPlayer.player().setGameMode(GameMode.SPECTATOR); + if (TowerRunGame.PLAYERS_ALIVE.size() == 1 && TowerRunGame.PLAYERS_ESCAPED.isEmpty()) { + TowerRunGame.win(TowerRunGame.PLAYERS_ALIVE.get(0)); + } + } + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerQuit(PlayerQuitEvent event) { + TowerRunPlayer tPlayer = TowerRunPlayer.get(event.getPlayer()); + if (TowerRunGame.isAlive(tPlayer)) { + TowerRunGame.PLAYERS_ALIVE.remove(tPlayer); + TowerRunGame.PLAYERS_ESCAPED.remove(tPlayer); + if (TowerRunGame.PLAYERS_ALIVE.size() == 1 && TowerRunGame.PLAYERS_ESCAPED.isEmpty()) { + TowerRunGame.win(TowerRunGame.PLAYERS_ALIVE.get(0)); + } + } + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/winconditions/OutsideWincondition.java b/TowerRun/src/de/steamwar/towerrun/winconditions/OutsideWincondition.java new file mode 100644 index 00000000..9419e7ee --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/winconditions/OutsideWincondition.java @@ -0,0 +1,73 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.winconditions; + +import de.steamwar.towerrun.TowerRun; +import de.steamwar.towerrun.config.Config; +import de.steamwar.towerrun.config.WorldConfig; +import de.steamwar.towerrun.game.TowerRunGame; +import de.steamwar.towerrun.game.TowerRunPlayer; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerMoveEvent; + +import java.util.Arrays; + +public abstract class OutsideWincondition extends WinCondition { + + protected OutsideWincondition(String name) { + super(name); + } + + public abstract void onPlayerOutside(Player player); + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + if (event.getTo().getY() > WorldConfig.ESCAPE_HEIGHT) { + return; + } + + TowerRunPlayer tPlayer = TowerRunPlayer.get(event.getPlayer()); + if (!TowerRunGame.isAlive(tPlayer)) { + return; + } + + if (Arrays.stream(WorldConfig.REGIONS).anyMatch(region -> region.contains(event.getTo().toVector()))) { + return; + } + + if (event.getTo().getY() - event.getFrom().getY() != 0) { + return; + } + + onPlayerOutside(event.getPlayer()); + } + + protected void outside(TowerRunPlayer towerRunPlayer) { + TowerRunGame.PLAYERS_ESCAPED.add(towerRunPlayer); + TowerRunGame.PLAYERS_ALIVE.remove(towerRunPlayer); + towerRunPlayer.player().setGameMode(GameMode.SPECTATOR); + TowerRun.getMessage().broadcast("PLAYER_ESCAPE", towerRunPlayer.player().getName()); + if (TowerRunGame.PLAYERS_ESCAPED.size() == 1) { + TowerRun.getGameCountdown().setTime(Config.GAME_ESCAPE_TIMER); + } + } +} diff --git a/TowerRun/src/de/steamwar/towerrun/winconditions/WinCondition.java b/TowerRun/src/de/steamwar/towerrun/winconditions/WinCondition.java new file mode 100644 index 00000000..4a736dd5 --- /dev/null +++ b/TowerRun/src/de/steamwar/towerrun/winconditions/WinCondition.java @@ -0,0 +1,54 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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 . + */ + +package de.steamwar.towerrun.winconditions; + +import de.steamwar.towerrun.state.GameStateBukkitListener; +import de.steamwar.towerrun.state.GameStates; +import lombok.Getter; +import lombok.Setter; + +import java.util.*; + +@Getter +public abstract class WinCondition extends GameStateBukkitListener { + + private final String name; + @Setter + private boolean active = false; + + protected WinCondition(String name) { + super(EnumSet.of(GameStates.INGAME)); + this.name = name; + } + + @Override + public void enable() { + if (active) { + super.enable(); + } + } + + @Override + public void disable() { + if (active) { + super.disable(); + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 16df909a..68857a8a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,6 +27,7 @@ include("LobbySystem_2") include("MissileWars") include("SpigotCore") +include("SpigotCore:CRIUDummy") include("SpigotCore:SpigotCore_8") include("SpigotCore:SpigotCore_9") include("SpigotCore:SpigotCore_10") @@ -38,6 +39,7 @@ include("SpigotCore:SpigotCore_19") include("SpigotCore:SpigotCore_20") include("SpigotCore:SpigotCore_Main") +include("TowerRun") + include("VelocityCore") include("VelocityCore:Persistent") -include("SpigotCore:CRIUDummy")