diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Cursor.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Cursor.java new file mode 100644 index 0000000..8d465ee --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Cursor.java @@ -0,0 +1,58 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense; + +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.EntityType; +import net.minestom.server.entity.Player; +import net.minestom.server.instance.block.Block; +import org.jetbrains.annotations.Nullable; + +public class Cursor extends Entity { + private boolean cursorActive = false; + private final int reach; + + public Cursor(int reach) { + super(EntityType.ARMOR_STAND); + this.setBoundingBox(0,0,0); + this.reach = reach; + } + + public void updateCursorPosition(Player player) { + Point targetPosition = player.getTargetBlockPosition(this.reach); + this.moveCursorTo(targetPosition); + } + + private void moveCursorTo(@Nullable Point newTargetBlockPosition) { + this.setCursorEnabled(true); + if(newTargetBlockPosition == null) { + this.setCursorEnabled(false); + return; + } + if(!this.getInstance().getBlock(newTargetBlockPosition).equals(Block.BLACK_WOOL)) this.setCursorEnabled(false); + if(!this.getInstance().getGame().getAvailableTowers().containsKey(this.getInstance().getPlayer().getItemInMainHand().material())) this.setCursorEnabled(false); + this.teleport(new Pos(newTargetBlockPosition.add(0.5,1,0.5))); + } + + private void setCursorEnabled(boolean enabled) { + this.cursorActive = enabled; + this.setInvisible(!enabled); + } + + @Override + public TowerdefenseRoom getInstance() { + return (TowerdefenseRoom) super.getInstance(); + } + + public boolean isCursorActive() { + return this.cursorActive; + } + + public Block getTargetBlock() { + return this.getInstance().getBlock(this.getTargetBlockPosition()); + } + + public Pos getTargetBlockPosition() { + return this.getPosition().sub(0, 0.5, 0); + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Towerdefense.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Towerdefense.java index 48b4422..e4222a5 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Towerdefense.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Towerdefense.java @@ -31,9 +31,9 @@ public class Towerdefense extends StatelessGame { Material.BLAZE_SPAWN_EGG, BlazeTower.class ); private final Map, Integer> prices = Map.of( - ZombieTower.class, 14, - SkeletonTower.class, 10, - BlazeTower.class, 30 + ZombieTower.class, 4, + SkeletonTower.class, 3, + BlazeTower.class, 8 ); private static final int pathLength = 10; @@ -45,6 +45,13 @@ public class Towerdefense extends StatelessGame { this.generateMaze(); } + @Override + protected void checkAbandoned() { + this.scheduleNextTick((instance) -> { + if(this.instances.stream().allMatch(room -> room.getPlayers().isEmpty()) && this.getPlayers().isEmpty()) this.unload(); + }); + } + @Override protected void onStart() { this.getPlayers().forEach(player -> { @@ -52,7 +59,7 @@ public class Towerdefense extends StatelessGame { this.instances.add(newRoom); player.setInstance(newRoom); newRoom.startWave(List.of( - new GroupFactory(new EnemyFactory(EntityType.VILLAGER), 1, 800) + new GroupFactory(new EnemyFactory(EntityType.VILLAGER, 2, 0.1), 1, 800) )); }); } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/TowerdefenseRoom.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/TowerdefenseRoom.java index c96dda8..f4b67c9 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/TowerdefenseRoom.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/TowerdefenseRoom.java @@ -9,8 +9,6 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextColor; import net.minestom.server.MinecraftServer; import net.minestom.server.collision.Aerodynamics; -import net.minestom.server.coordinate.Point; -import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.*; import net.minestom.server.entity.attribute.Attribute; @@ -40,7 +38,7 @@ public class TowerdefenseRoom extends InstanceContainer { private final Towerdefense game; private final List enemies = new ArrayList<>(); private final List towers = new ArrayList<>(); - private final Entity cursor; + private final Cursor cursor; private int money = 0; public TowerdefenseRoom(Player player, Towerdefense game) { @@ -56,31 +54,37 @@ public class TowerdefenseRoom extends InstanceContainer { this.player.getInventory().addItemStack(ItemStack.of(Material.SPECTRAL_ARROW).withCustomName(Component.text("Schießen", TextColor.color(255, 180, 0)))); this.player.getInventory().addItemStack(ItemStack.of(Material.BARRIER).withCustomName(Component.text("Löschen", TextColor.color(255,0,0)))); - this.game.getAvailableTowers().keySet().forEach(material -> this.player.getInventory().addItemStack(ItemStack.of(material))); + this.game.getAvailableTowers().forEach((material, tower) -> { + int price = this.game.getPrices().get(tower); + this.player.getInventory().addItemStack(ItemStack.of(material).withMaxStackSize(price).withAmount(price)); + }); this.setGenerator(new MazeGenerator()); BatchUtil.loadAndApplyBatch(this.game.getMazeBatch(), this, () -> {}); - this.cursor = new Entity(EntityType.ARMOR_STAND); + this.cursor = new Cursor(reach); this.cursor.setInstance(this); this.cursor.setBoundingBox(0,0,0); MinecraftServer.getSchedulerManager().scheduleTask(() -> this.startWave(List.of( - new GroupFactory(new EnemyFactory(EntityType.VILLAGER), 2, 800) + new GroupFactory(new EnemyFactory(EntityType.VILLAGER, 3, 0.1), 1, 800) )), TaskSchedule.seconds(20), TaskSchedule.stop()); MinecraftServer.getSchedulerManager().scheduleTask(() -> this.startWave(List.of( - new GroupFactory(new EnemyFactory(EntityType.VILLAGER), 4, 800) + new GroupFactory(new EnemyFactory(EntityType.VILLAGER, 3, 0.1), 2, 800) )), TaskSchedule.seconds(50), TaskSchedule.stop()); MinecraftServer.getSchedulerManager().scheduleTask(() -> this.startWave(List.of( - new GroupFactory(new EnemyFactory(EntityType.VILLAGER), 6, 800) + new GroupFactory(new EnemyFactory(EntityType.VILLAGER), 2, 800) )), TaskSchedule.seconds(90), TaskSchedule.stop()); + MinecraftServer.getSchedulerManager().scheduleTask(() -> this.startWave(List.of( + new GroupFactory(new EnemyFactory(EntityType.EGG), 200, 800) + )), TaskSchedule.seconds(150), TaskSchedule.stop()); this.eventNode() .addListener(EntityDeathEvent.class, event -> { if(!(event.getEntity() instanceof EntityCreature enemy)) return; this.enemies.remove(enemy); }) - .addListener(PlayerTickEvent.class, this::setCursorPosition) + .addListener(PlayerTickEvent.class, event -> this.cursor.updateCursorPosition(this.player)) .addListener(PlayerUseItemEvent.class, event -> this.useItem()) .addListener(PlayerUseItemOnBlockEvent.class, event -> this.useItem()) .addListener(PlayerEntityInteractEvent.class, event -> this.useItem()) @@ -133,9 +137,14 @@ public class TowerdefenseRoom extends InstanceContainer { } private void placeTower() { - Material usedMaterial = this.player.getItemInMainHand().material(); - if(!this.canPlaceTower(usedMaterial)) return; + if(!this.canPlaceActiveTower()) { + if(this.cursor.isCursorActive() && !this.enoughMoneyForActiveTower()) { + this.player.sendActionBar(Component.text("Nicht genug Geld!", TextColor.color(255,0,0))); + } + return; + } try { + Material usedMaterial = this.player.getItemInMainHand().material(); Tower tower = this.game.getAvailableTowers().entrySet().stream() .filter(materialClassEntry -> materialClassEntry.getKey().equals(usedMaterial)) .findFirst() @@ -143,7 +152,7 @@ public class TowerdefenseRoom extends InstanceContainer { .getValue() .getConstructor() .newInstance(); - this.setBlock(this.cursor.getPosition().sub(0, 0.5, 0), Block.BLUE_WOOL); + this.setBlock(this.cursor.getTargetBlockPosition(), Block.BLUE_WOOL); tower.setInstance(this, this.cursor.getPosition()); this.towers.add(tower); this.addMoney(-this.game.getPrices().get(tower.getClass())); @@ -162,24 +171,24 @@ public class TowerdefenseRoom extends InstanceContainer { tower.remove(); } - private void setCursorPosition(PlayerTickEvent event) { - Point newPosition = this.player.getTargetBlockPosition(reach); - if(newPosition == null) newPosition = this.cursor.getPosition().withY(-2); - newPosition = newPosition.add(0.5,1,0.5); - this.cursor.teleport(new Pos(newPosition)); - this.cursor.setInvisible(!(this.canPlaceTower((Material) null) && this.game.getAvailableTowers().containsKey(this.player.getItemInMainHand().material()))); - } - - private boolean canPlaceTower(@Nullable Material usedMaterial) { - if(usedMaterial == null) return this.canPlaceTower((Class) null); + private boolean canPlaceActiveTower() { + Material usedMaterial = this.player.getItemInMainHand().material(); return this.canPlaceTower(this.game.getAvailableTowers().get(usedMaterial)); } private boolean canPlaceTower(@Nullable Class towerClass) { - boolean blockAllowed = this.getBlock(this.cursor.getPosition().sub(0, 1, 0)).equals(Block.BLACK_WOOL); - if(towerClass == null) return blockAllowed; - boolean enoughMoney = this.money >= this.game.getPrices().get(towerClass); - return blockAllowed && enoughMoney; + if(towerClass == null) return false; + return this.cursor.isCursorActive() && this.enoughMoney(towerClass); + } + + private boolean enoughMoneyForActiveTower() { + Material usedMaterial = this.player.getItemInMainHand().material(); + return this.enoughMoney(this.game.getAvailableTowers().get(usedMaterial)); + } + + private boolean enoughMoney(@Nullable Class towerClass) { + if(towerClass == null) return true; + return this.money >= this.game.getPrices().get(towerClass); } void startWave(List enemyGroups) { @@ -199,6 +208,8 @@ public class TowerdefenseRoom extends InstanceContainer { float damage = (float) Math.ceil(enemy.getHealth()/10); if(this.player.getHealth() - damage <= 0) { this.player.setInstance(this.game); + this.getEnemies().forEach(Entity::remove); + this.towers.forEach(Entity::remove); this.game.getScore().insertResult(this.player); return; } @@ -215,4 +226,8 @@ public class TowerdefenseRoom extends InstanceContainer { public List getEnemies() { return this.enemies; } + + public Player getPlayer() { + return this.player; + } }