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 extends ArenaMob> 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;
+ }
+
+}