studio.magemonkey.fabled.api.util.BuffData Maven / Gradle / Ivy
Show all versions of fabled Show documentation
/**
* Fabled
* studio.magemonkey.fabled.api.util.BuffData
*
* The MIT License (MIT)
*
* Copyright (c) 2024 MageMonkeyStudio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software") to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package studio.magemonkey.fabled.api.util;
import studio.magemonkey.fabled.Fabled;
import studio.magemonkey.fabled.api.event.BuffExpiredEvent;
import studio.magemonkey.fabled.log.LogType;
import studio.magemonkey.fabled.log.Logger;
import org.bukkit.Bukkit;
import org.bukkit.entity.LivingEntity;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Represents buffs set on an entity
*/
public class BuffData {
private final Map> buffs = new HashMap<>();
private final LivingEntity entity;
/**
* Initializes new buff data for the entity
*
* @param entity entity to initialize for
*/
public BuffData(LivingEntity entity) {
this.entity = entity;
}
public double getMultiplier(final BuffType buffType, final String category) {
return category == null || category.isEmpty()
? getMultiplier(buffType.name())
: getMultiplier(buffType.name(), buffType.name() + category);
}
public double getFlatBonus(final BuffType buffType, final String category) {
return category == null || category.isEmpty()
? getFlatBonus(buffType.name())
: getFlatBonus(buffType.name(), buffType.name() + category);
}
/**
* Adds a buff to the buff collection. If a buff already exists with the same
* key, it will be overwritten.
*
* @param type type of buff to add
* @param buff buff details
* @param ticks how long to apply the buff for
*/
public void addBuff(final BuffType type, final Buff buff, final int ticks) {
doAddBuff(type.name(), buff, ticks);
}
/**
* Adds a buff to the buff collection. If a buff already exists with the same
* key, it will be overwritten.
*
* @param type type of buff to add
* @param category sub category of the type to apply (e.g. damage classification)
* @param buff buff details
* @param ticks how long to apply the buff for
*/
public void addBuff(final BuffType type, final String category, final Buff buff, final int ticks) {
doAddBuff(type.name() + (category != null ? category : ""), buff, ticks);
}
private void doAddBuff(final String type, final Buff buff, final int ticks) {
final Map typeBuffs = buffs.computeIfAbsent(type, t -> new HashMap<>());
final Buff conflict = typeBuffs.remove(buff.getKey());
if (conflict != null)
conflict.task.cancel();
typeBuffs.put(buff.getKey(), buff);
buff.task = Fabled.schedule(new BuffTask(type, buff.getKey()), ticks);
}
/**
* @deprecated use {@link BuffData#addBuff(BuffType, Buff, int)} instead
*/
@Deprecated
public void addDamageBuff(Buff buff, int ticks) {
addBuff(BuffType.DAMAGE, buff, ticks);
}
/**
* @deprecated use {@link BuffData#addBuff(BuffType, Buff, int)} instead
*/
@Deprecated
public void addDefenseBuff(Buff buff, int ticks) {
addBuff(BuffType.DEFENSE, buff, ticks);
}
/**
* @deprecated use {@link BuffData#addBuff(BuffType, Buff, int)} instead
*/
@Deprecated
public void addSkillDamageBuff(Buff buff, int ticks) {
addBuff(BuffType.SKILL_DAMAGE, buff, ticks);
}
/**
* @deprecated use {@link BuffData#addBuff(BuffType, Buff, int)} instead
*/
@Deprecated
public void addSkillDefenseBuff(Buff buff, int ticks) {
addBuff(BuffType.SKILL_DEFENSE, buff, ticks);
}
/**
* Applies all buffs of the given type to the specified value
*
* @param type type of buff to apply
* @param value value to modify
* @return value after all buff applications
*/
public double apply(final BuffType type, final double value) {
return doApply(value, type.name());
}
public boolean isActive(final BuffType type) {
final Map typeBuffs = buffs.get(type.name());
return typeBuffs != null;
}
/**
* Clears buffs by the given type
*
* @param type type of buff
*/
public void clearByType(final BuffType type) {
Map buffType = buffs.get(type.name());
if (buffType == null) return;
for (final Buff buff : buffType.values()) {
buff.task.cancel();
}
buffs.remove(type.name());
}
/**
* Applies all buffs of the given type to the specified value
*
* @param type type of buff to apply
* @param category sub category of the buff type to apply (e.g. damage classification)
* @param value value to modify
* @return value after all buff applications
*/
public double apply(final BuffType type, final String category, final double value) {
return category == null || category.length() == 0
? doApply(value, type.name())
: doApply(value, type.name(), type.name() + category);
}
private double doApply(final double value, final String... types) {
// Ignore zeroed out values that shouldn't get buffs
if (value <= 0) return value;
double multiplier = 1;
double bonus = 0;
Logger.log(LogType.BUFF, 1, "Buffs:");
for (final String type : types) {
final Map typeBuffs = buffs.get(type);
if (typeBuffs == null) {
continue;
}
for (final Buff buff : typeBuffs.values()) {
if (buff.isPercent()) {
Logger.log(LogType.BUFF, 1, " - x" + buff.getValue());
multiplier *= buff.getValue();
} else {
Logger.log(LogType.BUFF, 1, " - +" + buff.getValue());
bonus += buff.getValue();
}
}
}
double result = Math.max(0, value * multiplier + bonus);
Logger.log(LogType.BUFF, 1, "Result: x" + multiplier + ", +" + bonus + ", " + value + " -> " + result);
// Negatives aren't well received by bukkit, so return 0 instead
if (multiplier <= 0) return 0;
return result;
}
private double getFlatBonus(final String... types) {
double bonus = 0;
for (final String type : types) {
for (final Buff buff : buffs.getOrDefault(type, Collections.emptyMap()).values()) {
if (!buff.isPercent()) {
bonus += buff.getValue();
}
}
}
// Negatives aren't well received by bukkit, so return 0 instead
return bonus;
}
private double getMultiplier(final String... types) {
double multiplier = 1;
for (final String type : types) {
for (final Buff buff : buffs.getOrDefault(type, Collections.emptyMap()).values()) {
if (buff.isPercent()) {
multiplier *= buff.getValue();
}
}
}
// Negatives aren't well received by bukkit, so return 0 instead
return Math.max(0, multiplier);
}
/**
* @deprecated use {@link BuffData#apply(BuffType, double)} instead
*/
@Deprecated
public double modifyDealtDamage(double damage) {
return apply(BuffType.DAMAGE, damage);
}
/**
* @deprecated use {@link BuffData#apply(BuffType, double)} instead
*/
@Deprecated
public double modifyTakenDamage(double damage) {
return apply(BuffType.DEFENSE, damage);
}
/**
* @deprecated use {@link BuffData#apply(BuffType, double)} instead
*/
@Deprecated
public double modifySkillDealtDamage(double damage) {
return apply(BuffType.SKILL_DAMAGE, damage);
}
/**
* @deprecated use {@link BuffData#apply(BuffType, double)} instead
*/
@Deprecated
public double modifySkillTakenDamage(double damage) {
return apply(BuffType.SKILL_DEFENSE, damage);
}
/**
* Clears all buffs on the entity and stops associated tasks.
*/
public void clear() {
for (final Map typeBuffs : buffs.values()) {
for (final Buff buff : typeBuffs.values()) {
buff.task.cancel();
}
}
buffs.clear();
BuffManager.clearData(entity);
}
private class BuffTask extends BukkitRunnable {
private final String type;
private final String key;
BuffTask(final String type, final String key) {
this.type = type;
this.key = key;
}
@Override
public void run() {
if (!entity.isValid() || entity.isDead()) {
BuffManager.clearData(entity);
return;
}
final Map typeBuffs = buffs.get(type);
typeBuffs.remove(key);
// Clean up buff data if the entity doesn't hold onto any buffs
if (typeBuffs.size() == 0) {
buffs.remove(type);
if (buffs.size() == 0) {
BuffManager.clearData(entity);
}
}
BuffExpiredEvent event = new BuffExpiredEvent(entity, typeBuffs.get(type), BuffType.valueOf(this.type));
Bukkit.getPluginManager().callEvent(event);
}
}
}