diff --git a/configs/mciv.yml b/configs/mciv.yml index 79fc54e..20fce57 100644 --- a/configs/mciv.yml +++ b/configs/mciv.yml @@ -15,11 +15,11 @@ tower: doors: 1: x: 138 - y: 229 + y: 228 z: 858 2: x: 137 - y: 229 + y: 228 z: 858 lavaY: 255 laveSpace: 7 @@ -32,7 +32,7 @@ tower: minX: 93 maxX: 167 minZ: 813 -maxZ: 863 +maxZ: 883 winconditions: - LAST_REMAINING diff --git a/configs/steamtower.yml b/configs/steamtower.yml new file mode 100644 index 0000000..251ae08 --- /dev/null +++ b/configs/steamtower.yml @@ -0,0 +1,154 @@ +tower: + regions: + 1: + minX: 0 + maxX: 69 + minZ: 0 + maxZ: 79 + 2: + minX: 54 + maxX: 74 + minZ: 63 + maxZ: 84 + escapeHeight: 1 + spawn: + x: 66 + y: 12 + z: 71 + yaw: 180.0 + pitch: 0.0 + doors: [] + keys: [] + lavaY: 62 + laveSpace: 8 + meltingBlocks: + SPRUCE_TRAPDOOR: 10 + SMOOTH_STONE: 120 + SMOOTH_STONE_SLAB: 60 + STONE_BRICK_SLAB: 60 + ANDESITE: 120 + ANDESITE_SLAB: 60 + ANDESITE_WALL: 45 + JUKEBOX: 10 + IRON_BLOCK: 240 + IRON_DOOR: 60 + IRON_TRAPDOOR: 60 + QUARTZ_SLAB: 30 + QUARTZ_STAIRS: 30 + SMOOTH_RED_SANDSTONE_STAIRS: 30 + SMOOTH_RED_SANDSTONE_SLAB: 30 + END_STONE: 240 + REDSTONE_LAMP: 30 + GLASS: 30 + GLASS_PANE: 3 + TINTED_GLASS: 30 + WHITE_STAINED_GLASS: 30 + WHITE_STAINED_GLASS_PANE: 3 + ORANGE_STAINED_GLASS: 30 + ORANGE_STAINED_GLASS_PANE: 3 + MAGENTA_STAINED_GLASS: 30 + MAGENTA_STAINED_GLASS_PANE: 3 + LIGHT_BLUE_STAINED_GLASS: 30 + LIGHT_BLUE_STAINED_GLASS_PANE: 3 + YELLOW_STAINED_GLASS: 30 + YELLOW_STAINED_GLASS_PANE: 3 + LIME_STAINED_GLASS: 30 + LIME_STAINED_GLASS_PANE: 3 + PINK_STAINED_GLASS: 30 + PINK_STAINED_GLASS_PANE: 3 + GRAY_STAINED_GLASS: 30 + GRAY_STAINED_GLASS_PANE: 3 + LIGHT_GRAY_STAINED_GLASS: 30 + LIGHT_GRAY_STAINED_GLASS_PANE: 3 + CYAN_STAINED_GLASS: 30 + CYAN_STAINED_GLASS_PANE: 3 + PURPLE_STAINED_GLASS: 30 + PURPLE_STAINED_GLASS_PANE: 3 + BLUE_STAINED_GLASS: 30 + BLUE_STAINED_GLASS_PANE: 3 + BROWN_STAINED_GLASS: 30 + BROWN_STAINED_GLASS_PANE: 3 + GREEN_STAINED_GLASS: 30 + GREEN_STAINED_GLASS_PANE: 3 + RED_STAINED_GLASS: 30 + RED_STAINED_GLASS_PANE: 3 + BLACK_STAINED_GLASS: 30 + BLACK_STAINED_GLASS_PANE: 3 + +towerGenerator: + x: 0 + y: 12 + z: 0 + minHeight: 190 + maxHeight: 240 + schematicType: steamtower + fillRegions: + 1: + minX: 31 + minZ: 29 + maxX: 38 + maxZ: 34 + percentage: 0.25 + material: MANGROVE_ROOTS + 2: + minX: 31 + minZ: 46 + maxX: 38 + maxZ: 51 + percentage: 0.25 + material: MANGROVE_ROOTS + minNoBombFloors: 2 + maxNoBombFloors: 7 + minBombs: 20 + maxBombs: 60 + tntChance: 0.3 + tntRegions: + 1: + minX: 3 + minZ: 3 + maxX: 30 + maxZ: 77 + 2: + minX: 40 + minZ: 3 + maxX: 67 + maxZ: 77 + 3: + minX: 30 + minZ: 3 + maxX: 40 + maxZ: 28 + 4: + minX: 30 + minZ: 52 + maxX: 40 + maxZ: 77 + keyChance: 0.8 + minNoKeyFloors: 2 + maxNoKeyFloors: 7 + doorBlocks: + 1: + x: 36 + dy: 1 + z: 32 + 2: + x: 36 + dy: 1 + z: 33 + 3: + x: 33 + dy: 1 + z: 46 + 4: + x: 33 + dy: 1 + z: 47 + +minX: -28 +maxX: 98 +minZ: -27 +maxZ: 107 + +winconditions: + - LAST_REMAINING + - LAST_OUTSIDE \ No newline at end of file diff --git a/src/TowerRun.properties b/src/TowerRun.properties index a62684d..88c2ecd 100644 --- a/src/TowerRun.properties +++ b/src/TowerRun.properties @@ -31,5 +31,6 @@ KEY_NAME= 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/src/TowerRun_de.properties b/src/TowerRun_de.properties index 0af02df..2c88c3b 100644 --- a/src/TowerRun_de.properties +++ b/src/TowerRun_de.properties @@ -30,4 +30,6 @@ GAME_TIE= 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/src/de/steamwar/towerrun/TowerRun.java b/src/de/steamwar/towerrun/TowerRun.java index 3c2ec52..c0717b2 100644 --- a/src/de/steamwar/towerrun/TowerRun.java +++ b/src/de/steamwar/towerrun/TowerRun.java @@ -21,9 +21,12 @@ 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; @@ -50,6 +53,9 @@ public class TowerRun extends JavaPlugin { @Getter private static Message message; + @Getter + private static TowerGenerator towerGenerator = null; + @Getter private static GameCountdown gameCountdown; @@ -59,6 +65,10 @@ public class TowerRun extends JavaPlugin { 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(); @@ -68,5 +78,7 @@ public class TowerRun extends JavaPlugin { new EndCountdown(lobbyCountdown); new StartCommand(lobbyCountdown); gameCountdown = new GameCountdown(); + + TowerRunGame.reset(); } } diff --git a/src/de/steamwar/towerrun/commands/StartCommand.java b/src/de/steamwar/towerrun/commands/StartCommand.java index fea225c..ac93fd8 100644 --- a/src/de/steamwar/towerrun/commands/StartCommand.java +++ b/src/de/steamwar/towerrun/commands/StartCommand.java @@ -37,8 +37,10 @@ public class StartCommand extends SWCommand { @Register public void command(@Validator Player player) { - countdown.setOverride(true); - countdown.setTime(10); + if (countdown.getTime() > 10) { + countdown.setOverride(true); + countdown.setTime(10); + } TowerRun.getMessage().send("COMMAND_START", player); } diff --git a/src/de/steamwar/towerrun/config/WorldConfig.java b/src/de/steamwar/towerrun/config/WorldConfig.java index 0a242af..fafdd45 100644 --- a/src/de/steamwar/towerrun/config/WorldConfig.java +++ b/src/de/steamwar/towerrun/config/WorldConfig.java @@ -19,6 +19,7 @@ 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; @@ -28,13 +29,13 @@ 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.ArrayList; -import java.util.List; +import java.util.*; @UtilityClass public class WorldConfig { @@ -53,8 +54,11 @@ public class WorldConfig { 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()); @@ -62,7 +66,7 @@ public class WorldConfig { } private static Location parseLocation(ConfigurationSection section) { - Location loc = new Location( + Location loc = new Location( Bukkit.getWorlds().get(0), section.getDouble("x"), section.getDouble("y"), @@ -123,20 +127,30 @@ public class WorldConfig { ESCAPE_HEIGHT = tower.getInt("escapeHeight"); SPAWN = parseLocation(tower.getConfigurationSection("spawn")); - List doors = tower.getConfigurationSection("doors").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)); + 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]; } - List keys = tower.getConfigurationSection("keys").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)); + 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"); @@ -147,8 +161,26 @@ public class WorldConfig { 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 @@ -176,4 +208,116 @@ public class WorldConfig { && 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/src/de/steamwar/towerrun/countdowns/Countdown.java b/src/de/steamwar/towerrun/countdowns/Countdown.java index 538eac5..727d19e 100644 --- a/src/de/steamwar/towerrun/countdowns/Countdown.java +++ b/src/de/steamwar/towerrun/countdowns/Countdown.java @@ -22,6 +22,7 @@ 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; @@ -31,6 +32,7 @@ import java.util.EnumSet; public abstract class Countdown extends GameStateToggleListener { @Setter + @Getter protected int time = defaultTime(); protected BukkitTask task; @@ -38,9 +40,13 @@ public abstract class Countdown extends GameStateToggleListener { super(enabledStates); } - int defaultTime() {return 0;} + int defaultTime() { + return 0; + } void timerEnd() {} - boolean timerShouldCancel() {return true;} + boolean timerShouldCancel() { + return true; + } void run() {} void timerStart() {} void timerReset() {} diff --git a/src/de/steamwar/towerrun/countdowns/EndCountdown.java b/src/de/steamwar/towerrun/countdowns/EndCountdown.java index b93b9d7..95aa367 100644 --- a/src/de/steamwar/towerrun/countdowns/EndCountdown.java +++ b/src/de/steamwar/towerrun/countdowns/EndCountdown.java @@ -32,7 +32,7 @@ public class EndCountdown extends Countdown { private final LobbyCountdown lobbyCountdown; - private static boolean RESETS = Objects.requireNonNull(Bukkit.getWorlds().get(0).getWorldFolder().list((dir, name) -> name.equals("backup"))).length > 0; + 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)); diff --git a/src/de/steamwar/towerrun/countdowns/LobbyCountdown.java b/src/de/steamwar/towerrun/countdowns/LobbyCountdown.java index bd46404..40808f1 100644 --- a/src/de/steamwar/towerrun/countdowns/LobbyCountdown.java +++ b/src/de/steamwar/towerrun/countdowns/LobbyCountdown.java @@ -47,7 +47,7 @@ public class LobbyCountdown extends Countdown { @Override void timerEnd() { - TowerRunGame.start(); + TowerRunGame.prepareTowerOrStart(); override = false; } diff --git a/src/de/steamwar/towerrun/game/TowerRunGame.java b/src/de/steamwar/towerrun/game/TowerRunGame.java index 0bc5db1..c1e17b8 100644 --- a/src/de/steamwar/towerrun/game/TowerRunGame.java +++ b/src/de/steamwar/towerrun/game/TowerRunGame.java @@ -48,10 +48,27 @@ public class TowerRunGame { return PLAYERS_ALIVE.contains(player); } - public static void start() { + 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(TowerRunPlayer::reset); + PLAYERS_ALIVE.forEach(p -> { + p.reset(); + p.player().setGameMode(GameMode.SURVIVAL); + }); GameState.nextState(); generateLava(); TowerRun.getMessage().broadcast("GAME_START"); @@ -70,8 +87,12 @@ public class TowerRunGame { 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))) { - WorldConfig.MIN_TOWER.getWorld().getBlockAt(x, WorldConfig.LAVA_Y, z).setType(Material.LAVA, true); - WorldConfig.MIN_TOWER.getWorld().getBlockAt(x, WorldConfig.LAVA_Y - 1, z).setType(Material.BEDROCK, true); + 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); } } } @@ -107,38 +128,42 @@ public class TowerRunGame { resetWorld(); GameState.reset(); Bukkit.getOnlinePlayers().forEach(player -> { - player.setGameMode(GameMode.SURVIVAL); + if (TowerRun.getTowerGenerator() != null) { + player.setGameMode(GameMode.SPECTATOR); + } else { + player.setGameMode(GameMode.SURVIVAL); + } player.teleport(WorldConfig.SPAWN); }); } - private static double posToChunk(int pos){ + private static double posToChunk(int pos) { return pos / 16.0; } - private static int getMinChunkX(){ + private static int getMinChunkX() { return (int) Math.floor(posToChunk(WorldConfig.MAP_MIN_X)); } - private static int getMaxChunkX(){ + private static int getMaxChunkX() { return (int) Math.ceil(posToChunk(WorldConfig.MAP_MAX_X)); } - private static int getMinChunkZ(){ + private static int getMinChunkZ() { return (int) Math.floor(posToChunk(WorldConfig.MAP_MIN_Z)); } - private static int getMaxChunkZ(){ + 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++) + for (int x = getMinChunkX(); x <= getMaxChunkX(); x++) + for (int z = getMinChunkZ(); z <= getMaxChunkZ(); z++) executor.accept(x, z); } - private static void resetWorld(){ + private static void resetWorld() { world.getEntities().stream().filter(entity -> entity.getType() != EntityType.PLAYER).forEach(Entity::remove); World backup = new WorldCreator(world.getName() + "/backup").createWorld(); @@ -153,7 +178,7 @@ public class TowerRunGame { System.arraycopy(backupChunk.d(), 0, chunk.d(), 0, chunk.d().length); - for(Player p : Bukkit.getOnlinePlayers()) + for (Player p : Bukkit.getOnlinePlayers()) CraftbukkitWrapper.impl.sendChunk(p, x, z); } } diff --git a/src/de/steamwar/towerrun/game/TowerRunPlayer.java b/src/de/steamwar/towerrun/game/TowerRunPlayer.java index 9aa4629..e7b1297 100644 --- a/src/de/steamwar/towerrun/game/TowerRunPlayer.java +++ b/src/de/steamwar/towerrun/game/TowerRunPlayer.java @@ -19,6 +19,7 @@ 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; @@ -43,7 +44,14 @@ public record TowerRunPlayer(Player player) { } public void reset() { - player.teleport(WorldConfig.SPAWN); + 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)); } diff --git a/src/de/steamwar/towerrun/generator/TowerGenerator.java b/src/de/steamwar/towerrun/generator/TowerGenerator.java new file mode 100644 index 0000000..71bbaba --- /dev/null +++ b/src/de/steamwar/towerrun/generator/TowerGenerator.java @@ -0,0 +1,206 @@ +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; + + { + 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/src/de/steamwar/towerrun/listener/IngameListener.java b/src/de/steamwar/towerrun/listener/IngameListener.java index 62b1295..b7de365 100644 --- a/src/de/steamwar/towerrun/listener/IngameListener.java +++ b/src/de/steamwar/towerrun/listener/IngameListener.java @@ -23,33 +23,103 @@ 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.Bukkit; -import org.bukkit.Material; -import org.bukkit.Sound; +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.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.scheduler.BukkitRunnable; -import java.util.Arrays; -import java.util.EnumSet; +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); @@ -84,13 +154,17 @@ public class IngameListener extends GameStateBukkitListener { @EventHandler public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { - if(event.getDamager().getType() == EntityType.PLAYER) { + 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; } @@ -99,19 +173,38 @@ public class IngameListener extends GameStateBukkitListener { return; } - if (Arrays.stream(WorldConfig.KEYS).noneMatch(location -> location.equals(event.getClickedBlock().getLocation()))) { + 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.AIR); + 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 onPlayerRespawn(PlayerRespawnEvent event) { - event.setRespawnLocation(WorldConfig.SPAWN); + 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); } } diff --git a/src/de/steamwar/towerrun/listener/LobbyListener.java b/src/de/steamwar/towerrun/listener/LobbyListener.java index 470b198..eb2cb15 100644 --- a/src/de/steamwar/towerrun/listener/LobbyListener.java +++ b/src/de/steamwar/towerrun/listener/LobbyListener.java @@ -19,6 +19,7 @@ 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; @@ -42,13 +43,21 @@ public class LobbyListener extends GameStateBukkitListener { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); - player.teleport(WorldConfig.SPAWN); - player.setGameMode(GameMode.SURVIVAL); + 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(event.getTo().getY() < WorldConfig.SPAWN.getY() - 10) { + if (TowerRun.getTowerGenerator() != null) { + return; + } + if (event.getTo().getY() < WorldConfig.SPAWN.getY() - 10) { event.getPlayer().teleport(WorldConfig.SPAWN); } } diff --git a/src/de/steamwar/towerrun/listener/NotLobbyListener.java b/src/de/steamwar/towerrun/listener/NotLobbyListener.java index 20078f9..4d3525e 100644 --- a/src/de/steamwar/towerrun/listener/NotLobbyListener.java +++ b/src/de/steamwar/towerrun/listener/NotLobbyListener.java @@ -19,6 +19,7 @@ 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; @@ -36,6 +37,10 @@ public class NotLobbyListener extends GameStateBukkitListener { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { event.getPlayer().setGameMode(GameMode.SPECTATOR); - event.getPlayer().teleport(WorldConfig.SPAWN); + if (TowerRun.getTowerGenerator() != null) { + event.getPlayer().teleport(TowerRun.getTowerGenerator().getSpawn()); + } else { + event.getPlayer().teleport(WorldConfig.SPAWN); + } } } diff --git a/src/de/steamwar/towerrun/state/GameStateToggleListener.java b/src/de/steamwar/towerrun/state/GameStateToggleListener.java index 85aa0f1..c8ba553 100644 --- a/src/de/steamwar/towerrun/state/GameStateToggleListener.java +++ b/src/de/steamwar/towerrun/state/GameStateToggleListener.java @@ -28,12 +28,13 @@ public abstract class GameStateToggleListener extends GameStateListener { protected GameStateToggleListener(EnumSet enabledStates) { super(); this.enabledStates = enabledStates; - if(enabledStates.contains(GameState.getCurrentState())) { + if (enabledStates.contains(GameState.getCurrentState())) { enable(); } } public abstract void enable(); + public abstract void disable(); @Override diff --git a/src/de/steamwar/towerrun/state/GameStates.java b/src/de/steamwar/towerrun/state/GameStates.java index 3c838a3..347e981 100644 --- a/src/de/steamwar/towerrun/state/GameStates.java +++ b/src/de/steamwar/towerrun/state/GameStates.java @@ -27,7 +27,8 @@ import lombok.Getter; public enum GameStates { ENDING(null), INGAME(ENDING), - LOBBY(INGAME); + GENERATING_TOWER(INGAME), + LOBBY(GENERATING_TOWER); private final GameStates nextState; } diff --git a/src/de/steamwar/towerrun/winconditions/LastOutsideWincondition.java b/src/de/steamwar/towerrun/winconditions/LastOutsideWincondition.java index 7784452..260ed0f 100644 --- a/src/de/steamwar/towerrun/winconditions/LastOutsideWincondition.java +++ b/src/de/steamwar/towerrun/winconditions/LastOutsideWincondition.java @@ -26,7 +26,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.PlayerDeathEvent; -public class LastOutsideWincondition extends OutsideWincondition{ +public class LastOutsideWincondition extends OutsideWincondition { public LastOutsideWincondition() { super("LAST_OUTSIDE"); } @@ -42,8 +42,8 @@ public class LastOutsideWincondition extends OutsideWincondition{ @EventHandler(priority = EventPriority.LOW) public void onPlayerDeath(PlayerDeathEvent event) { - if(TowerRunGame.PLAYERS_ALIVE.isEmpty()) { - if(TowerRunGame.PLAYERS_ESCAPED.isEmpty()) { + if (TowerRunGame.PLAYERS_ALIVE.isEmpty()) { + if (TowerRunGame.PLAYERS_ESCAPED.isEmpty()) { TowerRunGame.tie(); return; }