Fixed possible NPE in our events, and BrewingStands now remember who last interacted with them, this allows for hoppers to be used with Alchemy Fixes #5004 Fixes #4958 Fixes #4641

This commit is contained in:
nossr50
2024-05-12 14:09:00 -07:00
parent 17052861d1
commit 3ba6b93135
80 changed files with 1137 additions and 797 deletions

View File

@ -2,11 +2,13 @@ package com.gmail.nossr50.util;
import com.gmail.nossr50.config.experience.ExperienceConfig;
import com.gmail.nossr50.datatypes.meta.BonusDropMeta;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.skills.repair.Repair;
import com.gmail.nossr50.skills.salvage.Salvage;
import com.gmail.nossr50.util.player.UserManager;
import com.gmail.nossr50.util.random.ProbabilityUtil;
import org.bukkit.Material;
import org.bukkit.World;
@ -16,9 +18,12 @@ import org.bukkit.block.data.Ageable;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import static java.util.Objects.requireNonNull;
public final class BlockUtils {
public static final String SHORT_GRASS = "SHORT_GRASS";
@ -94,10 +99,28 @@ public final class BlockUtils {
*
* @param blockState the blockstate
* @return true if the player succeeded in the check
* @deprecated Use {@link #checkDoubleDrops(McMMOPlayer, BlockState, SubSkillType)} instead
*/
public static boolean checkDoubleDrops(Player player, BlockState blockState, PrimarySkillType skillType, SubSkillType subSkillType) {
if (mcMMO.p.getGeneralConfig().getDoubleDropsEnabled(skillType, blockState.getType()) && Permissions.isSubSkillEnabled(player, subSkillType)) {
return ProbabilityUtil.isSkillRNGSuccessful(subSkillType, player);
@Deprecated(forRemoval = true, since = "2.2.010")
public static boolean checkDoubleDrops(Player player, BlockState blockState, PrimarySkillType ignored, SubSkillType subSkillType) {
return checkDoubleDrops(UserManager.getPlayer(player), blockState, subSkillType);
}
/**
* Checks if a player successfully passed the double drop check
*
* @param mmoPlayer the player involved in the check
* @param blockState the blockstate of the block
* @param subSkillType the subskill involved
* @return true if the player succeeded in the check
*/
public static boolean checkDoubleDrops(@Nullable McMMOPlayer mmoPlayer, @NotNull BlockState blockState,
@NotNull SubSkillType subSkillType) {
requireNonNull(blockState, "blockState cannot be null");
requireNonNull(subSkillType, "subSkillType cannot be null");
if (mcMMO.p.getGeneralConfig().getDoubleDropsEnabled(subSkillType.getParentSkill(), blockState.getType())
&& Permissions.isSubSkillEnabled(mmoPlayer, subSkillType)) {
return ProbabilityUtil.isSkillRNGSuccessful(subSkillType, mmoPlayer);
}
return false;

View File

@ -0,0 +1,116 @@
package com.gmail.nossr50.util;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.player.UserManager;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataHolder;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
import static com.gmail.nossr50.util.MetadataService.NSK_CONTAINER_UUID_LEAST_SIG;
import static com.gmail.nossr50.util.MetadataService.NSK_CONTAINER_UUID_MOST_SIG;
import static java.util.Objects.requireNonNull;
public class ContainerMetadataUtils {
public static void changeContainerOwnership(@NotNull BlockState blockState, @NotNull Player player) {
requireNonNull(blockState, "blockState cannot be null");
requireNonNull(player, "Player cannot be null");
final McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
/*
Debug output
*/
printOwnershipGainDebug(blockState, mmoPlayer);
printOwnershipLossDebug(blockState);
setOwner(blockState, player.getUniqueId());
}
public static void printOwnershipGainDebug(@NotNull BlockState blockState, @Nullable McMMOPlayer mmoPlayer) {
if(mmoPlayer != null && mmoPlayer.isDebugMode()) {
mmoPlayer.getPlayer().sendMessage("Container ownership " +
ChatColor.GREEN +"gained " + ChatColor.RESET +
"at location: " + blockState.getLocation().toString());
}
}
public static void printOwnershipLossDebug(BlockState blockState) {
OfflinePlayer containerOwner = getContainerOwner(blockState);
if(containerOwner != null && containerOwner.isOnline()) {
final McMMOPlayer mmoContainerOwner = UserManager.getPlayer(containerOwner.getPlayer());
if(mmoContainerOwner != null) {
if(mmoContainerOwner.isDebugMode()) {
mmoContainerOwner.getPlayer().sendMessage("Container ownership " +
ChatColor.RED + "lost " + ChatColor.RESET +
"at location: " + blockState.getLocation().toString());
}
}
}
}
public static @Nullable OfflinePlayer getContainerOwner(BlockState container) {
if (container instanceof PersistentDataHolder persistentDataHolder) {
final UUID uuid = getOwner(persistentDataHolder);
if(uuid != null) {
return Bukkit.getOfflinePlayer(uuid);
}
}
return null;
}
public static boolean isContainerOwned(BlockState blockState) {
return getContainerOwner(blockState) != null;
}
public static void processContainerOwnership(BlockState blockState, Player player) {
if(!mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.SMELTING))
return;
if(getContainerOwner(blockState) != null) {
if(getContainerOwner(blockState).getUniqueId().equals(player.getUniqueId()))
return;
}
changeContainerOwnership(blockState, player);
}
public static @Nullable UUID getOwner(@NotNull PersistentDataHolder persistentDataHolder) {
//Get container from entity
final PersistentDataContainer dataContainer = persistentDataHolder.getPersistentDataContainer();
//Too lazy to make a custom data type for this stuff
final Long mostSigBits = dataContainer.get(NSK_CONTAINER_UUID_MOST_SIG, PersistentDataType.LONG);
final Long leastSigBits = dataContainer.get(NSK_CONTAINER_UUID_LEAST_SIG, PersistentDataType.LONG);
if (mostSigBits != null && leastSigBits != null) {
return new UUID(mostSigBits, leastSigBits);
} else {
return null;
}
}
public static void setOwner(@NotNull BlockState blockState, @NotNull UUID uuid) {
PersistentDataContainer dataContainer = ((PersistentDataHolder) blockState).getPersistentDataContainer();
dataContainer.set(NSK_CONTAINER_UUID_MOST_SIG, PersistentDataType.LONG, uuid.getMostSignificantBits());
dataContainer.set(NSK_CONTAINER_UUID_LEAST_SIG, PersistentDataType.LONG, uuid.getLeastSignificantBits());
blockState.update();
}
}

View File

@ -10,7 +10,6 @@ import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
import com.gmail.nossr50.datatypes.skills.subskills.AbstractSubSkill;
import com.gmail.nossr50.events.experience.McMMOPlayerLevelChangeEvent;
import com.gmail.nossr50.events.experience.McMMOPlayerLevelDownEvent;
import com.gmail.nossr50.events.experience.McMMOPlayerLevelUpEvent;
@ -59,6 +58,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import static java.util.Objects.requireNonNull;
/**
* This class is meant to help make event related code less boilerplate
*/
@ -163,8 +164,17 @@ public final class EventUtils {
* Others
*/
public static @NotNull McMMOPlayerAbilityActivateEvent callPlayerAbilityActivateEvent(@NotNull Player player, @NotNull PrimarySkillType skill) {
McMMOPlayerAbilityActivateEvent event = new McMMOPlayerAbilityActivateEvent(player, skill);
@Deprecated(forRemoval = true, since = "2.2.010")
public static @NotNull McMMOPlayerAbilityActivateEvent callPlayerAbilityActivateEvent(@NotNull Player player,
@NotNull PrimarySkillType skill) {
return callPlayerAbilityActivateEvent(requireNonNull(UserManager.getPlayer(player)), skill);
}
public static @NotNull McMMOPlayerAbilityActivateEvent callPlayerAbilityActivateEvent(@NotNull McMMOPlayer mmoPlayer,
@NotNull PrimarySkillType skill) {
requireNonNull(mmoPlayer, "mmoPlayer cannot be null");
requireNonNull(skill, "skill cannot be null");
McMMOPlayerAbilityActivateEvent event = new McMMOPlayerAbilityActivateEvent(mmoPlayer, skill);
mcMMO.p.getServer().getPluginManager().callEvent(event);
return event;
@ -183,8 +193,21 @@ public final class EventUtils {
* @param subSkillType target subskill
* @return the event after it has been fired
*/
@Deprecated(forRemoval = true, since = "2.2.010")
public static @NotNull SubSkillEvent callSubSkillEvent(@NotNull Player player, @NotNull SubSkillType subSkillType) {
SubSkillEvent event = new SubSkillEvent(player, subSkillType);
return callSubSkillEvent(requireNonNull(UserManager.getPlayer(player)), subSkillType);
}
/**
* Calls a new SubSkillEvent for this SubSkill and then returns it
* @param mmoPlayer target mmoPlayer
* @param subSkillType target subskill
* @return the event after it has been fired
*/
public static @NotNull SubSkillEvent callSubSkillEvent(@NotNull McMMOPlayer mmoPlayer, @NotNull SubSkillType subSkillType) {
requireNonNull(mmoPlayer, "mmoPlayer cannot be null");
requireNonNull(subSkillType, "subSkillType cannot be null");
final SubSkillEvent event = new SubSkillEvent(mmoPlayer, subSkillType);
mcMMO.p.getServer().getPluginManager().callEvent(event);
return event;
@ -204,26 +227,6 @@ public final class EventUtils {
return event;
}
/**
* Calls a new SubSkillEvent for this SubSkill and then returns it
* @param player target player
* @param abstractSubSkill target subskill
* @return the event after it has been fired
*/
public static SubSkillEvent callSubSkillEvent(Player player, AbstractSubSkill abstractSubSkill) {
SubSkillEvent event = new SubSkillEvent(player, abstractSubSkill);
mcMMO.p.getServer().getPluginManager().callEvent(event);
return event;
}
// public static Event callFakeArmSwingEvent(@NotNull Player player) {
// PlayerAnimationEvent event = new FakePlayerAnimationEvent(player, PlayerAnimationType.ARM_SWING);
// mcMMO.p.getServer().getPluginManager().callEvent(event);
//
// return event;
// }
public static boolean tryLevelChangeEvent(Player player, PrimarySkillType skill, int levelsChanged, float xpRemoved, boolean isLevelUp, XPGainReason xpGainReason) {
McMMOPlayerLevelChangeEvent event = isLevelUp ? new McMMOPlayerLevelUpEvent(player, skill, levelsChanged, xpGainReason) : new McMMOPlayerLevelDownEvent(player, skill, levelsChanged, xpGainReason);
mcMMO.p.getServer().getPluginManager().callEvent(event);
@ -497,15 +500,25 @@ public final class EventUtils {
return !isCancelled;
}
@Deprecated(forRemoval = true, since = "2.2.010")
public static McMMOPlayerAbilityDeactivateEvent callAbilityDeactivateEvent(Player player, SuperAbilityType ability) {
McMMOPlayerAbilityDeactivateEvent event = new McMMOPlayerAbilityDeactivateEvent(player, mcMMO.p.getSkillTools().getPrimarySkillBySuperAbility(ability));
return callAbilityDeactivateEvent(requireNonNull(UserManager.getPlayer(player)), ability);
}
public static McMMOPlayerAbilityDeactivateEvent callAbilityDeactivateEvent(@NotNull McMMOPlayer mmoPlayer, @NotNull SuperAbilityType ability) {
final McMMOPlayerAbilityDeactivateEvent event = new McMMOPlayerAbilityDeactivateEvent(mmoPlayer, mcMMO.p.getSkillTools().getPrimarySkillBySuperAbility(ability));
mcMMO.p.getServer().getPluginManager().callEvent(event);
return event;
}
@Deprecated(forRemoval = true, since = "2.2.010")
public static McMMOPlayerFishingTreasureEvent callFishingTreasureEvent(Player player, ItemStack treasureDrop, int treasureXp, Map<Enchantment, Integer> enchants) {
McMMOPlayerFishingTreasureEvent event = enchants.isEmpty() ? new McMMOPlayerFishingTreasureEvent(player, treasureDrop, treasureXp) : new McMMOPlayerMagicHunterEvent(player, treasureDrop, treasureXp, enchants);
return callFishingTreasureEvent(requireNonNull(UserManager.getPlayer(player)), treasureDrop, treasureXp, enchants);
}
public static McMMOPlayerFishingTreasureEvent callFishingTreasureEvent(McMMOPlayer mmoPlayer, ItemStack treasureDrop, int treasureXp, Map<Enchantment, Integer> enchants) {
final McMMOPlayerFishingTreasureEvent event = enchants.isEmpty() ? new McMMOPlayerFishingTreasureEvent(mmoPlayer, treasureDrop, treasureXp) : new McMMOPlayerMagicHunterEvent(mmoPlayer, treasureDrop, treasureXp, enchants);
mcMMO.p.getServer().getPluginManager().callEvent(event);
return event;

View File

@ -0,0 +1,104 @@
package com.gmail.nossr50.util;
import com.gmail.nossr50.mcMMO;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import static com.gmail.nossr50.util.MetadataService.NSK_SUPER_ABILITY_BOOSTED_ITEM;
public final class ItemMetadataUtils {
public static final @NotNull String LEGACY_ABILITY_TOOL_LORE = "mcMMO Ability Tool";
private ItemMetadataUtils() {
// private ctor
}
public static void setSuperAbilityBoostedItem(@NotNull ItemStack itemStack, int originalDigSpeed) {
if (itemStack.getItemMeta() == null) {
mcMMO.p.getLogger().severe("Can not assign persistent data to an item with null item metadata");
return;
}
ItemMeta itemMeta = itemStack.getItemMeta();
PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer();
dataContainer.set(NSK_SUPER_ABILITY_BOOSTED_ITEM, PersistentDataType.INTEGER, originalDigSpeed);
itemStack.setItemMeta(itemMeta);
}
public static boolean isSuperAbilityBoosted(@NotNull ItemStack itemStack) {
if (itemStack.getItemMeta() == null)
return false;
ItemMeta itemMeta = itemStack.getItemMeta();
//Get container from entity
PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer();
//If this value isn't null, then the tool can be considered dig speed boosted
Integer boostValue = dataContainer.get(NSK_SUPER_ABILITY_BOOSTED_ITEM, PersistentDataType.INTEGER);
return boostValue != null;
}
public static int getSuperAbilityToolOriginalDigSpeed(@NotNull ItemStack itemStack) {
//Get container from entity
ItemMeta itemMeta = itemStack.getItemMeta();
if (itemMeta == null)
return 0;
PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer();
if (dataContainer.get(NSK_SUPER_ABILITY_BOOSTED_ITEM, PersistentDataType.INTEGER) == null) {
mcMMO.p.getLogger().severe("Value should never be null for a boosted item");
return 0;
} else {
//Too lazy to make a custom data type for this stuff
Integer boostValue = dataContainer.get(NSK_SUPER_ABILITY_BOOSTED_ITEM, PersistentDataType.INTEGER);
return Math.max(boostValue, 0);
}
}
public static void removeBonusDigSpeedOnSuperAbilityTool(@NotNull ItemStack itemStack) {
int originalSpeed = getSuperAbilityToolOriginalDigSpeed(itemStack);
ItemMeta itemMeta = itemStack.getItemMeta();
if(itemMeta != null) {
//TODO: can be optimized
if (itemMeta.hasEnchant(mcMMO.p.getEnchantmentMapper().getEfficiency())) {
itemMeta.removeEnchant(mcMMO.p.getEnchantmentMapper().getEfficiency());
}
if (originalSpeed > 0) {
itemMeta.addEnchant(mcMMO.p.getEnchantmentMapper().getEfficiency(), originalSpeed, true);
}
PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer();
dataContainer.remove(NSK_SUPER_ABILITY_BOOSTED_ITEM); //Remove persistent data
//TODO: needed?
itemStack.setItemMeta(itemMeta);
}
}
public static boolean isLegacyAbilityTool(@NotNull ItemStack itemStack) {
ItemMeta itemMeta = itemStack.getItemMeta();
if (itemMeta == null)
return false;
List<String> lore = itemMeta.getLore();
if (lore == null || lore.isEmpty())
return false;
return lore.contains(LEGACY_ABILITY_TOOL_LORE);
}
}

View File

@ -34,8 +34,9 @@ public class MetadataConstants {
public static final @NotNull String METADATA_KEY_DISARMED_ITEM = "mcMMO: Disarmed Item";
public static final @NotNull String METADATA_KEY_PLAYER_DATA = "mcMMO: Player Data";
public static final @NotNull String METADATA_KEY_DATABASE_COMMAND = "mcMMO: Processing Database Command";
public static final @NotNull String METADATA_KEY_FURNACE_UUID_MOST_SIG = "furnace_uuid_most_sig";
public static final @NotNull String METADATA_KEY_FURNACE_UUID_LEAST_SIG = "furnace_uuid_least_sig";
// the value of these two keys have "furnace" to keep supporting legacy data
public static final @NotNull String METADATA_KEY_CONTAINER_UUID_MOST_SIG = "furnace_uuid_most_sig";
public static final @NotNull String METADATA_KEY_CONTAINER_UUID_LEAST_SIG = "furnace_uuid_least_sig";
public static final @NotNull String METADATA_KEY_SUPER_ABILITY_BOOSTED_ITEM = "super_ability_boosted";
public static final @NotNull String METADATA_KEY_MOB_SPAWNER_MOB = "mcmmo_mob_spawner_mob";
public static final @NotNull String METADATA_KEY_EGG_MOB = "mcmmo_egg_mob";

View File

@ -0,0 +1,48 @@
package com.gmail.nossr50.util;
import com.gmail.nossr50.mcMMO;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
public final class MetadataService {
static final @NotNull NamespacedKey NSK_SUPER_ABILITY_BOOSTED_ITEM;
static final @NotNull NamespacedKey NSK_MOB_SPAWNER_MOB;
static final @NotNull NamespacedKey NSK_EGG_MOB;
static final @NotNull NamespacedKey NSK_NETHER_GATE_MOB;
static final @NotNull NamespacedKey NSK_COTW_SUMMONED_MOB;
static final @NotNull NamespacedKey NSK_PLAYER_BRED_MOB;
static final @NotNull NamespacedKey NSK_PLAYER_TAMED_MOB;
static final @NotNull NamespacedKey NSK_VILLAGER_TRADE_ORIGIN_ITEM;
static final @NotNull NamespacedKey NSK_EXPLOITED_ENDERMEN;
static final @NotNull NamespacedKey NSK_CONTAINER_UUID_MOST_SIG;
static final @NotNull NamespacedKey NSK_CONTAINER_UUID_LEAST_SIG;
private MetadataService() {
// private ctor
}
static {
NSK_SUPER_ABILITY_BOOSTED_ITEM = getNamespacedKey(MetadataConstants.METADATA_KEY_SUPER_ABILITY_BOOSTED_ITEM);
NSK_MOB_SPAWNER_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_MOB_SPAWNER_MOB);
NSK_EGG_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_EGG_MOB);
NSK_NETHER_GATE_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_NETHER_PORTAL_MOB);
NSK_COTW_SUMMONED_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_COTW_SUMMONED_MOB);
NSK_PLAYER_BRED_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_PLAYER_BRED_MOB);
NSK_PLAYER_TAMED_MOB = getNamespacedKey(MetadataConstants.METADATA_KEY_PLAYER_TAMED_MOB);
NSK_VILLAGER_TRADE_ORIGIN_ITEM = getNamespacedKey(MetadataConstants.METADATA_KEY_VILLAGER_TRADE_ORIGIN_ITEM);
NSK_EXPLOITED_ENDERMEN = getNamespacedKey(MetadataConstants.METADATA_KEY_EXPLOITED_ENDERMEN);
NSK_CONTAINER_UUID_MOST_SIG = getNamespacedKey(MetadataConstants.METADATA_KEY_CONTAINER_UUID_MOST_SIG);
NSK_CONTAINER_UUID_LEAST_SIG = getNamespacedKey(MetadataConstants.METADATA_KEY_CONTAINER_UUID_LEAST_SIG);
}
/**
* Helper method to simplify generating namespaced keys
*
* @param key the {@link String} value of the key
*
* @return the generated {@link NamespacedKey}
*/
public static @NotNull NamespacedKey getNamespacedKey(@NotNull String key) {
return new NamespacedKey(mcMMO.p, key);
}
}

View File

@ -0,0 +1,178 @@
package com.gmail.nossr50.util;
import com.gmail.nossr50.api.exceptions.IncompleteNamespacedKeyRegister;
import com.gmail.nossr50.config.PersistentDataConfig;
import com.gmail.nossr50.metadata.MobMetaFlagType;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.WeakHashMap;
import static com.gmail.nossr50.util.MetadataService.*;
//TODO: Use SpawnReason where appropriate instead of MobMetaFlagType
public final class MobMetadataUtils {
private static final @NotNull WeakHashMap<Entity, HashSet<MobMetaFlagType>> mobRegistry; //transient data
private static final @NotNull EnumMap<MobMetaFlagType, NamespacedKey> mobFlagKeyMap; //used for persistent data
private static boolean isUsingPersistentData = false;
private MobMetadataUtils() {
// private ctor
}
static {
mobFlagKeyMap = new EnumMap<>(MobMetaFlagType.class);
mobRegistry = new WeakHashMap<>();
initMobFlagKeyMap();
for (MobMetaFlagType metaFlagType : MobMetaFlagType.values()) {
if (PersistentDataConfig.getInstance().isMobPersistent(metaFlagType))
isUsingPersistentData = true;
}
}
/**
* Registers the namespaced keys required by the API (CB/Spigot)
* Used primarily for persistent data
*/
private static void initMobFlagKeyMap() throws IncompleteNamespacedKeyRegister {
for (MobMetaFlagType mobMetaFlagType : MobMetaFlagType.values()) {
switch (mobMetaFlagType) {
case MOB_SPAWNER_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_MOB_SPAWNER_MOB);
case EGG_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_EGG_MOB);
case NETHER_PORTAL_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_NETHER_GATE_MOB);
case COTW_SUMMONED_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_COTW_SUMMONED_MOB);
case PLAYER_BRED_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_PLAYER_BRED_MOB);
case EXPLOITED_ENDERMEN -> mobFlagKeyMap.put(mobMetaFlagType, NSK_EXPLOITED_ENDERMEN);
case PLAYER_TAMED_MOB -> mobFlagKeyMap.put(mobMetaFlagType, NSK_PLAYER_TAMED_MOB);
default -> throw new IncompleteNamespacedKeyRegister("missing namespaced key register for type: " + mobMetaFlagType);
}
}
}
/**
* Whether a target {@link LivingEntity} has a specific mcMMO mob flags
*
* @param flag the type of mob flag to check for
* @param livingEntity the living entity to check for metadata
*
* @return true if the mob has metadata values for target {@link MobMetaFlagType}
*/
public static boolean hasMobFlag(@NotNull MobMetaFlagType flag, @NotNull LivingEntity livingEntity) {
if (PersistentDataConfig.getInstance().isMobPersistent(flag)) {
return livingEntity.getPersistentDataContainer().has(mobFlagKeyMap.get(flag), PersistentDataType.BYTE);
} else {
if (mobRegistry.containsKey(livingEntity)) {
return mobRegistry.get(livingEntity).contains(flag);
}
return false;
}
}
/**
* Whether a target {@link LivingEntity} has any mcMMO mob flags
*
* @param livingEntity the living entity to check for metadata
*
* @return true if the mob has any mcMMO mob related metadata values
*/
public static boolean hasMobFlags(@NotNull LivingEntity livingEntity) {
if (isUsingPersistentData) {
for (MobMetaFlagType metaFlagType : MobMetaFlagType.values()) {
if (hasMobFlag(metaFlagType, livingEntity))
return true;
}
return false;
} else {
return mobRegistry.containsKey(livingEntity) && mobRegistry.get(livingEntity).size() > 0;
}
}
/**
* Copies all mcMMO mob flags from one {@link LivingEntity} to another {@link LivingEntity}
* This does not clear existing mcMMO mob flags on the target
*
* @param sourceEntity entity to copy from
* @param targetEntity entity to copy to
*/
public static void addMobFlags(@NotNull LivingEntity sourceEntity, @NotNull LivingEntity targetEntity) {
if (!hasMobFlags(sourceEntity))
return;
if (isUsingPersistentData) {
for (MobMetaFlagType flag : MobMetaFlagType.values()) {
if (hasMobFlag(flag, sourceEntity)) {
flagMetadata(flag, targetEntity);
}
}
} else {
HashSet<MobMetaFlagType> flags = new HashSet<>(mobRegistry.get(sourceEntity));
mobRegistry.put(targetEntity, flags);
}
}
/**
* Adds a mob flag to a {@link LivingEntity} which effectively acts a true/false boolean
* Existence of the flag can be considered a true value, non-existence can be considered false for all intents and purposes
*
* @param flag the desired flag to assign
* @param livingEntity the target living entity
*/
public static void flagMetadata(@NotNull MobMetaFlagType flag, @NotNull LivingEntity livingEntity) {
if (PersistentDataConfig.getInstance().isMobPersistent(flag)) {
if (!hasMobFlag(flag, livingEntity)) {
PersistentDataContainer persistentDataContainer = livingEntity.getPersistentDataContainer();
persistentDataContainer.set(mobFlagKeyMap.get(flag), PersistentDataType.BYTE, MetadataConstants.SIMPLE_FLAG_VALUE);
}
} else {
HashSet<MobMetaFlagType> flags = mobRegistry.getOrDefault(livingEntity, new HashSet<>());
flags.add(flag); // add the new flag
mobRegistry.put(livingEntity, flags); //update registry
}
}
/**
* Removes a specific mob flag from target {@link LivingEntity}
*
* @param flag desired flag to remove
* @param livingEntity the target living entity
*/
public static void removeMobFlag(@NotNull MobMetaFlagType flag, @NotNull LivingEntity livingEntity) {
if (PersistentDataConfig.getInstance().isMobPersistent(flag)) {
if (hasMobFlag(flag, livingEntity)) {
PersistentDataContainer persistentDataContainer = livingEntity.getPersistentDataContainer();
persistentDataContainer.remove(mobFlagKeyMap.get(flag));
}
} else {
if (mobRegistry.containsKey(livingEntity)) {
mobRegistry.get(livingEntity).remove(flag);
if (mobRegistry.get(livingEntity).size() == 0)
mobRegistry.remove(livingEntity);
}
}
}
/**
* Remove all mcMMO related mob flags from the target {@link LivingEntity}
*
* @param livingEntity target entity
*/
public static void removeMobFlags(@NotNull LivingEntity livingEntity) {
if (isUsingPersistentData) {
for (MobMetaFlagType flag : MobMetaFlagType.values()) {
removeMobFlag(flag, livingEntity);
}
} else {
mobRegistry.remove(livingEntity);
}
}
}

View File

@ -1,6 +1,7 @@
package com.gmail.nossr50.util;
import com.gmail.nossr50.commands.party.PartySubcommandType;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.ItemType;
import com.gmail.nossr50.datatypes.skills.MaterialType;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
@ -17,6 +18,7 @@ import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Locale;
@ -171,10 +173,19 @@ public final class Permissions {
}
public static boolean vanillaXpBoost(Permissible permissible, PrimarySkillType skill) { return permissible.hasPermission("mcmmo.ability." + skill.toString().toLowerCase(Locale.ENGLISH) + ".vanillaxpboost"); }
public static boolean isSubSkillEnabled(Permissible permissible, SubSkillType subSkillType) {
public static boolean isSubSkillEnabled(@Nullable Permissible permissible, @NotNull SubSkillType subSkillType) {
if (permissible == null)
return false;
return permissible.hasPermission(subSkillType.getPermissionNodeAddress());
}
public static boolean isSubSkillEnabled(@Nullable McMMOPlayer permissible, @NotNull SubSkillType subSkillType) {
if (permissible == null)
return false;
return isSubSkillEnabled(permissible.getPlayer(), subSkillType);
}
/* ACROBATICS */
public static boolean dodge(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.acrobatics.dodge"); }
public static boolean gracefulRoll(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.acrobatics.gracefulroll"); }

View File

@ -18,6 +18,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.gmail.nossr50.util.MobMetadataUtils.removeMobFlags;
public class TransientEntityTracker {
//These two are updated in step with each other
private final @NotNull HashMap<UUID, HashMap<CallOfTheWildType, HashSet<TrackedTamingEntity>>> perPlayerTransientEntityMap;
@ -273,7 +275,7 @@ public class TransientEntityTracker {
}
//Remove our metadata
mcMMO.getMetadataService().getMobMetadataService().removeMobFlags(livingEntity);
removeMobFlags(livingEntity);
//Clean from trackers
unregisterEntity(livingEntity);

View File

@ -4,6 +4,8 @@ import com.gmail.nossr50.mcMMO;
import org.bukkit.entity.LivingEntity;
import org.jetbrains.annotations.NotNull;
import static com.gmail.nossr50.util.MobMetadataUtils.removeMobFlags;
public class TransientMetadataTools {
private final mcMMO pluginRef;
@ -30,7 +32,7 @@ public class TransientMetadataTools {
}
//Cleanup mob metadata
mcMMO.getMetadataService().getMobMetadataService().removeMobFlags(entity);
removeMobFlags(entity);
//TODO: This loop has some redundancy, this whole method needs to be rewritten
for(String key : MetadataConstants.MOB_METADATA_KEYS) {

View File

@ -11,26 +11,37 @@ import com.gmail.nossr50.util.player.UserManager;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.VisibleForTesting;
import java.text.DecimalFormat;
import static java.util.Objects.requireNonNull;
public class ProbabilityUtil {
public static final @NotNull DecimalFormat percent = new DecimalFormat("##0.00%");
public static final double LUCKY_MODIFIER = 1.333D;
/**
* Return a chance of success in "percentage" format, show to the player in UI elements
* Return a chance of success in "percentage" format, shown to the player in UI elements
*
* @param player target player
* @param subSkillType target subskill
* @param isLucky whether to apply luck modifiers
*
* @return "percentage" representation of success
* @deprecated use {@link #chanceOfSuccessPercentage(McMMOPlayer, SubSkillType, boolean)} instead
*/
public static double chanceOfSuccessPercentage(@NotNull Player player,
@Deprecated(forRemoval = true, since = "2.2.010")
public static double chanceOfSuccessPercentage(@Nullable Player player,
@NotNull SubSkillType subSkillType,
boolean isLucky) {
Probability probability = getSubSkillProbability(subSkillType, player);
return chanceOfSuccessPercentage(requireNonNull(UserManager.getPlayer(player)), subSkillType, isLucky);
}
public static double chanceOfSuccessPercentage(@Nullable McMMOPlayer mmoPlayer,
@NotNull SubSkillType subSkillType,
boolean isLucky) {
Probability probability = getSubSkillProbability(subSkillType, mmoPlayer);
//Probability values are on a 0-1 scale and need to be "transformed" into a 1-100 scale
double percentageValue = probability.getValue(); //Doesn't need to be scaled
@ -42,6 +53,13 @@ public class ProbabilityUtil {
return percentageValue;
}
/**
* Return a chance of success as a double representing a "percentage".
*
* @param probability the probability of success
* @param isLucky whether to apply luck modifiers
* @return a double as a "percentage" representation of success
*/
public static double chanceOfSuccessPercentage(@NotNull Probability probability, boolean isLucky) {
//Probability values are on a 0-1 scale and need to be "transformed" into a 1-100 scale
double percentageValue = probability.getValue();
@ -54,6 +72,7 @@ public class ProbabilityUtil {
return percentageValue;
}
@VisibleForTesting
static Probability getStaticRandomChance(@NotNull SubSkillType subSkillType) throws InvalidStaticChance {
return switch (subSkillType) {
case AXES_ARMOR_IMPACT -> Probability.ofPercent(mcMMO.p.getAdvancedConfig().getImpactChance());
@ -74,19 +93,20 @@ public class ProbabilityUtil {
return skillProbabilityType;
}
static @NotNull Probability ofSubSkill(@Nullable Player player,
@NotNull SubSkillType subSkillType) {
@Deprecated(forRemoval = true, since = "2.2.010")
private static @NotNull Probability ofSubSkill(@Nullable Player player, @NotNull SubSkillType subSkillType) {
// no null check needed here
return ofSubSkill(UserManager.getPlayer(player), subSkillType);
}
private static @NotNull Probability ofSubSkill(@Nullable McMMOPlayer mmoPlayer, @NotNull SubSkillType subSkillType) {
switch (getProbabilityType(subSkillType)) {
case DYNAMIC_CONFIGURABLE:
double probabilityCeiling;
double skillLevel;
double maxBonusLevel; // If a skill level is equal to the cap, it has the full probability
if (player != null) {
McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
if (mmoPlayer == null) {
return Probability.ofPercent(0);
}
if (mmoPlayer != null) {
skillLevel = mmoPlayer.getSkillLevel(subSkillType.getParentSkill());
} else {
skillLevel = 0;
@ -101,10 +121,10 @@ public class ProbabilityUtil {
try {
return getStaticRandomChance(subSkillType);
} catch (InvalidStaticChance invalidStaticChance) {
invalidStaticChance.printStackTrace();
throw new RuntimeException(invalidStaticChance);
}
default:
throw new RuntimeException("No case in switch statement for Skill Probability Type!");
throw new IllegalStateException("No case in switch statement for Skill Probability Type!");
}
}
@ -123,14 +143,43 @@ public class ProbabilityUtil {
* The outcome of the probability can also be modified by this event that is called
*
* @param subSkillType target subskill
* @param player target player, can be null (null players are given odds equivalent to a player with no levels or luck)
* @param player target player
* can be null (null players are given odds equivalent to a player with no levels or luck)
* @return true if the Skill RNG succeeds, false if it fails
* @deprecated use {@link #isSkillRNGSuccessful(SubSkillType, McMMOPlayer)} instead
*/
@Deprecated(forRemoval = true, since = "2.2.010")
public static boolean isSkillRNGSuccessful(@NotNull SubSkillType subSkillType, @Nullable Player player) {
return isSkillRNGSuccessful(subSkillType, UserManager.getPlayer(player));
}
/**
* This is one of several Skill RNG check methods
* This helper method is for specific {@link SubSkillType},
* which help mcMMO understand where the RNG values used in our calculations come from this {@link SubSkillType}
* <p>
* 1) Determine where the RNG values come from for the passed {@link SubSkillType}
* NOTE: In the config file, there are values which are static and which are more dynamic,
* this is currently a bit hardcoded and will need to be updated manually
* <p>
* 2) Determine whether to use Lucky multiplier and influence the outcome
* <p>
* 3)
* Creates a {@link Probability} and pipes it to {@link ProbabilityUtil} which processes the result and returns it
* <p>
* This also calls a {@link SubSkillEvent} which can be cancelled, if it is cancelled this will return false
* The outcome of the probability can also be modified by this event that is called
*
* @param subSkillType target subskill
* @param mmoPlayer target player
* can be null (null players are given odds equivalent to a player with no levels or luck)
* @return true if the Skill RNG succeeds, false if it fails
*/
public static boolean isSkillRNGSuccessful(@NotNull SubSkillType subSkillType, @NotNull Player player) {
final Probability probability = getSkillProbability(subSkillType, player);
public static boolean isSkillRNGSuccessful(@NotNull SubSkillType subSkillType, @Nullable McMMOPlayer mmoPlayer) {
final Probability probability = getSkillProbability(subSkillType, mmoPlayer);
//Luck
boolean isLucky = Permissions.lucky(player, subSkillType.getParentSkill());
boolean isLucky = mmoPlayer != null && Permissions.lucky(mmoPlayer.getPlayer(), subSkillType.getParentSkill());
if(isLucky) {
return probability.evaluate(LUCKY_MODIFIER);
@ -143,29 +192,50 @@ public class ProbabilityUtil {
* Returns the {@link Probability} for a specific {@link SubSkillType} for a specific {@link Player}.
* This does not take into account perks such as lucky for the player.
* This is affected by other plugins who can listen to the {@link SubSkillEvent} and cancel it or mutate it.
* Null players will be treated as zero skill players.
*
* @param subSkillType the target subskill
* @param player the target player
* can be null (null players have the worst odds)
* @return the probability for this skill
* @deprecated use {@link #getSkillProbability(SubSkillType, McMMOPlayer)} instead
*/
@Deprecated(forRemoval = true)
public static Probability getSkillProbability(@NotNull SubSkillType subSkillType, @Nullable Player player) {
return getSkillProbability(subSkillType, UserManager.getPlayer(player));
}
/**
* Returns the {@link Probability} for a specific {@link SubSkillType} for a specific {@link Player}.
* This does not take into account perks such as lucky for the player.
* This is affected by other plugins who can listen to the {@link SubSkillEvent} and cancel it or mutate it.
* Null players will be treated as zero skill players.
*
* @param subSkillType the target subskill
* @param mmoPlayer the target player
* can be null (null players have the worst odds)
* @return the probability for this skill
*/
public static Probability getSkillProbability(@NotNull SubSkillType subSkillType, @NotNull Player player) {
//Process probability
Probability probability = getSubSkillProbability(subSkillType, player);
public static Probability getSkillProbability(@NotNull SubSkillType subSkillType, @Nullable McMMOPlayer mmoPlayer) {
// Process probability
Probability probability = getSubSkillProbability(subSkillType, mmoPlayer);
//Send out event
SubSkillEvent subSkillEvent = EventUtils.callSubSkillEvent(player, subSkillType);
// Send out event
if (mmoPlayer != null) {
SubSkillEvent subSkillEvent = EventUtils.callSubSkillEvent(mmoPlayer, subSkillType);
if(subSkillEvent.isCancelled()) {
return Probability.ALWAYS_FAILS;
if(subSkillEvent.isCancelled()) {
return Probability.ALWAYS_FAILS;
}
// Result modifier
double resultModifier = subSkillEvent.getResultModifier();
// Mutate probability
if(resultModifier != 1.0D)
probability = Probability.ofPercent(probability.getValue() * resultModifier);
}
//Result modifier
double resultModifier = subSkillEvent.getResultModifier();
//Mutate probability
if(resultModifier != 1.0D)
probability = Probability.ofPercent(probability.getValue() * resultModifier);
return probability;
}
@ -177,12 +247,11 @@ public class ProbabilityUtil {
* @param player the target player can be null (null players have the worst odds)
* @param probabilityPercentage the probability of this player succeeding in "percentage" format (0-100 inclusive)
* @return true if the RNG succeeds, false if it fails
* @deprecated use {@link #isStaticSkillRNGSuccessful(PrimarySkillType, McMMOPlayer, double)} instead
*/
@Deprecated(forRemoval = true, since = "2.2.010")
public static boolean isStaticSkillRNGSuccessful(@NotNull PrimarySkillType primarySkillType, @Nullable Player player, double probabilityPercentage) {
//Grab a probability converted from a "percentage" value
Probability probability = Probability.ofPercent(probabilityPercentage);
return isStaticSkillRNGSuccessful(primarySkillType, player, probability);
return isStaticSkillRNGSuccessful(primarySkillType, player, Probability.ofPercent(probabilityPercentage));
}
/**
@ -190,12 +259,49 @@ public class ProbabilityUtil {
* This helper method is specific to static value RNG, which can be influenced by a player's Luck
*
* @param primarySkillType the related primary skill
* @param player the target player, can be null (null players have the worst odds)
* @param probability the probability of this player succeeding
* @param mmoPlayer the target player can be null (null players have the worst odds)
* @param probabilityPercentage the probability of this player succeeding in "percentage" format (0-100 inclusive)
* @return true if the RNG succeeds, false if it fails
*/
public static boolean isStaticSkillRNGSuccessful(@NotNull PrimarySkillType primarySkillType, @Nullable Player player, @NotNull Probability probability) {
boolean isLucky = player != null && Permissions.lucky(player, primarySkillType);
public static boolean isStaticSkillRNGSuccessful(@NotNull PrimarySkillType primarySkillType,
@Nullable McMMOPlayer mmoPlayer, double probabilityPercentage) {
//Grab a probability converted from a "percentage" value
final Probability probability = Probability.ofPercent(probabilityPercentage);
return isStaticSkillRNGSuccessful(primarySkillType, mmoPlayer, probability);
}
/**
* This is one of several Skill RNG check methods
* This helper method is specific to static value RNG, which can be influenced by a player's Luck
*
* @param primarySkillType the related primary skill
* @param player the target player
* can be null (null players have the worst odds)
* @param probability the probability of this player succeeding
* @return true if the RNG succeeds, false if it fails
* @deprecated use {@link #isStaticSkillRNGSuccessful(PrimarySkillType, McMMOPlayer, Probability)} instead, this
* method is redundant and will be removed.
*/
@Deprecated(forRemoval = true, since = "2.2.010")
public static boolean isStaticSkillRNGSuccessful(@NotNull PrimarySkillType primarySkillType,
@Nullable Player player, @NotNull Probability probability) {
return isStaticSkillRNGSuccessful(primarySkillType, UserManager.getPlayer(player), probability);
}
/**
* This is one of several Skill RNG check methods
* This helper method is specific to static value RNG, which can be influenced by a mmoPlayer's Luck
*
* @param primarySkillType the related primary skill
* @param mmoPlayer the target mmoPlayer
* can be null (null players have the worst odds)
* @param probability the probability of this mmoPlayer succeeding
* @return true if the RNG succeeds, false if it fails
*/
public static boolean isStaticSkillRNGSuccessful(@NotNull PrimarySkillType primarySkillType,
@Nullable McMMOPlayer mmoPlayer, @NotNull Probability probability) {
boolean isLucky = mmoPlayer != null && Permissions.lucky(mmoPlayer.getPlayer(), primarySkillType);
if(isLucky) {
return probability.evaluate(LUCKY_MODIFIER);
@ -209,25 +315,57 @@ public class ProbabilityUtil {
* @param subSkillType target subskill
* @param player target player
* @return true if the skill succeeds (wasn't cancelled by any other plugin)
* @deprecated use {@link #isNonRNGSkillActivationSuccessful(SubSkillType, McMMOPlayer)} instead
*/
@Deprecated(forRemoval = true, since = "2.2.010")
public static boolean isNonRNGSkillActivationSuccessful(@NotNull SubSkillType subSkillType, @NotNull Player player) {
return !EventUtils.callSubSkillEvent(player, subSkillType).isCancelled();
return isNonRNGSkillActivationSuccessful(subSkillType, requireNonNull(UserManager.getPlayer(player)));
}
/**
* Grab the {@link Probability} for a specific {@link SubSkillType} for a specific {@link Player}
*
* Skills activate without RNG, this allows other plugins to prevent that activation
* @param subSkillType target subskill
* @param player target player
* @return the Probability of this skill succeeding
* @param mmoPlayer target player
* @return true if the skill succeeds (wasn't cancelled by any other plugin)
*/
public static @NotNull Probability getSubSkillProbability(@NotNull SubSkillType subSkillType, @Nullable Player player) {
public static boolean isNonRNGSkillActivationSuccessful(@NotNull SubSkillType subSkillType,
@NotNull McMMOPlayer mmoPlayer) {
return !EventUtils.callSubSkillEvent(mmoPlayer, subSkillType).isCancelled();
}
/**
* Retrieves the {@link Probability} of success for a specified {@link SubSkillType} for a given {@link Player}.
*
* @param subSkillType The targeted subskill.
* @param player The player in question.
* If null, the method treats it as a player with no levels or luck and calculates the probability
* accordingly.
* @return The probability that the specified skill will succeed.
* @deprecated use {@link #getSubSkillProbability(SubSkillType, McMMOPlayer)} instead
*/
@Deprecated(forRemoval = true, since = "2.2.010")
public static @NotNull Probability getSubSkillProbability(@NotNull SubSkillType subSkillType,
@Nullable Player player) {
return ProbabilityUtil.ofSubSkill(player, subSkillType);
}
public static @NotNull String[] getRNGDisplayValues(@NotNull Player player, @NotNull SubSkillType subSkill) {
double firstValue = chanceOfSuccessPercentage(player, subSkill, false);
double secondValue = chanceOfSuccessPercentage(player, subSkill, true);
/**
* Retrieves the {@link Probability} of success for a specified {@link SubSkillType} for a given {@link Player}.
*
* @param subSkillType The targeted subskill.
* @param mmoPlayer The player in question.
* If null, the method treats it as a player with no levels or luck and calculates the probability
* accordingly.
* @return The probability that the specified skill will succeed.
*/
public static @NotNull Probability getSubSkillProbability(@NotNull SubSkillType subSkillType,
@Nullable McMMOPlayer mmoPlayer) {
return ProbabilityUtil.ofSubSkill(mmoPlayer, subSkillType);
}
public static @NotNull String[] getRNGDisplayValues(@Nullable McMMOPlayer mmoPlayer, @NotNull SubSkillType subSkill) {
double firstValue = chanceOfSuccessPercentage(mmoPlayer, subSkill, false);
double secondValue = chanceOfSuccessPercentage(mmoPlayer, subSkill, true);
return new String[]{percent.format(firstValue), percent.format(secondValue)};
}

View File

@ -9,7 +9,6 @@ import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.metadata.MobMetaFlagType;
import com.gmail.nossr50.metadata.MobMetadataService;
import com.gmail.nossr50.runnables.skills.AwardCombatXpTask;
import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager;
import com.gmail.nossr50.skills.archery.ArcheryManager;
@ -37,14 +36,12 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import static com.gmail.nossr50.util.MobMetadataUtils.hasMobFlag;
public final class CombatUtils {
private CombatUtils() {}
private static @NotNull MobMetadataService getMobMetadataService() {
return mcMMO.getMetadataService().getMobMetadataService();
}
public static boolean isDamageLikelyFromNormalCombat(@NotNull DamageCause damageCause) {
return switch (damageCause) {
case ENTITY_ATTACK, ENTITY_SWEEP_ATTACK, PROJECTILE -> true;
@ -926,17 +923,17 @@ public final class CombatUtils {
}
}
if(getMobMetadataService().hasMobFlag(MobMetaFlagType.COTW_SUMMONED_MOB, target)) {
if(hasMobFlag(MobMetaFlagType.COTW_SUMMONED_MOB, target)) {
baseXP = 0;
} else if(getMobMetadataService().hasMobFlag(MobMetaFlagType.MOB_SPAWNER_MOB, target) || target.hasMetadata("ES")) {
} else if(hasMobFlag(MobMetaFlagType.MOB_SPAWNER_MOB, target) || target.hasMetadata("ES")) {
baseXP *= ExperienceConfig.getInstance().getSpawnedMobXpMultiplier();
} else if(getMobMetadataService().hasMobFlag(MobMetaFlagType.NETHER_PORTAL_MOB, target)) {
} else if(hasMobFlag(MobMetaFlagType.NETHER_PORTAL_MOB, target)) {
baseXP *= ExperienceConfig.getInstance().getNetherPortalXpMultiplier();
} else if(getMobMetadataService().hasMobFlag(MobMetaFlagType.EGG_MOB, target)) {
} else if(hasMobFlag(MobMetaFlagType.EGG_MOB, target)) {
baseXP *= ExperienceConfig.getInstance().getEggXpMultiplier();
} else if (getMobMetadataService().hasMobFlag(MobMetaFlagType.PLAYER_BRED_MOB, target)) {
} else if (hasMobFlag(MobMetaFlagType.PLAYER_BRED_MOB, target)) {
baseXP *= ExperienceConfig.getInstance().getBredMobXpMultiplier();
} else if(getMobMetadataService().hasMobFlag(MobMetaFlagType.PLAYER_TAMED_MOB, target)) {
} else if(hasMobFlag(MobMetaFlagType.PLAYER_TAMED_MOB, target)) {
baseXP *= ExperienceConfig.getInstance().getTamedMobXpMultiplier();
}

View File

@ -1,6 +1,7 @@
package com.gmail.nossr50.util.skills;
import com.gmail.nossr50.config.experience.ExperienceConfig;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.events.skills.SkillActivationPerkEvent;
import com.gmail.nossr50.util.Permissions;
@ -106,4 +107,19 @@ public final class PerksUtils {
return NORMAL_SKILL_ACTIVATION_CHANCE;
}
/**
* Calculate activation chance for a skill.
*
* @param mmoPlayer Player to check the activation chance for
* @param skill PrimarySkillType to check the activation chance of
* @return the activation chance with "lucky perk" accounted for
*/
public static int handleLuckyPerks(McMMOPlayer mmoPlayer, PrimarySkillType skill) {
if (Permissions.lucky(mmoPlayer.getPlayer(), skill)) {
return LUCKY_SKILL_ACTIVATION_CHANCE;
}
return NORMAL_SKILL_ACTIVATION_CHANCE;
}
}

View File

@ -10,7 +10,7 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.metadata.ItemMetadataService;
import com.gmail.nossr50.util.ItemMetadataUtils;
import com.gmail.nossr50.util.ItemUtils;
import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.Permissions;
@ -32,6 +32,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import static com.gmail.nossr50.util.ItemMetadataUtils.*;
import static com.gmail.nossr50.util.PotionEffectMapper.getHaste;
public final class SkillUtils {
@ -152,7 +153,7 @@ public final class SkillUtils {
ItemUtils.addDigSpeedToItem(heldItem, heldItem.getEnchantmentLevel(mcMMO.p.getEnchantmentMapper().getEfficiency()));
//1.13.2+ will have persistent metadata for this item
mcMMO.getMetadataService().getItemMetadataService().setSuperAbilityBoostedItem(heldItem, originalDigSpeed);
ItemMetadataUtils.setSuperAbilityBoostedItem(heldItem, originalDigSpeed);
} else {
int duration = 0;
int amplifier = 0;
@ -209,9 +210,7 @@ public final class SkillUtils {
//1.13.2+ will have persistent metadata for this itemStack
ItemMetadataService itemMetadataService = mcMMO.getMetadataService().getItemMetadataService();
if(itemMetadataService.isLegacyAbilityTool(itemStack)) {
if(isLegacyAbilityTool(itemStack)) {
ItemMeta itemMeta = itemStack.getItemMeta();
if(itemMeta != null) {
@ -223,8 +222,8 @@ public final class SkillUtils {
}
}
if(itemMetadataService.isSuperAbilityBoosted(itemStack)) {
itemMetadataService.removeBonusDigSpeedOnSuperAbilityTool(itemStack);
if(isSuperAbilityBoosted(itemStack)) {
removeBonusDigSpeedOnSuperAbilityTool(itemStack);
}
}

View File

@ -1,102 +0,0 @@
package com.gmail.nossr50.util.skills;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.player.UserManager;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.block.Furnace;
import org.bukkit.entity.Player;
import org.bukkit.inventory.FurnaceInventory;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class SmeltingTracker {
// private final HashMap<Furnace, OfflinePlayer> furnaceOwners;
private void changeFurnaceOwnership(Furnace furnace, Player player) {
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
/*
Debug output
*/
printOwnershipGainDebug(furnace, mcMMOPlayer);
printOwnershipLossDebug(furnace);
setFurnaceOwner(furnace, player);
}
private void setFurnaceOwner(Furnace furnace, Player player) {
mcMMO.getMetadataService().getBlockMetadataService().setFurnaceOwner(furnace, player.getUniqueId());
}
private void printOwnershipGainDebug(Furnace furnace, McMMOPlayer mcMMOPlayer) {
if(mcMMOPlayer != null) {
if(mcMMOPlayer.isDebugMode()) {
mcMMOPlayer.getPlayer().sendMessage("Furnace ownership " +
ChatColor.GREEN +"gained " + ChatColor.RESET +
"at location: " + furnace.getLocation().toString());
}
}
}
private void printOwnershipLossDebug(Furnace furnace) {
OfflinePlayer furnaceOwner = getFurnaceOwner(furnace);
if(furnaceOwner != null && furnaceOwner.isOnline()) {
McMMOPlayer furnaceOwnerProfile = UserManager.getPlayer(furnaceOwner.getPlayer());
if(furnaceOwnerProfile != null) {
if(furnaceOwnerProfile.isDebugMode()) {
furnaceOwnerProfile.getPlayer().sendMessage("Furnace ownership " +
ChatColor.RED + "lost " + ChatColor.RESET +
"at location: " + furnace.getLocation().toString());
}
}
}
}
public @Nullable OfflinePlayer getFurnaceOwner(Furnace furnace) {
UUID uuid = mcMMO.getMetadataService().getBlockMetadataService().getFurnaceOwner(furnace);
if(uuid != null) {
return Bukkit.getOfflinePlayer(uuid);
} else {
return null;
}
}
@Nullable
public Furnace getFurnaceFromInventory(Inventory inventory) {
if (!(inventory instanceof FurnaceInventory)) {
return null;
}
return (Furnace) inventory.getHolder();
}
public boolean isFurnaceOwned(Furnace furnace) {
return getFurnaceOwner(furnace) != null;
}
public void processFurnaceOwnership(Furnace furnace, Player player) {
if(!mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(player, PrimarySkillType.SMELTING))
return;
//Don't swap ownership if its the same player
if(getFurnaceOwner(furnace) != null) {
if(getFurnaceOwner(furnace).getUniqueId().equals(player.getUniqueId()))
return;
}
changeFurnaceOwnership(furnace, player);
}
}

View File

@ -10,6 +10,7 @@ import com.gmail.nossr50.listeners.InteractionManager;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.player.UserManager;
import com.gmail.nossr50.util.skills.RankUtils;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
@ -345,7 +346,8 @@ public class TextComponentFactory {
componentBuilder.append(Component.newline());
//Finally, add details to the tooltip
abstractSubSkill.addStats(componentBuilder, player);
// TODO: pass in McMMOPlayer instead
abstractSubSkill.addStats(componentBuilder, UserManager.getPlayer(player));
}
return componentBuilder.build();