REWRITE INIT.
This commit is contained in:
@@ -1,29 +0,0 @@
|
|||||||
package fyi.tiko.battletower;
|
|
||||||
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Tiko
|
|
||||||
* @since 03.02.2026, 19:33
|
|
||||||
*/
|
|
||||||
public class BattleTowerPlugin extends JavaPlugin {
|
|
||||||
private TowerSpawnManager spawnManager;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnable() {
|
|
||||||
saveDefaultConfig();
|
|
||||||
|
|
||||||
spawnManager = new TowerSpawnManager(this);
|
|
||||||
|
|
||||||
getServer().getPluginManager().registerEvents(
|
|
||||||
new ChunkPopulateListener(spawnManager),
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
getLogger().info("Enabled BattleTowerPlugin");
|
|
||||||
}
|
|
||||||
|
|
||||||
public TowerSpawnManager spawnManager() {
|
|
||||||
return spawnManager;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package fyi.tiko.battletower;
|
|
||||||
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.world.ChunkPopulateEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Tiko
|
|
||||||
* @since 03.02.2026, 19:37
|
|
||||||
*/
|
|
||||||
public class ChunkPopulateListener implements Listener {
|
|
||||||
private final TowerSpawnManager spawnManager;
|
|
||||||
|
|
||||||
public ChunkPopulateListener(final TowerSpawnManager spawnManager) {
|
|
||||||
this.spawnManager = spawnManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void handleChunkPopulate(final ChunkPopulateEvent event) {
|
|
||||||
spawnManager.attemptSpawn(event.getChunk());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
package fyi.tiko.battletower;
|
|
||||||
import org.bukkit.*;
|
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.bukkit.block.Chest;
|
|
||||||
import org.bukkit.block.CreatureSpawner;
|
|
||||||
import org.bukkit.entity.EntityType;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class OriginalTowerGenerator {
|
|
||||||
|
|
||||||
private int floor = 1;
|
|
||||||
private int floorIterator = 0;
|
|
||||||
private boolean topFloor;
|
|
||||||
|
|
||||||
public boolean generate(World world, Random random, int ix, int surfaceY, int kz) {
|
|
||||||
|
|
||||||
TowerType type = TowerType.COBBLE;
|
|
||||||
|
|
||||||
Material WALL = type.wall();
|
|
||||||
Material FLOOR = type.floor();
|
|
||||||
Material PILLAR = type.pillar();
|
|
||||||
Material LIGHT = type.light();
|
|
||||||
Material STAIRS = type.stairs();
|
|
||||||
|
|
||||||
int startY = surfaceY - 6;
|
|
||||||
int maxHeight = 120;
|
|
||||||
|
|
||||||
floor = 1;
|
|
||||||
|
|
||||||
for (int builderHeight = startY; builderHeight < maxHeight; builderHeight += 7) {
|
|
||||||
|
|
||||||
topFloor = builderHeight + 7 >= maxHeight;
|
|
||||||
|
|
||||||
// ✅ FULL Forge Pattern Segment
|
|
||||||
buildSegment(world, random, ix, builderHeight, kz,
|
|
||||||
WALL, FLOOR, PILLAR, STAIRS);
|
|
||||||
|
|
||||||
// ✅ Spawner/Floor
|
|
||||||
if (!topFloor) {
|
|
||||||
placeSpawner(world, ix + 2, builderHeight + 6, kz + 2, random);
|
|
||||||
placeSpawner(world, ix - 3, builderHeight + 6, kz + 2, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Chests
|
|
||||||
placeChests(world, ix, builderHeight + 7, kz);
|
|
||||||
|
|
||||||
// ✅ Lights
|
|
||||||
placeLights(world, ix, builderHeight, kz, LIGHT);
|
|
||||||
|
|
||||||
// ✅ Random Damage Holes (Original feel)
|
|
||||||
randomDamage(world, random, ix, builderHeight + 5, kz, FLOOR);
|
|
||||||
|
|
||||||
floor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Boss Placeholder
|
|
||||||
spawnBoss(world, ix, maxHeight, kz);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================================================= */
|
|
||||||
|
|
||||||
private void buildSegment(World world, Random random,
|
|
||||||
int ix, int baseY, int kz,
|
|
||||||
Material WALL,
|
|
||||||
Material FLOOR,
|
|
||||||
Material PILLAR,
|
|
||||||
Material STAIRS) {
|
|
||||||
|
|
||||||
for (floorIterator = 0; floorIterator < 7; floorIterator++) {
|
|
||||||
|
|
||||||
if (floor == 1 && floorIterator < 4) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int xIt = -7; xIt < 7; xIt++) {
|
|
||||||
for (int zIt = -7; zIt < 7; zIt++) {
|
|
||||||
|
|
||||||
int x = ix + xIt;
|
|
||||||
int y = baseY + floorIterator;
|
|
||||||
int z = kz + zIt;
|
|
||||||
|
|
||||||
// ==== EXACT FORGE SHAPE ====
|
|
||||||
|
|
||||||
if (zIt == -7) {
|
|
||||||
if (xIt > -5 && xIt < 4) {
|
|
||||||
wallPiece(world, x, y, z, WALL);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main interior
|
|
||||||
if (zIt != -6 && zIt != -5) {
|
|
||||||
|
|
||||||
// Window rows excluded
|
|
||||||
if (zIt != -4 && zIt != -3 && zIt != 2 && zIt != 3) {
|
|
||||||
|
|
||||||
if (zIt > -3 && zIt < 2) {
|
|
||||||
|
|
||||||
if (xIt != -7 && xIt != 6) {
|
|
||||||
|
|
||||||
if (floorIterator == 5) {
|
|
||||||
place(world, x, y, z, FLOOR);
|
|
||||||
} else {
|
|
||||||
place(world, x, y, z, Material.AIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
wallPiece(world, x, y, z, WALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (zIt == 4) {
|
|
||||||
|
|
||||||
if (xIt != -5 && xIt != 4) {
|
|
||||||
if (floorIterator == 5) {
|
|
||||||
place(world, x, y, z, FLOOR);
|
|
||||||
} else {
|
|
||||||
place(world, x, y, z, Material.AIR);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wallPiece(world, x, y, z, WALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (zIt == 5) {
|
|
||||||
|
|
||||||
if (xIt != -4 && xIt != -3 && xIt != 2 && xIt != 3) {
|
|
||||||
|
|
||||||
if (xIt > -3 && xIt < 2) {
|
|
||||||
if (floorIterator == 5) {
|
|
||||||
place(world, x, y, z, FLOOR);
|
|
||||||
} else {
|
|
||||||
wallPiece(world, x, y, z, WALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
wallPiece(world, x, y, z, WALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (zIt == 6 && xIt > -3 && xIt < 2) {
|
|
||||||
wallPiece(world, x, y, z, WALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Window rows (-4,-3,2,3)
|
|
||||||
else {
|
|
||||||
|
|
||||||
if (xIt != -6 && xIt != 5) {
|
|
||||||
if (floorIterator == 5) {
|
|
||||||
place(world, x, y, z, FLOOR);
|
|
||||||
} else {
|
|
||||||
place(world, x, y, z, Material.AIR);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wallPiece(world, x, y, z, WALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stair gap
|
|
||||||
else {
|
|
||||||
|
|
||||||
if (xIt == -5 || xIt == 4) {
|
|
||||||
wallPiece(world, x, y, z, WALL);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zIt == -6) {
|
|
||||||
|
|
||||||
int stairX = (floorIterator + 1) % 7 - 3;
|
|
||||||
|
|
||||||
if (xIt == stairX) {
|
|
||||||
place(world, x, y, z, STAIRS);
|
|
||||||
} else if (xIt > -5 && xIt < 4) {
|
|
||||||
place(world, x, y, z, Material.AIR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zIt == -5) {
|
|
||||||
wallPiece(world, x, y, z, WALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================================================= */
|
|
||||||
|
|
||||||
private void wallPiece(World world, int x, int y, int z, Material wall) {
|
|
||||||
|
|
||||||
// Mossy pillar accents
|
|
||||||
if ((x % 5 == 0) && (z % 5 == 0)) {
|
|
||||||
place(world, x, y, z, Material.MOSSY_COBBLESTONE);
|
|
||||||
} else {
|
|
||||||
place(world, x, y, z, wall);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill base into ground
|
|
||||||
if (floor == 1 && floorIterator == 4) {
|
|
||||||
fillDown(world, x, y, z, wall);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillDown(World world, int x, int y, int z, Material mat) {
|
|
||||||
for (int yy = y - 1; yy > 0; yy--) {
|
|
||||||
Block b = world.getBlockAt(x, yy, z);
|
|
||||||
if (b.getType().isSolid()) break;
|
|
||||||
b.setType(mat, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================================================= */
|
|
||||||
|
|
||||||
private void randomDamage(World world, Random random,
|
|
||||||
int ix, int y, int kz, Material floorMat) {
|
|
||||||
|
|
||||||
if (topFloor) return;
|
|
||||||
|
|
||||||
int holes = floor * 2;
|
|
||||||
|
|
||||||
for (int i = 0; i < holes; i++) {
|
|
||||||
|
|
||||||
int dx = random.nextInt(10) - 5;
|
|
||||||
int dz = random.nextInt(10) - 5;
|
|
||||||
|
|
||||||
if (Math.abs(dx) < 2 && Math.abs(dz) < 2) continue;
|
|
||||||
|
|
||||||
Block b = world.getBlockAt(ix + dx, y, kz + dz);
|
|
||||||
|
|
||||||
if (b.getType() == floorMat) {
|
|
||||||
b.setType(Material.AIR, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= Spawner ================= */
|
|
||||||
|
|
||||||
private void placeSpawner(World world, int x, int y, int z, Random random) {
|
|
||||||
|
|
||||||
Block b = world.getBlockAt(x, y, z);
|
|
||||||
b.setType(Material.SPAWNER);
|
|
||||||
|
|
||||||
CreatureSpawner spawner = (CreatureSpawner) b.getState();
|
|
||||||
spawner.setSpawnedType(EntityType.ZOMBIE);
|
|
||||||
spawner.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= Loot ================= */
|
|
||||||
|
|
||||||
private void placeChests(World world, int ix, int y, int kz) {
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
|
|
||||||
Block chestBlock = world.getBlockAt(ix - i, y, kz + 3);
|
|
||||||
chestBlock.setType(Material.CHEST);
|
|
||||||
|
|
||||||
Chest chest = (Chest) chestBlock.getState();
|
|
||||||
chest.getInventory().addItem(new ItemStack(Material.BREAD, 2));
|
|
||||||
chest.getInventory().addItem(new ItemStack(Material.IRON_INGOT, 2));
|
|
||||||
chest.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= Lights ================= */
|
|
||||||
|
|
||||||
private void placeLights(World world, int ix, int y, int kz, Material light) {
|
|
||||||
|
|
||||||
place(world, ix + 3, y + 1, kz - 6, light);
|
|
||||||
place(world, ix - 4, y + 1, kz - 6, light);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================= Boss ================= */
|
|
||||||
|
|
||||||
private void spawnBoss(World world, int ix, int y, int kz) {
|
|
||||||
|
|
||||||
world.spawnEntity(
|
|
||||||
new Location(world, ix + 0.5, y + 2, kz + 0.5),
|
|
||||||
EntityType.IRON_GOLEM
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================================================= */
|
|
||||||
|
|
||||||
private void place(World world, int x, int y, int z, Material mat) {
|
|
||||||
world.getBlockAt(x, y, z).setType(mat, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
package fyi.tiko.battletower;
|
|
||||||
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.World;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Tiko
|
|
||||||
* @since 03.02.2026, 19:41
|
|
||||||
*/
|
|
||||||
public class TowerGenerator {
|
|
||||||
// 7 blöcke hoch pro etage
|
|
||||||
private static final int FLOOR_HEIGHT = 7;
|
|
||||||
|
|
||||||
// tower radius in blöcken
|
|
||||||
private static final int RADIUS = 7;
|
|
||||||
|
|
||||||
public boolean generate(final Location location) {
|
|
||||||
final var world = location.getWorld();
|
|
||||||
final var x = location.getBlockX();
|
|
||||||
final var z = location.getBlockZ();
|
|
||||||
|
|
||||||
final var surfaceY = world.getHighestBlockYAt(x, z);
|
|
||||||
|
|
||||||
if (surfaceY < 60) {
|
|
||||||
return false; // zu niedrig
|
|
||||||
}
|
|
||||||
|
|
||||||
// original: tower soll leicht im boden starten
|
|
||||||
final var startY = surfaceY - 6;
|
|
||||||
|
|
||||||
TowerType type = TowerType.COBBLE;
|
|
||||||
|
|
||||||
// 6 etagen hoch (zusammenbasteln hier)
|
|
||||||
for (var floor = 0; floor < 6; floor++) {
|
|
||||||
final var floorBaseY = startY + (floor * FLOOR_HEIGHT);
|
|
||||||
buildFloor(world, x, floorBaseY, z, type);
|
|
||||||
buildWalls(world, x, floorBaseY, z, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// top zusammenbasteln hier
|
|
||||||
buildTop(world, x, startY + (6 * FLOOR_HEIGHT), z, type);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildFloor(
|
|
||||||
final World world,
|
|
||||||
final int centerX,
|
|
||||||
final int y,
|
|
||||||
final int centerZ,
|
|
||||||
final TowerType type
|
|
||||||
) {
|
|
||||||
for (var dx = -6; dx <= 6; dx++) {
|
|
||||||
for (var dz = -6; dz <= 6; dz++) {
|
|
||||||
// nur drinnen
|
|
||||||
if (Math.abs(dx) < 6 && Math.abs(dz) < 6) {
|
|
||||||
world.getBlockAt(centerX + dx, y, centerZ + dz).setType(type.floor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildWalls(
|
|
||||||
final World world,
|
|
||||||
final int centerX,
|
|
||||||
final int y,
|
|
||||||
final int centerZ,
|
|
||||||
final TowerType type
|
|
||||||
) {
|
|
||||||
for (var dy = 0; dy < FLOOR_HEIGHT; dy++) {
|
|
||||||
final var currentY = y + dy;
|
|
||||||
|
|
||||||
for (int dx = -RADIUS; dx <= RADIUS; dx++) {
|
|
||||||
for (int dz = -RADIUS; dz <= RADIUS; dz++) {
|
|
||||||
// outer border = wall
|
|
||||||
if (Math.abs(dx) == RADIUS || Math.abs(dz) == RADIUS) {
|
|
||||||
world.getBlockAt(centerX + dx, currentY, centerZ + dz).setType(type.wall());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildTop(
|
|
||||||
final World world,
|
|
||||||
final int centerX,
|
|
||||||
final int y,
|
|
||||||
final int centerZ,
|
|
||||||
final TowerType type
|
|
||||||
) {
|
|
||||||
for(var dx = -6; dx <= 6; dx++) {
|
|
||||||
for(var dz = -6; dz <= 6; dz++) {
|
|
||||||
world.getBlockAt(centerX + dx, y, centerZ + dz).setType(type.floor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package fyi.tiko.battletower;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Tiko
|
|
||||||
* @since 03.02.2026, 19:37
|
|
||||||
*/
|
|
||||||
public class TowerSpawnManager {
|
|
||||||
private final JavaPlugin plugin;
|
|
||||||
private final Random random = new Random();
|
|
||||||
private final Set<Location> towerPositions = new HashSet<>();
|
|
||||||
|
|
||||||
public TowerSpawnManager(final JavaPlugin plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void attemptSpawn(final Chunk chunk) {
|
|
||||||
final var chance = plugin.getConfig().getInt("spawn.chance-per-chunk");
|
|
||||||
|
|
||||||
if (random.nextInt(chance) != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final var world = chunk.getWorld();
|
|
||||||
final var x = chunk.getX() * 16 + 8;
|
|
||||||
final var z = chunk.getZ() * 16 + 8;
|
|
||||||
final var y = world.getHighestBlockYAt(x, z);
|
|
||||||
|
|
||||||
final var baseLocation = new Location(world, x, y, z);
|
|
||||||
|
|
||||||
if (!canSpawnAt(baseLocation)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final var generator = new OriginalTowerGenerator();
|
|
||||||
final var success = generator.generate(world, random, x, y, z);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
towerPositions.add(baseLocation);
|
|
||||||
plugin.getLogger().info("Spawned tower at " + baseLocation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canSpawnAt(final Location location) {
|
|
||||||
final var minDistance = plugin.getConfig().getInt("spawn.min-distance");
|
|
||||||
|
|
||||||
for (final var other : towerPositions) {
|
|
||||||
if (other.getWorld() != location.getWorld()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other.distanceSquared(location) < minDistance) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package fyi.tiko.battletower;
|
|
||||||
|
|
||||||
import org.bukkit.Material;
|
|
||||||
|
|
||||||
public enum TowerType {
|
|
||||||
|
|
||||||
COBBLE(
|
|
||||||
Material.COBBLESTONE,
|
|
||||||
Material.STONE_BRICKS,
|
|
||||||
Material.MOSSY_COBBLESTONE, // ✅ Pillars
|
|
||||||
Material.TORCH,
|
|
||||||
Material.COBBLESTONE_STAIRS
|
|
||||||
);
|
|
||||||
|
|
||||||
private final Material wall;
|
|
||||||
private final Material floor;
|
|
||||||
private final Material pillar;
|
|
||||||
private final Material light;
|
|
||||||
private final Material stairs;
|
|
||||||
|
|
||||||
TowerType(Material wall,
|
|
||||||
Material floor,
|
|
||||||
Material pillar,
|
|
||||||
Material light,
|
|
||||||
Material stairs) {
|
|
||||||
|
|
||||||
this.wall = wall;
|
|
||||||
this.floor = floor;
|
|
||||||
this.pillar = pillar;
|
|
||||||
this.light = light;
|
|
||||||
this.stairs = stairs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Material wall() { return wall; }
|
|
||||||
public Material floor() { return floor; }
|
|
||||||
public Material pillar() { return pillar; }
|
|
||||||
public Material light() { return light; }
|
|
||||||
public Material stairs() { return stairs; }
|
|
||||||
}
|
|
||||||
54
src/main/java/fyi/tiko/battletowers/BattleTowersPlugin.java
Normal file
54
src/main/java/fyi/tiko/battletowers/BattleTowersPlugin.java
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package fyi.tiko.battletowers;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
public final class BattleTowersPlugin extends JavaPlugin {
|
||||||
|
|
||||||
|
private static BattleTowersPlugin instance;
|
||||||
|
|
||||||
|
public static BattleTowersPlugin instance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TowerLootManager lootManager;
|
||||||
|
private NamespacedKey towerKey;
|
||||||
|
|
||||||
|
public NamespacedKey towerKey() {
|
||||||
|
return towerKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TowerLootManager getLootManager() {
|
||||||
|
return lootManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
instance = this;
|
||||||
|
|
||||||
|
saveDefaultConfig();
|
||||||
|
|
||||||
|
towerKey = new NamespacedKey(this, "tower_location");
|
||||||
|
lootManager = new TowerLootManager();
|
||||||
|
|
||||||
|
Bukkit.getWorlds().forEach(world -> {
|
||||||
|
world.getPopulators().add(new TowerPopulator());
|
||||||
|
});
|
||||||
|
|
||||||
|
getServer().getPluginManager().registerEvents(
|
||||||
|
new TowerChestListener(), this
|
||||||
|
);
|
||||||
|
|
||||||
|
getServer().getPluginManager().registerEvents(
|
||||||
|
new TowerBossDeathListener(), this
|
||||||
|
);
|
||||||
|
|
||||||
|
getLogger().info("BattleTowers enabled!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
getLogger().info("BattleTowers disabled!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package fyi.tiko.battletowers;
|
||||||
|
|
||||||
|
import org.bukkit.entity.IronGolem;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.entity.EntityDeathEvent;
|
||||||
|
|
||||||
|
import static fyi.tiko.battletowers.BattleTowersPlugin.instance;
|
||||||
|
|
||||||
|
public class TowerBossDeathListener implements Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onBossDeath(EntityDeathEvent event) {
|
||||||
|
|
||||||
|
if (!(event.getEntity() instanceof IronGolem golem)) return;
|
||||||
|
|
||||||
|
var data = golem.getPersistentDataContainer().get(
|
||||||
|
instance().towerKey(),
|
||||||
|
org.bukkit.persistence.PersistentDataType.STRING
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
// Tower Collapse Start
|
||||||
|
TowerDestroyer.startCollapse(golem.getLocation(), event.getEntity().getKiller());
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/main/java/fyi/tiko/battletowers/TowerBossSpawner.java
Normal file
50
src/main/java/fyi/tiko/battletowers/TowerBossSpawner.java
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package fyi.tiko.battletowers;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.attribute.Attribute;
|
||||||
|
import org.bukkit.entity.IronGolem;
|
||||||
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
|
|
||||||
|
import static fyi.tiko.battletowers.BattleTowersPlugin.instance;
|
||||||
|
|
||||||
|
public class TowerBossSpawner {
|
||||||
|
|
||||||
|
public static void spawnDormantBoss(org.bukkit.World world,
|
||||||
|
Location loc,
|
||||||
|
int towerX, int towerY, int towerZ) {
|
||||||
|
|
||||||
|
IronGolem golem = world.spawn(loc, IronGolem.class);
|
||||||
|
|
||||||
|
// ===== Boss Stats like Forge =====
|
||||||
|
golem.getAttribute(Attribute.MAX_HEALTH).setBaseValue(300.0);
|
||||||
|
golem.setHealth(300.0);
|
||||||
|
|
||||||
|
golem.getAttribute(Attribute.ATTACK_DAMAGE).setBaseValue(12.0);
|
||||||
|
golem.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.30);
|
||||||
|
|
||||||
|
golem.setCustomName("§6Battletower Guardian");
|
||||||
|
golem.setCustomNameVisible(true);
|
||||||
|
|
||||||
|
// ===== Dormant Mode =====
|
||||||
|
golem.setAI(false);
|
||||||
|
|
||||||
|
// Save tower coords inside entity
|
||||||
|
golem.getPersistentDataContainer().set(
|
||||||
|
instance().towerKey(),
|
||||||
|
PersistentDataType.STRING,
|
||||||
|
towerX + ";" + towerY + ";" + towerZ
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void wakeUp(IronGolem golem) {
|
||||||
|
|
||||||
|
if (golem.hasAI()) return;
|
||||||
|
|
||||||
|
golem.setAI(true);
|
||||||
|
golem.getWorld().playSound(
|
||||||
|
golem.getLocation(),
|
||||||
|
"entity.iron_golem.repair",
|
||||||
|
3f, 0.6f
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/main/java/fyi/tiko/battletowers/TowerChestListener.java
Normal file
31
src/main/java/fyi/tiko/battletowers/TowerChestListener.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package fyi.tiko.battletowers;
|
||||||
|
|
||||||
|
import org.bukkit.entity.IronGolem;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.inventory.InventoryOpenEvent;
|
||||||
|
|
||||||
|
public class TowerChestListener implements Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onChestOpen(InventoryOpenEvent event) {
|
||||||
|
|
||||||
|
if (!(event.getInventory().getHolder() instanceof org.bukkit.block.Chest)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var player = event.getPlayer();
|
||||||
|
|
||||||
|
// Search nearby golems
|
||||||
|
for (var entity : player.getNearbyEntities(8, 8, 8)) {
|
||||||
|
|
||||||
|
if (entity instanceof IronGolem golem) {
|
||||||
|
|
||||||
|
if (!golem.hasAI()) {
|
||||||
|
TowerBossSpawner.wakeUp(golem);
|
||||||
|
golem.setTarget(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/main/java/fyi/tiko/battletowers/TowerDestroyer.java
Normal file
40
src/main/java/fyi/tiko/battletowers/TowerDestroyer.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package fyi.tiko.battletowers;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
|
import static fyi.tiko.battletowers.BattleTowersPlugin.instance;
|
||||||
|
|
||||||
|
public class TowerDestroyer {
|
||||||
|
|
||||||
|
// TODO: die einzelnen floors werden nicht kaputt gemacht, sondern nur ganz oben + nachricht nur an spielern in reichweite von X blöcken...
|
||||||
|
public static void startCollapse(Location center, Player killer) {
|
||||||
|
|
||||||
|
World world = center.getWorld();
|
||||||
|
|
||||||
|
Bukkit.broadcastMessage("§cA Battletower's Guardian has fallen! The Tower will collapse...");
|
||||||
|
|
||||||
|
new BukkitRunnable() {
|
||||||
|
|
||||||
|
int floor = 6;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
if (floor <= 0) {
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double y = center.getY() - (floor * 7);
|
||||||
|
|
||||||
|
world.createExplosion(center, 10f);
|
||||||
|
floor--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}.runTaskTimer(instance(), 20 * 15, 20 * 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
528
src/main/java/fyi/tiko/battletowers/TowerGenerator.java
Normal file
528
src/main/java/fyi/tiko/battletowers/TowerGenerator.java
Normal file
@@ -0,0 +1,528 @@
|
|||||||
|
package fyi.tiko.battletowers;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockFace;
|
||||||
|
import org.bukkit.block.CreatureSpawner;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
public class TowerGenerator {
|
||||||
|
|
||||||
|
private static final int MAX_HOLE_DEPTH = 22;
|
||||||
|
|
||||||
|
private static final int[][] CANDIDATES = {
|
||||||
|
{4, -5}, {4, 0}, {4, 5},
|
||||||
|
{0, -5}, {0, 0}, {0, 5},
|
||||||
|
{-4, -5}, {-4, 0}, {-4, 5}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final Random random = new Random();
|
||||||
|
|
||||||
|
public void trySpawnTower(World world, Random random, int x, int z) {
|
||||||
|
|
||||||
|
int surfaceY = getSurfaceY(world, x, z);
|
||||||
|
if (surfaceY < 60) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TowerType type = pickTowerType(world, random, x, z, surfaceY);
|
||||||
|
if (type == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean underground = random.nextInt(100) < 15;
|
||||||
|
|
||||||
|
generate(world, x, surfaceY, z, type, underground);
|
||||||
|
|
||||||
|
Bukkit.getLogger().info("Spawned BattleTower at " + x + " " + z + " type=" + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TowerType pickTowerType(World world, Random random, int x, int z, int centerY) {
|
||||||
|
|
||||||
|
int water = 0, snow = 0, sand = 0, foliage = 0, other = 0;
|
||||||
|
|
||||||
|
for (int[] pair : CANDIDATES) {
|
||||||
|
|
||||||
|
int checkY = getSurfaceY(world, x + pair[0], z + pair[1]);
|
||||||
|
Block block = world.getBlockAt(x + pair[0], checkY, z + pair[1]);
|
||||||
|
|
||||||
|
Material mat = block.getType();
|
||||||
|
|
||||||
|
if (mat == Material.SNOW || mat == Material.ICE) {
|
||||||
|
snow++;
|
||||||
|
} else if (mat == Material.SAND || mat == Material.SANDSTONE) {
|
||||||
|
sand++;
|
||||||
|
} else if (mat == Material.WATER) {
|
||||||
|
water++;
|
||||||
|
} else if (mat.name().contains("LEAVES") || mat.name().contains("LOG")) {
|
||||||
|
foliage++;
|
||||||
|
} else {
|
||||||
|
other++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.abs(checkY - centerY) > MAX_HOLE_DEPTH) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sand >= snow && sand >= water && sand >= foliage) {
|
||||||
|
return TowerType.SANDSTONE;
|
||||||
|
}
|
||||||
|
if (snow >= water && snow >= foliage) {
|
||||||
|
return TowerType.ICE;
|
||||||
|
}
|
||||||
|
if (water >= foliage) {
|
||||||
|
return TowerType.MOSSY;
|
||||||
|
}
|
||||||
|
if (random.nextInt(10) == 0) {
|
||||||
|
return TowerType.NETHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return random.nextInt(5) == 0 ? TowerType.SMOOTH : TowerType.COBBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSurfaceY(World world, int x, int z) {
|
||||||
|
int y = world.getHighestBlockYAt(x, z);
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 1:1 Block Placement Core
|
||||||
|
public void generate(World world, int ix, int jy, int kz, TowerType towerChosen, boolean underground) {
|
||||||
|
|
||||||
|
Material towerWallBlock = towerChosen.wall();
|
||||||
|
Material towerLightBlock = towerChosen.light();
|
||||||
|
Material towerFloorBlock = towerChosen.floor();
|
||||||
|
Material towerStairBlock = towerChosen.stair();
|
||||||
|
|
||||||
|
int startingHeight = underground ? Math.max(jy - 70, 15) : jy - 6;
|
||||||
|
int maximumHeight = underground ? jy + 7 : 120;
|
||||||
|
|
||||||
|
int floor = 1;
|
||||||
|
|
||||||
|
for (int builderHeight = startingHeight; builderHeight < maximumHeight; builderHeight += 7) {
|
||||||
|
|
||||||
|
boolean topFloor = builderHeight + 7 >= maximumHeight;
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// BUILD FLOOR (7 BLOCK HIGH)
|
||||||
|
// ============================
|
||||||
|
for (int floorIterator = 0; floorIterator < 7; floorIterator++) {
|
||||||
|
|
||||||
|
// initial floor skip like Forge
|
||||||
|
if (floor == 1 && floorIterator < 4) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int xIterator = -7; xIterator < 7; xIterator++) {
|
||||||
|
for (int zIterator = -7; zIterator < 7; zIterator++) {
|
||||||
|
|
||||||
|
int x = ix + xIterator;
|
||||||
|
int y = builderHeight + floorIterator;
|
||||||
|
int z = kz + zIterator;
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// ORIGINAL FORGE SHAPE LOGIC
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
// ---- Back wall row z=-7
|
||||||
|
if (zIterator == -7) {
|
||||||
|
if (xIterator > -5 && xIterator < 4) {
|
||||||
|
buildWallPiece(world, x, y, z, towerWallBlock, floor, floorIterator);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Stairwell rows z=-6,-5
|
||||||
|
if (zIterator == -6 || zIterator == -5) {
|
||||||
|
|
||||||
|
// Outer wall supports
|
||||||
|
if (xIterator == -5 || xIterator == 4) {
|
||||||
|
buildWallPiece(world, x, y, z, towerWallBlock, floor, floorIterator);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stairwell special row z=-6
|
||||||
|
if (zIterator == -6) {
|
||||||
|
|
||||||
|
// Stairwell column formula
|
||||||
|
if (xIterator == (floorIterator + 1) % 7 - 3) {
|
||||||
|
|
||||||
|
if (!(underground && floor == 1)) {
|
||||||
|
placeStair(world, x, y, z, towerStairBlock, floorIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// floorIterator=5 → platform extension
|
||||||
|
if (floorIterator == 5) {
|
||||||
|
setBlock(world, x - 7, y, z, towerFloorBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top ledge
|
||||||
|
if (floorIterator == 6 && topFloor) {
|
||||||
|
buildWallPiece(world, x, y, z, towerWallBlock, floor, floorIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tower inside air clearing
|
||||||
|
if (xIterator < 4 && xIterator > -5) {
|
||||||
|
setBlock(world, x, y, z, Material.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// row z=-5 outer bounds
|
||||||
|
if (xIterator <= -5 || xIterator >= 5) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// under stairwell blocks
|
||||||
|
if ((floorIterator != 0 && floorIterator != 6)
|
||||||
|
|| (xIterator != -4 && xIterator != 3)) {
|
||||||
|
|
||||||
|
if (floorIterator == 5 && (xIterator == 3 || xIterator == -4)) {
|
||||||
|
buildFloorPiece(world, x, y, z, towerFloorBlock);
|
||||||
|
} else {
|
||||||
|
buildWallPiece(world, x, y, z, towerWallBlock, floor, floorIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Stairwell space
|
||||||
|
setBlock(world, x, y, z, Material.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Side rows z=-4,-3,z=2,3
|
||||||
|
if (zIterator == -4 || zIterator == -3 || zIterator == 2 || zIterator == 3) {
|
||||||
|
|
||||||
|
if (xIterator == -6 || xIterator == 5) {
|
||||||
|
buildWallPiece(world, x, y, z, towerWallBlock, floor, floorIterator);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xIterator <= -6 || xIterator >= 5) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (floorIterator == 5) {
|
||||||
|
buildFloorPiece(world, x, y, z, towerFloorBlock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEU: SCHUTZ FÜR DIE KISTEN
|
||||||
|
// Wenn wir im neuen Stockwerk ganz unten sind (floorIterator == 0),
|
||||||
|
// dürfen wir an der Stelle x=0 und x=-1 bei z=3 die Luft nicht löschen,
|
||||||
|
// weil dort die Kisten vom Stockwerk darunter stehen.
|
||||||
|
if (floorIterator == 0 && zIterator == 3 && (xIterator == 0 || xIterator == -1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBlock(world, x, y, z, Material.AIR);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Middle rows z=-2,-1,0,1
|
||||||
|
if (zIterator > -3 && zIterator < 2) {
|
||||||
|
|
||||||
|
if (xIterator == -7 || xIterator == 6) {
|
||||||
|
|
||||||
|
// Window logic
|
||||||
|
if (floorIterator < 0 || floorIterator > 3
|
||||||
|
|| underground
|
||||||
|
|| (zIterator != -1 && zIterator != 0)) {
|
||||||
|
buildWallPiece(world, x, y, z, towerWallBlock, floor, floorIterator);
|
||||||
|
} else {
|
||||||
|
setBlock(world, x, y, z, Material.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xIterator <= -7 || xIterator >= 6) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (floorIterator == 5) {
|
||||||
|
buildFloorPiece(world, x, y, z, towerFloorBlock);
|
||||||
|
} else {
|
||||||
|
setBlock(world, x, y, z, Material.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Front row z=4
|
||||||
|
if (zIterator == 4) {
|
||||||
|
|
||||||
|
if (xIterator == -5 || xIterator == 4) {
|
||||||
|
buildWallPiece(world, x, y, z, towerWallBlock, floor, floorIterator);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xIterator <= -5 || xIterator >= 4) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (floorIterator == 5) {
|
||||||
|
buildFloorPiece(world, x, y, z, towerFloorBlock);
|
||||||
|
} else {
|
||||||
|
setBlock(world, x, y, z, Material.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Row z=5 entrance ring
|
||||||
|
if (zIterator == 5) {
|
||||||
|
|
||||||
|
if (xIterator == -4 || xIterator == -3 || xIterator == 2 || xIterator == 3) {
|
||||||
|
buildWallPiece(world, x, y, z, towerWallBlock, floor, floorIterator);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xIterator <= -3 || xIterator >= 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (floorIterator == 5) {
|
||||||
|
buildFloorPiece(world, x, y, z, towerFloorBlock);
|
||||||
|
} else {
|
||||||
|
buildWallPiece(world, x, y, z, towerWallBlock, floor, floorIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Final row z=6
|
||||||
|
if (zIterator == 6 && xIterator > -3 && xIterator < 2) {
|
||||||
|
|
||||||
|
buildWallPiece(world, x, y, z, towerWallBlock, floor, floorIterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// FLOOR LIGHTS (EXACT)
|
||||||
|
// ============================
|
||||||
|
setBlock(world, ix + 3, builderHeight + 2, kz - 6, towerLightBlock);
|
||||||
|
setBlock(world, ix - 4, builderHeight + 2, kz - 6, towerLightBlock);
|
||||||
|
|
||||||
|
setBlock(world, ix + 1, builderHeight + 2, kz - 4, towerLightBlock);
|
||||||
|
setBlock(world, ix - 2, builderHeight + 2, kz - 4, towerLightBlock);
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// SPAWNERS OR BOSS
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
if ((!underground && topFloor) || (underground && floor == 1)) {
|
||||||
|
|
||||||
|
// ===== TOP FLOOR → BOSS GOLEM =====
|
||||||
|
TowerBossSpawner.spawnDormantBoss(world,
|
||||||
|
new Location(world, ix + 0.5, builderHeight + 6, kz + 0.5),
|
||||||
|
ix, builderHeight + 6, kz);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// ===== NORMAL FLOOR → 2 SPAWNERS =====
|
||||||
|
spawnMobSpawner(world, ix + 2, builderHeight + 6, kz + 2);
|
||||||
|
spawnMobSpawner(world, ix - 3, builderHeight + 6, kz + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// ALWAYS PLACE FLOOR CHESTS
|
||||||
|
// ============================
|
||||||
|
// ============================
|
||||||
|
// FIXED: PLACE FLOOR CHESTS
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
// Wir übergeben 'false' für topFloor, damit wir die Logik in der Methode selbst regeln können
|
||||||
|
// oder nutzen einfach deine Logik weiter, aber rufen es sicher auf.
|
||||||
|
placeFloorChests(world, ix, builderHeight, kz, floor, underground, topFloor);
|
||||||
|
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// CHEST PETAL (PODEST) - WIEDER EINGEFÜGT
|
||||||
|
// ============================
|
||||||
|
// Das Podest kommt auf Höhe +6 (einen Block über dem normalen Boden)
|
||||||
|
setBlock(world, ix, builderHeight + 6, kz + 3, towerFloorBlock);
|
||||||
|
setBlock(world, ix - 1, builderHeight + 6, kz + 3, towerFloorBlock);
|
||||||
|
|
||||||
|
floor++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void placeStair(World world, int x, int y, int z, Material stairMat, int floorIterator) {
|
||||||
|
var stair = (org.bukkit.block.data.type.Stairs) stairMat.createBlockData();
|
||||||
|
|
||||||
|
// KORREKTUR:
|
||||||
|
// Da deine Treppen sich in der `generate`-Methode nur entlang der X-Achse bewegen
|
||||||
|
// (Z ist fest auf -6), ist es eine gerade Treppe.
|
||||||
|
// Die X-Werte steigen an (-2, -1, 0...), also laufen wir nach OSTEN hoch.
|
||||||
|
// Deshalb müssen alle Treppen stur nach Osten zeigen.
|
||||||
|
|
||||||
|
stair.setFacing(BlockFace.EAST);
|
||||||
|
|
||||||
|
stair.setHalf(org.bukkit.block.data.type.Stairs.Half.BOTTOM);
|
||||||
|
|
||||||
|
world.getBlockAt(x, y, z).setBlockData(stair, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void placeFloorChests(World world,
|
||||||
|
int ix,
|
||||||
|
int builderHeight,
|
||||||
|
int kz,
|
||||||
|
int floor,
|
||||||
|
boolean underground,
|
||||||
|
boolean topFloor
|
||||||
|
) {
|
||||||
|
TowerLootManager loot = BattleTowersPlugin.instance().getLootManager();
|
||||||
|
TowerStageItemManager floorChestManager;
|
||||||
|
|
||||||
|
// 1. Loot-Logik (Original Forge Style)
|
||||||
|
if (!underground) {
|
||||||
|
floorChestManager = topFloor
|
||||||
|
? loot.getManagerForFloor(10, random)
|
||||||
|
: loot.getManagerForFloor(floor, random);
|
||||||
|
} else {
|
||||||
|
floorChestManager = (floor == 1)
|
||||||
|
? loot.getManagerForFloor(10, random)
|
||||||
|
: loot.getManagerForFloor(Math.abs(11 - floor), random);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Koordinaten berechnen
|
||||||
|
// cy = builderHeight + 7 stellt die Kisten auf das Podest (welches auf +6 ist)
|
||||||
|
int cx = ix;
|
||||||
|
int cy = builderHeight + 7;
|
||||||
|
int cz = kz + 3;
|
||||||
|
|
||||||
|
// 3. Blöcke initial auf Kiste setzen
|
||||||
|
world.getBlockAt(cx, cy, cz).setType(Material.CHEST, false);
|
||||||
|
world.getBlockAt(cx - 1, cy, cz).setType(Material.CHEST, false);
|
||||||
|
|
||||||
|
// 4. Doppelkisten-Daten anwenden (Verbindung korrigieren)
|
||||||
|
// Von vorne (Norden) betrachtet:
|
||||||
|
// cx - 1 ist LINKS (Westen) -> Type.LEFT
|
||||||
|
// cx ist RECHTS (Osten) -> Type.RIGHT
|
||||||
|
|
||||||
|
// Linke Hälfte (cx - 1)
|
||||||
|
var leftData = (org.bukkit.block.data.type.Chest) Material.CHEST.createBlockData();
|
||||||
|
leftData.setFacing(BlockFace.NORTH);
|
||||||
|
leftData.setType(org.bukkit.block.data.type.Chest.Type.LEFT);
|
||||||
|
|
||||||
|
// Rechte Hälfte (cx)
|
||||||
|
var rightData = (org.bukkit.block.data.type.Chest) Material.CHEST.createBlockData();
|
||||||
|
rightData.setFacing(BlockFace.NORTH);
|
||||||
|
rightData.setType(org.bukkit.block.data.type.Chest.Type.RIGHT);
|
||||||
|
|
||||||
|
// Daten auf die Blöcke schreiben
|
||||||
|
world.getBlockAt(cx - 1, cy, cz).setBlockData(leftData, false);
|
||||||
|
world.getBlockAt(cx, cy, cz).setBlockData(rightData, false);
|
||||||
|
|
||||||
|
// 5. Inventar füllen
|
||||||
|
fillChest(world, cx, cy, cz, floorChestManager, underground);
|
||||||
|
fillChest(world, cx - 1, cy, cz, floorChestManager, underground);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillChest(World world,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int z,
|
||||||
|
TowerStageItemManager manager,
|
||||||
|
boolean underground) {
|
||||||
|
|
||||||
|
var state = world.getBlockAt(x, y, z).getState();
|
||||||
|
if (!(state instanceof org.bukkit.block.Chest chest)) return;
|
||||||
|
|
||||||
|
int attempts = BattleTowersPlugin.instance()
|
||||||
|
.getConfig()
|
||||||
|
.getInt("tower.itemGenerateAttemptsPerFloor");
|
||||||
|
|
||||||
|
if (underground) attempts *= 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < attempts; i++) {
|
||||||
|
|
||||||
|
ItemStack item = manager.getStageItem(random);
|
||||||
|
if (item == null) continue;
|
||||||
|
|
||||||
|
chest.getInventory().setItem(
|
||||||
|
random.nextInt(chest.getInventory().getSize()),
|
||||||
|
item
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void spawnMobSpawner(World world, int x, int y, int z) {
|
||||||
|
|
||||||
|
Block block = world.getBlockAt(x, y, z);
|
||||||
|
block.setType(Material.SPAWNER, false);
|
||||||
|
|
||||||
|
if (block.getState() instanceof CreatureSpawner spawner) {
|
||||||
|
|
||||||
|
EntityType mob = switch (new Random().nextInt(4)) {
|
||||||
|
case 0 -> EntityType.SKELETON;
|
||||||
|
case 1 -> EntityType.ZOMBIE;
|
||||||
|
case 2 -> EntityType.SPIDER;
|
||||||
|
default -> EntityType.CAVE_SPIDER;
|
||||||
|
};
|
||||||
|
|
||||||
|
spawner.setSpawnedType(mob);
|
||||||
|
spawner.update(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void buildFloorPiece(World world, int x, int y, int z, Material floor) {
|
||||||
|
setBlock(world, x, y, z, floor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildWallPiece(World world, int x, int y, int z,
|
||||||
|
Material wall, int floor, int floorIterator) {
|
||||||
|
|
||||||
|
setBlock(world, x, y, z, wall);
|
||||||
|
|
||||||
|
// Forge: fill base downwards
|
||||||
|
if (floor == 1 && floorIterator == 4) {
|
||||||
|
fillTowerBaseToGround(world, x, y, z, wall);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillTowerBaseToGround(World world, int x, int y, int z, Material wall) {
|
||||||
|
|
||||||
|
int yy = y - 1;
|
||||||
|
|
||||||
|
while (yy > 0) {
|
||||||
|
|
||||||
|
Material below = world.getBlockAt(x, yy, z).getType();
|
||||||
|
|
||||||
|
if (isBuildableBlock(below)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBlock(world, x, yy, z, wall);
|
||||||
|
yy--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBuildableBlock(Material mat) {
|
||||||
|
return mat == Material.STONE
|
||||||
|
|| mat == Material.GRASS_BLOCK
|
||||||
|
|| mat == Material.DIRT
|
||||||
|
|| mat == Material.SAND
|
||||||
|
|| mat == Material.SANDSTONE
|
||||||
|
|| mat == Material.GRAVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBlock(World world, int x, int y, int z, Material mat) {
|
||||||
|
world.getBlockAt(x, y, z).setType(mat, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/main/java/fyi/tiko/battletowers/TowerLootManager.java
Normal file
36
src/main/java/fyi/tiko/battletowers/TowerLootManager.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package fyi.tiko.battletowers;
|
||||||
|
|
||||||
|
import fyi.tiko.battletowers.BattleTowersPlugin;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class TowerLootManager {
|
||||||
|
|
||||||
|
private final TowerStageItemManager[] floors = new TowerStageItemManager[10];
|
||||||
|
|
||||||
|
public TowerLootManager() {
|
||||||
|
|
||||||
|
var cfg = BattleTowersPlugin.instance().getConfig();
|
||||||
|
|
||||||
|
floors[0] = new TowerStageItemManager(cfg.getString("loot.floor1"));
|
||||||
|
floors[1] = new TowerStageItemManager(cfg.getString("loot.floor2"));
|
||||||
|
floors[2] = new TowerStageItemManager(cfg.getString("loot.floor3"));
|
||||||
|
floors[3] = new TowerStageItemManager(cfg.getString("loot.floor4"));
|
||||||
|
floors[4] = new TowerStageItemManager(cfg.getString("loot.floor5"));
|
||||||
|
floors[5] = new TowerStageItemManager(cfg.getString("loot.floor6"));
|
||||||
|
floors[6] = new TowerStageItemManager(cfg.getString("loot.floor7"));
|
||||||
|
floors[7] = new TowerStageItemManager(cfg.getString("loot.floor8"));
|
||||||
|
floors[8] = new TowerStageItemManager(cfg.getString("loot.floor9"));
|
||||||
|
floors[9] = new TowerStageItemManager(cfg.getString("loot.top"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public TowerStageItemManager getManagerForFloor(int floor, Random rand) {
|
||||||
|
|
||||||
|
floor--; // Forge subtract
|
||||||
|
|
||||||
|
if (floor < 0) floor = 0;
|
||||||
|
if (floor >= floors.length) floor = floors.length - 1;
|
||||||
|
|
||||||
|
return new TowerStageItemManager(floors[floor]);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/main/java/fyi/tiko/battletowers/TowerPopulator.java
Normal file
24
src/main/java/fyi/tiko/battletowers/TowerPopulator.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package fyi.tiko.battletowers;
|
||||||
|
|
||||||
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.generator.BlockPopulator;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class TowerPopulator extends BlockPopulator {
|
||||||
|
|
||||||
|
private final TowerGenerator generator = new TowerGenerator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void populate(World world, Random random, Chunk chunk) {
|
||||||
|
|
||||||
|
// Chance pro Chunk (erstmal test)
|
||||||
|
if (random.nextInt(250) != 0) return;
|
||||||
|
|
||||||
|
int x = chunk.getX() * 16 + 8;
|
||||||
|
int z = chunk.getZ() * 16 + 8;
|
||||||
|
|
||||||
|
generator.trySpawnTower(world, random, x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package fyi.tiko.battletowers;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class TowerStageItemManager {
|
||||||
|
|
||||||
|
private final List<LootEntry> entries = new ArrayList<>();
|
||||||
|
private int curIndex = 0;
|
||||||
|
|
||||||
|
public TowerStageItemManager(String configString) {
|
||||||
|
|
||||||
|
String[] elements = configString.split(";");
|
||||||
|
|
||||||
|
for (String element : elements) {
|
||||||
|
|
||||||
|
String[] data = element.trim().split("-");
|
||||||
|
|
||||||
|
if (data.length < 5) {
|
||||||
|
System.err.println("Invalid loot entry: " + element);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Material mat = Material.matchMaterial(data[0].toUpperCase());
|
||||||
|
if (mat == null) {
|
||||||
|
System.err.println("Unknown material: " + data[0]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int damage = Integer.parseInt(data[1]); // ignored in modern MC
|
||||||
|
int chance = Integer.parseInt(data[2]);
|
||||||
|
int min = Integer.parseInt(data[3]);
|
||||||
|
int max = Integer.parseInt(data[4]);
|
||||||
|
|
||||||
|
entries.add(new LootEntry(mat, chance, min, max));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TowerStageItemManager(TowerStageItemManager copy) {
|
||||||
|
entries.addAll(copy.entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean floorHasItemsLeft() {
|
||||||
|
return curIndex < entries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getStageItem(Random rand) {
|
||||||
|
|
||||||
|
if (!floorHasItemsLeft()) return null;
|
||||||
|
|
||||||
|
LootEntry entry = entries.get(curIndex);
|
||||||
|
|
||||||
|
ItemStack result = null;
|
||||||
|
|
||||||
|
if (rand.nextInt(100) < entry.chance) {
|
||||||
|
int amount = entry.min + rand.nextInt(entry.max - entry.min + 1);
|
||||||
|
result = new ItemStack(entry.material, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
curIndex++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record LootEntry(Material material, int chance, int min, int max) {}
|
||||||
|
}
|
||||||
30
src/main/java/fyi/tiko/battletowers/TowerType.java
Normal file
30
src/main/java/fyi/tiko/battletowers/TowerType.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package fyi.tiko.battletowers;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
public enum TowerType {
|
||||||
|
|
||||||
|
COBBLE(Material.COBBLESTONE, Material.STONE_BRICKS, Material.TORCH, Material.STONE_STAIRS),
|
||||||
|
MOSSY(Material.MOSSY_COBBLESTONE, Material.STONE_BRICKS, Material.TORCH, Material.STONE_STAIRS),
|
||||||
|
SANDSTONE(Material.SANDSTONE, Material.CUT_SANDSTONE, Material.TORCH, Material.SANDSTONE_STAIRS),
|
||||||
|
ICE(Material.ICE, Material.CLAY, Material.AIR, Material.OAK_STAIRS),
|
||||||
|
SMOOTH(Material.STONE, Material.SMOOTH_STONE, Material.TORCH, Material.STONE_STAIRS),
|
||||||
|
NETHER(Material.NETHERRACK, Material.SOUL_SAND, Material.GLOWSTONE, Material.NETHER_BRICK_STAIRS);
|
||||||
|
|
||||||
|
private final Material wall;
|
||||||
|
private final Material floor;
|
||||||
|
private final Material light;
|
||||||
|
private final Material stair;
|
||||||
|
|
||||||
|
TowerType(Material wall, Material floor, Material light, Material stair) {
|
||||||
|
this.wall = wall;
|
||||||
|
this.floor = floor;
|
||||||
|
this.light = light;
|
||||||
|
this.stair = stair;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Material wall() { return wall; }
|
||||||
|
public Material floor() { return floor; }
|
||||||
|
public Material light() { return light; }
|
||||||
|
public Material stair() { return stair; }
|
||||||
|
}
|
||||||
@@ -4,3 +4,18 @@ spawn:
|
|||||||
max-y-difference: 22
|
max-y-difference: 22
|
||||||
|
|
||||||
debug: true
|
debug: true
|
||||||
|
|
||||||
|
tower:
|
||||||
|
itemGenerateAttemptsPerFloor: 7
|
||||||
|
|
||||||
|
loot:
|
||||||
|
floor1: "stick-0-75-5-6;wheat_seeds-0-75-3-5;oak_planks-0-75-5-6;sugar_cane-0-75-3-5"
|
||||||
|
floor2: "stone_pickaxe-0-50-1-1;stone_axe-0-50-1-1;torch-0-80-3-3;stone_button-0-50-2-2"
|
||||||
|
floor3: "bowl-0-75-2-4;coal-0-90-4-4;string-0-80-5-5;white_wool-0-75-2-2"
|
||||||
|
floor4: "glass-0-75-3-3;feather-0-75-4-4;bread-0-75-2-2;apple-0-75-2-2"
|
||||||
|
floor5: "brown_mushroom-0-75-2-2;red_mushroom-0-75-2-2;oak_sapling-0-90-3-3;wheat-0-75-4-4"
|
||||||
|
floor6: "oak_sign-0-50-1-2;fishing_rod-0-75-1-1;pumpkin_seeds-0-60-2-2;melon_seeds-0-60-3-3"
|
||||||
|
floor7: "iron_sword-0-60-1-1;gunpowder-0-75-3-3;leather-0-75-4-4;cod-0-75-3-3;blue_dye-0-60-1-2"
|
||||||
|
floor8: "chainmail_helmet-0-40-1-1;chainmail_chestplate-0-40-1-1;chainmail_leggings-0-40-1-1;chainmail_boots-0-40-1-1"
|
||||||
|
floor9: "bookshelf-0-70-1-3;redstone_lamp-0-60-2-2;lily_pad-0-75-3-3;brewing_stand-0-50-1-1"
|
||||||
|
top: "ender_pearl-0-50-2-2;diamond-0-70-2-2;redstone-0-75-5-5;gold_ingot-0-90-8-8"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: battle-towers-revamped
|
name: battle-towers-revamped
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
main: fyi.tiko.battletower.BattleTowerPlugin
|
main: fyi.tiko.battletowers.BattleTowersPlugin
|
||||||
api-version: 1.21
|
api-version: 1.21
|
||||||
author: tiko
|
author: tiko
|
||||||
description: A revamped version of the old battle towers mod for the old modpack "hexxit". Ported from 1.5.2 to paper 1.21
|
description: A revamped version of the old battle towers mod for the old modpack "hexxit". Ported from 1.5.2 to paper 1.21
|
||||||
|
|||||||
Reference in New Issue
Block a user