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.PartyManager;
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.Neptune_Gift;
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 BlockListener(), this);
Bukkit.getPluginManager().registerEvents(new LootableChestsListener(), this);
Bukkit.getPluginManager().registerEvents(new SpellCast(), this);
Bukkit.getPluginManager().registerEvents(new PartyListener(), this);
Bukkit.getPluginManager().registerEvents(new GuildListener(), 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.api.ConfigMessage;
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.PlayerAttributes;
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.experience.EXPSource;
import net.Indyuce.mmocore.experience.PlayerProfessions;
import net.Indyuce.mmocore.listener.SpellCast.SkillCasting;
import net.Indyuce.mmocore.manager.SoundManager;
import net.Indyuce.mmocore.skill.ClassSkill;
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.chat.TextComponent;
import org.bukkit.*;
@ -485,7 +487,7 @@ public class PlayerData extends OfflinePlayerData implements Closable {
* @param source How the player earned experience
*/
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 hologramLocation Location used to display the hologram. If it's null, no
* 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()) {
setExperience(0);
return;
@ -509,6 +512,14 @@ public class PlayerData extends OfflinePlayerData implements Closable {
value = MMOCore.plugin.boosterManager.calculateExp(null, value);
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);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled())
@ -678,7 +689,7 @@ public class PlayerData extends OfflinePlayerData implements Closable {
/**
* @return If the action bar is not being used to display anything else
* i.e if the "general info" action bar can be displayed
* i.e if the "general info" action bar can be displayed
*/
public boolean canSeeActionBar() {
return actionBarTimeOut < System.currentTimeMillis();
@ -825,7 +836,7 @@ public class PlayerData extends OfflinePlayerData implements Closable {
* checks if they could potentially upgrade to one of these
*
* @return If the player can change its current class to
* a subclass
* a subclass
*/
public boolean canChooseSubclass() {
for (Subclass subclass : getProfess().getSubclasses())

View File

@ -12,7 +12,7 @@ import java.util.*;
import java.util.function.Consumer;
public class Party {
private final PartyMembers members = new PartyMembers();
private final List<PlayerData> members = new ArrayList<>();
private final Map<UUID, Long> invites = new HashMap<>();
// used to check if two parties are the same
@ -36,10 +36,24 @@ public class Party {
return owner;
}
public PartyMembers getMembers() {
public List<PlayerData> getMembers() {
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) {
return invites.containsKey(player.getUniqueId()) ? invites.get(player.getUniqueId()) : 0;
}
@ -48,6 +62,21 @@ public class Party {
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) {
removeMember(data, true);
}
@ -59,11 +88,12 @@ public class Party {
members.remove(data);
data.setParty(null);
reopenInventories();
clearStatBonuses(data);
members.forEach(this::applyStatBonuses);
updateOpenInventories();
// Disband the party if no member left
if (members.count() < 1) {
if (members.size() < 1) {
MMOCore.plugin.partyManager.unregisterParty(this);
return;
}
@ -82,18 +112,24 @@ public class Party {
data.setParty(this);
members.add(data);
members.forEach(this::applyStatBonuses);
reopenInventories();
updateOpenInventories();
}
public void reopenInventories() {
for (PlayerData member : members.members)
private void updateOpenInventories() {
for (PlayerData member : members)
if (member.isOnline() && member.getPlayer().getOpenInventory() != null
&& member.getPlayer().getOpenInventory().getTopInventory().getHolder() instanceof PartyViewInventory)
((PluginInventory) member.getPlayer().getOpenInventory().getTopInventory().getHolder()).open();
}
@Deprecated
public void sendPartyInvite(PlayerData inviter, PlayerData target) {
sendInvite(inviter, target);
}
public void sendInvite(PlayerData inviter, PlayerData target) {
invites.put(target.getUniqueId(), System.currentTimeMillis());
Request request = new PartyInvite(this, inviter, target);
if (inviter.isOnline() && target.isOnline())
@ -102,61 +138,36 @@ public class Party {
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
public boolean equals(Object obj) {
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() {
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());
return;
}

View File

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

View File

@ -118,7 +118,7 @@ public class RPGPlaceholders extends PlaceholderExpansion {
.getLevelUpExperience(identifier.substring(22).replace(" ", "-").replace("_", "-").toLowerCase());
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_"))
return String

View File

@ -17,6 +17,11 @@ public enum EXPSource {
*/
VANILLA,
/**
* When party members share exp
*/
PARTY_SHARING,
/**
* When using the experience trigger. Keep in mind the
* 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) {
Placeholders holders = new Placeholders();
int count = inv.getPlayerData().getParty().getMembers().count();
int count = inv.getPlayerData().getParty().getMembers().size();
holders.register("count", "" + count);
for (StatType stat : MMOCore.plugin.partyManager.getBonuses())
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
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.gui.api.EditableInventory;
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.Placeholders;
import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem;
@ -98,7 +97,7 @@ public class EditablePartyView extends EditableInventory {
@Override
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
@ -127,7 +126,7 @@ public class EditablePartyView extends EditableInventory {
@Override
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
@ -142,7 +141,7 @@ public class EditablePartyView extends EditableInventory {
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);
player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 1);
return;
@ -165,14 +164,14 @@ public class EditablePartyView extends EditableInventory {
}
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);
player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 1);
open();
return;
}
playerData.getParty().sendPartyInvite(playerData, targetData);
playerData.getParty().sendInvite(playerData, targetData);
MMOCore.plugin.configManager.getSimpleMessage("sent-party-invite", "player", target.getName()).send(player);
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
open();

View File

@ -54,7 +54,7 @@ public class PartyListener implements Listener {
LivingEntity entity = event.getEntity();
if (entity instanceof Player && !entity.hasMetadata("NPC")) {
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);
}
}

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.stats.StatType;
import net.Indyuce.mmocore.manager.MMOCoreManager;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection;
import java.util.HashMap;
@ -34,7 +35,8 @@ public class PartyManager implements MMOCoreManager {
public void unregisterParty(Party 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);
}