Skip to content

Commit

Permalink
Add ability to catch entities with fishing (#301)
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSzabo authored Jul 6, 2024
1 parent fe04380 commit ff5e888
Show file tree
Hide file tree
Showing 18 changed files with 346 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import dev.aurelium.auraskills.api.user.SkillsUser;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Called when AuraSkills drops extra loot from mechanics like Fishing, Luck, and custom loot tables.
Expand All @@ -22,6 +25,7 @@ public class LootDropEvent extends Event implements Cancellable {
private Location location;
private final Cause cause;
private boolean toInventory;
private Entity entity;
private boolean cancelled = false;

public LootDropEvent(Player player, SkillsUser user, ItemStack item, Location location, Cause cause, boolean toInventory) {
Expand All @@ -33,6 +37,26 @@ public LootDropEvent(Player player, SkillsUser user, ItemStack item, Location lo
this.toInventory = toInventory;
}

public LootDropEvent(Player player, SkillsUser user, Entity entity, Location location, Cause cause) {
this.player = player;
this.user = user;
this.location = location;
this.cause = cause;
this.toInventory = false;
this.entity = entity;
// Let's not break things and make the item not nullable still
this.item = new ItemStack(Material.AIR);
}

/**
* Gets the spawned entity if the loot was an entity.
*
* @return the entity
*/
public @Nullable Entity getEntity() {
return entity;
}

/**
* Gets the player that caused the loot drop.
*
Expand All @@ -53,6 +77,7 @@ public SkillsUser getUser() {

/**
* Gets the item that will be dropped by the event.
* If the drop is an entity, this will be AIR.
*
* @return the item
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import dev.aurelium.auraskills.bukkit.AuraSkills;
import dev.aurelium.auraskills.bukkit.damage.DamageHandler;
import dev.aurelium.auraskills.bukkit.damage.DamageResult;
import dev.aurelium.auraskills.bukkit.hooks.mythicmobs.loot.MythicEntityLootParser;
import dev.aurelium.auraskills.common.hooks.Hook;
import dev.aurelium.auraskills.api.damage.DamageType;
import io.lumine.mythic.api.adapters.AbstractEntity;
Expand All @@ -30,6 +31,10 @@ public MythicMobsHook(AuraSkills plugin, ConfigurationNode config) {
super(plugin, config);
this.plugin = plugin;
this.damageHandler = new DamageHandler();

// Wait for loot manager to be created, but add parser before it is loaded
plugin.getScheduler().executeSync(() ->
plugin.getLootTableManager().getLootManager().registerCustomEntityParser(new MythicEntityLootParser()));
}

@EventHandler(priority = EventPriority.HIGH)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.aurelium.auraskills.bukkit.hooks.mythicmobs.loot;

import dev.aurelium.auraskills.bukkit.loot.entity.EntityProperties;
import dev.aurelium.auraskills.bukkit.loot.entity.EntitySupplier;
import dev.aurelium.auraskills.bukkit.loot.parser.CustomEntityParser;
import org.spongepowered.configurate.ConfigurationNode;

public class MythicEntityLootParser implements CustomEntityParser {

@Override
public EntitySupplier getEntitySupplier(ConfigurationNode config) {
return new MythicEntitySupplier(EntityProperties.fromConfig(config));
}

@Override
public boolean shouldUseParser(ConfigurationNode config) {
return config.node("entity").getString("").startsWith("mythicmobs:");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package dev.aurelium.auraskills.bukkit.hooks.mythicmobs.loot;

import dev.aurelium.auraskills.bukkit.AuraSkills;
import dev.aurelium.auraskills.bukkit.loot.entity.EntityProperties;
import dev.aurelium.auraskills.bukkit.loot.entity.EntitySupplier;
import io.lumine.mythic.bukkit.BukkitAdapter;
import io.lumine.mythic.bukkit.MythicBukkit;
import io.lumine.mythic.core.mobs.ActiveMob;
import org.bukkit.Location;
import org.bukkit.entity.Entity;

public class MythicEntitySupplier extends EntitySupplier {

public MythicEntitySupplier(EntityProperties entityProperties) {
super(entityProperties);
}

@Override
public Entity spawnEntity(AuraSkills plugin, Location location) {
ActiveMob activeMob;

if (getEntityProperties().level() != null) {
activeMob = MythicBukkit.inst().getMobManager().spawnMob(getEntityProperties().entityId(), location, getEntityProperties().level());
} else {
activeMob = MythicBukkit.inst().getMobManager().spawnMob(getEntityProperties().entityId(), location);
}

return BukkitAdapter.adapt(activeMob.getEntity());
}

@Override
public void removeEntity(Entity entity) {
MythicBukkit.inst().getMobManager().getActiveMob(entity.getUniqueId()).ifPresent(ActiveMob::remove);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dev.aurelium.auraskills.api.loot.*;
import dev.aurelium.auraskills.api.registry.NamespacedId;
import dev.aurelium.auraskills.bukkit.loot.parser.CommandLootParser;
import dev.aurelium.auraskills.bukkit.loot.parser.EntityLootParser;
import dev.aurelium.auraskills.bukkit.loot.parser.ItemLootParser;
import dev.aurelium.auraskills.bukkit.loot.parser.LootParsingContextImpl;
import dev.aurelium.auraskills.bukkit.util.VersionUtils;
Expand Down Expand Up @@ -70,6 +71,9 @@ public LootTable loadLootTable(NamespacedId id, File file, ConfigurationNode con
// Command loot
else if (lootType.equalsIgnoreCase("command")) {
loot = new CommandLootParser().parse(context, lootNode);
// Entity loot, mainly for fishing
} else if (lootType.equalsIgnoreCase("entity")) {
loot = new EntityLootParser(manager).parse(context, lootNode);
} else {
// Parse custom loot registered from API
LootParser customParser = manager.getCustomLootParsers().get(lootType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dev.aurelium.auraskills.api.loot.LootParser;
import dev.aurelium.auraskills.bukkit.AuraSkills;
import dev.aurelium.auraskills.bukkit.loot.context.ContextProvider;
import dev.aurelium.auraskills.bukkit.loot.parser.CustomEntityParser;
import dev.aurelium.auraskills.bukkit.loot.parser.CustomItemParser;
import org.jetbrains.annotations.Nullable;

Expand All @@ -16,6 +17,7 @@ public class LootManager {
private final Set<String> lootOptionKeys;
private final Set<String> poolOptionKeys;
private final List<CustomItemParser> customItemParsers;
private final List<CustomEntityParser> customEntityParsers;
private final Map<String, LootParser> customLootParsers;

public LootManager(AuraSkills plugin) {
Expand All @@ -24,6 +26,7 @@ public LootManager(AuraSkills plugin) {
this.contextProviders = new HashMap<>();
this.lootOptionKeys = new HashSet<>();
this.poolOptionKeys = new HashSet<>();
this.customEntityParsers = new ArrayList<>();
this.customItemParsers = new ArrayList<>();
this.customLootParsers = new HashMap<>();
}
Expand Down Expand Up @@ -77,10 +80,18 @@ public List<CustomItemParser> getCustomItemParsers() {
return customItemParsers;
}

public List<CustomEntityParser> getCustomEntityParsers() {
return customEntityParsers;
}

public void registerCustomItemParser(CustomItemParser customItemParser) {
customItemParsers.add(customItemParser);
}

public void registerCustomEntityParser(CustomEntityParser customEntityParser) {
customEntityParsers.add(customEntityParser);
}

public Map<String, LootParser> getCustomLootParsers() {
return customLootParsers;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import dev.aurelium.auraskills.bukkit.AuraSkills;
import dev.aurelium.auraskills.bukkit.loot.context.MobContextProvider;
import dev.aurelium.auraskills.bukkit.loot.context.SourceContextProvider;
import dev.aurelium.auraskills.bukkit.loot.entity.VanillaEntityParser;
import dev.aurelium.auraskills.bukkit.util.ItemUtils;
import dev.aurelium.auraskills.common.api.ApiAuraSkills;
import dev.aurelium.auraskills.common.config.ConfigurateLoader;
Expand Down Expand Up @@ -44,6 +45,7 @@ public void initLootManager() {
lootManager.registerContextProvider(new SourceContextProvider(plugin));
lootManager.registerContextProvider(new MobContextProvider());
lootManager.registerCustomItemParser(new ItemKeyParser(plugin));
lootManager.registerCustomEntityParser(new VanillaEntityParser());
lootManager.addLootOptionKeys("xp");
lootManager.addPoolOptionKeys("chance_per_luck", "require_open_water");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package dev.aurelium.auraskills.bukkit.loot.entity;

import org.spongepowered.configurate.ConfigurationNode;

public record EntityProperties(String entityId,
String name,
Integer level,
Double health,
Double damage,
Float horizontalVelocity,
Float verticalVelocity) {

public static EntityProperties fromConfig(ConfigurationNode config) {
String[] id = config.node("entity").getString("").split(":");

return new EntityProperties(
id.length > 1 ? id[1] : id[0],
config.node("name").getString(),
config.node("level").empty() ? null : config.node("level").getInt(),
config.node("health").empty() ? null : config.node("health").getDouble(),
config.node("damage").empty() ? null : config.node("damage").getDouble(),
config.node("velocity", "horizontal").empty() ? null : config.node("velocity", "horizontal").getFloat(),
config.node("velocity", "vertical").empty() ? null : config.node("velocity", "vertical").getFloat()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dev.aurelium.auraskills.bukkit.loot.entity;

import dev.aurelium.auraskills.bukkit.AuraSkills;
import org.bukkit.Location;
import org.bukkit.entity.Entity;

public abstract class EntitySupplier {

private final EntityProperties entityProperties;

public EntitySupplier(EntityProperties entityProperties) {
this.entityProperties = entityProperties;
}

public abstract Entity spawnEntity(AuraSkills plugin, Location location);

public abstract void removeEntity(Entity entity);

public EntityProperties getEntityProperties() {
return entityProperties;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dev.aurelium.auraskills.bukkit.loot.entity;

import dev.aurelium.auraskills.bukkit.loot.parser.CustomEntityParser;
import org.spongepowered.configurate.ConfigurationNode;

public class VanillaEntityParser implements CustomEntityParser {

@Override
public EntitySupplier getEntitySupplier(ConfigurationNode config) {
return new VanillaEntitySupplier(EntityProperties.fromConfig(config));
}

@Override
public boolean shouldUseParser(ConfigurationNode config) {
String entity = config.node("entity").getString();

if (entity == null) return false;

// If it has a colon, it's a custom entity
// But if it starts with minecraft:, it's a vanilla entity stated explicitly
return !entity.contains(":") || entity.startsWith("minecraft:");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package dev.aurelium.auraskills.bukkit.loot.entity;

import dev.aurelium.auraskills.bukkit.AuraSkills;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.metadata.FixedMetadataValue;

public class VanillaEntitySupplier extends EntitySupplier {

public VanillaEntitySupplier(EntityProperties entityProperties) {
super(entityProperties);
}

@Override
public Entity spawnEntity(AuraSkills plugin, Location location) {
World world = location.getWorld();
if (world == null) return null;

Entity entity = world.spawnEntity(location, EntityType.valueOf(getEntityProperties().entityId().toUpperCase()));

if (entity instanceof LivingEntity livingEntity) {
if (getEntityProperties().health() != null) {
AttributeInstance attribute = livingEntity.getAttribute(Attribute.GENERIC_MAX_HEALTH);
if (attribute != null) {
attribute.setBaseValue(getEntityProperties().health());
livingEntity.setHealth(Math.min(getEntityProperties().health(), attribute.getValue()));
}
}
if (getEntityProperties().damage() != null) {
AttributeInstance attribute = livingEntity.getAttribute(Attribute.GENERIC_ATTACK_DAMAGE);
if (attribute != null) {
attribute.setBaseValue(getEntityProperties().damage());
}
}
}

if (getEntityProperties().name() != null) {
entity.setCustomName(plugin.getMessageProvider().applyFormatting(getEntityProperties().name()));
entity.setCustomNameVisible(true);
}

if (getEntityProperties().level() != null) {
entity.setMetadata("auraskills_level", new FixedMetadataValue(plugin, getEntityProperties().level()));
}

return entity;
}

@Override
public void removeEntity(Entity entity) {
entity.remove();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import dev.aurelium.auraskills.api.loot.LootTable;
import dev.aurelium.auraskills.bukkit.loot.context.SourceContext;
import dev.aurelium.auraskills.bukkit.loot.type.CommandLoot;
import dev.aurelium.auraskills.bukkit.loot.type.EntityLoot;
import dev.aurelium.auraskills.bukkit.loot.type.ItemLoot;
import dev.aurelium.auraskills.bukkit.source.FishingLeveler;
import dev.aurelium.auraskills.bukkit.util.VersionUtils;
Expand Down Expand Up @@ -103,6 +104,8 @@ public void onFish(PlayerFishEvent event) {
giveFishingItemLoot(player, itemLoot, event, source, skill, cause, table);
} else if (selectedLoot instanceof CommandLoot commandLoot) {
giveCommandLoot(player, commandLoot, source, skill);
} else if (selectedLoot instanceof EntityLoot entityLoot) {
giveFishingEntityLoot(player, entityLoot, event, source, skill, cause);
}
break; // Stop iterating pools
}
Expand Down
Loading

0 comments on commit ff5e888

Please sign in to comment.