added towers and added tower spawn mechanics
This commit is contained in:
parent
38c944e6c1
commit
893923cc56
@ -5,6 +5,16 @@ import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.attribute.Attribute;
|
||||
|
||||
record EnemyFactory(EntityType entityType, float health, double speed) {
|
||||
/**
|
||||
* Factory for a tower defense enemy.
|
||||
* @param entityType type of enemy
|
||||
* @param health base health, between 0 and 1024
|
||||
* @param speed walk speed of enemy
|
||||
*/
|
||||
public EnemyFactory {
|
||||
if(health > 1024 || health <= 0) throw new IllegalArgumentException("Enemy health has to be between 0 and 1024");
|
||||
}
|
||||
|
||||
public EnemyFactory(EntityType entityType) {
|
||||
this(entityType, 20, 0.3);
|
||||
}
|
||||
@ -12,6 +22,7 @@ record EnemyFactory(EntityType entityType, float health, double speed) {
|
||||
public EntityCreature buildEntity() {
|
||||
EntityCreature entity = new EntityCreature(this.entityType);
|
||||
entity.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(this.speed);
|
||||
entity.getAttribute(Attribute.MAX_HEALTH).setBaseValue(this.health);
|
||||
entity.setHealth(this.health);
|
||||
return entity;
|
||||
}
|
||||
|
@ -3,15 +3,21 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
|
||||
import eu.mhsl.minenet.minigames.instance.Dimension;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.BlazeTower;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.SkeletonTower;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.Tower;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.ZombieTower;
|
||||
import eu.mhsl.minenet.minigames.score.LastWinsScore;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.Material;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
public class Towerdefense extends StatelessGame {
|
||||
@ -19,6 +25,11 @@ public class Towerdefense extends StatelessGame {
|
||||
private final AbsoluteBlockBatch mazeBatch = new AbsoluteBlockBatch();
|
||||
private final List<Pos> mazePath = new ArrayList<>();
|
||||
private final List<TowerdefenseRoom> instances = new ArrayList<>();
|
||||
private final Map<Material, Class<? extends Tower>> availableTowers = Map.of(
|
||||
Material.SKELETON_SPAWN_EGG, SkeletonTower.class,
|
||||
Material.ZOMBIE_SPAWN_EGG, ZombieTower.class,
|
||||
Material.BLAZE_SPAWN_EGG, BlazeTower.class
|
||||
);
|
||||
|
||||
private static final int pathLength = 10;
|
||||
|
||||
@ -79,8 +90,13 @@ public class Towerdefense extends StatelessGame {
|
||||
p.setInstance(newRoom);
|
||||
newRoom.startWave(List.of(
|
||||
new GroupFactory(new EnemyFactory(EntityType.VILLAGER, 20, 0.1), 5, 800),
|
||||
new GroupFactory(new EnemyFactory(EntityType.BEE, 10, 1), 3, 400)
|
||||
new GroupFactory(new EnemyFactory(EntityType.BEE, 10, 1), 3, 400),
|
||||
new GroupFactory(new EnemyFactory(EntityType.SNIFFER, 1000, 0.02), 1, 0)
|
||||
));
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map<Material, Class<? extends Tower>> getAvailableTowers() {
|
||||
return this.availableTowers;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
|
||||
|
||||
import eu.mhsl.minenet.minigames.instance.Dimension;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.SkeletonTower;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.Tower;
|
||||
import eu.mhsl.minenet.minigames.util.BatchUtil;
|
||||
import eu.mhsl.minenet.minigames.util.CommonEventHandles;
|
||||
@ -13,7 +12,10 @@ import net.minestom.server.entity.*;
|
||||
import net.minestom.server.entity.attribute.Attribute;
|
||||
import net.minestom.server.entity.damage.DamageType;
|
||||
import net.minestom.server.event.entity.EntityDeathEvent;
|
||||
import net.minestom.server.event.inventory.InventoryPreClickEvent;
|
||||
import net.minestom.server.event.item.ItemDropEvent;
|
||||
import net.minestom.server.event.player.PlayerEntityInteractEvent;
|
||||
import net.minestom.server.event.player.PlayerSwapItemEvent;
|
||||
import net.minestom.server.event.player.PlayerTickEvent;
|
||||
import net.minestom.server.event.player.PlayerUseItemEvent;
|
||||
import net.minestom.server.instance.InstanceContainer;
|
||||
@ -40,13 +42,15 @@ public class TowerdefenseRoom extends InstanceContainer {
|
||||
this.player.setGameMode(GameMode.ADVENTURE);
|
||||
this.player.setAllowFlying(true);
|
||||
this.player.getAttribute(Attribute.BLOCK_INTERACTION_RANGE).setBaseValue(200);
|
||||
this.player.getInventory().setItemStack(0, ItemStack.of(Material.SKELETON_SPAWN_EGG));
|
||||
|
||||
this.game.getAvailableTowers().keySet().forEach(material -> this.player.getInventory().addItemStack(ItemStack.of(material)));
|
||||
|
||||
this.setGenerator(new MazeGenerator());
|
||||
BatchUtil.loadAndApplyBatch(this.game.getMazeBatch(), this, () -> {});
|
||||
|
||||
this.cursor = new Entity(EntityType.ARMOR_STAND);
|
||||
this.cursor.setInstance(this);
|
||||
this.cursor.setBoundingBox(0,0,0);
|
||||
|
||||
this.eventNode()
|
||||
.addListener(EntityDeathEvent.class, event -> {
|
||||
@ -54,19 +58,27 @@ public class TowerdefenseRoom extends InstanceContainer {
|
||||
this.enemies.remove(enemy);
|
||||
})
|
||||
.addListener(PlayerTickEvent.class, this::setCursorPosition)
|
||||
.addListener(PlayerUseItemEvent.class, this::setTower)
|
||||
.addListener(ItemDropEvent.class, CommonEventHandles::cancel);
|
||||
.addListener(PlayerUseItemEvent.class, event -> this.setTower(event.getItemStack().material()))
|
||||
.addListener(PlayerEntityInteractEvent.class, event -> this.setTower(event.getPlayer().getItemInMainHand().material()))
|
||||
.addListener(ItemDropEvent.class, CommonEventHandles::cancel)
|
||||
.addListener(InventoryPreClickEvent.class, CommonEventHandles::cancel)
|
||||
.addListener(PlayerSwapItemEvent.class, CommonEventHandles::cancel);
|
||||
}
|
||||
|
||||
private void setTower(PlayerUseItemEvent event) {
|
||||
if(event.getItemStack().material().equals(Material.SKELETON_SPAWN_EGG)) {
|
||||
Point newPosition = this.player.getTargetBlockPosition(20);
|
||||
if(newPosition == null) return;
|
||||
if(!this.getBlock(newPosition).equals(Block.BLACK_WOOL)) return;
|
||||
this.setBlock(newPosition, Block.BLUE_WOOL);
|
||||
newPosition = newPosition.add(0.5,1,0.5);
|
||||
SkeletonTower tower = new SkeletonTower();
|
||||
tower.setInstance(this, newPosition);
|
||||
private void setTower(Material usedMaterial) {
|
||||
if(!this.canPlaceTower()) return;
|
||||
try {
|
||||
Tower tower = this.game.getAvailableTowers().entrySet().stream()
|
||||
.filter(materialClassEntry -> materialClassEntry.getKey().equals(usedMaterial))
|
||||
.findFirst()
|
||||
.orElseThrow()
|
||||
.getValue()
|
||||
.getConstructor()
|
||||
.newInstance();
|
||||
this.setBlock(this.cursor.getPosition().sub(0, 0.5, 0), Block.BLUE_WOOL);
|
||||
tower.setInstance(this, this.cursor.getPosition());
|
||||
this.towers.add(tower);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,14 +89,13 @@ public class TowerdefenseRoom extends InstanceContainer {
|
||||
private void setCursorPosition(PlayerTickEvent event) {
|
||||
Point newPosition = this.player.getTargetBlockPosition(20);
|
||||
if(newPosition == null) return;
|
||||
if(this.getBlock(newPosition).equals(Block.BLACK_WOOL)) {
|
||||
this.cursor.setInvisible(false);
|
||||
} else {
|
||||
this.cursor.setInvisible(true);
|
||||
return;
|
||||
}
|
||||
newPosition = newPosition.add(0.5,1,0.5);
|
||||
this.cursor.teleport(new Pos(newPosition));
|
||||
this.cursor.setInvisible(!this.canPlaceTower());
|
||||
}
|
||||
|
||||
private boolean canPlaceTower() {
|
||||
return this.getBlock(this.cursor.getPosition().sub(0, 1, 0)).equals(Block.BLACK_WOOL);
|
||||
}
|
||||
|
||||
void startWave(List<GroupFactory> enemyGroups) {
|
||||
|
@ -0,0 +1,15 @@
|
||||
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
|
||||
|
||||
import net.minestom.server.entity.EntityCreature;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
|
||||
public class BlazeTower extends Tower {
|
||||
public BlazeTower() {
|
||||
super(EntityType.BLAZE, 2, 1, 20);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attack(EntityCreature enemy) {
|
||||
this.shootProjectile(enemy, EntityType.FIREBALL, 2, 0);
|
||||
}
|
||||
}
|
@ -2,23 +2,23 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.tow
|
||||
|
||||
import net.minestom.server.entity.EntityCreature;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.attribute.Attribute;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
|
||||
public class SkeletonTower extends Tower {
|
||||
public SkeletonTower() {
|
||||
super(EntityType.SKELETON, 1, 300, 20);
|
||||
super(EntityType.SKELETON, 1, 3, 15);
|
||||
this.setItemInMainHand(ItemStack.of(Material.BOW));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shoot(EntityCreature enemy) {
|
||||
protected void attack(EntityCreature enemy) {
|
||||
this.shootProjectile(enemy, EntityType.ARROW, 2, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnemyHit(EntityCreature enemy) {
|
||||
enemy.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(enemy.getAttribute(Attribute.MOVEMENT_SPEED).getBaseValue()*0.5);
|
||||
protected void onProjectileHit(EntityCreature enemy) {
|
||||
super.onProjectileHit(enemy);
|
||||
// enemy.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(enemy.getAttribute(Attribute.MOVEMENT_SPEED).getBaseValue()*0.5);
|
||||
}
|
||||
}
|
||||
|
@ -45,22 +45,23 @@ public abstract class Tower extends EntityCreature {
|
||||
}
|
||||
}
|
||||
|
||||
private final static int damageDivider = 10;
|
||||
private Priority priority = Priority.FIRST;
|
||||
protected float damage;
|
||||
protected int range;
|
||||
protected TaskSchedule attackDelay;
|
||||
private final Task shootTask;
|
||||
|
||||
public Tower(@NotNull EntityType entityType, float damage, int attackDelay, int range) {
|
||||
public Tower(@NotNull EntityType entityType, int damage, double attacksPerSecond, int range) {
|
||||
super(entityType);
|
||||
this.damage = damage;
|
||||
this.damage = (float) damage / damageDivider;
|
||||
this.range = range;
|
||||
this.attackDelay = TaskSchedule.millis(attackDelay);
|
||||
this.attackDelay = TaskSchedule.millis((long) (1000/attacksPerSecond));
|
||||
this.shootTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> {
|
||||
EntityCreature nextEnemy = this.getNextEnemy();
|
||||
if(nextEnemy == null) return this.attackDelay;
|
||||
this.lookAt(nextEnemy);
|
||||
this.shoot(nextEnemy);
|
||||
this.attack(nextEnemy);
|
||||
return this.attackDelay;
|
||||
}, TaskSchedule.immediate());
|
||||
}
|
||||
@ -92,14 +93,13 @@ public abstract class Tower extends EntityCreature {
|
||||
if(!(event.getTarget() instanceof EntityCreature target)) return;
|
||||
if(!this.getRoomInstance().getEnemies().contains(target)) return;
|
||||
target.damage(DamageType.PLAYER_ATTACK, this.damage);
|
||||
this.onEnemyHit(target);
|
||||
this.onProjectileHit(target);
|
||||
projectile.remove();
|
||||
})
|
||||
.addListener(ProjectileCollideWithBlockEvent.class, event -> projectile.remove());
|
||||
}
|
||||
|
||||
protected void onEnemyHit(EntityCreature enemy) {
|
||||
|
||||
protected void onProjectileHit(EntityCreature enemy) {
|
||||
}
|
||||
|
||||
protected TowerdefenseRoom getRoomInstance() {
|
||||
@ -146,5 +146,5 @@ public abstract class Tower extends EntityCreature {
|
||||
super.remove(permanent);
|
||||
}
|
||||
|
||||
protected abstract void shoot(EntityCreature enemy);
|
||||
protected abstract void attack(EntityCreature enemy);
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
|
||||
|
||||
import net.minestom.server.entity.EntityCreature;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.damage.DamageType;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
|
||||
public class ZombieTower extends Tower {
|
||||
public ZombieTower() {
|
||||
super(EntityType.ZOMBIE, 3, 0.7, 4);
|
||||
this.setItemInMainHand(ItemStack.of(Material.IRON_SWORD));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attack(EntityCreature enemy) {
|
||||
this.swingMainHand();
|
||||
enemy.damage(DamageType.PLAYER_ATTACK, this.damage);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user