added wave mechanic
This commit is contained in:
@ -2,18 +2,21 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
|
|||||||
|
|
||||||
import eu.mhsl.minenet.minigames.instance.Dimension;
|
import eu.mhsl.minenet.minigames.instance.Dimension;
|
||||||
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
|
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
|
||||||
|
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies.WaveGenerator;
|
||||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator;
|
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.BlazeTower;
|
||||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.SkeletonTower;
|
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.Tower;
|
||||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.ZombieTower;
|
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.ZombieTower;
|
||||||
import eu.mhsl.minenet.minigames.score.LastWinsScore;
|
import eu.mhsl.minenet.minigames.score.LastWinsScore;
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.coordinate.Pos;
|
import net.minestom.server.coordinate.Pos;
|
||||||
import net.minestom.server.entity.EntityType;
|
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
|
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.item.Material;
|
import net.minestom.server.item.Material;
|
||||||
|
import net.minestom.server.timer.Task;
|
||||||
|
import net.minestom.server.timer.TaskSchedule;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -25,6 +28,8 @@ public class Towerdefense extends StatelessGame {
|
|||||||
private final AbsoluteBlockBatch mazeBatch = new AbsoluteBlockBatch();
|
private final AbsoluteBlockBatch mazeBatch = new AbsoluteBlockBatch();
|
||||||
private final List<Pos> mazePath = new ArrayList<>();
|
private final List<Pos> mazePath = new ArrayList<>();
|
||||||
private final List<TowerdefenseRoom> instances = new ArrayList<>();
|
private final List<TowerdefenseRoom> instances = new ArrayList<>();
|
||||||
|
private final WaveGenerator waveGenerator;
|
||||||
|
private Task waveTask;
|
||||||
private final Map<Material, Class<? extends Tower>> availableTowers = Map.of(
|
private final Map<Material, Class<? extends Tower>> availableTowers = Map.of(
|
||||||
Material.SKELETON_SPAWN_EGG, SkeletonTower.class,
|
Material.SKELETON_SPAWN_EGG, SkeletonTower.class,
|
||||||
Material.ZOMBIE_SPAWN_EGG, ZombieTower.class,
|
Material.ZOMBIE_SPAWN_EGG, ZombieTower.class,
|
||||||
@ -35,7 +40,6 @@ public class Towerdefense extends StatelessGame {
|
|||||||
SkeletonTower.class, 3,
|
SkeletonTower.class, 3,
|
||||||
BlazeTower.class, 8
|
BlazeTower.class, 8
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final int pathLength = 10;
|
private static final int pathLength = 10;
|
||||||
|
|
||||||
public Towerdefense() {
|
public Towerdefense() {
|
||||||
@ -43,12 +47,17 @@ public class Towerdefense extends StatelessGame {
|
|||||||
|
|
||||||
this.setGenerator(new MazeGenerator());
|
this.setGenerator(new MazeGenerator());
|
||||||
this.generateMaze();
|
this.generateMaze();
|
||||||
|
|
||||||
|
this.waveGenerator = new WaveGenerator(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void checkAbandoned() {
|
protected void checkAbandoned() {
|
||||||
this.scheduleNextTick((instance) -> {
|
this.scheduleNextTick((instance) -> {
|
||||||
if(this.instances.stream().allMatch(room -> room.getPlayers().isEmpty()) && this.getPlayers().isEmpty()) this.unload();
|
if(this.instances.stream().allMatch(room -> room.getPlayers().isEmpty()) && this.getPlayers().isEmpty()) {
|
||||||
|
this.waveTask.cancel();
|
||||||
|
this.unload();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,10 +67,12 @@ public class Towerdefense extends StatelessGame {
|
|||||||
TowerdefenseRoom newRoom = new TowerdefenseRoom(player, this);
|
TowerdefenseRoom newRoom = new TowerdefenseRoom(player, this);
|
||||||
this.instances.add(newRoom);
|
this.instances.add(newRoom);
|
||||||
player.setInstance(newRoom, new Pos(0, 1, 0));
|
player.setInstance(newRoom, new Pos(0, 1, 0));
|
||||||
newRoom.startWave(List.of(
|
|
||||||
new GroupFactory(new EnemyFactory(EntityType.VILLAGER, 2, 0.1), 1, 800)
|
|
||||||
));
|
|
||||||
});
|
});
|
||||||
|
this.waveTask = MinecraftServer.getSchedulerManager().scheduleTask(
|
||||||
|
this.waveGenerator::startNextWave,
|
||||||
|
TaskSchedule.seconds(3),
|
||||||
|
TaskSchedule.seconds(25)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateMaze() {
|
private void generateMaze() {
|
||||||
@ -107,9 +118,14 @@ public class Towerdefense extends StatelessGame {
|
|||||||
return this.mazePath;
|
return this.mazePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TowerdefenseRoom> getInstances() {
|
||||||
|
return this.instances;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean onPlayerJoin(Player p) {
|
protected boolean onPlayerJoin(Player p) {
|
||||||
return false;
|
MinecraftServer.getSchedulerManager().scheduleNextTick(() -> p.teleport(new Pos((long) -this.getPlayers().size(), 1, 0)));
|
||||||
|
return super.onPlayerJoin(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Class<? extends Tower>, Integer> getPrices() {
|
public Map<Class<? extends Tower>, Integer> getPrices() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
|
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
|
||||||
|
|
||||||
import eu.mhsl.minenet.minigames.instance.Dimension;
|
import eu.mhsl.minenet.minigames.instance.Dimension;
|
||||||
|
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies.GroupFactory;
|
||||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator;
|
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator;
|
||||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.Tower;
|
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.BatchUtil;
|
||||||
@ -25,7 +26,6 @@ import net.minestom.server.instance.block.Block;
|
|||||||
import net.minestom.server.item.ItemAnimation;
|
import net.minestom.server.item.ItemAnimation;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.item.Material;
|
import net.minestom.server.item.Material;
|
||||||
import net.minestom.server.timer.TaskSchedule;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@ -72,19 +72,6 @@ public class TowerdefenseRoom extends InstanceContainer {
|
|||||||
this.cursor.setInstance(this);
|
this.cursor.setInstance(this);
|
||||||
this.cursor.setBoundingBox(0,0,0);
|
this.cursor.setBoundingBox(0,0,0);
|
||||||
|
|
||||||
MinecraftServer.getSchedulerManager().scheduleTask(() -> this.startWave(List.of(
|
|
||||||
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, 3, 0.1), 2, 800)
|
|
||||||
)), TaskSchedule.seconds(50), TaskSchedule.stop());
|
|
||||||
MinecraftServer.getSchedulerManager().scheduleTask(() -> this.startWave(List.of(
|
|
||||||
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()
|
this.eventNode()
|
||||||
.addListener(EntityDeathEvent.class, event -> {
|
.addListener(EntityDeathEvent.class, event -> {
|
||||||
if(!(event.getEntity() instanceof EntityCreature enemy)) return;
|
if(!(event.getEntity() instanceof EntityCreature enemy)) return;
|
||||||
@ -123,7 +110,7 @@ public class TowerdefenseRoom extends InstanceContainer {
|
|||||||
EntityProjectile projectile = new EntityProjectile(p, EntityType.SPECTRAL_ARROW);
|
EntityProjectile projectile = new EntityProjectile(p, EntityType.SPECTRAL_ARROW);
|
||||||
projectile.setView(p.getPosition().yaw(), p.getPosition().pitch());
|
projectile.setView(p.getPosition().yaw(), p.getPosition().pitch());
|
||||||
projectile.setNoGravity(true);
|
projectile.setNoGravity(true);
|
||||||
projectile.setAerodynamics(new Aerodynamics(0, 1, 0));
|
projectile.setAerodynamics(new Aerodynamics(0, 1, 1));
|
||||||
|
|
||||||
Vec projectileVelocity = p.getPosition().direction().normalize().mul(20);
|
Vec projectileVelocity = p.getPosition().direction().normalize().mul(20);
|
||||||
projectile.setVelocity(projectileVelocity);
|
projectile.setVelocity(projectileVelocity);
|
||||||
@ -224,6 +211,8 @@ public class TowerdefenseRoom extends InstanceContainer {
|
|||||||
if(this.player.getHealth() - damage <= 0) {
|
if(this.player.getHealth() - damage <= 0) {
|
||||||
this.getEnemies().forEach(Entity::remove);
|
this.getEnemies().forEach(Entity::remove);
|
||||||
this.towers.forEach(Entity::remove);
|
this.towers.forEach(Entity::remove);
|
||||||
|
this.player.getAttribute(Attribute.BLOCK_INTERACTION_RANGE).setBaseValue(this.player.getAttribute(Attribute.BLOCK_INTERACTION_RANGE).attribute().defaultValue());
|
||||||
|
this.player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE).setBaseValue(this.player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE).attribute().defaultValue());
|
||||||
this.player.setInstance(this.game).thenRun(() -> MinecraftServer.getInstanceManager().unregisterInstance(this));
|
this.player.setInstance(this.game).thenRun(() -> MinecraftServer.getInstanceManager().unregisterInstance(this));
|
||||||
this.player.heal();
|
this.player.heal();
|
||||||
this.game.getScore().insertResult(this.player);
|
this.game.getScore().insertResult(this.player);
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
|
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies;
|
||||||
|
|
||||||
import net.minestom.server.entity.EntityCreature;
|
import net.minestom.server.entity.EntityCreature;
|
||||||
import net.minestom.server.entity.EntityType;
|
import net.minestom.server.entity.EntityType;
|
||||||
import net.minestom.server.entity.attribute.Attribute;
|
import net.minestom.server.entity.attribute.Attribute;
|
||||||
|
|
||||||
record EnemyFactory(EntityType entityType, float health, double speed) {
|
public record EnemyFactory(EntityType entityType, float health, double speed) {
|
||||||
/**
|
/**
|
||||||
* Factory for a tower defense enemy.
|
* Factory for a tower defense enemy.
|
||||||
* @param entityType type of enemy
|
* @param entityType type of enemy
|
||||||
@ -26,4 +26,8 @@ record EnemyFactory(EntityType entityType, float health, double speed) {
|
|||||||
entity.setHealth(this.health);
|
entity.setHealth(this.health);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getStrength() {
|
||||||
|
return (int) Math.floor(this.health * this.speed * 5);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
|
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies;
|
||||||
|
|
||||||
|
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.TowerdefenseRoom;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.timer.TaskSchedule;
|
import net.minestom.server.timer.TaskSchedule;
|
||||||
|
|
||||||
record GroupFactory(EnemyFactory enemyFactory, int count, long delay) {
|
public record GroupFactory(EnemyFactory enemyFactory, int count, long delay) {
|
||||||
public void summonGroup(TowerdefenseRoom instance) {
|
public void summonGroup(TowerdefenseRoom instance) {
|
||||||
for (int i = 0; i < this.count; i++) {
|
for (int i = 0; i < this.count; i++) {
|
||||||
MinecraftServer.getSchedulerManager().scheduleTask(() -> {
|
MinecraftServer.getSchedulerManager().scheduleTask(() -> {
|
@ -0,0 +1,78 @@
|
|||||||
|
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies;
|
||||||
|
|
||||||
|
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.Towerdefense;
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.entity.EntityType;
|
||||||
|
import net.minestom.server.timer.TaskSchedule;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class WaveGenerator {
|
||||||
|
final Towerdefense game;
|
||||||
|
int currentLevel;
|
||||||
|
Random random = new Random();
|
||||||
|
private final Map<EnemyFactory, Integer> availableEnemies = Map.of(
|
||||||
|
new EnemyFactory(EntityType.PIG, 2, 0.1), new EnemyFactory(EntityType.PIG, 2, 0.1).getStrength(),
|
||||||
|
new EnemyFactory(EntityType.VILLAGER, 6, 0.1), new EnemyFactory(EntityType.VILLAGER, 6, 0.1).getStrength(),
|
||||||
|
new EnemyFactory(EntityType.SHEEP, 4, 0.2), new EnemyFactory(EntityType.SHEEP, 4, 0.2).getStrength(),
|
||||||
|
new EnemyFactory(EntityType.COW, 10, 0.1), new EnemyFactory(EntityType.COW, 10, 0.1).getStrength(),
|
||||||
|
new EnemyFactory(EntityType.CAMEL, 10, 0.2), new EnemyFactory(EntityType.CAMEL, 10, 0.2).getStrength(),
|
||||||
|
new EnemyFactory(EntityType.ARMADILLO, 20, 0.3), new EnemyFactory(EntityType.ARMADILLO, 20, 0.3).getStrength(),
|
||||||
|
new EnemyFactory(EntityType.CHICKEN, 8, 0.6), new EnemyFactory(EntityType.CHICKEN, 8, 0.6).getStrength()
|
||||||
|
);
|
||||||
|
|
||||||
|
public WaveGenerator(Towerdefense game) {
|
||||||
|
this(game, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WaveGenerator(Towerdefense game, int startLevel) {
|
||||||
|
this.game = game;
|
||||||
|
this.currentLevel = startLevel - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startNextWave() {
|
||||||
|
this.currentLevel += 1;
|
||||||
|
List<EnemyFactory> enemyList = this.chooseEnemies();
|
||||||
|
Collections.shuffle(enemyList);
|
||||||
|
System.out.println(enemyList);
|
||||||
|
|
||||||
|
int averageDelay = Math.min(20000 / enemyList.size(), 1500);
|
||||||
|
AtomicInteger delay = new AtomicInteger(0);
|
||||||
|
enemyList.forEach(enemy -> {
|
||||||
|
delay.addAndGet(averageDelay);
|
||||||
|
MinecraftServer.getSchedulerManager().scheduleTask(
|
||||||
|
() -> this.spawnEnemy(enemyList.removeFirst()),
|
||||||
|
TaskSchedule.millis(delay.get() + this.random.nextInt(averageDelay-200, averageDelay+200)),
|
||||||
|
TaskSchedule.stop()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void spawnEnemy(EnemyFactory enemy) {
|
||||||
|
this.game.getInstances().forEach(instance -> instance.addEnemy(enemy.buildEntity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getCurrentLevelStrength() {
|
||||||
|
return (int) (0.5 * Math.pow(2, this.currentLevel));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<EnemyFactory> chooseEnemies() {
|
||||||
|
int strength = this.getCurrentLevelStrength();
|
||||||
|
final List<EnemyFactory> usedEnemies = new ArrayList<>();
|
||||||
|
|
||||||
|
while(strength > 0) {
|
||||||
|
int finalStrength = strength;
|
||||||
|
Map.Entry<EnemyFactory, Integer> chosenEnemy = this.availableEnemies.entrySet().stream()
|
||||||
|
.filter(e -> e.getValue() <= finalStrength)
|
||||||
|
.sorted((o1, o2) -> ThreadLocalRandom.current().nextInt(-1, 2))
|
||||||
|
.findAny()
|
||||||
|
.get();
|
||||||
|
usedEnemies.add(chosenEnemy.getKey());
|
||||||
|
strength -= chosenEnemy.getKey().getStrength();
|
||||||
|
}
|
||||||
|
|
||||||
|
return usedEnemies;
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ public abstract class ShootingTower extends Tower {
|
|||||||
EntityProjectile projectile = new EntityProjectile(this, projectileType);
|
EntityProjectile projectile = new EntityProjectile(this, projectileType);
|
||||||
projectile.setView(this.getPosition().yaw(), this.getPosition().pitch());
|
projectile.setView(this.getPosition().yaw(), this.getPosition().pitch());
|
||||||
projectile.setNoGravity(true);
|
projectile.setNoGravity(true);
|
||||||
projectile.setAerodynamics(new Aerodynamics(0, 1, 0));
|
projectile.setAerodynamics(new Aerodynamics(0, 1, 1));
|
||||||
Pos startingPoint = this.getPosition().add(0, this.getEyeHeight(), 0);
|
Pos startingPoint = this.getPosition().add(0, this.getEyeHeight(), 0);
|
||||||
|
|
||||||
double enemySpeed = enemy.getAttribute(Attribute.MOVEMENT_SPEED).getBaseValue() / 0.05;
|
double enemySpeed = enemy.getAttribute(Attribute.MOVEMENT_SPEED).getBaseValue() / 0.05;
|
||||||
|
Reference in New Issue
Block a user