wip
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.gradle/workspace-id.txt
Normal file
BIN
.gradle/workspace-id.txt
Normal file
Binary file not shown.
BIN
.gradle/workspace-id.txt.lock
Normal file
BIN
.gradle/workspace-id.txt.lock
Normal file
Binary file not shown.
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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<Player> 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;
|
||||
|
||||
@@ -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<Entity> 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<String> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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<Entity> consumer;
|
||||
private long lastSummon;
|
||||
private Entity target;
|
||||
|
||||
public ActionGoal(@NotNull EntityCreature entityCreature, @NotNull Duration cooldown, Consumer<Entity> 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() {}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
12
src/main/java/eu/mhsl/minenet/minestom/pve/util/Mapping.java
Normal file
12
src/main/java/eu/mhsl/minenet/minestom/pve/util/Mapping.java
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user