271 lines
12 KiB
Java

package com.massivecraft.factions.engine;
import com.massivecraft.factions.Rel;
import com.massivecraft.factions.entity.BoardColl;
import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.FactionColl;
import com.massivecraft.factions.entity.MConf;
import com.massivecraft.factions.entity.MFlag;
import com.massivecraft.factions.entity.MPerm;
import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.factions.event.EventFactionsChunksChange;
import com.massivecraft.massivecore.Engine;
import com.massivecraft.massivecore.collections.MassiveList;
import com.massivecraft.massivecore.collections.MassiveSet;
import com.massivecraft.massivecore.mixin.MixinWorld;
import com.massivecraft.massivecore.ps.PS;
import com.massivecraft.massivecore.util.Txt;
import org.bukkit.ChatColor;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class EngineChunkChange extends Engine {
// -------------------------------------------- //
// INSTANCE & CONSTRUCT
// -------------------------------------------- //
private static EngineChunkChange i = new EngineChunkChange();
public static EngineChunkChange get() {
return i;
}
// -------------------------------------------- //
// CHUNK CHANGE: ALLOWED
// -------------------------------------------- //
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onChunksChange(EventFactionsChunksChange event) {
// For security reasons we block the chunk change on any error since an error might block security checks from happening.
try {
onChunksChangeInner(event);
} catch (Throwable throwable) {
event.setCancelled(true);
throwable.printStackTrace();
}
}
public void onChunksChangeInner(EventFactionsChunksChange event) {
// Args
final MPlayer mplayer = event.getMPlayer();
final Faction newFaction = event.getNewFaction();
final Map<Faction, Set<PS>> currentFactionChunks = event.getOldFactionChunks();
final Set<Faction> currentFactions = currentFactionChunks.keySet();
final Set<PS> chunks = event.getChunks();
// Override Mode? Sure!
if (mplayer.isOverriding()) {
return;
}
// CALC: Is there at least one normal faction among the current ones?
boolean currentFactionsContainsAtLeastOneNormal = false;
for (Faction currentFaction : currentFactions) {
if (currentFaction.isNormal()) {
currentFactionsContainsAtLeastOneNormal = true;
break;
}
}
// If the new faction is normal (not wilderness/none), meaning if we are claiming for a faction ...
if (newFaction.isNormal()) {
// ... ensure claiming is enabled for the worlds of all chunks ...
for (PS chunk : chunks) {
String worldId = chunk.getWorld();
if (!MConf.get().worldsClaimingEnabled.contains(worldId)) {
String worldName = MixinWorld.get().getWorldDisplayName(worldId);
mplayer.msg("<b>Land claiming is disabled in <h>%s<b>.", worldName);
event.setCancelled(true);
return;
}
}
// ... ensure we have permission to alter the territory of the new faction ...
if (!MPerm.getPermTerritory().has(mplayer, newFaction, true)) {
// NOTE: No need to send a message. We send message from the permission check itself.
event.setCancelled(true);
return;
}
// ... ensure the new faction has enough players to claim ...
if (newFaction.getMPlayers().size() < MConf.get().claimsRequireMinFactionMembers) {
mplayer.msg("<b>Factions must have at least <h>%s<b> members to claim land.", MConf.get().claimsRequireMinFactionMembers);
event.setCancelled(true);
return;
}
int claimedLandCount = newFaction.getLandCount();
if (!newFaction.getFlag(MFlag.getFlagInfpower())) {
// ... ensure the claim would not bypass the global max limit ...
if (MConf.get().claimedLandsMax != 0 && claimedLandCount + chunks.size() > MConf.get().claimedLandsMax) {
mplayer.msg("<b>Limit reached. You can't claim more land.");
event.setCancelled(true);
return;
}
// ... ensure the claim would not bypass the global world limit ...
if (MConf.get().claimedWorldsMax >= 0) {
Set<String> oldWorlds = newFaction.getClaimedWorlds();
Set<String> newWorlds = PS.getDistinctWorlds(chunks);
Set<String> worlds = new MassiveSet<>();
worlds.addAll(oldWorlds);
worlds.addAll(newWorlds);
if (!oldWorlds.containsAll(newWorlds) && worlds.size() > MConf.get().claimedWorldsMax) {
List<String> worldNames = new MassiveList<>();
for (String world : oldWorlds) {
worldNames.add(MixinWorld.get().getWorldDisplayName(world));
}
String worldsMax = MConf.get().claimedWorldsMax == 1 ? "world" : "worlds";
String worldsAlready = oldWorlds.size() == 1 ? "world" : "worlds";
mplayer.msg("<b>A faction may only be present on <h>%d<b> different %s.", MConf.get().claimedWorldsMax, worldsMax);
mplayer.msg("%s<i> is already present on <h>%d<i> %s:", newFaction.describeTo(mplayer), oldWorlds.size(), worldsAlready);
mplayer.message(Txt.implodeCommaAndDot(worldNames, ChatColor.YELLOW.toString()));
mplayer.msg("<i>Please unclaim bases on other worlds to claim here.");
event.setCancelled(true);
return;
}
}
}
// ... ensure the claim would not bypass the faction power ...
if (claimedLandCount + chunks.size() > newFaction.getPowerRounded()) {
mplayer.msg("<b>You don't have enough power to claim that land.");
event.setCancelled(true);
return;
}
// ... ensure the claim would not violate distance to neighbors ...
// HOW: Calculate the factions nearby, excluding the chunks themselves, the faction itself and the wilderness faction.
// HOW: The chunks themselves will be handled in the "if (oldFaction.isNormal())" section below.
Set<PS> nearbyChunks = BoardColl.getNearbyChunks(chunks, MConf.get().claimMinimumChunksDistanceToOthers);
nearbyChunks.removeAll(chunks);
Set<Faction> nearbyFactions = BoardColl.getDistinctFactions(nearbyChunks);
nearbyFactions.remove(FactionColl.get().getNone());
nearbyFactions.remove(newFaction);
// HOW: Next we check if the new faction has permission to claim nearby the nearby factions.
MPerm claimnear = MPerm.getPermClaimnear();
for (Faction nearbyFaction : nearbyFactions) {
if (claimnear.has(mplayer, nearbyFaction, true)) {
continue;
}
event.setCancelled(true);
return;
}
// ... ensure claims are properly connected ...
if
(
// If claims must be connected ...
MConf.get().claimsMustBeConnected
// ... and this faction already has claimed something on this map (meaning it's not their first claim) ...
&&
newFaction.getLandCountInWorld(chunks.iterator().next().getWorld()) > 0
// ... and none of the chunks are connected to an already claimed chunk for the faction ...
&&
!BoardColl.get().isAnyConnectedPs(chunks, newFaction)
// ... and either claims must always be connected or there is at least one normal faction among the old factions ...
&&
(!MConf.get().claimsCanBeUnconnectedIfOwnedByOtherFaction || currentFactionsContainsAtLeastOneNormal)
) {
if (MConf.get().claimsCanBeUnconnectedIfOwnedByOtherFaction) {
mplayer.msg("<b>You can only claim additional land which is connected to your first claim or controlled by another faction!");
} else {
mplayer.msg("<b>You can only claim additional land which is connected to your first claim!");
}
event.setCancelled(true);
return;
}
}
// Ensure claims are properly connected when unclaiming
if (newFaction.isNone() && MConf.get().claimsMustBeConnected && MConf.get().claimsMustBeConnectedStrict) {
for (Entry<Faction, Set<PS>> entry : currentFactionChunks.entrySet()) {
Faction faction = entry.getKey();
Set<PS> factionRemovedChunks = entry.getValue();
Set<PS> pssBefore = BoardColl.get().getChunks(faction);
// Get how many "forests" of claims there are right now
List<Collection<PS>> forestsBefore = BoardColl.getForests(pssBefore);
Set<PS> pssAfter = new MassiveSet<>(pssBefore);
pssAfter.removeAll(factionRemovedChunks);
List<Collection<PS>> forestsAfter = BoardColl.getForests(pssAfter);
if (forestsAfter.size() > forestsBefore.size()) {
mplayer.msg("<b>Claims must be connected. You can't make them disconnected by unclaiming.");
event.setCancelled(true);
return;
}
}
}
// For each of the old factions ...
for (Entry<Faction, Set<PS>> entry : currentFactionChunks.entrySet()) {
Faction oldFaction = entry.getKey();
Set<PS> oldChunks = entry.getValue();
// ... that is an actual faction ...
if (oldFaction.isNone()) {
continue;
}
// ... for which the mplayer lacks permission ...
if (MPerm.getPermTerritory().has(mplayer, oldFaction, false)) {
continue;
}
// ... consider all reasons to forbid "overclaiming/warclaiming" ...
// ... claiming from others may be forbidden ...
if (!MConf.get().claimingFromOthersAllowed) {
mplayer.msg("<b>You may not claim land from others.");
event.setCancelled(true);
return;
}
// ... and they must actually be claiming ...
if (newFaction.isNone()) {
mplayer.msg("<b>You can't unclaim land belonging to others.");
event.setCancelled(true);
return;
}
// ... the relation may forbid ...
if (oldFaction.getRelationTo(newFaction).isAtLeast(Rel.TRUCE)) {
mplayer.msg("<b>You can't claim this land due to your relation with the current owner.");
event.setCancelled(true);
return;
}
// ... the old faction might not be inflated enough ...
if (oldFaction.getPowerRounded() > oldFaction.getLandCount() - oldChunks.size() && MConf.get().claimingFromOthersMustBeInflated) {
mplayer.msg("%s<i> owns this land and is strong enough to keep it.", oldFaction.getName(mplayer));
event.setCancelled(true);
return;
}
// ... and you might be trying to claim without starting at the border ...
if (!BoardColl.get().isAnyBorderPs(chunks)) {
mplayer.msg("<b>You must start claiming land at the border of the territory.");
event.setCancelled(true);
return;
}
// ... otherwise you may claim from this old faction even though you lack explicit permission from them.
}
}
}