Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ka0rX 2022-12-01 22:16:51 +01:00
commit 57db8b91c3
7 changed files with 306 additions and 226 deletions

View File

@ -60,6 +60,7 @@ import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
public class PlayerData extends OfflinePlayerData implements Closable, ExperienceTableClaimer { public class PlayerData extends OfflinePlayerData implements Closable, ExperienceTableClaimer {
@ -376,6 +377,10 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
// Close quest data // Close quest data
questData.close(); questData.close();
// Stop skill casting
if (isCasting())
leaveSkillCasting();
} }
public MMOPlayerData getMMOPlayerData() { public MMOPlayerData getMMOPlayerData() {
@ -786,19 +791,26 @@ public class PlayerData extends OfflinePlayerData implements Closable, Experienc
if (value <= 0) if (value <= 0)
return; return;
if (hasReachedMaxLevel()) {
setExperience(0);
return;
}
// Splitting exp through party members // Splitting exp through party members
AbstractParty party; AbstractParty party;
if (splitExp && (party = getParty()) != null) { if (splitExp && (party = getParty()) != null) {
List<PlayerData> onlineMembers = party.getOnlineMembers(); final List<PlayerData> nearbyMembers = party.getOnlineMembers().stream()
value /= onlineMembers.size(); .filter(pd -> {
for (PlayerData member : onlineMembers) if (equals(pd) || pd.hasReachedMaxLevel())
if (!equals(member)) return false;
member.giveExperience(value, source, null, false);
final double maxDis = MMOCore.plugin.configManager.partyMaxExpSplitRange;
return maxDis <= 0 || pd.getPlayer().getLocation().distanceSquared(getPlayer().getLocation()) < maxDis * maxDis;
}).collect(Collectors.toList());
value /= (nearbyMembers.size() + 1);
for (PlayerData member : nearbyMembers)
member.giveExperience(value, source, null, false);
}
// Must be placed after exp splitting
if (hasReachedMaxLevel()) {
setExperience(0);
return;
} }
// Apply buffs AFTER splitting exp // Apply buffs AFTER splitting exp

View File

@ -0,0 +1,52 @@
package net.Indyuce.mmocore.api.player.attribute;
import io.lumine.mythic.lib.api.stat.StatMap;
import io.lumine.mythic.lib.api.stat.handler.StatHandler;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
/**
* This fixes an issue where registering new stat modifiers in ML
* to add extra attribute points does NOT update the stats granted
* by the attribute.
* <p>
* This stat handler MAY call subsequent stat handlers. There might
* be infinite recursion problems if another attr. grants extra attribute pts.
*/
public class MMOCoreAttributeStatHandler implements StatHandler {
private final PlayerAttribute attr;
private final String statName;
public MMOCoreAttributeStatHandler(PlayerAttribute attr) {
this.attr = attr;
this.statName = "ADDITIONAL_" + attr.getId().toUpperCase().replace("-", "_");
}
public String getStat() {
return statName;
}
/**
* This method is called on login but the MMOCore playerData
* is not loaded yet, hence the try/catch clause
*/
@Override
public void runUpdate(StatMap statMap) {
try {
final PlayerData playerData = MMOCore.plugin.dataProvider.getDataManager().get(statMap.getPlayerData().getUniqueId());
playerData.getAttributes().getInstance(attr).updateStats();
} catch (NullPointerException exception) {
// Player data is not loaded yet so there's nothing to update.
}
}
@Override
public double getBaseValue(StatMap statMap) {
return 0;
}
@Override
public double getTotalValue(StatMap statMap) {
return statMap.getStat(statName);
}
}

View File

@ -22,202 +22,206 @@ import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
public class PlayerAttributes { public class PlayerAttributes {
private final PlayerData data; private final PlayerData data;
private final Map<String, AttributeInstance> instances = new HashMap<>(); private final Map<String, AttributeInstance> instances = new HashMap<>();
public PlayerAttributes(PlayerData data) { public PlayerAttributes(PlayerData data) {
this.data = data; this.data = data;
} }
public void load(ConfigurationSection config) { public void load(ConfigurationSection config) {
for (String key : config.getKeys(false)) for (String key : config.getKeys(false))
try { try {
String id = key.toLowerCase().replace("_", "-").replace(" ", "-"); String id = key.toLowerCase().replace("_", "-").replace(" ", "-");
Validate.isTrue(MMOCore.plugin.attributeManager.has(id), "Could not find attribute '" + id + "'"); Validate.isTrue(MMOCore.plugin.attributeManager.has(id), "Could not find attribute '" + id + "'");
PlayerAttribute attribute = MMOCore.plugin.attributeManager.get(id); PlayerAttribute attribute = MMOCore.plugin.attributeManager.get(id);
AttributeInstance ins = new AttributeInstance(attribute.getId()); AttributeInstance ins = new AttributeInstance(attribute.getId());
ins.setBase(config.getInt(key)); ins.setBase(config.getInt(key));
instances.put(id, ins); instances.put(id, ins);
} catch (IllegalArgumentException exception) { } catch (IllegalArgumentException exception) {
data.log(Level.WARNING, exception.getMessage()); data.log(Level.WARNING, exception.getMessage());
} }
} }
public void save(ConfigurationSection config) { public void save(ConfigurationSection config) {
instances.values().forEach(ins -> config.set(ins.id, ins.getBase())); instances.values().forEach(ins -> config.set(ins.id, ins.getBase()));
} }
public String toJsonString() { public String toJsonString() {
JsonObject json = new JsonObject(); JsonObject json = new JsonObject();
for (AttributeInstance ins : instances.values()) for (AttributeInstance ins : instances.values())
json.addProperty(ins.getId(), ins.getBase()); json.addProperty(ins.getId(), ins.getBase());
return json.toString(); return json.toString();
} }
public void load(String json) { public void load(String json) {
Gson parser = new Gson(); Gson parser = new Gson();
JsonObject jo = parser.fromJson(json, JsonObject.class); JsonObject jo = parser.fromJson(json, JsonObject.class);
for (Entry<String, JsonElement> entry : jo.entrySet()) { for (Entry<String, JsonElement> entry : jo.entrySet()) {
try { try {
String id = entry.getKey().toLowerCase().replace("_", "-").replace(" ", "-"); String id = entry.getKey().toLowerCase().replace("_", "-").replace(" ", "-");
Validate.isTrue(MMOCore.plugin.attributeManager.has(id), "Could not find attribute '" + id + "'"); Validate.isTrue(MMOCore.plugin.attributeManager.has(id), "Could not find attribute '" + id + "'");
PlayerAttribute attribute = MMOCore.plugin.attributeManager.get(id); PlayerAttribute attribute = MMOCore.plugin.attributeManager.get(id);
AttributeInstance ins = new AttributeInstance(attribute.getId()); AttributeInstance ins = new AttributeInstance(attribute.getId());
ins.setBase(entry.getValue().getAsInt()); ins.setBase(entry.getValue().getAsInt());
instances.put(id, ins); instances.put(id, ins);
} catch (IllegalArgumentException exception) { } catch (IllegalArgumentException exception) {
data.log(Level.WARNING, exception.getMessage()); data.log(Level.WARNING, exception.getMessage());
} }
} }
} }
public PlayerData getData() { public PlayerData getData() {
return data; return data;
} }
public int getAttribute(PlayerAttribute attribute) { public int getAttribute(PlayerAttribute attribute) {
return getInstance(attribute).getTotal(); return getInstance(attribute).getTotal();
} }
public Collection<AttributeInstance> getInstances() { public Collection<AttributeInstance> getInstances() {
return instances.values(); return instances.values();
} }
public Map<String, Integer> mapPoints() { public Map<String, Integer> mapPoints() {
Map<String, Integer> map = new HashMap<>(); Map<String, Integer> map = new HashMap<>();
instances.values().forEach(ins -> map.put(ins.id, ins.spent)); instances.values().forEach(ins -> map.put(ins.id, ins.spent));
return map; return map;
} }
@NotNull @NotNull
public AttributeInstance getInstance(String attribute) { public AttributeInstance getInstance(String attribute) {
return instances.computeIfAbsent(attribute, AttributeInstance::new); return instances.computeIfAbsent(attribute, AttributeInstance::new);
} }
@NotNull @NotNull
public AttributeInstance getInstance(PlayerAttribute attribute) { public AttributeInstance getInstance(PlayerAttribute attribute) {
return getInstance(attribute.getId()); return getInstance(attribute.getId());
} }
@Deprecated @Deprecated
public int countSkillPoints() { public int countSkillPoints() {
return countPoints(); return countPoints();
} }
public int countPoints() { public int countPoints() {
int n = 0; int n = 0;
for (AttributeInstance ins : instances.values()) for (AttributeInstance ins : instances.values())
n += ins.getBase(); n += ins.getBase();
return n; return n;
} }
public class AttributeInstance { public class AttributeInstance {
private int spent; private int spent;
private final String id, enumName; private final String id, enumName;
private final Map<String, AttributeModifier> map = new HashMap<>(); private final Map<String, AttributeModifier> map = new HashMap<>();
public AttributeInstance(String id) { public AttributeInstance(String id) {
this.id = id; this.id = id;
this.enumName = UtilityMethods.enumName(this.id); this.enumName = UtilityMethods.enumName(this.id);
} }
public int getBase() { public int getBase() {
return spent; return spent;
} }
public void setBase(int value) { public void setBase(int value) {
spent = Math.max(0, value); spent = Math.max(0, value);
if (data.isOnline()) if (data.isOnline())
update(); updateStats();
} }
public void addBase(int value) { public void addBase(int value) {
setBase(spent + value); setBase(spent + value);
} }
/* /*
* 1) two types of attributes: flat attributes which add X to the value, * 1) two types of attributes: flat attributes which add X to the value,
* and relative attributes which add X% and which must be applied * and relative attributes which add X% and which must be applied
* afterwards 2) the 'd' parameter lets you choose if the relative * afterwards 2) the 'd' parameter lets you choose if the relative
* attributes also apply on the base stat, or if they only apply on the * attributes also apply on the base stat, or if they only apply on the
* instances stat value * instances stat value
*/ */
public int getTotal() { public int getTotal() {
double d = spent; double d = spent;
for (AttributeModifier attr : map.values()) for (AttributeModifier attr : map.values())
if (attr.getType() == ModifierType.FLAT) if (attr.getType() == ModifierType.FLAT)
d += attr.getValue(); d += attr.getValue();
d += data.getMMOPlayerData().getStatMap().getStat("ADDITIONAL_" + enumName); d += data.getMMOPlayerData().getStatMap().getStat("ADDITIONAL_" + enumName);
for (AttributeModifier attr : map.values()) for (AttributeModifier attr : map.values())
if (attr.getType() == ModifierType.RELATIVE) if (attr.getType() == ModifierType.RELATIVE)
d *= attr.getValue(); d *= attr.getValue();
d *= 1 + data.getMMOPlayerData().getStatMap().getStat("ADDITIONAL_" + enumName + "_PERCENT") / 100; d *= 1 + data.getMMOPlayerData().getStatMap().getStat("ADDITIONAL_" + enumName + "_PERCENT") / 100;
// cast to int at the last moment // cast to int at the last moment
return (int) d; return (int) d;
} }
public AttributeModifier getModifier(String key) { public AttributeModifier getModifier(String key) {
return map.get(key); return map.get(key);
} }
public AttributeModifier addModifier(String key, double value) { public AttributeModifier addModifier(String key, double value) {
return addModifier(new AttributeModifier(key, id, value, ModifierType.FLAT, EquipmentSlot.OTHER, ModifierSource.OTHER)); return addModifier(new AttributeModifier(key, id, value, ModifierType.FLAT, EquipmentSlot.OTHER, ModifierSource.OTHER));
} }
public AttributeModifier addModifier(AttributeModifier modifier) { public AttributeModifier addModifier(AttributeModifier modifier) {
AttributeModifier mod = map.put(modifier.getKey(), modifier); final AttributeModifier current = map.put(modifier.getKey(), modifier);
update();
return mod;
}
public Set<String> getKeys() { if (current != null && current instanceof Closeable)
return map.keySet(); ((Closeable) current).close();
}
public boolean contains(String key) { updateStats();
return map.containsKey(key); return current;
} }
public AttributeModifier removeModifier(String key) { public Set<String> getKeys() {
AttributeModifier mod = map.remove(key); return map.keySet();
}
/* public boolean contains(String key) {
* Closing stat is really important with temporary stats because return map.containsKey(key);
* otherwise the runnable will try to remove the key from the map }
* even though the attribute was cancelled before hand
*/
if (mod != null) {
if (mod instanceof Closeable)
((Closeable) mod).close();
update();
}
return mod;
}
public void update() { public AttributeModifier removeModifier(String key) {
PlayerAttribute attr = MMOCore.plugin.attributeManager.get(id); final AttributeModifier mod = map.remove(key);
int total = getTotal();
attr.getBuffs().forEach(buff -> buff.multiply(total).register(data.getMMOPlayerData()));
}
public String getId() { /*
return id; * Closing stat is really important with temporary stats because
} * otherwise the runnable will try to remove the key from the map
} * even though the attribute was cancelled before hand
*/
if (mod != null) {
if (mod instanceof Closeable)
((Closeable) mod).close();
updateStats();
}
return mod;
}
public void setBaseAttribute(String id, int value) { public void updateStats() {
getInstances().forEach(ins -> { final PlayerAttribute attr = MMOCore.plugin.attributeManager.get(id);
if (ins.getId().equals(id)) final int total = getTotal();
ins.setBase(value); attr.getBuffs().forEach(buff -> buff.multiply(total).register(data.getMMOPlayerData()));
}); }
}
public String getId() {
return id;
}
}
public void setBaseAttribute(String id, int value) {
getInstances().forEach(ins -> {
if (ins.getId().equals(id))
ins.setBase(value);
});
}
} }

View File

@ -1,45 +1,55 @@
package net.Indyuce.mmocore.manager; package net.Indyuce.mmocore.manager;
import io.lumine.mythic.lib.MythicLib;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigFile;
import net.Indyuce.mmocore.api.player.attribute.MMOCoreAttributeStatHandler;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import net.Indyuce.mmocore.api.ConfigFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class AttributeManager implements MMOCoreManager { public class AttributeManager implements MMOCoreManager {
private final Map<String, PlayerAttribute> map = new HashMap<>(); private final Map<String, PlayerAttribute> map = new HashMap<>();
@Nullable @Nullable
public PlayerAttribute get(String id) { public PlayerAttribute get(String id) {
return map.get(id); return map.get(id);
} }
public boolean has(String id) { public boolean has(String id) {
return map.containsKey(id); return map.containsKey(id);
} }
@NotNull @NotNull
public Collection<PlayerAttribute> getAll() { public Collection<PlayerAttribute> getAll() {
return map.values(); return map.values();
} }
@Override @Override
public void initialize(boolean clearBefore) { public void initialize(boolean clearBefore) {
if (clearBefore) if (clearBefore) {
map.clear(); map.clear();
MythicLib.plugin.getStats().clearRegisteredStats(handler -> handler instanceof MMOCoreAttributeStatHandler);
}
ConfigFile config = new ConfigFile("attributes"); final ConfigFile config = new ConfigFile("attributes");
for (String key : config.getConfig().getKeys(false)) for (String key : config.getConfig().getKeys(false))
try { try {
String path = key.toLowerCase().replace("_", "-").replace(" ", "-"); String path = key.toLowerCase().replace("_", "-").replace(" ", "-");
map.put(path, new PlayerAttribute(config.getConfig().getConfigurationSection(key))); map.put(path, new PlayerAttribute(config.getConfig().getConfigurationSection(key)));
} catch (IllegalArgumentException exception) { } catch (IllegalArgumentException exception) {
MMOCore.log(Level.WARNING, "Could not load attribute '" + key + "': " + exception.getMessage()); MMOCore.log(Level.WARNING, "Could not load attribute '" + key + "': " + exception.getMessage());
} }
}
for (PlayerAttribute attr : getAll()) {
final MMOCoreAttributeStatHandler handler = new MMOCoreAttributeStatHandler(attr);
MythicLib.plugin.getStats().registerStat(handler.getStat(), handler);
MythicLib.plugin.getStats().registerStat(handler.getStat() + "_PERCENT", handler);
}
}
} }

View File

@ -27,7 +27,7 @@ public class ConfigManager {
public ChatColor staminaFull, staminaHalf, staminaEmpty; public ChatColor staminaFull, staminaHalf, staminaEmpty;
public long combatLogTimer, lootChestExpireTime, lootChestPlayerCooldown, globalSkillCooldown; public long combatLogTimer, lootChestExpireTime, lootChestPlayerCooldown, globalSkillCooldown;
public double lootChestsChanceWeight,dropItemsChanceWeight, fishingDropsChanceWeight, partyMaxExpSplitRange; public double lootChestsChanceWeight,dropItemsChanceWeight, fishingDropsChanceWeight, partyMaxExpSplitRange;
public int maxPartyLevelDifference, maxBoundActiveSkills, maxBoundPassiveSkills, waypointWarpTime; public int maxPartyLevelDifference, maxBoundActiveSkills, maxBoundPassiveSkills;
private final FileConfiguration messages; private final FileConfiguration messages;
@ -82,8 +82,6 @@ public class ConfigManager {
loadDefaultFile("skill-trees","rogue-marksman.yml"); loadDefaultFile("skill-trees","rogue-marksman.yml");
loadDefaultFile("skill-trees","warrior-paladin.yml"); loadDefaultFile("skill-trees","warrior-paladin.yml");
loadDefaultFile("skill-trees","general.yml"); loadDefaultFile("skill-trees","general.yml");
} }
loadDefaultFile("attributes.yml"); loadDefaultFile("attributes.yml");

View File

@ -31,15 +31,18 @@ public class SkillBar implements Listener {
} }
@EventHandler @EventHandler
public void a(PlayerKeyPressEvent event) { public void enterSkillCasting(PlayerKeyPressEvent event) {
if (event.getPressed() != mainKey) return; if (event.getPressed() != mainKey) return;
// Extra option to improve support with other plugins
final Player player = event.getPlayer();
if (disableSneak && player.isSneaking()) return;
// Always cancel event // Always cancel event
if (event.getPressed().shouldCancelEvent()) event.setCancelled(true); if (event.getPressed().shouldCancelEvent()) event.setCancelled(true);
// Enter spell casting // Enter spell casting
Player player = event.getData().getPlayer(); final PlayerData playerData = event.getData();
PlayerData playerData = event.getData();
if (player.getGameMode() != GameMode.SPECTATOR && (MMOCore.plugin.configManager.canCreativeCast || player.getGameMode() != GameMode.CREATIVE) && !playerData.isCasting() && !playerData.getBoundSkills() if (player.getGameMode() != GameMode.SPECTATOR && (MMOCore.plugin.configManager.canCreativeCast || player.getGameMode() != GameMode.CREATIVE) && !playerData.isCasting() && !playerData.getBoundSkills()
.isEmpty()) { .isEmpty()) {
playerData.setSkillCasting(new CustomSkillCastingHandler(playerData)); playerData.setSkillCasting(new CustomSkillCastingHandler(playerData));
@ -64,10 +67,9 @@ public class SkillBar implements Listener {
public void onSkillCast(PlayerItemHeldEvent event) { public void onSkillCast(PlayerItemHeldEvent event) {
if (!event.getPlayer().equals(getCaster().getPlayer())) return; if (!event.getPlayer().equals(getCaster().getPlayer())) return;
if (!getCaster().isOnline()) { // Extra option to improve support with other plugins
getCaster().leaveSkillCasting(); final Player player = event.getPlayer();
return; if (disableSneak && player.isSneaking()) return;
}
/* /*
* When the event is cancelled, another playerItemHeldEvent is * When the event is cancelled, another playerItemHeldEvent is
@ -76,10 +78,6 @@ public class SkillBar implements Listener {
*/ */
if (event.getPreviousSlot() == event.getNewSlot()) return; if (event.getPreviousSlot() == event.getNewSlot()) return;
// Extra option to improve support with other plugins
final Player player = event.getPlayer();
if (disableSneak && player.isSneaking()) return;
event.setCancelled(true); event.setCancelled(true);
int slot = event.getNewSlot() + (event.getNewSlot() >= player.getInventory().getHeldItemSlot() ? -1 : 0); int slot = event.getNewSlot() + (event.getNewSlot() >= player.getInventory().getHeldItemSlot() ? -1 : 0);
@ -97,18 +95,23 @@ public class SkillBar implements Listener {
@EventHandler @EventHandler
public void stopCasting(PlayerKeyPressEvent event) { public void stopCasting(PlayerKeyPressEvent event) {
Player player = event.getPlayer(); if (!event.getPlayer().equals(getCaster().getPlayer())) return;
if (event.getPressed() == mainKey && event.getPlayer().equals(getCaster().getPlayer())) {
MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_END).playTo(player);
new BukkitRunnable() { if (event.getPressed() != mainKey) return;
@Override
public void run() { // Extra option to improve support with other plugins
MMOCore.plugin.configManager.getSimpleMessage("casting.no-longer").send(getCaster().getPlayer()); final Player player = event.getPlayer();
} if (disableSneak && player.isSneaking()) return;
}.runTask(MMOCore.plugin);
getCaster().leaveSkillCasting(); MMOCore.plugin.soundManager.getSound(SoundEvent.SPELL_CAST_END).playTo(player);
}
new BukkitRunnable() {
@Override
public void run() {
MMOCore.plugin.configManager.getSimpleMessage("casting.no-longer").send(getCaster().getPlayer());
}
}.runTask(MMOCore.plugin);
getCaster().leaveSkillCasting();
} }
private String getFormat(PlayerData data) { private String getFormat(PlayerData data) {

View File

@ -90,6 +90,7 @@ should-cobblestone-generators-give-exp: false
skill-casting: skill-casting:
mode: SKILL_BAR mode: SKILL_BAR
open: SWAP_HANDS open: SWAP_HANDS
disable-sneak: false
loot-chests: loot-chests: