diff --git a/.gradle/7.4/checksums/checksums.lock b/.gradle/7.4/checksums/checksums.lock index 38f938b..21451b7 100644 Binary files a/.gradle/7.4/checksums/checksums.lock and b/.gradle/7.4/checksums/checksums.lock differ diff --git a/.gradle/7.4/checksums/md5-checksums.bin b/.gradle/7.4/checksums/md5-checksums.bin index 35ecd4e..7685501 100644 Binary files a/.gradle/7.4/checksums/md5-checksums.bin and b/.gradle/7.4/checksums/md5-checksums.bin differ diff --git a/.gradle/7.4/checksums/sha1-checksums.bin b/.gradle/7.4/checksums/sha1-checksums.bin index 0e0c92d..7a3b1c2 100644 Binary files a/.gradle/7.4/checksums/sha1-checksums.bin and b/.gradle/7.4/checksums/sha1-checksums.bin differ diff --git a/.gradle/7.4/dependencies-accessors/dependencies-accessors.lock b/.gradle/7.4/dependencies-accessors/dependencies-accessors.lock index 2320b0d..ac58c2e 100644 Binary files a/.gradle/7.4/dependencies-accessors/dependencies-accessors.lock and b/.gradle/7.4/dependencies-accessors/dependencies-accessors.lock differ diff --git a/.gradle/7.4/executionHistory/executionHistory.bin b/.gradle/7.4/executionHistory/executionHistory.bin index 4178264..0b54bd6 100644 Binary files a/.gradle/7.4/executionHistory/executionHistory.bin and b/.gradle/7.4/executionHistory/executionHistory.bin differ diff --git a/.gradle/7.4/executionHistory/executionHistory.lock b/.gradle/7.4/executionHistory/executionHistory.lock index d15a549..b6b8392 100644 Binary files a/.gradle/7.4/executionHistory/executionHistory.lock and b/.gradle/7.4/executionHistory/executionHistory.lock differ diff --git a/.gradle/7.4/fileHashes/fileHashes.bin b/.gradle/7.4/fileHashes/fileHashes.bin index c074609..f26a51a 100644 Binary files a/.gradle/7.4/fileHashes/fileHashes.bin and b/.gradle/7.4/fileHashes/fileHashes.bin differ diff --git a/.gradle/7.4/fileHashes/fileHashes.lock b/.gradle/7.4/fileHashes/fileHashes.lock index a44c1e6..7b2cab2 100644 Binary files a/.gradle/7.4/fileHashes/fileHashes.lock and b/.gradle/7.4/fileHashes/fileHashes.lock differ diff --git a/.gradle/7.4/fileHashes/resourceHashesCache.bin b/.gradle/7.4/fileHashes/resourceHashesCache.bin index 846640d..418ab56 100644 Binary files a/.gradle/7.4/fileHashes/resourceHashesCache.bin and b/.gradle/7.4/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 002c95d..b827067 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/workspace-id.txt b/.gradle/workspace-id.txt new file mode 100644 index 0000000..bb32508 Binary files /dev/null and b/.gradle/workspace-id.txt differ diff --git a/.gradle/workspace-id.txt.lock b/.gradle/workspace-id.txt.lock new file mode 100644 index 0000000..a47484b Binary files /dev/null and b/.gradle/workspace-id.txt.lock differ diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/Main.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/Main.class index 1f1cee8..9d20e33 100644 Binary files a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/Main.class and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/Main.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/Arena.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/Arena.class index 7cbd332..c68d26b 100644 Binary files a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/Arena.class and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/Arena.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/ArenaMob.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/ArenaMob.class deleted file mode 100644 index 97f624f..0000000 Binary files a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/ArenaMob.class and /dev/null differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/ArenaMob.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/ArenaMob.class new file mode 100644 index 0000000..14c2bfb Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/ArenaMob.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/MobType.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/MobType.class new file mode 100644 index 0000000..88b84b6 Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/MobType.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/Zombie.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/Zombie.class deleted file mode 100644 index 19e4a0b..0000000 Binary files a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/Zombie.class and /dev/null differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Evoker$ActionGoal.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Evoker$ActionGoal.class new file mode 100644 index 0000000..fe1f5f7 Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Evoker$ActionGoal.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Evoker.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Evoker.class new file mode 100644 index 0000000..8560e90 Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Evoker.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Fang.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Fang.class new file mode 100644 index 0000000..6f6847b Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Fang.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Pillager.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Pillager.class new file mode 100644 index 0000000..cf2bc20 Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Pillager.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Spider.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Spider.class new file mode 100644 index 0000000..1f98d18 Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Spider.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Vex.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Vex.class new file mode 100644 index 0000000..f9aafce Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Vex.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Zombie.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Zombie.class new file mode 100644 index 0000000..21ffd27 Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Zombie.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/util/Knockback.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/util/Knockback.class new file mode 100644 index 0000000..c18d8e1 Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/util/Knockback.class differ diff --git a/build/classes/java/main/eu/mhsl/minenet/minestom/pve/util/Mapping.class b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/util/Mapping.class new file mode 100644 index 0000000..342ad9b Binary files /dev/null and b/build/classes/java/main/eu/mhsl/minenet/minestom/pve/util/Mapping.class differ diff --git a/build/tmp/compileJava/previous-compilation-data.bin b/build/tmp/compileJava/previous-compilation-data.bin index 328cc8a..bb8c3fa 100644 Binary files a/build/tmp/compileJava/previous-compilation-data.bin and b/build/tmp/compileJava/previous-compilation-data.bin differ diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/Main.java b/src/main/java/eu/mhsl/minenet/minestom/pve/Main.java index bab7ee3..490d8b3 100644 --- a/src/main/java/eu/mhsl/minenet/minestom/pve/Main.java +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/Main.java @@ -6,6 +6,9 @@ import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; +import net.minestom.server.advancements.FrameType; +import net.minestom.server.advancements.notifications.Notification; +import net.minestom.server.advancements.notifications.NotificationCenter; import net.minestom.server.adventure.audience.Audiences; import net.minestom.server.command.CommandManager; import net.minestom.server.coordinate.Pos; @@ -14,6 +17,7 @@ import net.minestom.server.entity.Player; import net.minestom.server.entity.PlayerSkin; import net.minestom.server.event.GlobalEventHandler; import net.minestom.server.event.player.PlayerChatEvent; +import net.minestom.server.event.player.PlayerDeathEvent; import net.minestom.server.event.player.PlayerLoginEvent; import net.minestom.server.event.player.PlayerSpawnEvent; import net.minestom.server.event.server.ServerTickMonitorEvent; @@ -21,6 +25,8 @@ import net.minestom.server.extras.lan.OpenToLAN; import net.minestom.server.instance.InstanceContainer; import net.minestom.server.instance.InstanceManager; import net.minestom.server.instance.block.Block; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; import net.minestom.server.monitoring.TickMonitor; import net.minestom.server.potion.Potion; import net.minestom.server.potion.PotionEffect; @@ -49,6 +55,15 @@ public class Main { playerLoginEvent.setSpawningInstance(Lobby.INSTANCE); + NotificationCenter.send( + new Notification( + Component.text("Test", NamedTextColor.GOLD), + FrameType.GOAL, + ItemStack.of(Material.TOTEM_OF_UNDYING) + ), + p + ); + if(!p.getName().toString().equals("MineTec")) p.setReducedDebugScreenInformation(true); //TODO remove condition p.setRespawnPoint(new Pos(0,5,0)); p.setGameMode(GameMode.CREATIVE); @@ -61,8 +76,14 @@ public class Main { .append(Component.text(event.getMessage(), NamedTextColor.WHITE)))); }); + events.addListener(PlayerDeathEvent.class, playerDeathEvent -> { + playerDeathEvent.setDeathText(null); + playerDeathEvent.setChatMessage(null); + }); + events.addListener(PlayerSpawnEvent.class, playerSpawnEvent -> { playerSpawnEvent.getPlayer().addEffect(new Potion(PotionEffect.BLINDNESS, (byte) 1, 30)); + playerSpawnEvent.getPlayer().refreshCommands(); }); @@ -79,10 +100,6 @@ public class Main { Collection players = MinecraftServer.getConnectionManager().getOnlinePlayers(); if (players.isEmpty()) return; - for(Player p : players) { - p.refreshCommands(); //TODO this is a dumb idea - } - final Runtime runtime = Runtime.getRuntime(); final TickMonitor tickMonitor = lastTick.get(); final long ramUsage = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024; diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/Arena.java b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/Arena.java index bea3d2a..0574713 100644 --- a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/Arena.java +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/Arena.java @@ -1,46 +1,38 @@ package eu.mhsl.minenet.minestom.pve.arena; import eu.mhsl.minenet.minestom.pve.Lobby; -import eu.mhsl.minenet.minestom.pve.arena.mob.Zombie; +import eu.mhsl.minenet.minestom.pve.arena.mob.ArenaMob; +import eu.mhsl.minenet.minestom.pve.arena.mob.MobType; +import eu.mhsl.minenet.minestom.pve.arena.mob.creature.Vex; import eu.mhsl.minenet.minestom.pve.util.FullbrightDimension; +import eu.mhsl.minenet.minestom.pve.util.Knockback; +import eu.mhsl.minenet.minestom.pve.util.Mapping; import eu.mhsl.minenet.minestom.pve.util.PaneGenerator; +import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; 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.ai.EntityAIGroup; -import net.minestom.server.entity.ai.EntityAIGroupBuilder; -import net.minestom.server.entity.ai.goal.DoNothingGoal; -import net.minestom.server.entity.ai.goal.FollowTargetGoal; -import net.minestom.server.entity.ai.goal.RandomLookAroundGoal; -import net.minestom.server.entity.ai.goal.RandomStrollGoal; -import net.minestom.server.entity.ai.target.ClosestEntityTarget; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.LivingEntity; +import net.minestom.server.entity.Player; import net.minestom.server.entity.damage.DamageType; import net.minestom.server.event.entity.EntityAttackEvent; -import net.minestom.server.event.entity.EntityDamageEvent; -import net.minestom.server.event.entity.EntityDeathEvent; import net.minestom.server.event.instance.AddEntityToInstanceEvent; import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent; +import net.minestom.server.instance.Explosion; +import net.minestom.server.instance.Instance; import net.minestom.server.instance.InstanceContainer; import net.minestom.server.instance.block.Block; -import net.minestom.server.item.ItemStack; -import net.minestom.server.item.Material; -import net.minestom.server.potion.Potion; -import net.minestom.server.potion.PotionEffect; +import net.minestom.server.particle.Particle; +import net.minestom.server.particle.ParticleCreator; import net.minestom.server.sound.SoundEvent; import net.minestom.server.timer.Task; import net.minestom.server.timer.TaskSchedule; -import net.minestom.server.world.DimensionType; -import org.jetbrains.annotations.NotNull; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; public class Arena extends InstanceContainer { private UUID arenaId; @@ -70,33 +62,43 @@ public class Arena extends InstanceContainer { eventNode().addListener(EntityAttackEvent.class, entityAttackEvent -> { //Player attacks Mob if(!(entityAttackEvent.getEntity() instanceof Player)) return; - sendMessage(Component.text("EntityAttackEvent " + System.currentTimeMillis())); Player attacker = (Player) entityAttackEvent.getEntity(); - LivingEntity target = (LivingEntity) entityAttackEvent.getTarget(); + ArenaMob target = (ArenaMob) entityAttackEvent.getTarget(); - target.damage(DamageType.fromPlayer(attacker), 20); + Collection nearby = getNearbyEntities(entityAttackEvent.getTarget().getPosition(), 3); - double knockbackX = Math.sin(attacker.getPosition().yaw() * (Math.PI / 180)); - double knockbackZ = -Math.cos(attacker.getPosition().yaw() * (Math.PI / 180)); + if(nearby.size() > 1) + target.particle(Particle.SWEEP_ATTACK, target.getPosition(), 0.5f, (int) Mapping.map(nearby.size(), 1, 10, 1, 5)); - entityAttackEvent.getTarget().takeKnockback( - 0.5f, - knockbackX, - knockbackZ - ); - scheduler().scheduleNextTick(() -> { - if(target.isDead()) { - target.takeKnockback( - 1f, - knockbackX, - knockbackZ - ); - } else - target.setVelocity(new Vec(0, 10, 0)); - }); + for(Entity entity : nearby) { + if(!(entity instanceof ArenaMob)) continue; //Only damage ArenaMobs + + ArenaMob secondaryTarget = (ArenaMob) entity; + + + secondaryTarget.damage( + DamageType.fromPlayer(attacker), + (float) Mapping.map(secondaryTarget.getDistance(target), + 3, + 0, + 0, + 5 + ) + ); + + Knockback knb = Knockback.calc(attacker); + knb.apply(secondaryTarget, (float) Mapping.map(secondaryTarget.getDistance(target), 3, 0, 0.1, 0.5)); + + secondaryTarget.particle(Particle.FALLING_OBSIDIAN_TEAR, secondaryTarget.getPosition(), new Vec(knb.x(), 0, knb.z()), 10); + + scheduler().scheduleNextTick(() -> { + if(secondaryTarget.isDead()) knb.apply(secondaryTarget, 1f); + }); + } }); + } public void stop() { @@ -119,9 +121,42 @@ public class Arena extends InstanceContainer { isRunning = true; this.spawner = scheduler().scheduleTask(() -> { - if(this.getEntities().size() > 100) return; - new Zombie().setInstance(this, new Pos(0, 5, 0)); - }, TaskSchedule.seconds(1), TaskSchedule.seconds(1)); + + try { + spawnMob((ArenaMob) MobType.getRandom().instance()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + }, TaskSchedule.seconds(3), TaskSchedule.seconds(2)); + } + + public void spawnMob(ArenaMob mob) { + this.spawnMob(mob, new Pos(0, 5, 0), 10); + } + public void spawnMob(ArenaMob mob, Pos pos, int area) { + if(getEntities().size() >= 150) return; + + if(getEntities().stream().filter(entity -> mob.getClass().isInstance(entity)).count() > 10) return; + + Random rnd = new Random(); + mob.setInstance(this, pos.add(rnd.nextInt(-area, area), 0, rnd.nextInt(-area, area))); + + } + + public void explosiveDamage(Pos pos, float offset, int damage, int range) { + sendGroupedPacket(ParticleCreator.createParticlePacket( + Particle.EXPLOSION, pos.x(), pos.y(), pos.z(), + offset, offset, offset, 5 + )); + playSound( + Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.NEUTRAL, 0.5f, 1), + pos.x(), pos.y(), pos.z() + ); + for (Entity entity : getNearbyEntities(pos, range)) { + if (entity instanceof LivingEntity livingEntity) + livingEntity.damage(DamageType.GRAVITY, (float) Mapping.map(damage, entity.getDistance(pos), 0, 0, damage)); + } } diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/ArenaMob.java b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/ArenaMob.java deleted file mode 100644 index 9e19d41..0000000 --- a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/ArenaMob.java +++ /dev/null @@ -1,126 +0,0 @@ -package eu.mhsl.minenet.minestom.pve.arena; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.minestom.server.MinecraftServer; -import net.minestom.server.coordinate.Pos; -import net.minestom.server.entity.EntityCreature; -import net.minestom.server.entity.EntityType; -import net.minestom.server.entity.LivingEntity; -import net.minestom.server.entity.Player; -import net.minestom.server.entity.ai.EntityAIGroupBuilder; -import net.minestom.server.entity.ai.goal.MeleeAttackGoal; -import net.minestom.server.entity.ai.goal.RandomLookAroundGoal; -import net.minestom.server.entity.ai.goal.RandomStrollGoal; -import net.minestom.server.entity.ai.target.ClosestEntityTarget; -import net.minestom.server.entity.damage.DamageType; -import net.minestom.server.event.entity.EntityAttackEvent; -import net.minestom.server.event.entity.EntityDamageEvent; -import net.minestom.server.event.entity.EntityDeathEvent; -import net.minestom.server.event.entity.EntitySpawnEvent; -import net.minestom.server.item.ItemStack; -import net.minestom.server.item.Material; -import net.minestom.server.particle.Particle; -import net.minestom.server.particle.ParticleCreator; -import net.minestom.server.potion.Potion; -import net.minestom.server.potion.PotionEffect; -import net.minestom.server.sound.SoundEvent; -import net.minestom.server.timer.TaskSchedule; -import net.minestom.server.utils.time.TimeUnit; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class ArenaMob extends EntityCreature { - public ArenaMob(@NotNull EntityType entityType) { - super(entityType); - - setCustomName(generateHealthBar(getMaxHealth(), getHealth())); - setCustomNameVisible(true); - setRemovalAnimationDelay(3500); - - addAIGroup( - new EntityAIGroupBuilder() - .addGoalSelector(new MeleeAttackGoal(this, 1.2, 20, TimeUnit.SERVER_TICK)) - .addTargetSelector(new ClosestEntityTarget(this, 32, Player.class)) - .addGoalSelector(new RandomLookAroundGoal(this, 30)) - .addGoalSelector(new RandomStrollGoal(this, 5)) - .build() - ); - - eventNode().addListener(EntitySpawnEvent.class, entitySpawnEvent -> { - Pos position = entitySpawnEvent.getEntity().getPosition(); - if(entitySpawnEvent.getEntity() == this) ParticleCreator.createParticlePacket(Particle.FIREWORK, - position.x(), - position.y(), - position.z(), - (float) position.sub(1).x(), - (float) position.sub(1).y(), - (float) position.sub(1).z(), - 10); - }); - - eventNode().addListener(EntityAttackEvent.class, entityAttackEvent -> { - //Mob attacks Player - if(entityAttackEvent.getEntity() instanceof Player) return; - - instance.sendMessage(Component.text("EntityAttackEvent " + System.currentTimeMillis())); - entityAttackEvent.getTarget().takeKnockback( - 0.2f, - Math.sin(entityAttackEvent.getEntity().getPosition().yaw() * (Math.PI / 180)), - -Math.cos(entityAttackEvent.getEntity().getPosition().yaw() * (Math.PI / 180)) - ); - - ((LivingEntity) entityAttackEvent.getTarget()).damage(DamageType.fromEntity(entityAttackEvent.getEntity()), 1); - - ((EntityCreature) entityAttackEvent.getEntity()).swingMainHand(); - - }); - - - eventNode().addListener(EntityDamageEvent.class, entityDamageEvent -> { - instance.sendMessage(Component.text("EntityDamageEvent " + System.currentTimeMillis())); - - entityDamageEvent.setSound(SoundEvent.BLOCK_AMETHYST_BLOCK_STEP); - - setCustomName(generateHealthBar(getMaxHealth(), getHealth())); - lookAt(new Pos(0,0,0)); - }); - - eventNode().addListener(EntityDeathEvent.class, entityDeathEvent -> { - instance.sendMessage(Component.text("EntityDeathEvent " + System.currentTimeMillis())); - - setCustomNameVisible(false); - setCustomName(null); - - scheduler().scheduleTask(() -> { - teleport(getPosition().sub(0, 0.1, 0)); - }, TaskSchedule.seconds(3), TaskSchedule.stop()); - - - }); - } - - - - private static final int BLOCK_LENGTH = 6; - - private static final List CHARACTERS = List.of( - "", "▏", "▎", "▍", - "▌", "▋", "▊", "▉" - ); - private static final String FULL_BLOCK_CHAR = "█"; - private static @NotNull Component generateHealthBar(float maxHealth, float currentHealth) { - // Converts the health percentage into a number from 0-{blockLength} -- only 0 if the mob's health is 0 - final double charHealth = (currentHealth / maxHealth) * BLOCK_LENGTH; - return Component.text() - .append(Component.text( - FULL_BLOCK_CHAR.repeat((int) Math.floor(charHealth)), - NamedTextColor.DARK_RED - )).append(Component.text(CHARACTERS.get((int) Math.round( - (charHealth - Math.floor(charHealth)) // number from 0-1 - * (CHARACTERS.size() - 1) // indexes start at 0 - )), NamedTextColor.GOLD)) - .build(); - } -} diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/ArenaMob.java b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/ArenaMob.java new file mode 100644 index 0000000..40a5a0f --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/ArenaMob.java @@ -0,0 +1,198 @@ +package eu.mhsl.minenet.minestom.pve.arena.mob; + +import eu.mhsl.minenet.minestom.pve.util.Knockback; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.*; +import net.minestom.server.entity.ai.EntityAIGroupBuilder; +import net.minestom.server.entity.ai.goal.MeleeAttackGoal; +import net.minestom.server.entity.ai.goal.RandomLookAroundGoal; +import net.minestom.server.entity.ai.goal.RandomStrollGoal; +import net.minestom.server.entity.ai.target.ClosestEntityTarget; +import net.minestom.server.entity.damage.DamageType; +import net.minestom.server.entity.metadata.LivingEntityMeta; +import net.minestom.server.event.entity.*; +import net.minestom.server.event.item.PickupExperienceEvent; +import net.minestom.server.instance.Instance; +import net.minestom.server.particle.Particle; +import net.minestom.server.particle.ParticleCreator; +import net.minestom.server.sound.SoundEvent; +import net.minestom.server.timer.TaskSchedule; +import net.minestom.server.utils.time.TimeUnit; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Random; + +public class ArenaMob extends EntityCreature { + private int damage = 1; + private int experience = 10; + public ArenaMob(@NotNull EntityType entityType) { + super(entityType); + + setCustomName(generateHealthBar(getMaxHealth(), getHealth())); + setCustomNameVisible(true); + setRemovalAnimationDelay(3500); + + aiGroup(); + + eventNode().addListener(EntitySpawnEvent.class, this::onSpawn); + eventNode().addListener(EntityAttackEvent.class, this::onAttack); + eventNode().addListener(EntityDamageEvent.class, this::onDamage); + eventNode().addListener(EntityDeathEvent.class, this::onDeath); + + eventNode().addListener(EntityTickEvent.class, ArenaMob::onTick); + + + eventNode().addListener(PickupExperienceEvent.class, pickupExperienceEvent -> { + pickupExperienceEvent.setCancelled(true); + + Knockback knb = Knockback.calc(pickupExperienceEvent.getPlayer()); + pickupExperienceEvent.getExperienceOrb().setVelocity(new Vec(knb.x(), 0, knb.z())); + }); + } + + + public void particle(Particle particle, Pos pos, Vec offset, int count) { + instance.sendGroupedPacket( + ParticleCreator.createParticlePacket( + particle, + pos.x(), + pos.y(), + pos.z(), + (float) offset.x(), + (float) offset.y(), + (float) offset.z(), + count + ) + ); + } + public void particle(Particle particle, Pos pos, float offset, int count) { + this.particle(particle, pos, new Vec(offset, offset, offset), count); + } + + + protected void aiGroup() { + addAIGroup( + new EntityAIGroupBuilder() + .addGoalSelector(new MeleeAttackGoal(this, 1.2, 20, TimeUnit.SERVER_TICK)) + .addTargetSelector(new ClosestEntityTarget(this, 32, Player.class)) + .addGoalSelector(new RandomLookAroundGoal(this, 30)) + .addGoalSelector(new RandomStrollGoal(this, 5)) + .build() + ); + } + + private static void onTick(@NotNull EntityTickEvent entityTickEvent) { + + } + protected void onSpawn(@NotNull EntitySpawnEvent entitySpawnEvent) { + particle(Particle.POOF, entitySpawnEvent.getEntity().getPosition(), 1, 3); + } + + protected void onAttack(@NotNull EntityAttackEvent entityAttackEvent) { + //Mob attacks Player + if (entityAttackEvent.getEntity() instanceof Player) return; + + ArenaMob attacker = (ArenaMob) entityAttackEvent.getEntity(); + Player target = (Player) entityAttackEvent.getTarget(); + + particle(Particle.LANDING_LAVA, target.getPosition(), 0.5f, 10); + + Knockback.calc(attacker).apply(target, 0.2f); + + target.damage(DamageType.fromEntity(entityAttackEvent.getEntity()), damage); + attacker.swingMainHand(); + + } + + protected void onDamage(@NotNull EntityDamageEvent entityDamageEvent) { + + particle(Particle.DAMAGE_INDICATOR, entityDamageEvent.getEntity().getPosition(), 1, 5); + entityDamageEvent.setSound(SoundEvent.BLOCK_AMETHYST_BLOCK_STEP); + + updateHealthBar(); + lookAt(new Pos(0, 0, 0)); + } + + protected void onDeath(@NotNull EntityDeathEvent entityDeathEvent) { + + particle(Particle.SOUL, entityDeathEvent.getEntity().getPosition(), 0.5f, 3); + + setCustomNameVisible(false); + setCustomName(null); + + Random rnd = new Random(); + short value = 1; + //TODO prevent to many orbs + for(int i = 0; i <= experience; i++) { + ExperienceOrb orb = new ExperienceOrb(value); + orb.setInstance(instance, entityDeathEvent.getEntity().getPosition().add(0, 0.1, 0)); + orb.setVelocity(new Vec(rnd.nextFloat(-5, 5), rnd.nextFloat(1, 3), rnd.nextFloat(-5, 5))); + orb.spawn(); + + orb.setGravity(0, 0); + + instance.scheduler().scheduleTask(() -> { + particle(Particle.LAVA, orb.getPosition(), 0, 1); + orb.remove(); + }, TaskSchedule.millis(rnd.nextInt(1000, 5000)), TaskSchedule.stop()); + } + + scheduler().scheduleTask(() -> { + teleport(getPosition().sub(0, 0.1, 0)); + }, TaskSchedule.seconds(3), TaskSchedule.stop()); + } + + public void updateHealthBar() { + setCustomName(generateHealthBar(getMaxHealth(), getHealth())); + } + + public int getDamage() { + return damage; + } + + public void setDamage(int damage) { + this.damage = damage; + } + + public int getExperience() { + return experience; + } + + public void setExperience(int experience) { + this.experience = experience; + } + + @Override + public void setHealth(float health) { + super.setHealth(health); + updateHealthBar(); + } + + private static final int BLOCK_LENGTH = 6; + + private static final List CHARACTERS = List.of( + "", "▏", "▎", "▍", + "▌", "▋", "▊", "▉" + ); + private static final String FULL_BLOCK_CHAR = "█"; + private static @NotNull Component generateHealthBar(float maxHealth, float currentHealth) { + // Converts the health percentage into a number from 0-{blockLength} -- only 0 if the mob's health is 0 + final double charHealth = (currentHealth / maxHealth) * BLOCK_LENGTH; + return Component.text() + .append(Component.text( + FULL_BLOCK_CHAR.repeat((int) Math.floor(charHealth)), + NamedTextColor.DARK_RED + )).append(Component.text(CHARACTERS.get((int) Math.round( + (charHealth - Math.floor(charHealth)) // number from 0-1 + * (CHARACTERS.size() - 1) // indexes start at 0 + )), NamedTextColor.GOLD)) + .build(); + } + + +} diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/MobType.java b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/MobType.java new file mode 100644 index 0000000..a160200 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/MobType.java @@ -0,0 +1,31 @@ +package eu.mhsl.minenet.minestom.pve.arena.mob; + +import eu.mhsl.minenet.minestom.pve.arena.mob.creature.Evoker; +import eu.mhsl.minenet.minestom.pve.arena.mob.creature.Pillager; +import eu.mhsl.minenet.minestom.pve.arena.mob.creature.Spider; +import eu.mhsl.minenet.minestom.pve.arena.mob.creature.Zombie; + +import java.lang.reflect.InvocationTargetException; +import java.util.Random; + +public enum MobType { + ZOMBIE(Zombie.class), + PILLAGER(Pillager.class), + EVOKER(Evoker.class), + SPIDER(Spider.class); + + private Class clazz; + + MobType(Class type) { + this.clazz = type; + } + + public Object instance() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + return this.clazz.getDeclaredConstructor().newInstance(); + } + + public static MobType getRandom() { + int random = new Random().nextInt(values().length); + return values()[random]; + } +} diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/Zombie.java b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/Zombie.java deleted file mode 100644 index 6e8d055..0000000 --- a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/Zombie.java +++ /dev/null @@ -1,19 +0,0 @@ -package eu.mhsl.minenet.minestom.pve.arena.mob; - -import eu.mhsl.minenet.minestom.pve.arena.ArenaMob; -import net.minestom.server.entity.EntityCreature; -import net.minestom.server.entity.EntityType; -import net.minestom.server.entity.Player; -import net.minestom.server.item.ItemStack; -import net.minestom.server.item.Material; -import org.jetbrains.annotations.NotNull; - -public class Zombie extends ArenaMob { - public Zombie() { - super(EntityType.ZOMBIE); - - setItemInHand(Player.Hand.MAIN, ItemStack.builder(Material.TNT).build()); - setItemInHand(Player.Hand.OFF, ItemStack.builder(Material.FLINT_AND_STEEL).build()); - - } -} diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Evoker.java b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Evoker.java new file mode 100644 index 0000000..6d2175a --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Evoker.java @@ -0,0 +1,134 @@ +package eu.mhsl.minenet.minestom.pve.arena.mob.creature; + +import eu.mhsl.minenet.minestom.pve.arena.Arena; +import eu.mhsl.minenet.minestom.pve.arena.mob.ArenaMob; +import net.kyori.adventure.sound.Sound; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.EntityCreature; +import net.minestom.server.entity.EntityType; +import net.minestom.server.entity.Player; +import net.minestom.server.entity.ai.EntityAIGroupBuilder; +import net.minestom.server.entity.ai.GoalSelector; +import net.minestom.server.entity.ai.goal.RangedAttackGoal; +import net.minestom.server.entity.ai.target.ClosestEntityTarget; +import net.minestom.server.entity.metadata.EntityMeta; +import net.minestom.server.entity.metadata.monster.raider.EvokerMeta; +import net.minestom.server.entity.metadata.monster.raider.SpellcasterIllagerMeta; +import net.minestom.server.event.entity.EntityDeathEvent; +import net.minestom.server.event.entity.EntitySpawnEvent; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.minestom.server.particle.Particle; +import net.minestom.server.sound.SoundEvent; +import net.minestom.server.tag.Tag; +import net.minestom.server.timer.TaskSchedule; +import net.minestom.server.utils.time.Cooldown; +import net.minestom.server.utils.time.TimeUnit; +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; +import java.util.Random; +import java.util.function.Consumer; + +public class Evoker extends ArenaMob { + public Evoker() { + super(EntityType.EVOKER); + } + + @Override + protected void aiGroup() { + + Random rnd = new Random(); + int range = rnd.nextInt(3, 16); + + addAIGroup( + new EntityAIGroupBuilder() + .addGoalSelector(new ActionGoal(this, Duration.ofSeconds(20), target -> { + lookAt(target); + ((EvokerMeta) getEntityMeta()).setSpell(SpellcasterIllagerMeta.Spell.ATTACK); + + scheduler().scheduleTask(() -> { + + for(int i = 0; i < rnd.nextInt(2, 5); i++) { + ((Arena) instance).spawnMob(new Vex(), position.add(0, 2, 0), 3); + } + + ((EvokerMeta) getEntityMeta()).setSpell(SpellcasterIllagerMeta.Spell.NONE); + }, TaskSchedule.seconds(2), TaskSchedule.stop()); + + })) + .addGoalSelector(new RangedAttackGoal(this, Duration.ofDays(1), range, range, true, 0, 0)) + .addTargetSelector(new ClosestEntityTarget(this, 32, Player.class)) + .build() + ); + } + + @Override + protected void onSpawn(@NotNull EntitySpawnEvent entitySpawnEvent) { + super.onSpawn(entitySpawnEvent); + + setTag(Tag.Boolean("revived"), Math.random() < 0.5); + } + + @Override + protected void onDeath(@NotNull EntityDeathEvent entityDeathEvent) { + if(getTag(Tag.Boolean("revived"))) { + super.onDeath(entityDeathEvent); + return; + } + + particle(Particle.TOTEM_OF_UNDYING, entityDeathEvent.getEntity().getPosition(), 2, 60); + instance.playSound(Sound.sound(SoundEvent.ITEM_TOTEM_USE, Sound.Source.MASTER, 0.5f, 1)); + setTag(Tag.Boolean("revived"), true); + setHealth(getMaxHealth()/2); + updateHealthBar(); + refreshIsDead(false); + } + + private static final class ActionGoal extends GoalSelector { + private final Duration cooldown; + private final Consumer consumer; + private long lastSummon; + private Entity target; + + public ActionGoal(@NotNull EntityCreature entityCreature, @NotNull Duration cooldown, Consumer consumer) { + super(entityCreature); + this.cooldown = cooldown; + this.consumer = consumer; + } + + @Override + public boolean shouldStart() { + Entity target = entityCreature.getTarget(); + if (target == null) target = findTarget(); + if (target == null) return false; + if (Cooldown.hasCooldown(System.currentTimeMillis(), lastSummon, cooldown)) return false; + this.target = target; + return true; + } + + @Override + public void start() { + if (target == null) return; + entityCreature.setTarget(target); + } + + @Override + public void tick(long time) { + if (!Cooldown.hasCooldown(time, lastSummon, cooldown) && entityCreature.getTarget() != null) { + lastSummon = time; + consumer.accept(entityCreature.getTarget()); + } + } + + @Override + public boolean shouldEnd() { + final Entity target = entityCreature.getTarget(); + return target == null || target.isRemoved() || + Cooldown.hasCooldown(System.currentTimeMillis(), lastSummon, cooldown); + } + + @Override + public void end() {} + } +} diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Pillager.java b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Pillager.java new file mode 100644 index 0000000..7a43aef --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Pillager.java @@ -0,0 +1,14 @@ +package eu.mhsl.minenet.minestom.pve.arena.mob.creature; + +import eu.mhsl.minenet.minestom.pve.arena.mob.ArenaMob; +import net.minestom.server.entity.EntityType; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; + +public class Pillager extends ArenaMob { + public Pillager() { + super(EntityType.PILLAGER); + + setItemInMainHand(ItemStack.of(Material.STONE_AXE)); + } +} diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Spider.java b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Spider.java new file mode 100644 index 0000000..d98d526 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Spider.java @@ -0,0 +1,22 @@ +package eu.mhsl.minenet.minestom.pve.arena.mob.creature; + +import eu.mhsl.minenet.minestom.pve.arena.mob.ArenaMob; +import net.kyori.adventure.sound.Sound; +import net.minestom.server.entity.EntityType; +import net.minestom.server.entity.metadata.EntityMeta; +import net.minestom.server.entity.metadata.LivingEntityMeta; +import net.minestom.server.entity.metadata.monster.SpiderMeta; +import net.minestom.server.event.entity.EntityDamageEvent; +import net.minestom.server.event.entity.EntityDeathEvent; +import net.minestom.server.event.entity.EntitySpawnEvent; +import net.minestom.server.sound.SoundEvent; +import org.jetbrains.annotations.NotNull; + +public class Spider extends ArenaMob { + public Spider() { + super(EntityType.SPIDER); + setHealth(3); + } + + +} diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Vex.java b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Vex.java new file mode 100644 index 0000000..0587ae3 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Vex.java @@ -0,0 +1,19 @@ +package eu.mhsl.minenet.minestom.pve.arena.mob.creature; + +import eu.mhsl.minenet.minestom.pve.arena.mob.ArenaMob; +import net.minestom.server.entity.EntityType; +import net.minestom.server.entity.Player; +import net.minestom.server.entity.ai.EntityAIGroupBuilder; +import net.minestom.server.entity.ai.target.ClosestEntityTarget; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import org.jetbrains.annotations.NotNull; + +public class Vex extends ArenaMob { + public Vex() { + super(EntityType.VEX); + + setItemInMainHand(ItemStack.of(Material.GOLDEN_SWORD)); + } + +} diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Zombie.java b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Zombie.java new file mode 100644 index 0000000..772c71f --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/arena/mob/creature/Zombie.java @@ -0,0 +1,56 @@ +package eu.mhsl.minenet.minestom.pve.arena.mob.creature; + +import eu.mhsl.minenet.minestom.pve.arena.Arena; +import eu.mhsl.minenet.minestom.pve.arena.mob.ArenaMob; +import net.kyori.adventure.sound.Sound; +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.LivingEntity; +import net.minestom.server.entity.Player; +import net.minestom.server.entity.damage.DamageType; +import net.minestom.server.event.entity.EntityDeathEvent; +import net.minestom.server.event.entity.EntitySpawnEvent; +import net.minestom.server.instance.Explosion; +import net.minestom.server.instance.Instance; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.minestom.server.particle.Particle; +import net.minestom.server.particle.ParticleCreator; +import net.minestom.server.sound.SoundEvent; +import net.minestom.server.tag.Tag; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class Zombie extends ArenaMob { + public Zombie() { + super(EntityType.ZOMBIE); + } + + @Override + protected void onSpawn(@NotNull EntitySpawnEvent entitySpawnEvent) { + super.onSpawn(entitySpawnEvent); + + setTag(Tag.Boolean("exploder"), Math.random() < 0.1); + if(getTag(Tag.Boolean("exploder"))) { + setItemInHand(Player.Hand.MAIN, ItemStack.builder(Material.TNT).build()); + setItemInHand(Player.Hand.OFF, ItemStack.builder(Material.FLINT_AND_STEEL).build()); + } + + if(Math.random() < 0.1) { + setItemInMainHand(ItemStack.of(Material.EXPERIENCE_BOTTLE)); + setExperience(50); + } + } + + @Override + protected void onDeath(@NotNull EntityDeathEvent entityDeathEvent) { + super.onDeath(entityDeathEvent); + if(getTag(Tag.Boolean("exploder"))) { + Pos pos = entityDeathEvent.getEntity().getPosition(); + ((Arena) instance).explosiveDamage(pos, 5, 10, 3); + } + } +} diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/util/Knockback.java b/src/main/java/eu/mhsl/minenet/minestom/pve/util/Knockback.java new file mode 100644 index 0000000..7500d3c --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/util/Knockback.java @@ -0,0 +1,31 @@ +package eu.mhsl.minenet.minestom.pve.util; + +import net.minestom.server.entity.LivingEntity; + +import javax.swing.text.html.parser.Entity; + +public class Knockback { + double x = 0; + double z = 0; + + public static Knockback calc(LivingEntity attacker) { + return new Knockback(attacker); + } + + private Knockback(LivingEntity attacker) { + this.x= Math.sin(attacker.getPosition().yaw() * (Math.PI / 180)); + this.z = -Math.cos(attacker.getPosition().yaw() * (Math.PI / 180)); + } + + public void apply(LivingEntity victim, float strength) { + victim.takeKnockback(strength, x, z); + } + + public double x() { + return x; + } + + public double z() { + return z; + } +} diff --git a/src/main/java/eu/mhsl/minenet/minestom/pve/util/Mapping.java b/src/main/java/eu/mhsl/minenet/minestom/pve/util/Mapping.java new file mode 100644 index 0000000..772d667 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minestom/pve/util/Mapping.java @@ -0,0 +1,12 @@ +package eu.mhsl.minenet.minestom.pve.util; + +public class Mapping { + public static double map(double input, double in_min, double in_max, double out_min, double out_max) { +// if(input > in_max) in_max = input; +// if(in_min > in_max) in_max = in_min; +// if(out_min > out_max) out_max = out_min; + + return (input - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } + +}