Magnus Ulf 35c38ce4b8 First try at a tax system
This hasn't been tested. This is a tax system where Factions can tax their players. Later it should be extended do that servers can tax factions based on how much land they have claimed. We start of with this simpler system to make sure that can be tested in real-life environments before we go ahead with the more full fledged system.
2019-07-15 07:55:12 +02:00

1374 lines
35 KiB
Java

package com.massivecraft.factions.entity;
import com.massivecraft.factions.Factions;
import com.massivecraft.factions.FactionsIndex;
import com.massivecraft.factions.FactionsParticipator;
import com.massivecraft.factions.Rel;
import com.massivecraft.factions.RelationParticipator;
import com.massivecraft.factions.entity.MPerm.MPermable;
import com.massivecraft.factions.predicate.PredicateCommandSenderFaction;
import com.massivecraft.factions.predicate.PredicateMPlayerRank;
import com.massivecraft.factions.util.MiscUtil;
import com.massivecraft.factions.util.RelationUtil;
import com.massivecraft.massivecore.Couple;
import com.massivecraft.massivecore.Identified;
import com.massivecraft.massivecore.collections.MassiveList;
import com.massivecraft.massivecore.collections.MassiveMap;
import com.massivecraft.massivecore.collections.MassiveMapDef;
import com.massivecraft.massivecore.collections.MassiveSet;
import com.massivecraft.massivecore.mixin.MixinMessage;
import com.massivecraft.massivecore.money.Money;
import com.massivecraft.massivecore.predicate.PredicateAnd;
import com.massivecraft.massivecore.predicate.PredicateVisibleTo;
import com.massivecraft.massivecore.ps.PS;
import com.massivecraft.massivecore.store.Entity;
import com.massivecraft.massivecore.store.EntityInternalMap;
import com.massivecraft.massivecore.store.SenderColl;
import com.massivecraft.massivecore.util.IdUtil;
import com.massivecraft.massivecore.util.MUtil;
import com.massivecraft.massivecore.util.Txt;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
public class Faction extends Entity<Faction> implements FactionsParticipator, MPerm.MPermable
{
// -------------------------------------------- //
// CONSTANTS
// -------------------------------------------- //
public static final transient String NODESCRIPTION = Txt.parse("<em><silver>no description set");
public static final transient String NOMOTD = Txt.parse("<em><silver>no message of the day set");
// -------------------------------------------- //
// META
// -------------------------------------------- //
public static Faction get(Object oid)
{
return FactionColl.get().get(oid);
}
// -------------------------------------------- //
// OVERRIDE: ENTITY
// -------------------------------------------- //
@Override
public Faction load(Faction that)
{
this.setName(that.name);
this.setDescription(that.description);
this.setMotd(that.motd);
this.setCreatedAtMillis(that.createdAtMillis);
this.warps.load(that.warps);
this.setPowerBoost(that.powerBoost);
this.invitations.load(that.invitations);
this.ranks.load(that.ranks);
this.votes.load(that.votes);
this.setRelationWishes(that.relationWishes);
this.setFlagIds(that.flags);
this.perms = that.perms;
return this;
}
@Override
public void preDetach(String id)
{
if (!this.isLive()) return;
// NOTE: Existence check is required for compatibility with some plugins.
// If they have money ...
if (Money.exists(this))
{
// ... remove it.
Money.set(this, null, 0, "Factions");
}
}
// -------------------------------------------- //
// VERSION
// -------------------------------------------- //
public int version = 4;
// -------------------------------------------- //
// FIELDS: RAW
// -------------------------------------------- //
// In this section of the source code we place the field declarations only.
// Each field has it's own section further down since just the getter and setter logic takes up quite some place.
// The actual faction id looks something like "54947df8-0e9e-4471-a2f9-9af509fb5889" and that is not too easy to remember for humans.
// Thus we make use of a name. Since the id is used in all foreign key situations changing the name is fine.
// Null should never happen. The name must not be null.
private String name = null;
// Factions can optionally set a description for themselves.
// This description can for example be seen in territorial alerts.
// Null means the faction has no description.
private String description = null;
// Factions can optionally set a message of the day.
// This message will be shown when logging on to the server.
// Null means the faction has no motd
private String motd = null;
// We store the creation date for the faction.
// It can be displayed on info pages etc.
private long createdAtMillis = System.currentTimeMillis();
// Factions can set a few significant locations (warps)
private EntityInternalMap<Warp> warps = new EntityInternalMap<>(this, Warp.class);
// Factions usually do not have a powerboost. It defaults to 0.
// The powerBoost is a custom increase/decrease to default and maximum power.
// Null means the faction has powerBoost (0).
private Double powerBoost = null;
// The money a Faction has
// null means 0.0
private Double money = null;
// This is the ids of the invited players.
// They are actually "senderIds" since you can invite "@console" to your faction.
private EntityInternalMap<Invitation> invitations = new EntityInternalMap<>(this, Invitation.class);
// This is where all the ranks are, they are faction specific
private EntityInternalMap<Rank> ranks = this.createRankMap();
// This is the votes currently open in the faction
private EntityInternalMap<Vote> votes = new EntityInternalMap<>(this, Vote.class);
// The keys in this map are factionIds.
// Null means no special relation whishes.
private MassiveMapDef<String, Rel> relationWishes = new MassiveMapDef<>();
// The flag overrides are modifications to the default values.
// Null means default.
private MassiveMapDef<String, Boolean> flags = new MassiveMapDef<>();
private Map<String, Set<String>> perms = this.createNewPermMap();
// What is the base tax on members of the faction?
// Specific taxes on ranks or players.
public static String IDENTIFIER_TAX_BASE = "base";
private Map<String, Double> tax = new MassiveMap<>();
// -------------------------------------------- //
// FIELD: id
// -------------------------------------------- //
// FINER
public boolean isNone()
{
return this.getId().equals(Factions.ID_NONE);
}
public boolean isNormal()
{
return ! this.isNone();
}
// -------------------------------------------- //
// FIELD: name
// -------------------------------------------- //
// RAW
@Override
public String getName()
{
return this.name;
}
public void setName(String name)
{
// Clean input
String target = name;
// Detect Nochange
if (MUtil.equals(this.name, target)) return;
// Apply
this.name = target;
// Mark as changed
this.changed();
}
// FINER
public String getComparisonName()
{
return MiscUtil.getComparisonString(this.getName());
}
public String getName(String prefix)
{
return prefix + this.getName();
}
public String getName(RelationParticipator observer)
{
if (observer == null) return getName();
return this.getName(this.getColorTo(observer).toString());
}
// -------------------------------------------- //
// FIELD: description
// -------------------------------------------- //
// RAW
public boolean hasDescription()
{
return this.description != null;
}
public String getDescription()
{
return this.description;
}
public void setDescription(String description)
{
// Clean input
String target = clean(description);
// Detect Nochange
if (MUtil.equals(this.description, target)) return;
// Apply
this.description = target;
// Mark as changed
this.changed();
}
// FINER
public String getDescriptionDesc()
{
String motd = this.getDescription();
if (motd == null) motd = NODESCRIPTION;
return motd;
}
// -------------------------------------------- //
// FIELD: motd
// -------------------------------------------- //
// RAW
public boolean hasMotd()
{
return this.motd != null;
}
public String getMotd()
{
return this.motd;
}
public void setMotd(String motd)
{
// Clean input
String target = clean(motd);
// Detect Nochange
if (MUtil.equals(this.motd, target)) return;
// Apply
this.motd = target;
// Mark as changed
this.changed();
}
// FINER
public String getMotdDesc()
{
return getMotdDesc(this.getMotd());
}
private static String getMotdDesc(String motd)
{
if (motd == null) motd = NOMOTD;
return motd;
}
public List<Object> getMotdMessages()
{
// Create
List<Object> ret = new MassiveList<>();
// Fill
Object title = this.getName() + " - Message of the Day";
title = Txt.titleize(title);
ret.add(title);
String motd = this.getMotdDesc();
List<String> motds = Arrays.asList(motd.split("\\\\n"));
motds = motds.stream().map(s -> Txt.parse("<i>") + s).collect(Collectors.toList());
ret.addAll(motds);
ret.add("");
// Return
return ret;
}
// -------------------------------------------- //
// FIELD: createdAtMillis
// -------------------------------------------- //
public long getCreatedAtMillis()
{
return this.createdAtMillis;
}
public void setCreatedAtMillis(long createdAtMillis)
{
// Detect Nochange
if (MUtil.equals(this.createdAtMillis, createdAtMillis)) return;
// Apply
this.createdAtMillis = createdAtMillis;
// Mark as changed
this.changed();
}
public long getAge()
{
return this.getAge(System.currentTimeMillis());
}
public long getAge(long now)
{
return now - this.getCreatedAtMillis();
}
// -------------------------------------------- //
// FIELD: warp
// -------------------------------------------- //
public EntityInternalMap<Warp> getWarps() { return this.warps; }
public Warp getWarp(Object oid)
{
if (oid == null) throw new NullPointerException("oid");
Warp warp = this.getWarps().get(oid);
if (warp == null) return null;
if (!warp.verifyIsValid()) return null;
return warp;
}
public PS getWarpPS(Object oid)
{
if (oid == null) throw new NullPointerException("oid");
Warp warp = this.getWarp(oid);
if (warp == null) return null;
return warp.getLocation();
}
public String addWarp(Warp warp)
{
return this.getWarps().attach(warp);
}
public Warp removeWarp(Warp warp)
{
return this.getWarps().detachEntity(warp);
}
// -------------------------------------------- //
// FIELD: powerBoost
// -------------------------------------------- //
// RAW
@Override
public double getPowerBoost()
{
Double ret = this.powerBoost;
if (ret == null) ret = 0D;
return ret;
}
@Override
public void setPowerBoost(Double powerBoost)
{
// Clean input
Double target = powerBoost;
if (target == null || target == 0) target = null;
// Detect Nochange
if (MUtil.equals(this.powerBoost, target)) return;
// Apply
this.powerBoost = target;
// Mark as changed
this.changed();
}
// -------------------------------------------- //
// FIELD: money
// -------------------------------------------- //
public double getMoney()
{
if (!MConf.get().econEnabled) throw new UnsupportedOperationException("econ not enabled");
if (!MConf.get().bankEnabled) throw new UnsupportedOperationException("bank not enabled");
if (!MConf.get().useNewMoneySystem) throw new UnsupportedOperationException("this server does not use the new econ system");
return this.convertGet(this.money, 0D);
}
public void setMoney(Double money)
{
if (!MConf.get().econEnabled) throw new UnsupportedOperationException("econ not enabled");
if (!MConf.get().bankEnabled) throw new UnsupportedOperationException("bank not enabled");
if (!MConf.get().useNewMoneySystem) throw new UnsupportedOperationException("this server does not use the new econ system");
this.money = this.convertSet(money, this.money, 0D);
}
// -------------------------------------------- //
// FIELD: open
// -------------------------------------------- //
// Nowadays this is a flag!
@Deprecated
public boolean isDefaultOpen()
{
return MFlag.getFlagOpen().isStandard();
}
@Deprecated
public boolean isOpen()
{
return this.getFlag(MFlag.getFlagOpen());
}
@Deprecated
public void setOpen(Boolean open)
{
MFlag flag = MFlag.getFlagOpen();
if (open == null) open = flag.isStandard();
this.setFlag(flag, open);
}
// -------------------------------------------- //
// FIELD: invitations
// -------------------------------------------- //
// RAW
public EntityInternalMap<Invitation> getInvitations() { return this.invitations; }
// FINER
public boolean isInvited(String playerId)
{
return this.getInvitations().containsKey(playerId);
}
public boolean isInvited(MPlayer mplayer)
{
return this.isInvited(mplayer.getId());
}
public boolean uninvite(String playerId)
{
return this.getInvitations().detachId(playerId) != null;
}
public boolean uninvite(MPlayer mplayer)
{
return uninvite(mplayer.getId());
}
public void invite(String playerId, Invitation invitation)
{
uninvite(playerId);
this.invitations.attach(invitation, playerId);
}
// -------------------------------------------- //
// FIELD: ranks
// -------------------------------------------- //
// RAW
public EntityInternalMap<Rank> getRanks() { return this.ranks; }
// FINER
public boolean hasRank(Rank rank)
{
return this.getRanks().containsKey(rank.getId());
}
public Rank getRank(String rankId)
{
if (rankId == null) throw new NullPointerException("rankId");
return this.getRanks().getFixed(rankId);
}
private EntityInternalMap<Rank> createRankMap()
{
EntityInternalMap<Rank> ret = new EntityInternalMap<>(this, Rank.class);
Rank leader = new Rank("Leader", 400, "**");
Rank officer = new Rank("Officer", 300, "*");
Rank member = new Rank("Member", 200, "+");
Rank recruit = new Rank("Recruit", 100, "-");
ret.attach(leader);
ret.attach(officer);
ret.attach(member);
ret.attach(recruit);
return ret;
}
public Rank getLeaderRank()
{
Rank ret = null;
for (Rank rank : this.getRanks().getAll())
{
if (ret != null && ret.isMoreThan(rank)) continue;
ret = rank;
}
return ret;
}
public Rank getLowestRank()
{
Rank ret = null;
for (Rank rank : this.getRanks().getAll())
{
if (ret != null && ret.isLessThan(rank)) continue;
ret = rank;
}
return ret;
}
// -------------------------------------------- //
// FIELD: votes
// -------------------------------------------- //
// RAW
public EntityInternalMap<Vote> getVotes() { return this.votes; }
public void addVote(Vote vote)
{
if (vote == null) throw new NullPointerException("vote");
this.getVotes().attach(vote);
}
public Optional<Vote> getVoteByName(String name)
{
if (name == null) throw new NullPointerException("name");
return this.getVotes().getAll().stream().filter(vote -> vote.getName().equalsIgnoreCase(name)).findFirst();
}
// -------------------------------------------- //
// FIELD: relationWish
// -------------------------------------------- //
// RAW
public Map<String, Rel> getRelationWishes()
{
return this.relationWishes;
}
public void setRelationWishes(Map<String, Rel> relationWishes)
{
// Clean input
MassiveMapDef<String, Rel> target = new MassiveMapDef<>(relationWishes);
// Detect Nochange
if (MUtil.equals(this.relationWishes, target)) return;
// Apply
this.relationWishes = target;
// Mark as changed
this.changed();
}
// FINER
public Rel getRelationWish(String factionId)
{
Rel ret = this.getRelationWishes().get(factionId);
if (ret == null) ret = Rel.NEUTRAL;
return ret;
}
public Rel getRelationWish(Faction faction)
{
return this.getRelationWish(faction.getId());
}
public void setRelationWish(String factionId, Rel rel)
{
Map<String, Rel> relationWishes = this.getRelationWishes();
if (rel == null || rel == Rel.NEUTRAL)
{
relationWishes.remove(factionId);
}
else
{
relationWishes.put(factionId, rel);
}
this.setRelationWishes(relationWishes);
}
public void setRelationWish(Faction faction, Rel rel)
{
this.setRelationWish(faction.getId(), rel);
}
// -------------------------------------------- //
// FIELD: flagOverrides
// -------------------------------------------- //
// RAW
public Map<MFlag, Boolean> getFlags()
{
// We start with default values ...
Map<MFlag, Boolean> ret = new MassiveMap<>();
for (MFlag mflag : MFlag.getAll())
{
ret.put(mflag, mflag.isStandard());
}
// ... and if anything is explicitly set we use that info ...
Iterator<Map.Entry<String, Boolean>> iter = this.flags.entrySet().iterator();
while (iter.hasNext())
{
// ... for each entry ...
Map.Entry<String, Boolean> entry = iter.next();
// ... extract id and remove null values ...
String id = entry.getKey();
if (id == null)
{
iter.remove();
this.changed();
continue;
}
// ... resolve object and skip unknowns ...
MFlag mflag = MFlag.get(id);
if (mflag == null) continue;
ret.put(mflag, entry.getValue());
}
return ret;
}
public void setFlags(Map<MFlag, Boolean> flags)
{
Map<String, Boolean> flagIds = new MassiveMap<>();
for (Map.Entry<MFlag, Boolean> entry : flags.entrySet())
{
flagIds.put(entry.getKey().getId(), entry.getValue());
}
setFlagIds(flagIds);
}
public void setFlagIds(Map<String, Boolean> flagIds)
{
// Clean input
MassiveMapDef<String, Boolean> target = new MassiveMapDef<>();
for (Map.Entry<String, Boolean> entry : flagIds.entrySet())
{
String key = entry.getKey();
if (key == null) continue;
key = key.toLowerCase(); // Lowercased Keys Version 2.6.0 --> 2.7.0
Boolean value = entry.getValue();
if (value == null) continue;
target.put(key, value);
}
// Detect Nochange
if (MUtil.equals(this.flags, target)) return;
// Apply
this.flags = new MassiveMapDef<>(target);
// Mark as changed
this.changed();
}
// FINER
public boolean getFlag(String flagId)
{
if (flagId == null) throw new NullPointerException("flagId");
Boolean ret = this.flags.get(flagId);
if (ret != null) return ret;
MFlag flag = MFlag.get(flagId);
if (flag == null) throw new NullPointerException("flag");
return flag.isStandard();
}
public boolean getFlag(MFlag flag)
{
if (flag == null) throw new NullPointerException("flag");
String flagId = flag.getId();
if (flagId == null) throw new NullPointerException("flagId");
Boolean ret = this.flags.get(flagId);
if (ret != null) return ret;
return flag.isStandard();
}
public Boolean setFlag(String flagId, boolean value)
{
if (flagId == null) throw new NullPointerException("flagId");
Boolean ret = this.flags.put(flagId, value);
if (ret == null || ret != value) this.changed();
return ret;
}
public Boolean setFlag(MFlag flag, boolean value)
{
if (flag == null) throw new NullPointerException("flag");
String flagId = flag.getId();
if (flagId == null) throw new NullPointerException("flagId");
Boolean ret = this.flags.put(flagId, value);
if (ret == null || ret != value) this.changed();
return ret;
}
// -------------------------------------------- //
// FIELD: perms
// -------------------------------------------- //
public Map<String, Set<String>> getPerms()
{
return this.perms;
}
public Map<String, Set<String>> createNewPermMap()
{
Map<String, Set<String>> ret = new MassiveMap<>();
Optional<String> leaderId = this.getRanks().getAll().stream().filter(r -> r.getName().equalsIgnoreCase("leader")).map(Rank::getId).findFirst();
Optional<String> officerId = this.getRanks().getAll().stream().filter(r -> r.getName().equalsIgnoreCase("officer")).map(Rank::getId).findFirst();
Optional<String> memberId = this.getRanks().getAll().stream().filter(r -> r.getName().equalsIgnoreCase("member")).map(Rank::getId).findFirst();
Optional<String> recruitId = this.getRanks().getAll().stream().filter(r -> r.getName().equalsIgnoreCase("recruit")).map(Rank::getId).findAny();
for (MPerm mperm : MPerm.getAll())
{
String id = mperm.getId();
MassiveSet<String> value = new MassiveSet<>(mperm.getStandard());
if (value.remove("LEADER") && leaderId.isPresent()) value.add(leaderId.get());
if (value.remove("OFFICER") && officerId.isPresent()) value.add(officerId.get());
if (value.remove("MEMBER") && memberId.isPresent()) value.add(memberId.get());
if (value.remove("RECRUIT") && recruitId.isPresent()) value.add(recruitId.get());
ret.put(mperm.getId(), value);
}
return ret;
}
// IS PERMITTED
public boolean isPlayerPermitted(MPlayer mplayer, String permId)
{
if (isPermitted(mplayer.getId(), permId)) return true;
if (isPermitted(mplayer.getFaction().getId(), permId)) return true;
if (isPermitted(mplayer.getRank().getId(), permId)) return true;
if (isPermitted(RelationUtil.getRelationOfThatToMe(mplayer, this).toString(), permId)) return true;
return false;
}
public boolean isPlayerPermitted(MPlayer mplayer, MPerm mperm)
{
return isPlayerPermitted(mplayer, mperm.getId());
}
public boolean isFactionPermitted(Faction faction, String permId)
{
if (isPermitted(faction.getId(), permId)) return true;
if (isPermitted(RelationUtil.getRelationOfThatToMe(faction, this).toString(), permId)) return true;
return false;
}
public boolean isFactionPermitted(Faction faction, MPerm mperm)
{
return isFactionPermitted(faction, mperm.getId());
}
public Set<String> getPermitted(String permId)
{
if (permId == null) throw new NullPointerException("permId");
Set<String> permables = this.perms.get(permId);
if (permables == null)
{
// No perms was found, but likely this is just a new MPerm.
// So if this does not exist in the database, throw an error.
if (!doesPermExist(permId)) throw new NullPointerException(permId + " caused null");
permables = new MassiveSet<>();
this.perms.put(permId, permables);
}
return permables;
}
public Set<String> getPermitted(MPerm mperm)
{
return getPermitted(mperm.getId());
}
public Set<MPermable> getPermittedPermables(String permId)
{
return MPerm.idsToMPermables(getPermitted(permId));
}
public Set<MPermable> getPermittedPermables(MPerm mperm)
{
return getPermittedPermables(mperm.getId());
}
public boolean isPermitted(String permableId, String permId)
{
if (permableId == null) throw new NullPointerException("permableId");
if (permId == null) throw new NullPointerException("permId");
// TODO: Isn't this section redundant and just a copy of that from getPermitted?
Set<String> permables = this.perms.get(permId);
if (permables == null)
{
// No perms was found, but likely this is just a new MPerm.
// So if this does not exist in the database, throw an error.
if (!doesPermExist(permId)) throw new NullPointerException(permId + " caused null");
// Otherwise handle it
return false;
}
return getPermitted(permId).contains(permableId);
}
// SET PERMITTED
public boolean setPermitted(MPerm.MPermable mpermable, String permId, boolean permitted)
{
boolean changed = false;
Set<String> perms = this.perms.get(permId);
if (perms == null)
{
// No perms was found, but likely this is just a new MPerm.
// So if this does not exist in the database, throw an error.
if (!doesPermExist(permId)) throw new NullPointerException(permId + " caused null");
// Otherwise handle it
Set<String> permables = new MassiveSet<>();
this.perms.put(permId, permables);
changed = true;
}
if (permitted)
{
changed = this.getPerms().get(permId).add(mpermable.getId()) | changed;
}
else
{
changed = this.getPerms().get(permId).remove(mpermable.getId()) | changed;
}
if (changed) this.changed();
return changed;
}
public boolean setPermitted(MPerm.MPermable mpermable, MPerm mperm, boolean permitted)
{
return setPermitted(mpermable, mperm.getId(), permitted);
}
public void setPermittedRelations(String permId, Collection<MPerm.MPermable> permables)
{
Set<String> ids = permables.stream().map(MPerm.MPermable::getId).collect(Collectors.toSet());
this.getPerms().put(permId, ids);
}
public void setPermittedRelations(MPerm perm, Collection<MPerm.MPermable> permables)
{
setPermittedRelations(perm.getId(), permables);
}
private boolean doesPermExist(String permId)
{
return MPermColl.get().getFixed(permId) != null;
}
// -------------------------------------------- //
// FIELD: tax
// -------------------------------------------- //
// RAW
public Map<String, Double> getTax() { return this.tax; }
// FINER GET
public Double getTaxFor(String id)
{
if (id == null) throw new NullPointerException("id");
return this.tax.get(id);
}
public Double getTaxFor(Identified identified)
{
if (identified == null) throw new NullPointerException("identified");
return this.getTaxFor(identified.getId());
}
public double getTaxForPlayer(MPlayer mplayer)
{
return getTaxAndReasonForPlayer(mplayer).map(Entry::getValue).orElse(0D);
}
public Optional<Entry<String, Double>> getTaxAndReasonForPlayer(MPlayer mplayer)
{
if (mplayer == null) throw new NullPointerException("mplayer");
if (mplayer.getFaction() != this) throw new IllegalArgumentException("Player " + mplayer.getId() + " not in " + this.getId());
Double ret = null;
ret = this.getTaxFor(mplayer);
if (ret != null) return Optional.of(new Couple<>(mplayer.getId(), ret));
ret = this.getTaxFor(mplayer.getRank());
if (ret != null) return Optional.of(new Couple<>(mplayer.getRank().getId(), ret));
ret = this.getTaxFor(IDENTIFIER_TAX_BASE);
if (ret != null) return Optional.of(new Couple<>(IDENTIFIER_TAX_BASE, ret));
return Optional.empty();
}
// FINER SET
public Double setTaxFor(String id, Double value)
{
return this.tax.put(id, value);
}
public Double setTaxFor(Identified identified, Double value)
{
return this.setTaxFor(identified.getId(), value);
}
// -------------------------------------------- //
// OVERRIDE: RelationParticipator
// -------------------------------------------- //
@Override
public String describeTo(RelationParticipator observer, boolean ucfirst)
{
return RelationUtil.describeThatToMe(this, observer, ucfirst);
}
@Override
public String describeTo(RelationParticipator observer)
{
return RelationUtil.describeThatToMe(this, observer);
}
@Override
public Rel getRelationTo(RelationParticipator observer)
{
return RelationUtil.getRelationOfThatToMe(this, observer);
}
@Override
public Rel getRelationTo(RelationParticipator observer, boolean ignorePeaceful)
{
return RelationUtil.getRelationOfThatToMe(this, observer, ignorePeaceful);
}
@Override
public ChatColor getColorTo(RelationParticipator observer)
{
return RelationUtil.getColorOfThatToMe(this, observer);
}
// -------------------------------------------- //
// OVERRIDE: permable
// -------------------------------------------- //
@Override
public String getDisplayName(Object senderObject)
{
MPlayer mplayer = MPlayer.get(senderObject);
if (mplayer == null) return this.getName();
return this.describeTo(mplayer);
}
// -------------------------------------------- //
// POWER
// -------------------------------------------- //
// TODO: Implement a has enough feature.
public double getPower()
{
if (this.getFlag(MFlag.getFlagInfpower())) return 999999;
double ret = 0;
for (MPlayer mplayer : this.getMPlayers())
{
ret += mplayer.getPower();
}
ret = this.limitWithPowerMax(ret);
ret += this.getPowerBoost();
return ret;
}
public double getPowerMax()
{
if (this.getFlag(MFlag.getFlagInfpower())) return 999999;
double ret = 0;
for (MPlayer mplayer : this.getMPlayers())
{
ret += mplayer.getPowerMax();
}
ret = this.limitWithPowerMax(ret);
ret += this.getPowerBoost();
return ret;
}
private double limitWithPowerMax(double power)
{
// NOTE: 0.0 powerMax means there is no max power
double powerMax = MConf.get().factionPowerMax;
return powerMax <= 0 || power < powerMax ? power : powerMax;
}
public int getPowerRounded()
{
return (int) Math.round(this.getPower());
}
public int getPowerMaxRounded()
{
return (int) Math.round(this.getPowerMax());
}
public int getLandCount()
{
return BoardColl.get().getCount(this);
}
public int getLandCountInWorld(String worldName)
{
return Board.get(worldName).getCount(this);
}
public boolean hasLandInflation()
{
return this.getLandCount() > this.getPowerRounded();
}
// -------------------------------------------- //
// WORLDS
// -------------------------------------------- //
public Set<String> getClaimedWorlds()
{
return BoardColl.get().getClaimedWorlds(this);
}
// -------------------------------------------- //
// FOREIGN KEY: MPLAYER
// -------------------------------------------- //
public List<MPlayer> getMPlayers()
{
return new MassiveList<>(FactionsIndex.get().getMPlayers(this));
}
public List<MPlayer> getMPlayers(java.util.function.Predicate<? super MPlayer> where, Comparator<? super MPlayer> orderby, Integer limit, Integer offset)
{
return MUtil.transform(this.getMPlayers(), where, orderby, limit, offset);
}
public List<MPlayer> getMPlayersWhere(java.util.function.Predicate<? super MPlayer> predicate)
{
return this.getMPlayers(predicate, null, null, null);
}
public List<MPlayer> getMPlayersWhereOnline(boolean online)
{
return this.getMPlayersWhere(online ? SenderColl.PREDICATE_ONLINE : SenderColl.PREDICATE_OFFLINE);
}
public List<MPlayer> getMPlayersWhereOnlineTo(Object senderObject)
{
return this.getMPlayersWhere(PredicateAnd.get(SenderColl.PREDICATE_ONLINE, PredicateVisibleTo.get(senderObject)));
}
public List<MPlayer> getMPlayersWhereRank(Rank rank)
{
return this.getMPlayersWhere(PredicateMPlayerRank.get(rank));
}
public MPlayer getLeader()
{
List<MPlayer> ret = this.getMPlayersWhereRank(this.getLeaderRank());
if (ret.size() == 0) return null;
return ret.get(0);
}
public Set<String> getMPlayerIds()
{
return this.getMPlayers().stream().map(MPlayer::getId).collect(Collectors.toSet());
}
public List<CommandSender> getOnlineCommandSenders()
{
// Create Ret
List<CommandSender> ret = new MassiveList<>();
// Fill Ret
for (CommandSender sender : IdUtil.getLocalSenders())
{
if (MUtil.isntSender(sender)) continue;
MPlayer mplayer = MPlayer.get(sender);
if (mplayer.getFaction() != this) continue;
ret.add(sender);
}
// Return Ret
return ret;
}
public List<Player> getOnlinePlayers()
{
// Create Ret
List<Player> ret = new MassiveList<>();
// Fill Ret
for (Player player : Bukkit.getOnlinePlayers())
{
if (MUtil.isntPlayer(player)) continue;
MPlayer mplayer = MPlayer.get(player);
if (mplayer.getFaction() != this) continue;
ret.add(player);
}
// Return Ret
return ret;
}
// used when current leader is about to be removed from the faction; promotes new leader, or disbands faction if no other members left
public void promoteNewLeader()
{
if ( ! this.isNormal()) return;
if (this.getFlag(MFlag.getFlagPermanent()) && MConf.get().permanentFactionsDisableLeaderPromotion) return;
MPlayer oldLeader = this.getLeader();
Rank leaderRank = oldLeader.getRank();
List<MPlayer> replacements = Collections.emptyList();
for (Rank rank = leaderRank; rank != null; rank = rank.getRankBelow())
{
//Skip first
if (rank == leaderRank) continue;
replacements = this.getMPlayersWhereRank(rank);
if (!replacements.isEmpty()) break;
}
// if we found a replacement
if (replacements == null || replacements.isEmpty())
{
// faction leader is the only member; one-man faction
if (this.getFlag(MFlag.getFlagPermanent()))
{
if (oldLeader != null)
{
oldLeader.setRank(this.getLeaderRank().getRankBelow());
}
return;
}
// no members left and faction isn't permanent, so disband it
if (MConf.get().logFactionDisband)
{
Factions.get().log("The faction "+this.getName()+" ("+this.getId()+") has been disbanded since it has no members left.");
}
for (MPlayer mplayer : MPlayerColl.get().getAllOnline())
{
mplayer.msg("<i>The faction %s<i> was disbanded.", this.getName(mplayer));
}
this.detach();
}
else
{
// promote new faction leader
if (oldLeader != null)
{
oldLeader.setRank(this.getLeaderRank().getRankBelow());
}
replacements.get(0).setRank(this.getLeaderRank());
this.msg("<i>Faction leader <h>%s<i> has been removed. %s<i> has been promoted as the new faction leader.", oldLeader == null ? "" : oldLeader.getName(), replacements.get(0).getName());
Factions.get().log("Faction "+this.getName()+" ("+this.getId()+") leader was removed. Replacement leader: "+replacements.get(0).getName());
}
}
// -------------------------------------------- //
// FACTION ONLINE STATE
// -------------------------------------------- //
public boolean isAllMPlayersOffline()
{
return this.getMPlayersWhereOnline(true).size() == 0;
}
public boolean isAnyMPlayersOnline()
{
return !this.isAllMPlayersOffline();
}
public boolean isFactionConsideredOffline()
{
return this.isAllMPlayersOffline();
}
public boolean isFactionConsideredOnline()
{
return !this.isFactionConsideredOffline();
}
public boolean isExplosionsAllowed()
{
boolean explosions = this.getFlag(MFlag.getFlagExplosions());
boolean offlineexplosions = this.getFlag(MFlag.getFlagOfflineexplosions());
if (explosions && offlineexplosions) return true;
if ( ! explosions && ! offlineexplosions) return false;
boolean online = this.isFactionConsideredOnline();
return (online && explosions) || (!online && offlineexplosions);
}
// -------------------------------------------- //
// MESSAGES
// -------------------------------------------- //
// These methods are simply proxied in from the Mixin.
// CONVENIENCE SEND MESSAGE
public boolean sendMessage(Object message)
{
return MixinMessage.get().messagePredicate(new PredicateCommandSenderFaction(this), message);
}
public boolean sendMessage(Object... messages)
{
return MixinMessage.get().messagePredicate(new PredicateCommandSenderFaction(this), messages);
}
public boolean sendMessage(Collection<Object> messages)
{
return MixinMessage.get().messagePredicate(new PredicateCommandSenderFaction(this), messages);
}
// CONVENIENCE MSG
public boolean msg(String msg)
{
return MixinMessage.get().msgPredicate(new PredicateCommandSenderFaction(this), msg);
}
public boolean msg(String msg, Object... args)
{
return MixinMessage.get().msgPredicate(new PredicateCommandSenderFaction(this), msg, args);
}
public boolean msg(Collection<String> msgs)
{
return MixinMessage.get().msgPredicate(new PredicateCommandSenderFaction(this), msgs);
}
// -------------------------------------------- //
// UTIL
// -------------------------------------------- //
// FIXME this probably needs to be moved elsewhere
public static String clean(String message)
{
String target = message;
if (target == null) return null;
target = target.trim();
if (target.isEmpty()) target = null;
return target;
}
}