123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- package com.gmail.nossr50.skills.taming;
- import com.gmail.nossr50.config.experience.ExperienceConfig;
- import com.gmail.nossr50.datatypes.experience.XPGainReason;
- import com.gmail.nossr50.datatypes.interactions.NotificationType;
- 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.datatypes.skills.subskills.taming.CallOfTheWildType;
- import com.gmail.nossr50.datatypes.skills.subskills.taming.TamingSummon;
- import com.gmail.nossr50.locale.LocaleLoader;
- import com.gmail.nossr50.mcMMO;
- import com.gmail.nossr50.metadata.MobMetaFlagType;
- import com.gmail.nossr50.skills.SkillManager;
- import com.gmail.nossr50.util.Misc;
- import com.gmail.nossr50.util.Permissions;
- import com.gmail.nossr50.util.player.NotificationManager;
- import com.gmail.nossr50.util.random.ProbabilityUtil;
- import com.gmail.nossr50.util.skills.ParticleEffectUtils;
- import com.gmail.nossr50.util.skills.RankUtils;
- import com.gmail.nossr50.util.sounds.SoundManager;
- import com.gmail.nossr50.util.sounds.SoundType;
- import com.gmail.nossr50.util.text.StringUtils;
- import org.bukkit.Location;
- import org.bukkit.Material;
- import org.bukkit.attribute.AttributeInstance;
- import org.bukkit.entity.*;
- import org.bukkit.inventory.ItemStack;
- import org.jetbrains.annotations.NotNull;
- import java.util.HashMap;
- import static com.gmail.nossr50.util.AttributeMapper.MAPPED_JUMP_STRENGTH;
- import static com.gmail.nossr50.util.AttributeMapper.MAPPED_MOVEMENT_SPEED;
- import static com.gmail.nossr50.util.MobMetadataUtils.flagMetadata;
- public class TamingManager extends SkillManager {
- //TODO: Temporary static cache, will be changed in 2.2
- private static HashMap<Material, CallOfTheWildType> summoningItems;
- private static HashMap<CallOfTheWildType, TamingSummon> cotwSummonDataProperties;
- private long lastSummonTimeStamp;
- public TamingManager(@NotNull McMMOPlayer mcMMOPlayer) {
- super(mcMMOPlayer, PrimarySkillType.TAMING);
- init();
- }
- //TODO: Hacky stuff for 2.1, will be cleaned up in 2.2
- private void init() {
- //prevents accidentally summoning too many things when holding down left click
- lastSummonTimeStamp = 0L;
- //Init per-player tracking of summoned entities
- mcMMO.getTransientEntityTracker().initPlayer(mmoPlayer.getPlayer());
- //Hacky stuff used as a band-aid
- initStaticCaches();
- }
- private void initStaticCaches() {
- //TODO: Temporary static cache, will be changed in 2.2
- //This is shared between instances of TamingManager
- if (summoningItems == null) {
- summoningItems = new HashMap<>();
- summoningItems.put(mcMMO.p.getGeneralConfig().getTamingCOTWMaterial(CallOfTheWildType.CAT.getConfigEntityTypeEntry()), CallOfTheWildType.CAT);
- summoningItems.put(mcMMO.p.getGeneralConfig().getTamingCOTWMaterial(CallOfTheWildType.WOLF.getConfigEntityTypeEntry()), CallOfTheWildType.WOLF);
- summoningItems.put(mcMMO.p.getGeneralConfig().getTamingCOTWMaterial(CallOfTheWildType.HORSE.getConfigEntityTypeEntry()), CallOfTheWildType.HORSE);
- }
- //TODO: Temporary static cache, will be changed in 2.2
- //This is shared between instances of TamingManager
- if (cotwSummonDataProperties == null) {
- cotwSummonDataProperties = new HashMap<>();
- for(CallOfTheWildType callOfTheWildType : CallOfTheWildType.values()) {
- Material itemSummonMaterial = mcMMO.p.getGeneralConfig().getTamingCOTWMaterial(callOfTheWildType.getConfigEntityTypeEntry());
- int itemAmountRequired = mcMMO.p.getGeneralConfig().getTamingCOTWCost(callOfTheWildType.getConfigEntityTypeEntry());
- int entitiesSummonedPerCOTW = mcMMO.p.getGeneralConfig().getTamingCOTWAmount(callOfTheWildType.getConfigEntityTypeEntry());
- int summonLifespanSeconds = mcMMO.p.getGeneralConfig().getTamingCOTWLength(callOfTheWildType.getConfigEntityTypeEntry());
- int perPlayerMaxAmount = mcMMO.p.getGeneralConfig().getTamingCOTWMaxAmount(callOfTheWildType.getConfigEntityTypeEntry());
- TamingSummon tamingSummon = new TamingSummon(callOfTheWildType, itemSummonMaterial, itemAmountRequired, entitiesSummonedPerCOTW, summonLifespanSeconds, perPlayerMaxAmount);
- cotwSummonDataProperties.put(callOfTheWildType, tamingSummon);
- }
- }
- }
- public boolean canUseThickFur() {
- return RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_THICK_FUR)
- && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.TAMING_THICK_FUR);
- }
- public boolean canUseEnvironmentallyAware() {
- return RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_ENVIRONMENTALLY_AWARE)
- && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.TAMING_ENVIRONMENTALLY_AWARE);
- }
- public boolean canUseShockProof() {
- return RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_SHOCK_PROOF)
- && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.TAMING_SHOCK_PROOF);
- }
- public boolean canUseHolyHound() {
- return RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_ENVIRONMENTALLY_AWARE)
- && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.TAMING_HOLY_HOUND);
- }
- public boolean canUseFastFoodService() {
- return RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_FAST_FOOD_SERVICE)
- && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.TAMING_FAST_FOOD_SERVICE);
- }
- public boolean canUseSharpenedClaws() {
- return RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_SHARPENED_CLAWS)
- && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.TAMING_SHARPENED_CLAWS);
- }
- public boolean canUseGore() {
- if (!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_GORE))
- return false;
- return Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.TAMING_GORE);
- }
- public boolean canUseBeastLore() {
- if (!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_BEAST_LORE))
- return false;
- return Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.TAMING_BEAST_LORE);
- }
- /**
- * Award XP for taming.
- *
- * @param entity The LivingEntity to award XP for
- */
- public void awardTamingXP(@NotNull LivingEntity entity) {
- applyXpGain(ExperienceConfig.getInstance().getTamingXP(entity.getType()), XPGainReason.PVE);
- }
- /**
- * Apply the Fast Food Service ability.
- *
- * @param wolf The wolf using the ability
- * @param damage The damage being absorbed by the wolf
- */
- public void fastFoodService(@NotNull Wolf wolf, double damage) {
- if (!ProbabilityUtil.isSkillRNGSuccessful(SubSkillType.TAMING_FAST_FOOD_SERVICE, mmoPlayer)) {
- return;
- }
- double health = wolf.getHealth();
- double maxHealth = wolf.getMaxHealth();
- if (health < maxHealth) {
- double newHealth = health + damage;
- wolf.setHealth(Math.min(newHealth, maxHealth));
- }
- }
- /**
- * Apply the Gore ability.
- *
- * @param target The LivingEntity to apply Gore on
- * @param damage The initial damage
- */
- public double gore(@NotNull LivingEntity target, double damage) {
- damage = (damage * Taming.goreModifier) - damage;
- return damage;
- }
- public double sharpenedClaws() {
- return Taming.sharpenedClawsBonusDamage;
- }
- /**
- * Summon an ocelot to your side.
- */
- public void summonOcelot() {
- if (!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_CALL_OF_THE_WILD))
- return;
- if (!Permissions.callOfTheWild(getPlayer(), EntityType.OCELOT)) {
- return;
- }
- processCallOfTheWild();
- }
- /**
- * Summon a wolf to your side.
- */
- public void summonWolf() {
- if (!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_CALL_OF_THE_WILD))
- return;
- if (!Permissions.callOfTheWild(getPlayer(), EntityType.WOLF)) {
- return;
- }
- processCallOfTheWild();
- }
- /**
- * Summon a horse to your side.
- */
- public void summonHorse() {
- if (!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_CALL_OF_THE_WILD))
- return;
- if (!Permissions.callOfTheWild(getPlayer(), EntityType.HORSE)) {
- return;
- }
- processCallOfTheWild();
- }
- /**
- * Handle the Beast Lore ability.
- *
- * @param target The entity to examine
- */
- public void beastLore(LivingEntity target) {
- Player player = getPlayer();
- Tameable beast = (Tameable) target;
- String message = LocaleLoader.getString("Combat.BeastLore") + " ";
- if (beast.isTamed() && beast.getOwner() != null) {
- message = message.concat(LocaleLoader.getString("Combat.BeastLoreOwner", beast.getOwner().getName()) + " ");
- }
- message = message.concat(LocaleLoader.getString("Combat.BeastLoreHealth", target.getHealth(), target.getMaxHealth()));
- // Bred mules & donkeys can actually have horse-like stats, but llamas cannot.
- if (beast instanceof AbstractHorse horseLikeCreature && !(beast instanceof Llama)) {
- AttributeInstance jumpAttribute = horseLikeCreature.getAttribute(MAPPED_JUMP_STRENGTH);
- if (jumpAttribute != null) {
- double jumpStrength = jumpAttribute.getValue();
- // Taken from https://minecraft.wiki/w/Horse#Jump_strength
- jumpStrength = -0.1817584952 * Math.pow(jumpStrength, 3) + 3.689713992 * Math.pow(jumpStrength, 2) + 2.128599134 * jumpStrength - 0.343930367;
- message = message.concat("\n" + LocaleLoader.getString("Combat.BeastLoreHorseSpeed", horseLikeCreature.getAttribute(MAPPED_MOVEMENT_SPEED).getValue() * 43))
- .concat("\n" + LocaleLoader.getString("Combat.BeastLoreHorseJumpStrength", jumpStrength));
- }
- }
- player.sendMessage(message);
- }
- public void processEnvironmentallyAware(@NotNull Wolf wolf, double damage) {
- if (damage > wolf.getHealth()) {
- return;
- }
- Player owner = getPlayer();
- wolf.teleport(owner);
- NotificationManager.sendPlayerInformation(owner, NotificationType.SUBSKILL_MESSAGE, "Taming.Listener.Wolf");
- }
- public void pummel(LivingEntity target, Wolf wolf) {
- if (!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.TAMING_PUMMEL))
- return;
- if (!ProbabilityUtil.isStaticSkillRNGSuccessful(PrimarySkillType.TAMING, mmoPlayer, mcMMO.p.getAdvancedConfig().getPummelChance()))
- return;
- ParticleEffectUtils.playGreaterImpactEffect(target);
- target.setVelocity(wolf.getLocation().getDirection().normalize().multiply(1.5D));
- if (target instanceof Player defender) {
- if (NotificationManager.doesPlayerUseNotifications(defender)) {
- NotificationManager.sendPlayerInformation(defender, NotificationType.SUBSKILL_MESSAGE, "Taming.SubSkill.Pummel.TargetMessage");
- }
- }
- }
- public void attackTarget(LivingEntity target) {
- if (target instanceof Tameable tameable) {
- if (tameable.getOwner() == getPlayer()) {
- return;
- }
- }
- double range = 5;
- Player player = getPlayer();
- if (!target.getWorld().equals(player.getWorld()))
- return;
- for (Entity entity : player.getNearbyEntities(range, range, range)) {
- if (entity.getType() != EntityType.WOLF) {
- continue;
- }
- Wolf wolf = (Wolf) entity;
- if (!wolf.isTamed() || (wolf.getOwner() != player) || wolf.isSitting()) {
- continue;
- }
- wolf.setTarget(target);
- }
- }
- private void processCallOfTheWild() {
- //Prevent summoning too many things accidentally if a player holds down the button
- if (lastSummonTimeStamp + 150 > System.currentTimeMillis()) {
- return;
- } else {
- lastSummonTimeStamp = System.currentTimeMillis();
- }
- Player player = getPlayer();
- ItemStack itemInMainHand = player.getInventory().getItemInMainHand();
- //Check if the item the player is currently holding is a COTW item
- if (isCOTWItem(itemInMainHand)) {
- //Get the summoning type
- CallOfTheWildType callOfTheWildType = summoningItems.get(itemInMainHand.getType());
- TamingSummon tamingSummon = cotwSummonDataProperties.get(callOfTheWildType);
- //Players will pay for the cost if at least one thing was summoned
- int amountSummoned = 0;
- //Check to see if players have the correct amount of the item required to summon
- if (itemInMainHand.getAmount() >= tamingSummon.getItemAmountRequired()) {
- //Initial Spawn location
- Location spawnLocation = Misc.getLocationOffset(player.getLocation(), 1);
- //COTW can summon multiple entities per usage
- for (int i = 0; i < tamingSummon.getEntitiesSummoned(); i++) {
- if (getAmountCurrentlySummoned(callOfTheWildType) >= tamingSummon.getSummonCap()) {
- NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.Limit",
- String.valueOf(tamingSummon.getSummonCap()),
- StringUtils.getCapitalized(callOfTheWildType.toString()));
- break;
- }
- spawnLocation = Misc.getLocationOffset(spawnLocation, 1);
- spawnCOTWEntity(callOfTheWildType, spawnLocation, tamingSummon.getEntityType());
- //Inform the player about what they have just done
- if (tamingSummon.getSummonLifespan() > 0) {
- NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.Success.WithLifespan",
- StringUtils.getCapitalized(callOfTheWildType.toString()), String.valueOf(tamingSummon.getSummonLifespan()));
- } else {
- NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.Success.WithoutLifespan", StringUtils.getCapitalized(callOfTheWildType.toString()));
- }
- //Send Sound
- SoundManager.sendSound(player, player.getLocation(), SoundType.ABILITY_ACTIVATED_GENERIC);
- amountSummoned++;
- }
- //Remove items from the player if they had at least one entity summoned successfully
- if (amountSummoned >= 1) {
- //Remove the items used to summon
- int itemAmountAfterPayingCost = itemInMainHand.getAmount() - tamingSummon.getItemAmountRequired();
- itemInMainHand.setAmount(itemAmountAfterPayingCost);
- player.updateInventory();
- }
- } else {
- //Player did not have enough of the item in their main hand
- int difference = tamingSummon.getItemAmountRequired() - itemInMainHand.getAmount();
- NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.NeedMoreItems", String.valueOf(difference), StringUtils.getPrettyMaterialString(itemInMainHand.getType()));
- }
- }
- }
- private void spawnCOTWEntity(CallOfTheWildType callOfTheWildType, Location spawnLocation, EntityType entityType) {
- switch (callOfTheWildType) {
- case CAT ->
- //Entity type is needed for cats because in 1.13 and below we spawn ocelots, in 1.14 and above we spawn cats
- spawnCat(spawnLocation, entityType);
- case HORSE -> spawnHorse(spawnLocation);
- case WOLF -> spawnWolf(spawnLocation);
- }
- }
- private void spawnWolf(Location spawnLocation) {
- LivingEntity callOfWildEntity = (LivingEntity) getPlayer().getWorld().spawnEntity(spawnLocation, EntityType.WOLF);
- //This is used to prevent XP gains for damaging this entity
- applyMetaDataToCOTWEntity(callOfWildEntity);
- setBaseCOTWEntityProperties(callOfWildEntity);
- ((Wolf) callOfWildEntity).setAdult();
- addToTracker(callOfWildEntity, CallOfTheWildType.WOLF);
- //Setup wolf stats
- callOfWildEntity.setMaxHealth(20.0);
- callOfWildEntity.setHealth(callOfWildEntity.getMaxHealth());
- callOfWildEntity.setCustomName(LocaleLoader.getString("Taming.Summon.Name.Format", getPlayer().getName(), StringUtils.getPrettyEntityTypeString(EntityType.WOLF)));
- }
- @SuppressWarnings("deprecation")
- private void spawnCat(Location spawnLocation, EntityType entityType) {
- LivingEntity callOfWildEntity = (LivingEntity) getPlayer().getWorld().spawnEntity(spawnLocation, entityType);
- //This is used to prevent XP gains for damaging this entity
- applyMetaDataToCOTWEntity(callOfWildEntity);
- setBaseCOTWEntityProperties(callOfWildEntity);
- addToTracker(callOfWildEntity, CallOfTheWildType.CAT);
- //Randomize the cat
- if (callOfWildEntity instanceof Ocelot) {
- // Ocelot.Type is deprecated, but that's fine since this only runs on 1.13
- int numberOfTypes = Ocelot.Type.values().length;
- ((Ocelot) callOfWildEntity).setCatType(Ocelot.Type.values()[Misc.getRandom().nextInt(numberOfTypes)]);
- }
- ((Ageable) callOfWildEntity).setAdult();
- callOfWildEntity.setCustomName(LocaleLoader.getString("Taming.Summon.Name.Format", getPlayer().getName(), StringUtils.getPrettyEntityTypeString(entityType)));
- //Particle effect
- ParticleEffectUtils.playCallOfTheWildEffect(callOfWildEntity);
- }
- private void spawnHorse(Location spawnLocation) {
- LivingEntity callOfWildEntity = (LivingEntity) getPlayer().getWorld().spawnEntity(spawnLocation, EntityType.HORSE);
- applyMetaDataToCOTWEntity(callOfWildEntity);
- setBaseCOTWEntityProperties(callOfWildEntity);
- addToTracker(callOfWildEntity, CallOfTheWildType.HORSE);
- //Randomize Horse
- Horse horse = (Horse) callOfWildEntity;
- callOfWildEntity.setMaxHealth(15.0 + (Misc.getRandom().nextDouble() * 15));
- callOfWildEntity.setHealth(callOfWildEntity.getMaxHealth());
- horse.setColor(Horse.Color.values()[Misc.getRandom().nextInt(Horse.Color.values().length)]);
- horse.setStyle(Horse.Style.values()[Misc.getRandom().nextInt(Horse.Style.values().length)]);
- horse.setJumpStrength(Math.max(mcMMO.p.getAdvancedConfig().getMinHorseJumpStrength(), Math.min(Math.min(Misc.getRandom().nextDouble(), Misc.getRandom().nextDouble()) * 2, mcMMO.p.getAdvancedConfig().getMaxHorseJumpStrength())));
- horse.setAdult();
- //TODO: setSpeed, once available
- callOfWildEntity.setCustomName(LocaleLoader.getString("Taming.Summon.Name.Format", getPlayer().getName(), StringUtils.getPrettyEntityTypeString(EntityType.HORSE)));
- //Particle effect
- ParticleEffectUtils.playCallOfTheWildEffect(callOfWildEntity);
- }
- private void setBaseCOTWEntityProperties(LivingEntity callOfWildEntity) {
- ((Tameable) callOfWildEntity).setOwner(getPlayer());
- callOfWildEntity.setRemoveWhenFarAway(false);
- }
- private void applyMetaDataToCOTWEntity(LivingEntity summonedEntity) {
- //This helps identify the entity as being summoned by COTW
- flagMetadata(MobMetaFlagType.COTW_SUMMONED_MOB, summonedEntity);
- }
- /**
- * Whether the itemstack is used for COTW
- * @param itemStack target ItemStack
- * @return true if it is used for any COTW
- */
- public boolean isCOTWItem(@NotNull ItemStack itemStack) {
- return summoningItems.containsKey(itemStack.getType());
- }
- private int getAmountCurrentlySummoned(@NotNull CallOfTheWildType callOfTheWildType) {
- return mcMMO.getTransientEntityTracker().getAmountCurrentlySummoned(getPlayer().getUniqueId(), callOfTheWildType);
- }
- private void addToTracker(@NotNull LivingEntity livingEntity, @NotNull CallOfTheWildType callOfTheWildType) {
- mcMMO.getTransientEntityTracker().registerEntity(getPlayer().getUniqueId(), new TrackedTamingEntity(livingEntity, callOfTheWildType, getPlayer()));
- }
- /**
- * Remove all tracked entities from existence if they currently exist
- * Clear the tracked entity lists afterwards
- */
- //TODO: The way this tracker was written is garbo, I should just rewrite it, I'll save that for a future update
- public void cleanupAllSummons() {
- mcMMO.getTransientEntityTracker().cleanupPlayer(getPlayer());
- }
- }
|