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.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.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.MinecraftServer;
|
||||
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 net.minestom.server.timer.Task;
|
||||
import net.minestom.server.timer.TaskSchedule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -25,6 +28,8 @@ 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 WaveGenerator waveGenerator;
|
||||
private Task waveTask;
|
||||
private final Map<Material, Class<? extends Tower>> availableTowers = Map.of(
|
||||
Material.SKELETON_SPAWN_EGG, SkeletonTower.class,
|
||||
Material.ZOMBIE_SPAWN_EGG, ZombieTower.class,
|
||||
@ -35,7 +40,6 @@ public class Towerdefense extends StatelessGame {
|
||||
SkeletonTower.class, 3,
|
||||
BlazeTower.class, 8
|
||||
);
|
||||
|
||||
private static final int pathLength = 10;
|
||||
|
||||
public Towerdefense() {
|
||||
@ -43,12 +47,17 @@ public class Towerdefense extends StatelessGame {
|
||||
|
||||
this.setGenerator(new MazeGenerator());
|
||||
this.generateMaze();
|
||||
|
||||
this.waveGenerator = new WaveGenerator(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkAbandoned() {
|
||||
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);
|
||||
this.instances.add(newRoom);
|
||||
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() {
|
||||
@ -107,9 +118,14 @@ public class Towerdefense extends StatelessGame {
|
||||
return this.mazePath;
|
||||
}
|
||||
|
||||
public List<TowerdefenseRoom> getInstances() {
|
||||
return this.instances;
|
||||
}
|
||||
|
||||
@Override
|
||||
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() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
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.enemies.GroupFactory;
|
||||
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.util.BatchUtil;
|
||||
@ -25,7 +26,6 @@ import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.ItemAnimation;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.timer.TaskSchedule;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
@ -72,19 +72,6 @@ public class TowerdefenseRoom extends InstanceContainer {
|
||||
this.cursor.setInstance(this);
|
||||
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()
|
||||
.addListener(EntityDeathEvent.class, event -> {
|
||||
if(!(event.getEntity() instanceof EntityCreature enemy)) return;
|
||||
@ -123,7 +110,7 @@ public class TowerdefenseRoom extends InstanceContainer {
|
||||
EntityProjectile projectile = new EntityProjectile(p, EntityType.SPECTRAL_ARROW);
|
||||
projectile.setView(p.getPosition().yaw(), p.getPosition().pitch());
|
||||
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);
|
||||
projectile.setVelocity(projectileVelocity);
|
||||
@ -224,6 +211,8 @@ public class TowerdefenseRoom extends InstanceContainer {
|
||||
if(this.player.getHealth() - damage <= 0) {
|
||||
this.getEnemies().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.heal();
|
||||
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.EntityType;
|
||||
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.
|
||||
* @param entityType type of enemy
|
||||
@ -26,4 +26,8 @@ record EnemyFactory(EntityType entityType, float health, double speed) {
|
||||
entity.setHealth(this.health);
|
||||
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.timer.TaskSchedule;
|
||||
|
||||
record GroupFactory(EnemyFactory enemyFactory, int count, long delay) {
|
||||
public record GroupFactory(EnemyFactory enemyFactory, int count, long delay) {
|
||||
public void summonGroup(TowerdefenseRoom instance) {
|
||||
for (int i = 0; i < this.count; i++) {
|
||||
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);
|
||||
projectile.setView(this.getPosition().yaw(), this.getPosition().pitch());
|
||||
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);
|
||||
|
||||
double enemySpeed = enemy.getAttribute(Attribute.MOVEMENT_SPEED).getBaseValue() / 0.05;
|
||||
|
Reference in New Issue
Block a user