party exp splitting

This commit is contained in:
Jules 2022-01-14 11:11:11 +01:00
parent 1fc6bdc945
commit 2d4a3cd3bd
11 changed files with 109 additions and 81 deletions

View File

@ -39,6 +39,7 @@ import net.Indyuce.mmocore.manager.profession.*;
import net.Indyuce.mmocore.manager.social.BoosterManager; import net.Indyuce.mmocore.manager.social.BoosterManager;
import net.Indyuce.mmocore.manager.social.PartyManager; import net.Indyuce.mmocore.manager.social.PartyManager;
import net.Indyuce.mmocore.manager.social.RequestManager; import net.Indyuce.mmocore.manager.social.RequestManager;
import net.Indyuce.mmocore.skill.cast.listener.SkillBar;
import net.Indyuce.mmocore.skill.list.Ambers; import net.Indyuce.mmocore.skill.list.Ambers;
import net.Indyuce.mmocore.skill.list.Neptune_Gift; import net.Indyuce.mmocore.skill.list.Neptune_Gift;
import net.Indyuce.mmocore.skill.list.Sneaky_Picky; import net.Indyuce.mmocore.skill.list.Sneaky_Picky;
@ -236,7 +237,6 @@ public class MMOCore extends LuminePlugin {
Bukkit.getPluginManager().registerEvents(new GoldPouchesListener(), this); Bukkit.getPluginManager().registerEvents(new GoldPouchesListener(), this);
Bukkit.getPluginManager().registerEvents(new BlockListener(), this); Bukkit.getPluginManager().registerEvents(new BlockListener(), this);
Bukkit.getPluginManager().registerEvents(new LootableChestsListener(), this); Bukkit.getPluginManager().registerEvents(new LootableChestsListener(), this);
Bukkit.getPluginManager().registerEvents(new SpellCast(), this);
Bukkit.getPluginManager().registerEvents(new PartyListener(), this); Bukkit.getPluginManager().registerEvents(new PartyListener(), this);
Bukkit.getPluginManager().registerEvents(new GuildListener(), this); Bukkit.getPluginManager().registerEvents(new GuildListener(), this);
Bukkit.getPluginManager().registerEvents(new FishingListener(), this); Bukkit.getPluginManager().registerEvents(new FishingListener(), this);

View File

@ -6,7 +6,9 @@ import io.lumine.mythic.lib.player.cooldown.CooldownMap;
import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage; import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.Waypoint; import net.Indyuce.mmocore.api.Waypoint;
import net.Indyuce.mmocore.api.event.*; import net.Indyuce.mmocore.api.event.PlayerExperienceGainEvent;
import net.Indyuce.mmocore.api.event.PlayerLevelUpEvent;
import net.Indyuce.mmocore.api.event.PlayerResourceUpdateEvent;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute; import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttributes; import net.Indyuce.mmocore.api.player.attribute.PlayerAttributes;
import net.Indyuce.mmocore.api.player.profess.PlayerClass; import net.Indyuce.mmocore.api.player.profess.PlayerClass;
@ -24,10 +26,10 @@ import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.api.util.math.particle.SmallParticleEffect; import net.Indyuce.mmocore.api.util.math.particle.SmallParticleEffect;
import net.Indyuce.mmocore.experience.EXPSource; import net.Indyuce.mmocore.experience.EXPSource;
import net.Indyuce.mmocore.experience.PlayerProfessions; import net.Indyuce.mmocore.experience.PlayerProfessions;
import net.Indyuce.mmocore.listener.SpellCast.SkillCasting;
import net.Indyuce.mmocore.manager.SoundManager; import net.Indyuce.mmocore.manager.SoundManager;
import net.Indyuce.mmocore.skill.ClassSkill; import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.skill.RegisteredSkill; import net.Indyuce.mmocore.skill.RegisteredSkill;
import net.Indyuce.mmocore.skill.cast.listener.SkillBar.SkillCasting;
import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.*; import org.bukkit.*;
@ -485,7 +487,7 @@ public class PlayerData extends OfflinePlayerData implements Closable {
* @param source How the player earned experience * @param source How the player earned experience
*/ */
public void giveExperience(int value, EXPSource source) { public void giveExperience(int value, EXPSource source) {
giveExperience(value, source, null); giveExperience(value, source, null, true);
} }
/** /**
@ -495,8 +497,9 @@ public class PlayerData extends OfflinePlayerData implements Closable {
* @param source How the player earned experience * @param source How the player earned experience
* @param hologramLocation Location used to display the hologram. If it's null, no * @param hologramLocation Location used to display the hologram. If it's null, no
* hologram will be displayed * hologram will be displayed
* @param splitExp Should the exp be split among party members
*/ */
public void giveExperience(double value, EXPSource source, @Nullable Location hologramLocation) { public void giveExperience(double value, EXPSource source, @Nullable Location hologramLocation, boolean splitExp) {
if (hasReachedMaxLevel()) { if (hasReachedMaxLevel()) {
setExperience(0); setExperience(0);
return; return;
@ -509,6 +512,14 @@ public class PlayerData extends OfflinePlayerData implements Closable {
value = MMOCore.plugin.boosterManager.calculateExp(null, value); value = MMOCore.plugin.boosterManager.calculateExp(null, value);
value *= 1 + getStats().getStat(StatType.ADDITIONAL_EXPERIENCE) / 100; value *= 1 + getStats().getStat(StatType.ADDITIONAL_EXPERIENCE) / 100;
// Splitting exp through party members
if (splitExp && hasParty()) {
List<PlayerData> onlineMembers = getParty().getOnlineMembers();
value /= onlineMembers.size();
for (PlayerData member : onlineMembers)
member.giveExperience(value, EXPSource.PARTY_SHARING, null, false);
}
PlayerExperienceGainEvent event = new PlayerExperienceGainEvent(this, (int) value, source); PlayerExperienceGainEvent event = new PlayerExperienceGainEvent(this, (int) value, source);
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) if (event.isCancelled())

View File

@ -12,7 +12,7 @@ import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
public class Party { public class Party {
private final PartyMembers members = new PartyMembers(); private final List<PlayerData> members = new ArrayList<>();
private final Map<UUID, Long> invites = new HashMap<>(); private final Map<UUID, Long> invites = new HashMap<>();
// used to check if two parties are the same // used to check if two parties are the same
@ -36,10 +36,24 @@ public class Party {
return owner; return owner;
} }
public PartyMembers getMembers() { public List<PlayerData> getMembers() {
return members; return members;
} }
public List<PlayerData> getOnlineMembers() {
List<PlayerData> online = new ArrayList<>();
for (PlayerData member : members)
if (member.isOnline())
online.add(member);
return online;
}
public PlayerData getMember(int index) {
return members.get(index);
}
public long getLastInvite(Player player) { public long getLastInvite(Player player) {
return invites.containsKey(player.getUniqueId()) ? invites.get(player.getUniqueId()) : 0; return invites.containsKey(player.getUniqueId()) ? invites.get(player.getUniqueId()) : 0;
} }
@ -48,6 +62,21 @@ public class Party {
invites.remove(player.getUniqueId()); invites.remove(player.getUniqueId());
} }
public boolean hasMember(PlayerData playerData) {
return hasMember(playerData.getUniqueId());
}
public boolean hasMember(Player player) {
return hasMember(player.getUniqueId());
}
public boolean hasMember(UUID uuid) {
for (PlayerData member : members)
if (member.getUniqueId().equals(uuid))
return true;
return false;
}
public void removeMember(PlayerData data) { public void removeMember(PlayerData data) {
removeMember(data, true); removeMember(data, true);
} }
@ -59,11 +88,12 @@ public class Party {
members.remove(data); members.remove(data);
data.setParty(null); data.setParty(null);
clearStatBonuses(data);
reopenInventories(); members.forEach(this::applyStatBonuses);
updateOpenInventories();
// Disband the party if no member left // Disband the party if no member left
if (members.count() < 1) { if (members.size() < 1) {
MMOCore.plugin.partyManager.unregisterParty(this); MMOCore.plugin.partyManager.unregisterParty(this);
return; return;
} }
@ -82,18 +112,24 @@ public class Party {
data.setParty(this); data.setParty(this);
members.add(data); members.add(data);
members.forEach(this::applyStatBonuses);
reopenInventories(); updateOpenInventories();
} }
public void reopenInventories() { private void updateOpenInventories() {
for (PlayerData member : members.members) for (PlayerData member : members)
if (member.isOnline() && member.getPlayer().getOpenInventory() != null if (member.isOnline() && member.getPlayer().getOpenInventory() != null
&& member.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory) && member.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory)
((PluginInventory) member.getPlayer().getOpenInventory().getTopInventory().getHolder()).open(); ((PluginInventory) member.getPlayer().getOpenInventory().getTopInventory().getHolder()).open();
} }
@Deprecated
public void sendPartyInvite(PlayerData inviter, PlayerData target) { public void sendPartyInvite(PlayerData inviter, PlayerData target) {
sendInvite(inviter, target);
}
public void sendInvite(PlayerData inviter, PlayerData target) {
invites.put(target.getUniqueId(), System.currentTimeMillis()); invites.put(target.getUniqueId(), System.currentTimeMillis());
Request request = new PartyInvite(this, inviter, target); Request request = new PartyInvite(this, inviter, target);
if (inviter.isOnline() && target.isOnline()) if (inviter.isOnline() && target.isOnline())
@ -102,61 +138,36 @@ public class Party {
MMOCore.plugin.requestManager.registerRequest(request); MMOCore.plugin.requestManager.registerRequest(request);
} }
/**
* An issue can happen if the consumer given as parameter
* modifies the member list during list iteration.
* <p>
* To solve this, this method first copies the member list
* and iterates through it to avoid the exception.
*/
public void forEachMember(Consumer<PlayerData> action) {
new ArrayList<>(members).forEach(action);
}
private static final String PARTY_BUFF_MODIFIER_KEY = "mmocoreParty";
/**
* Applies party stat bonuses to a specific player
*/
private void applyStatBonuses(PlayerData player) {
MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).addModifier(PARTY_BUFF_MODIFIER_KEY,
MMOCore.plugin.partyManager.getBonus(stat).multiply(members.size() - 1)));
}
/**
* Clear party stat bonuses from a player
*/
private void clearStatBonuses(PlayerData player) {
MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).remove(PARTY_BUFF_MODIFIER_KEY));
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
return obj instanceof Party && ((Party) obj).getUniqueId().equals(getUniqueId()); return obj instanceof Party && ((Party) obj).getUniqueId().equals(getUniqueId());
} }
/*
* this class makes controling entries and departures and APPLYING PARTY
* STAT ATTRIBUTES much easier
*/
public static class PartyMembers {
private final List<PlayerData> members = new ArrayList<>();
public PlayerData get(int count) {
return members.get(count);
}
public boolean has(PlayerData player) {
return members.contains(player);
}
public boolean has(Player player) {
for (PlayerData member : members)
if (member.getUniqueId().equals(player.getUniqueId()))
return true;
return false;
}
public void add(PlayerData player) {
members.add(player);
members.forEach(this::applyAttributes);
}
public void remove(PlayerData player) {
members.remove(player);
members.forEach(this::applyAttributes);
clearAttributes(player);
}
public void forEach(Consumer<? super PlayerData> action) {
members.forEach(action);
}
public int count() {
return members.size();
}
private void applyAttributes(PlayerData player) {
MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).addModifier("mmocoreParty",
MMOCore.plugin.partyManager.getBonus(stat).multiply(members.size() - 1)));
}
private void clearAttributes(PlayerData player) {
MMOCore.plugin.partyManager.getBonuses().forEach(stat -> player.getStats().getInstance(stat).remove("mmocoreParty"));
}
}
} }

View File

@ -28,7 +28,7 @@ public class PartyInvite extends Request {
} }
public void accept() { public void accept() {
if(party.getMembers().count() >= Math.min(8, Math.max(2, MMOCore.plugin.getConfig().getInt("party.max-players", 8)))) { if(party.getMembers().size() >= Math.max(2, MMOCore.plugin.getConfig().getInt("party.max-players", 8))) {
MMOCore.plugin.configManager.getSimpleMessage("party-is-full").send(target.getPlayer()); MMOCore.plugin.configManager.getSimpleMessage("party-is-full").send(target.getPlayer());
return; return;
} }

View File

@ -15,7 +15,7 @@ public class MMOCoreTargetRestriction implements TargetRestriction {
PlayerData targetData = PlayerData.get(target.getUniqueId()); PlayerData targetData = PlayerData.get(target.getUniqueId());
// Check for the same party // Check for the same party
if (targetData.hasParty() && targetData.getParty().getMembers().has(player)) if (targetData.hasParty() && targetData.getParty().hasMember(player))
return false; return false;
} }

View File

@ -118,7 +118,7 @@ public class RPGPlaceholders extends PlaceholderExpansion {
.getLevelUpExperience(identifier.substring(22).replace(" ", "-").replace("_", "-").toLowerCase()); .getLevelUpExperience(identifier.substring(22).replace(" ", "-").replace("_", "-").toLowerCase());
else if (identifier.startsWith("party_count")) else if (identifier.startsWith("party_count"))
return playerData.hasParty() ? String.valueOf(playerData.getParty().getMembers().count()) : "0"; return playerData.hasParty() ? String.valueOf(playerData.getParty().getMembers().size()) : "0";
else if (identifier.startsWith("profession_")) else if (identifier.startsWith("profession_"))
return String return String

View File

@ -17,6 +17,11 @@ public enum EXPSource {
*/ */
VANILLA, VANILLA,
/**
* When party members share exp
*/
PARTY_SHARING,
/** /**
* When using the experience trigger. Keep in mind the * When using the experience trigger. Keep in mind the
* experience trigger can also use another experience source * experience trigger can also use another experience source

View File

@ -176,7 +176,7 @@ public class PlayerStats extends EditableInventory {
public Placeholders getPlaceholders(GeneratedInventory inv, int n) { public Placeholders getPlaceholders(GeneratedInventory inv, int n) {
Placeholders holders = new Placeholders(); Placeholders holders = new Placeholders();
int count = inv.getPlayerData().getParty().getMembers().count(); int count = inv.getPlayerData().getParty().getMembers().size();
holders.register("count", "" + count); holders.register("count", "" + count);
for (StatType stat : MMOCore.plugin.partyManager.getBonuses()) for (StatType stat : MMOCore.plugin.partyManager.getBonuses())
holders.register("buff_" + stat.name().toLowerCase(), MMOCore.plugin.partyManager.getBonus(stat).multiply(count - 1).toString()); holders.register("buff_" + stat.name().toLowerCase(), MMOCore.plugin.partyManager.getBonus(stat).multiply(count - 1).toString());
@ -186,7 +186,7 @@ public class PlayerStats extends EditableInventory {
@Override @Override
public boolean canDisplay(GeneratedInventory inv) { public boolean canDisplay(GeneratedInventory inv) {
return inv.getPlayerData().hasParty() && inv.getPlayerData().getParty().getMembers().count() > 1; return inv.getPlayerData().hasParty() && inv.getPlayerData().getParty().getMembers().size() > 1;
} }
} }

View File

@ -8,7 +8,6 @@ import net.Indyuce.mmocore.api.util.input.PlayerInput.InputType;
import net.Indyuce.mmocore.api.util.math.format.DelayFormat; import net.Indyuce.mmocore.api.util.math.format.DelayFormat;
import net.Indyuce.mmocore.gui.api.EditableInventory; import net.Indyuce.mmocore.gui.api.EditableInventory;
import net.Indyuce.mmocore.gui.api.GeneratedInventory; import net.Indyuce.mmocore.gui.api.GeneratedInventory;
import net.Indyuce.mmocore.gui.api.PluginInventory;
import net.Indyuce.mmocore.gui.api.item.InventoryItem; import net.Indyuce.mmocore.gui.api.item.InventoryItem;
import net.Indyuce.mmocore.gui.api.item.Placeholders; import net.Indyuce.mmocore.gui.api.item.Placeholders;
import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem; import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem;
@ -98,7 +97,7 @@ public class EditablePartyView extends EditableInventory {
@Override @Override
public ItemStack display(GeneratedInventory inv, int n) { public ItemStack display(GeneratedInventory inv, int n) {
return inv.getPlayerData().getParty().getMembers().count() > n ? member.display(inv, n) : empty.display(inv, n); return inv.getPlayerData().getParty().getMembers().size() > n ? member.display(inv, n) : empty.display(inv, n);
} }
@Override @Override
@ -127,7 +126,7 @@ public class EditablePartyView extends EditableInventory {
@Override @Override
public String calculateName() { public String calculateName() {
return getName().replace("{max}", "" + max).replace("{players}", "" + getPlayerData().getParty().getMembers().count()); return getName().replace("{max}", "" + max).replace("{players}", "" + getPlayerData().getParty().getMembers().size());
} }
@Override @Override
@ -142,7 +141,7 @@ public class EditablePartyView extends EditableInventory {
if (item.getFunction().equals("invite")) { if (item.getFunction().equals("invite")) {
if (playerData.getParty().getMembers().count() >= max) { if (playerData.getParty().getMembers().size() >= max) {
MMOCore.plugin.configManager.getSimpleMessage("party-is-full").send(player); MMOCore.plugin.configManager.getSimpleMessage("party-is-full").send(player);
player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 1); player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 1);
return; return;
@ -165,14 +164,14 @@ public class EditablePartyView extends EditableInventory {
} }
PlayerData targetData = PlayerData.get(target); PlayerData targetData = PlayerData.get(target);
if (playerData.getParty().getMembers().has(targetData)) { if (playerData.getParty().hasMember(targetData)) {
MMOCore.plugin.configManager.getSimpleMessage("already-in-party", "player", target.getName()).send(player); MMOCore.plugin.configManager.getSimpleMessage("already-in-party", "player", target.getName()).send(player);
player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 1); player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 1);
open(); open();
return; return;
} }
playerData.getParty().sendPartyInvite(playerData, targetData); playerData.getParty().sendInvite(playerData, targetData);
MMOCore.plugin.configManager.getSimpleMessage("sent-party-invite", "player", target.getName()).send(player); MMOCore.plugin.configManager.getSimpleMessage("sent-party-invite", "player", target.getName()).send(player);
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1); player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
open(); open();

View File

@ -54,7 +54,7 @@ public class PartyListener implements Listener {
LivingEntity entity = event.getEntity(); LivingEntity entity = event.getEntity();
if (entity instanceof Player && !entity.hasMetadata("NPC")) { if (entity instanceof Player && !entity.hasMetadata("NPC")) {
PlayerData targetData = PlayerData.get((Player) event.getEntity()); PlayerData targetData = PlayerData.get((Player) event.getEntity());
if (targetData.hasParty() && targetData.getParty().getMembers().has(PlayerData.get(event.getData().getUniqueId()))) if (targetData.hasParty() && targetData.getParty().hasMember(PlayerData.get(event.getData().getUniqueId())))
event.setCancelled(true); event.setCancelled(true);
} }
} }

View File

@ -6,6 +6,7 @@ import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.social.Party; import net.Indyuce.mmocore.api.player.social.Party;
import net.Indyuce.mmocore.api.player.stats.StatType; import net.Indyuce.mmocore.api.player.stats.StatType;
import net.Indyuce.mmocore.manager.MMOCoreManager; import net.Indyuce.mmocore.manager.MMOCoreManager;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import java.util.HashMap; import java.util.HashMap;
@ -34,7 +35,8 @@ public class PartyManager implements MMOCoreManager {
public void unregisterParty(Party party) { public void unregisterParty(Party party) {
// IMPORTANT: clears all party members before unregistering the party // IMPORTANT: clears all party members before unregistering the party
party.getMembers().forEach(party::removeMember); party.forEachMember(party::removeMember);
Validate.isTrue(party.getMembers().isEmpty(), "Tried unregistering a non-empty party");
parties.remove(party); parties.remove(party);
} }