Better claiming. Step 1.

This commit is contained in:
Olof Larsson 2014-10-13 11:42:40 +02:00
parent b54293577d
commit c690d33ad6
16 changed files with 506 additions and 484 deletions

View File

@ -1,5 +1,7 @@
package com.massivecraft.factions.cmd; package com.massivecraft.factions.cmd;
import java.util.Collections;
import com.massivecraft.factions.Perm; import com.massivecraft.factions.Perm;
import com.massivecraft.factions.cmd.arg.ARFaction; import com.massivecraft.factions.cmd.arg.ARFaction;
import com.massivecraft.factions.entity.Faction; import com.massivecraft.factions.entity.Faction;
@ -50,7 +52,8 @@ public class CmdFactionsAutoClaim extends FactionsCommand
msender.setAutoClaimFaction(forFaction); msender.setAutoClaimFaction(forFaction);
msg("<i>Now auto-claiming land for <h>%s<i>.", forFaction.describeTo(msender)); msg("<i>Now auto-claiming land for <h>%s<i>.", forFaction.describeTo(msender));
msender.tryClaim(forFaction, PS.valueOf(me), true, true);
msender.tryClaim(forFaction, Collections.singletonList(PS.valueOf(me).getChunk(true)));
} }
} }

View File

@ -1,11 +1,13 @@
package com.massivecraft.factions.cmd; package com.massivecraft.factions.cmd;
import java.util.LinkedHashSet;
import java.util.Set;
import com.massivecraft.factions.Perm; import com.massivecraft.factions.Perm;
import com.massivecraft.factions.cmd.arg.ARFaction; import com.massivecraft.factions.cmd.arg.ARFaction;
import com.massivecraft.factions.entity.Faction; import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.MConf; import com.massivecraft.factions.entity.MConf;
import com.massivecraft.factions.entity.MPerm; import com.massivecraft.factions.entity.MPerm;
import com.massivecraft.factions.task.SpiralTask;
import com.massivecraft.massivecore.cmd.arg.ARInteger; import com.massivecraft.massivecore.cmd.arg.ARInteger;
import com.massivecraft.massivecore.cmd.req.ReqHasPerm; import com.massivecraft.massivecore.cmd.req.ReqHasPerm;
import com.massivecraft.massivecore.cmd.req.ReqIsPlayer; import com.massivecraft.massivecore.cmd.req.ReqIsPlayer;
@ -43,68 +45,52 @@ public class CmdFactionsClaim extends FactionsCommand
Integer radius = this.arg(0, ARInteger.get(), 1); Integer radius = this.arg(0, ARInteger.get(), 1);
if (radius == null) return; if (radius == null) return;
final Faction forFaction = this.arg(1, ARFaction.get(), msenderFaction); final Faction newFaction = this.arg(1, ARFaction.get(), msenderFaction);
if (forFaction == null) return; if (newFaction == null) return;
// MPerm // MPerm
if (forFaction.isNormal() && !MPerm.getPermTerritory().has(msender, forFaction, true)) return; if (newFaction.isNormal() && ! MPerm.getPermTerritory().has(msender, newFaction, true)) return;
// Validate // Radius Claim Min
if (radius < 1) if (radius < 1)
{ {
msg("<b>If you specify a radius, it must be at least 1."); msg("<b>If you specify a radius, it must be at least 1.");
return; return;
} }
if (radius > MConf.get().radiusClaimRadiusLimit && !msender.isUsingAdminMode()) // Radius Claim Perm
{ if (radius > 1 && ! Perm.CLAIM_RADIUS.has(sender, false))
msg("<b>The maximum radius allowed is <h>%s<b>.", MConf.get().radiusClaimRadiusLimit);
return;
}
// Apply
// single chunk
if (radius < 2)
{
msender.tryClaim(forFaction, PS.valueOf(me), true, true);
return;
}
// radius claim
if (!Perm.CLAIM_RADIUS.has(sender, false))
{ {
msg("<b>You do not have permission to claim in a radius."); msg("<b>You do not have permission to claim in a radius.");
return; return;
} }
// TODO: There must be a better way than using a spiral task. // Radius Claim Max
// TODO: Do some research to allow for claming sets of chunks in a batch with atomicity. if (radius > MConf.get().radiusClaimRadiusLimit && ! msender.isUsingAdminMode())
// This will probably result in an alteration to the owner change event.
// It would possibly contain a set of chunks instead of a single chunk.
new SpiralTask(PS.valueOf(me), radius)
{ {
private int failCount = 0; msg("<b>The maximum radius allowed is <h>%s<b>.", MConf.get().radiusClaimRadiusLimit);
private final int limit = MConf.get().radiusClaimFailureLimit - 1; return;
}
@Override // Get Chunks
public boolean work() final int radiusZero = radius -1;
final PS chunk = PS.valueOf(me).getChunk(true);
final int xmin = chunk.getChunkX() - radiusZero;
final int xmax = chunk.getChunkX() + radiusZero;
final int zmin = chunk.getChunkZ() - radiusZero;
final int zmax = chunk.getChunkZ() + radiusZero;
Set<PS> chunks = new LinkedHashSet<PS>();
chunks.add(chunk); // The center should come first for pretty messages
for (int x = xmin; x <= xmax; x++)
{
for (int z = zmin; z <= zmax; z++)
{ {
boolean success = msender.tryClaim(forFaction, PS.valueOf(this.currentLocation()), true, true); chunks.add(chunk.withChunkX(x).withChunkZ(z));
if (success)
{
this.failCount = 0;
}
else if (this.failCount++ >= this.limit)
{
this.stop();
return false;
}
return true;
} }
}; }
// Apply / Inform
msender.tryClaim(newFaction, chunks);
} }
} }

View File

@ -1,5 +1,7 @@
package com.massivecraft.factions.cmd; package com.massivecraft.factions.cmd;
import java.util.Collections;
import com.massivecraft.factions.cmd.req.ReqHasFaction; import com.massivecraft.factions.cmd.req.ReqHasFaction;
import com.massivecraft.factions.entity.Faction; import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.FactionColl; import com.massivecraft.factions.entity.FactionColl;
@ -37,7 +39,7 @@ public class CmdFactionsUnclaim extends FactionsCommand
Faction newFaction = FactionColl.get().getNone(); Faction newFaction = FactionColl.get().getNone();
// Apply // Apply
if (msender.tryClaim(newFaction, chunk, true, true)) return; if (msender.tryClaim(newFaction, Collections.singletonList(chunk))) return;
} }
} }

View File

@ -2,7 +2,6 @@ package com.massivecraft.factions.cmd;
import java.util.Set; import java.util.Set;
import com.massivecraft.factions.Factions;
import com.massivecraft.factions.Perm; import com.massivecraft.factions.Perm;
import com.massivecraft.factions.Rel; import com.massivecraft.factions.Rel;
import com.massivecraft.factions.cmd.req.ReqHasFaction; import com.massivecraft.factions.cmd.req.ReqHasFaction;
@ -10,9 +9,7 @@ import com.massivecraft.factions.cmd.req.ReqRoleIsAtLeast;
import com.massivecraft.factions.entity.BoardColl; import com.massivecraft.factions.entity.BoardColl;
import com.massivecraft.factions.entity.Faction; import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.FactionColl; import com.massivecraft.factions.entity.FactionColl;
import com.massivecraft.factions.entity.MConf;
import com.massivecraft.factions.entity.MPerm; import com.massivecraft.factions.entity.MPerm;
import com.massivecraft.factions.event.EventFactionsChunkChange;
import com.massivecraft.massivecore.cmd.req.ReqHasPerm; import com.massivecraft.massivecore.cmd.req.ReqHasPerm;
import com.massivecraft.massivecore.ps.PS; import com.massivecraft.massivecore.ps.PS;
@ -49,12 +46,15 @@ public class CmdFactionsUnclaimall extends FactionsCommand
// Apply // Apply
Set<PS> chunks = BoardColl.get().getChunks(faction); Set<PS> chunks = BoardColl.get().getChunks(faction);
msender.tryClaim(newFaction, chunks);
/*
int countTotal = chunks.size(); int countTotal = chunks.size();
int countSuccess = 0; int countSuccess = 0;
int countFail = 0; int countFail = 0;
for (PS chunk : chunks) for (PS chunk : chunks)
{ {
EventFactionsChunkChange event = new EventFactionsChunkChange(sender, chunk, newFaction); EventFactionsChunksChange event = new EventFactionsChunksChange(sender, chunk, newFaction);
event.run(); event.run();
if (event.isCancelled()) if (event.isCancelled())
{ {
@ -74,7 +74,8 @@ public class CmdFactionsUnclaimall extends FactionsCommand
if (MConf.get().logLandUnclaims) if (MConf.get().logLandUnclaims)
{ {
Factions.get().log(msender.getName()+" unclaimed everything for the faction: "+msenderFaction.getName()); Factions.get().log(msender.getName()+" unclaimed everything for the faction: "+msenderFaction.getName());
} }*/
} }
} }

View File

@ -1,5 +1,10 @@
package com.massivecraft.factions.engine; package com.massivecraft.factions.engine;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
@ -9,7 +14,7 @@ import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.MConf; import com.massivecraft.factions.entity.MConf;
import com.massivecraft.factions.entity.MPlayer; import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.factions.event.EventFactionsAbstractSender; import com.massivecraft.factions.event.EventFactionsAbstractSender;
import com.massivecraft.factions.event.EventFactionsChunkChange; import com.massivecraft.factions.event.EventFactionsChunksChange;
import com.massivecraft.factions.event.EventFactionsChunkChangeType; import com.massivecraft.factions.event.EventFactionsChunkChangeType;
import com.massivecraft.factions.event.EventFactionsCreate; import com.massivecraft.factions.event.EventFactionsCreate;
import com.massivecraft.factions.event.EventFactionsDescriptionChange; import com.massivecraft.factions.event.EventFactionsDescriptionChange;
@ -26,6 +31,8 @@ import com.massivecraft.factions.event.EventFactionsTitleChange;
import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.integration.Econ;
import com.massivecraft.massivecore.EngineAbstract; import com.massivecraft.massivecore.EngineAbstract;
import com.massivecraft.massivecore.money.Money; import com.massivecraft.massivecore.money.Money;
import com.massivecraft.massivecore.ps.PS;
import com.massivecraft.massivecore.util.Txt;
public class EngineEcon extends EngineAbstract public class EngineEcon extends EngineAbstract
{ {
@ -116,13 +123,27 @@ public class EngineEcon extends EngineAbstract
} }
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void payForAction(EventFactionsChunkChange event) public void payForAction(EventFactionsChunksChange event)
{ {
EventFactionsChunkChangeType type = event.getType(); double cost = 0;
Double cost = MConf.get().econChunkCost.get(type); List<String> typeNames = new ArrayList<String>();
String desc = type.toString().toLowerCase() + " this land"; for (Entry<EventFactionsChunkChangeType, Set<PS>> typeChunks : event.getTypeChunks().entrySet())
{
final EventFactionsChunkChangeType type = typeChunks.getKey();
final Set<PS> chunks = typeChunks.getValue();
Double typeCost = MConf.get().econChunkCost.get(type);
if (typeCost == null) continue;
if (typeCost == 0) continue;
typeCost *= chunks.size();
cost += typeCost;
typeNames.add(type.now);
}
String desc = Txt.implodeCommaAnd(typeNames) + " this land";
payForAction(event, cost, desc); payForAction(event, cost, desc);
} }

View File

@ -2,10 +2,12 @@ package com.massivecraft.factions.engine;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
@ -62,12 +64,14 @@ import com.massivecraft.factions.Factions;
import com.massivecraft.factions.Rel; import com.massivecraft.factions.Rel;
import com.massivecraft.factions.TerritoryAccess; import com.massivecraft.factions.TerritoryAccess;
import com.massivecraft.factions.entity.BoardColl; import com.massivecraft.factions.entity.BoardColl;
import com.massivecraft.factions.entity.FactionColl;
import com.massivecraft.factions.entity.MFlag; import com.massivecraft.factions.entity.MFlag;
import com.massivecraft.factions.entity.MPerm; import com.massivecraft.factions.entity.MPerm;
import com.massivecraft.factions.entity.MPlayer; import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.factions.entity.Faction; import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.MConf; import com.massivecraft.factions.entity.MConf;
import com.massivecraft.factions.entity.MPlayerColl; import com.massivecraft.factions.entity.MPlayerColl;
import com.massivecraft.factions.event.EventFactionsChunksChange;
import com.massivecraft.factions.event.EventFactionsPvpDisallowed; import com.massivecraft.factions.event.EventFactionsPvpDisallowed;
import com.massivecraft.factions.event.EventFactionsPowerChange; import com.massivecraft.factions.event.EventFactionsPowerChange;
import com.massivecraft.factions.event.EventFactionsPowerChange.PowerChangeReason; import com.massivecraft.factions.event.EventFactionsPowerChange.PowerChangeReason;
@ -230,6 +234,167 @@ public class EngineMain extends EngineAbstract
// CHUNK CHANGE: DETECT // CHUNK CHANGE: DETECT
// -------------------------------------------- // // -------------------------------------------- //
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onChunksChange(EventFactionsChunksChange event)
{
// Args
final MPlayer msender = event.getMSender();
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();
// Admin Mode? Sure!
if (msender.isUsingAdminMode()) 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 = Mixin.getWorldDisplayName(worldId);
msender.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(msender, 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)
{
msender.msg("<b>Factions must have at least <h>%s<b> members to claim land.", MConf.get().claimsRequireMinFactionMembers);
event.setCancelled(true);
return;
}
// ... ensure the claim would not bypass the global max limit ...
int ownedLand = newFaction.getLandCount();
if (MConf.get().claimedLandsMax != 0 && ownedLand + chunks.size() > MConf.get().claimedLandsMax && ! newFaction.getFlag(MFlag.getFlagInfpower()))
{
msender.msg("<b>Limit reached. You can't claim more land.");
event.setCancelled(true);
return;
}
// ... ensure the claim would not bypass the faction power ...
if (ownedLand + chunks.size() > newFaction.getPowerRounded())
{
msender.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(newFaction, nearbyFaction)) continue;
msender.sendMessage(claimnear.createDeniedMessage(msender, nearbyFaction));
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)
{
msender.msg("<b>You can only claim additional land which is connected to your first claim or controlled by another faction!");
}
else
{
msender.msg("<b>You can only claim additional land which is connected to your first claim!");
}
event.setCancelled(true);
return;
}
}
// For each of the old factions ...
for (Faction oldFaction : currentFactions)
{
// ... that is an actual faction ...
if (oldFaction.isNone()) continue;
// ... for which the msender lacks permission ...
if (MPerm.getPermTerritory().has(msender, oldFaction, false)) continue;
// ... print the error message of choice ...
if (msender.hasFaction() && msender.getFaction() == oldFaction)
{
msender.sendMessage(MPerm.getPermTerritory().createDeniedMessage(msender, oldFaction));
}
else if ( ! MConf.get().claimingFromOthersAllowed)
{
msender.msg("<b>You may not claim land from others.");
}
else if (oldFaction.getRelationTo(newFaction).isAtLeast(Rel.TRUCE))
{
msender.msg("<b>You can't claim this land due to your relation with the current owner.");
}
else if ( ! oldFaction.hasLandInflation())
{
msender.msg("%s<i> owns this land and is strong enough to keep it.", oldFaction.getName(msender));
}
else if ( ! BoardColl.get().isAnyBorderPs(chunks))
{
msender.msg("<b>You must start claiming land at the border of the territory.");
}
// ... and cancel.
event.setCancelled(true);
return;
}
}
// -------------------------------------------- //
// CHUNK CHANGE: DETECT
// -------------------------------------------- //
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void chunkChangeDetect(PlayerMoveEvent event) public void chunkChangeDetect(PlayerMoveEvent event)
{ {
@ -307,7 +472,7 @@ public class EngineMain extends EngineAbstract
if (autoClaimFaction == null) return; if (autoClaimFaction == null) return;
// ... try claim. // ... try claim.
mplayer.tryClaim(autoClaimFaction, chunkTo, true, true); mplayer.tryClaim(autoClaimFaction, Collections.singletonList(chunkTo));
} }
// -------------------------------------------- // // -------------------------------------------- //

View File

@ -243,6 +243,16 @@ public class Board extends Entity<Board> implements BoardInterface
return false; return false;
} }
@Override
public boolean isAnyBorderPs(Set<PS> pss)
{
for (PS ps : pss)
{
if (this.isBorderPs(ps)) return true;
}
return false;
}
// Is this coord connected to any coord claimed by the specified faction? // Is this coord connected to any coord claimed by the specified faction?
@Override @Override
public boolean isConnectedPs(PS ps, Faction faction) public boolean isConnectedPs(PS ps, Faction faction)
@ -266,6 +276,16 @@ public class Board extends Entity<Board> implements BoardInterface
return false; return false;
} }
@Override
public boolean isAnyConnectedPs(Set<PS> pss, Faction faction)
{
for (PS ps : pss)
{
if (this.isConnectedPs(ps, faction)) return true;
}
return false;
}
// MAP GENERATION // MAP GENERATION
@Override @Override

View File

@ -3,7 +3,9 @@ package com.massivecraft.factions.entity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
import com.massivecraft.factions.Const; import com.massivecraft.factions.Const;
@ -150,6 +152,16 @@ public class BoardColl extends Coll<Board> implements BoardInterface
return board.isBorderPs(ps); return board.isBorderPs(ps);
} }
@Override
public boolean isAnyBorderPs(Set<PS> pss)
{
for (PS ps : pss)
{
if (this.isBorderPs(ps)) return true;
}
return false;
}
@Override @Override
public boolean isConnectedPs(PS ps, Faction faction) public boolean isConnectedPs(PS ps, Faction faction)
{ {
@ -159,6 +171,16 @@ public class BoardColl extends Coll<Board> implements BoardInterface
return board.isConnectedPs(ps, faction); return board.isConnectedPs(ps, faction);
} }
@Override
public boolean isAnyConnectedPs(Set<PS> pss, Faction faction)
{
for (PS ps : pss)
{
if (this.isConnectedPs(ps, faction)) return true;
}
return false;
}
// MAP GENERATION // MAP GENERATION
@Override @Override
@ -177,7 +199,7 @@ public class BoardColl extends Coll<Board> implements BoardInterface
// Distance -1 returns 0 chunks always. // Distance -1 returns 0 chunks always.
// Distance 0 returns 1 chunk only (the one supplied). // Distance 0 returns 1 chunk only (the one supplied).
// Distance 1 returns 3x3 = 9 chunks. // Distance 1 returns 3x3 = 9 chunks.
public static Set<PS> getNearbyChunks(PS chunk, int distance, boolean includeSelf) public static Set<PS> getNearbyChunks(PS chunk, int distance)
{ {
// Fix Args // Fix Args
if (chunk == null) throw new NullPointerException("chunk"); if (chunk == null) throw new NullPointerException("chunk");
@ -187,7 +209,6 @@ public class BoardColl extends Coll<Board> implements BoardInterface
Set<PS> ret = new LinkedHashSet<PS>(); Set<PS> ret = new LinkedHashSet<PS>();
if (distance < 0) return ret; if (distance < 0) return ret;
// if (distance == 0 && ! includeSelf) return ret; // This will be done by the code below.
// Main // Main
int xmin = chunk.getChunkX() - distance; int xmin = chunk.getChunkX() - distance;
@ -200,7 +221,6 @@ public class BoardColl extends Coll<Board> implements BoardInterface
{ {
for (int z = zmin; z <= zmax; z++) for (int z = zmin; z <= zmax; z++)
{ {
if ( ! includeSelf && x == chunk.getChunkX() && z == chunk.getChunkZ()) continue;
ret.add(chunk.withChunkX(x).withChunkZ(z)); ret.add(chunk.withChunkX(x).withChunkZ(z));
} }
} }
@ -209,6 +229,26 @@ public class BoardColl extends Coll<Board> implements BoardInterface
return ret; return ret;
} }
public static Set<PS> getNearbyChunks(Collection<PS> chunks, int distance)
{
// Fix Args
if (chunks == null) throw new NullPointerException("chunks");
// Create Ret
Set<PS> ret = new LinkedHashSet<PS>();
if (distance < 0) return ret;
// Main
for (PS chunk : chunks)
{
ret.addAll(getNearbyChunks(chunk, distance));
}
// Return Ret
return ret;
}
public static Set<Faction> getDistinctFactions(Collection<PS> chunks) public static Set<Faction> getDistinctFactions(Collection<PS> chunks)
{ {
// Fix Args // Fix Args
@ -229,4 +269,19 @@ public class BoardColl extends Coll<Board> implements BoardInterface
return ret; return ret;
} }
public static Map<PS, Faction> getChunkFaction(Collection<PS> chunks)
{
Map<PS, Faction> ret = new LinkedHashMap<PS, Faction>();
for (PS chunk : chunks)
{
chunk = chunk.getChunk(true);
Faction faction = get().getFactionAt(chunk);
if (faction == null) faction = FactionColl.get().getNone();
ret.put(chunk, faction);
}
return ret;
}
} }

View File

@ -30,7 +30,9 @@ public interface BoardInterface
// NEARBY DETECTION // NEARBY DETECTION
public boolean isBorderPs(PS ps); public boolean isBorderPs(PS ps);
public boolean isAnyBorderPs(Set<PS> pss);
public boolean isConnectedPs(PS ps, Faction faction); public boolean isConnectedPs(PS ps, Faction faction);
public boolean isAnyConnectedPs(Set<PS> pss, Faction faction);
// MAP // MAP
// TODO: Could the degrees be embedded in centerPs yaw instead? // TODO: Could the degrees be embedded in centerPs yaw instead?

View File

@ -151,9 +151,6 @@ public class MConf extends Entity<MConf>
// CLAIM LIMITS // CLAIM LIMITS
// -------------------------------------------- // // -------------------------------------------- //
// if someone is doing a radius claim and the process fails to claim land this many times in a row, it will exit
public int radiusClaimFailureLimit = 9;
// the maximum radius allowed when using the claim command. // the maximum radius allowed when using the claim command.
public int radiusClaimRadiusLimit = 5; public int radiusClaimRadiusLimit = 5;

View File

@ -1,6 +1,9 @@
package com.massivecraft.factions.entity; package com.massivecraft.factions.entity;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -12,7 +15,8 @@ import com.massivecraft.factions.Lang;
import com.massivecraft.factions.Perm; import com.massivecraft.factions.Perm;
import com.massivecraft.factions.Rel; import com.massivecraft.factions.Rel;
import com.massivecraft.factions.RelationParticipator; import com.massivecraft.factions.RelationParticipator;
import com.massivecraft.factions.event.EventFactionsChunkChange; import com.massivecraft.factions.event.EventFactionsChunkChangeType;
import com.massivecraft.factions.event.EventFactionsChunksChange;
import com.massivecraft.factions.event.EventFactionsMembershipChange; import com.massivecraft.factions.event.EventFactionsMembershipChange;
import com.massivecraft.factions.event.EventFactionsRemovePlayerMillis; import com.massivecraft.factions.event.EventFactionsRemovePlayerMillis;
import com.massivecraft.factions.event.EventFactionsMembershipChange.MembershipChangeReason; import com.massivecraft.factions.event.EventFactionsMembershipChange.MembershipChangeReason;
@ -793,163 +797,91 @@ public class MPlayer extends SenderEntity<MPlayer> implements EconomyParticipato
} }
} }
public boolean tryClaim(Faction newFaction, PS ps, boolean verbooseChange, boolean verbooseSame) // NEW
public boolean tryClaim(Faction newFaction, Collection<PS> pss)
{ {
PS chunk = ps.getChunk(true); // Args
Faction oldFaction = BoardColl.get().getFactionAt(chunk); if (newFaction == null) throw new NullPointerException("newFaction");
MConf mconf = MConf.get(); if (pss == null) throw new NullPointerException("pss");
final Set<PS> chunks = PS.getDistinctChunks(pss);
// NoChange // NoChange
if (newFaction == oldFaction) // We clean the chunks further by removing what does not change.
// This is also very suggested cleaning of EventFactionsChunksChange input.
Iterator<PS> iter = chunks.iterator();
while (iter.hasNext())
{
PS chunk = iter.next();
Faction oldFaction = BoardColl.get().getFactionAt(chunk);
if (newFaction == oldFaction) iter.remove();
}
if (chunks.isEmpty())
{ {
msg("%s<i> already owns this land.", newFaction.describeTo(this, true)); msg("%s<i> already owns this land.", newFaction.describeTo(this, true));
return true; return true;
} }
if ( ! this.isUsingAdminMode())
{
if (newFaction.isNormal())
{
if ( ! mconf.worldsClaimingEnabled.contains(ps.getWorld()))
{
msg("<b>Sorry, this world has land claiming disabled.");
return false;
}
if ( ! MPerm.getPermTerritory().has(this, newFaction, true))
{
return false;
}
if (newFaction.getMPlayers().size() < mconf.claimsRequireMinFactionMembers)
{
msg("Factions must have at least <h>%s<b> members to claim land.", mconf.claimsRequireMinFactionMembers);
return false;
}
int ownedLand = newFaction.getLandCount();
if (mconf.claimedLandsMax != 0 && ownedLand >= mconf.claimedLandsMax && ! newFaction.getFlag(MFlag.getFlagInfpower()))
{
msg("<b>Limit reached. You can't claim more land.");
return false;
}
if (ownedLand >= newFaction.getPowerRounded())
{
msg("<b>You can't claim more land. You need more power.");
return false;
}
// Calculate the factions nearby, excluding the chunk itself, the faction itself and the wilderness faction.
// The chunk itself is handled in the "if (oldFaction.isNormal())" section below.
Set<PS> nearbyChunks = BoardColl.getNearbyChunks(chunk, MConf.get().claimMinimumChunksDistanceToOthers, false);
Set<Faction> nearbyFactions = BoardColl.getDistinctFactions(nearbyChunks);
nearbyFactions.remove(FactionColl.get().getNone());
nearbyFactions.remove(newFaction);
// 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(newFaction, nearbyFaction)) continue;
sendMessage(claimnear.createDeniedMessage(this, nearbyFaction));
return false;
}
if
(
mconf.claimsMustBeConnected
&&
newFaction.getLandCountInWorld(ps.getWorld()) > 0
&&
! BoardColl.get().isConnectedPs(chunk, newFaction)
&&
( ! mconf.claimsCanBeUnconnectedIfOwnedByOtherFaction || oldFaction.isNone())
)
{
if (mconf.claimsCanBeUnconnectedIfOwnedByOtherFaction)
{
msg("<b>You can only claim additional land which is connected to your first claim or controlled by another faction!");
}
else
{
msg("<b>You can only claim additional land which is connected to your first claim!");
}
return false;
}
}
if (oldFaction.isNormal())
{
if ( ! MPerm.getPermTerritory().has(this, oldFaction, false))
{
if (this.hasFaction() && this.getFaction() == oldFaction)
{
sendMessage(MPerm.getPermTerritory().createDeniedMessage(this, oldFaction));
return false;
}
if ( ! mconf.claimingFromOthersAllowed)
{
msg("<b>You may not claim land from others.");
return false;
}
if (oldFaction.getRelationTo(newFaction).isAtLeast(Rel.TRUCE))
{
msg("<b>You can't claim this land due to your relation with the current owner.");
return false;
}
if ( ! oldFaction.hasLandInflation())
{
msg("%s<i> owns this land and is strong enough to keep it.", oldFaction.getName(this));
return false;
}
if ( ! BoardColl.get().isBorderPs(chunk))
{
msg("<b>You must start claiming land at the border of the territory.");
return false;
}
}
}
}
// Event // Event
EventFactionsChunkChange event = new EventFactionsChunkChange(this.getSender(), chunk, newFaction); // NOTE: We listen to this event ourselves at LOW.
// NOTE: That is where we apply the standard checks.
EventFactionsChunksChange event = new EventFactionsChunksChange(this.getSender(), chunks, newFaction);
event.run(); event.run();
if (event.isCancelled()) return false; if (event.isCancelled()) return false;
// Apply // Apply
BoardColl.get().setFactionAt(chunk, newFaction); for (PS chunk : chunks)
{
BoardColl.get().setFactionAt(chunk, newFaction);
}
// Inform // Inform
Set<MPlayer> informees = new HashSet<MPlayer>(); for (Entry<Faction, Set<PS>> entry : event.getOldFactionChunks().entrySet())
informees.add(this);
if (newFaction.isNormal())
{ {
informees.addAll(newFaction.getMPlayers()); final Faction oldFaction = entry.getKey();
} final Set<PS> oldChunks = entry.getValue();
if (oldFaction.isNormal()) final PS oldChunk = oldChunks.iterator().next();
{ final Set<MPlayer> informees = getClaimInformees(this, oldFaction, newFaction);
informees.addAll(oldFaction.getMPlayers()); final EventFactionsChunkChangeType type = EventFactionsChunkChangeType.get(oldFaction, newFaction, this.getFaction());
}
if (MConf.get().logLandClaims) String chunkString = oldChunk.toString(PSFormatHumanSpace.get());
{ String typeString = type.past;
informees.add(MPlayer.get(IdUtil.getConsole()));
} for (MPlayer informee : informees)
{
String chunkString = chunk.toString(PSFormatHumanSpace.get()); informee.msg("<h>%s<i> %s <h>%d <i>" + (oldChunks.size() == 1 ? "chunk" : "chunks") + " near %s", this.describeTo(informee, true), typeString, oldChunks.size(), chunkString);
String typeString = event.getType().past; informee.msg(" <h>%s<i> --> <h>%s", oldFaction.describeTo(informee, true), newFaction.describeTo(informee, true));
for (MPlayer informee : informees) }
{
informee.msg("<h>%s<i> %s %s <i>| <h>%s<i> --> <h>%s", this.describeTo(informee, true), typeString, chunkString, oldFaction.describeTo(informee, true), newFaction.describeTo(informee, true));
} }
// Success
return true; return true;
} }
// -------------------------------------------- //
// UTIL
// -------------------------------------------- //
public static Set<MPlayer> getClaimInformees(MPlayer msender, Faction... factions)
{
Set<MPlayer> ret = new HashSet<MPlayer>();
ret.add(msender);
for (Faction faction : factions)
{
if (faction.isNormal())
{
ret.addAll(faction.getMPlayers());
}
}
if (MConf.get().logLandClaims)
{
ret.add(MPlayer.get(IdUtil.getConsole()));
}
return ret;
}
} }

View File

@ -1,59 +0,0 @@
package com.massivecraft.factions.event;
import org.bukkit.command.CommandSender;
import org.bukkit.event.HandlerList;
import com.massivecraft.factions.entity.BoardColl;
import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.massivecore.ps.PS;
public class EventFactionsChunkChange extends EventFactionsAbstractSender
{
// -------------------------------------------- //
// REQUIRED EVENT CODE
// -------------------------------------------- //
private static final HandlerList handlers = new HandlerList();
@Override public HandlerList getHandlers() { return handlers; }
public static HandlerList getHandlerList() { return handlers; }
// -------------------------------------------- //
// FIELDS
// -------------------------------------------- //
private final PS chunk;
public PS getChunk() { return this.chunk; }
private final Faction currentFaction;
private final Faction newFaction;
public Faction getNewFaction() { return this.newFaction; }
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public EventFactionsChunkChange(CommandSender sender, PS chunk, Faction newFaction)
{
super(sender);
this.chunk = chunk.getChunk(true);
this.currentFaction = BoardColl.get().getFactionAt(chunk);
this.newFaction = newFaction;
}
// -------------------------------------------- //
// UTIL
// -------------------------------------------- //
public EventFactionsChunkChangeType getType()
{
if (currentFaction.isNone()) return EventFactionsChunkChangeType.BUY;
if (newFaction.isNormal()) return EventFactionsChunkChangeType.CONQUER;
MPlayer usender = this.getMSender();
if (usender != null && usender.getFaction() == currentFaction) return EventFactionsChunkChangeType.SELL;
return EventFactionsChunkChangeType.PILLAGE;
}
}

View File

@ -1,11 +1,14 @@
package com.massivecraft.factions.event; package com.massivecraft.factions.event;
import com.massivecraft.factions.entity.Faction;
public enum EventFactionsChunkChangeType public enum EventFactionsChunkChangeType
{ {
// -------------------------------------------- // // -------------------------------------------- //
// ENUM // ENUM
// -------------------------------------------- // // -------------------------------------------- //
NONE("none", "none"),
BUY("buy", "bought"), BUY("buy", "bought"),
SELL("sell", "sold"), SELL("sell", "sold"),
CONQUER("conquer", "conquered"), CONQUER("conquer", "conquered"),
@ -31,4 +34,17 @@ public enum EventFactionsChunkChangeType
this.past = past; this.past = past;
} }
// -------------------------------------------- //
// UTIL
// -------------------------------------------- //
public static EventFactionsChunkChangeType get(Faction oldFaction, Faction newFaction, Faction self)
{
if (newFaction == oldFaction) return NONE;
if (oldFaction.isNone()) return BUY;
if (newFaction.isNormal()) return CONQUER;
if (oldFaction == self) return SELL;
return PILLAGE;
}
} }

View File

@ -0,0 +1,78 @@
package com.massivecraft.factions.event;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.bukkit.command.CommandSender;
import org.bukkit.event.HandlerList;
import com.massivecraft.factions.entity.BoardColl;
import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.massivecore.ps.PS;
import com.massivecraft.massivecore.util.MUtil;
public class EventFactionsChunksChange extends EventFactionsAbstractSender
{
// -------------------------------------------- //
// REQUIRED EVENT CODE
// -------------------------------------------- //
private static final HandlerList handlers = new HandlerList();
@Override public HandlerList getHandlers() { return handlers; }
public static HandlerList getHandlerList() { return handlers; }
// -------------------------------------------- //
// FIELDS
// -------------------------------------------- //
private final Set<PS> chunks;
public Set<PS> getChunks() { return this.chunks; }
private final Faction newFaction;
public Faction getNewFaction() { return this.newFaction; }
private final Map<PS, Faction> oldChunkFaction;
public Map<PS, Faction> getOldChunkFaction() { return this.oldChunkFaction; }
private final Map<Faction, Set<PS>> oldFactionChunks;
public Map<Faction, Set<PS>> getOldFactionChunks() { return this.oldFactionChunks; }
private final Map<PS, EventFactionsChunkChangeType> chunkType;
public Map<PS, EventFactionsChunkChangeType> getChunkType() { return this.chunkType; }
private final Map<EventFactionsChunkChangeType, Set<PS>> typeChunks;
public Map<EventFactionsChunkChangeType, Set<PS>> getTypeChunks() { return this.typeChunks; }
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public EventFactionsChunksChange(CommandSender sender, Set<PS> chunks, Faction newFaction)
{
super(sender);
chunks = PS.getDistinctChunks(chunks);
this.chunks = Collections.unmodifiableSet(chunks);
this.newFaction = newFaction;
this.oldChunkFaction = Collections.unmodifiableMap(BoardColl.getChunkFaction(chunks));
this.oldFactionChunks = Collections.unmodifiableMap(MUtil.reverseIndex(this.oldChunkFaction));
MPlayer msender = this.getMSender();
Faction self = null;
if (msender != null) self = msender.getFaction();
Map<PS, EventFactionsChunkChangeType> currentChunkType = new LinkedHashMap<PS, EventFactionsChunkChangeType>();
for (Entry<PS, Faction> entry : this.oldChunkFaction.entrySet())
{
PS chunk = entry.getKey();
Faction from = entry.getValue();
currentChunkType.put(chunk, EventFactionsChunkChangeType.get(from, newFaction, self));
}
this.chunkType = Collections.unmodifiableMap(currentChunkType);
this.typeChunks = Collections.unmodifiableMap(MUtil.reverseIndex(this.chunkType));
}
}

View File

@ -1,6 +1,9 @@
package com.massivecraft.factions.integration.lwc; package com.massivecraft.factions.integration.lwc;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -14,7 +17,7 @@ import com.massivecraft.factions.Factions;
import com.massivecraft.factions.entity.Faction; import com.massivecraft.factions.entity.Faction;
import com.massivecraft.factions.entity.MConf; import com.massivecraft.factions.entity.MConf;
import com.massivecraft.factions.entity.MPlayer; import com.massivecraft.factions.entity.MPlayer;
import com.massivecraft.factions.event.EventFactionsChunkChange; import com.massivecraft.factions.event.EventFactionsChunksChange;
import com.massivecraft.factions.event.EventFactionsChunkChangeType; import com.massivecraft.factions.event.EventFactionsChunkChangeType;
import com.massivecraft.massivecore.EngineAbstract; import com.massivecraft.massivecore.EngineAbstract;
import com.massivecraft.massivecore.ps.PS; import com.massivecraft.massivecore.ps.PS;
@ -59,12 +62,9 @@ public class EngineLwc extends EngineAbstract
// LISTENER // LISTENER
// -------------------------------------------- // // -------------------------------------------- //
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void removeProtectionsOnChunkChange(Faction newFaction, EventFactionsChunkChangeType type, Set<PS> chunks)
public void removeProtectionsOnChunkChange(EventFactionsChunkChange event)
{ {
// If we are supposed to clear at this chunk change type ... // If we are supposed to clear at this chunk change type ...
Faction newFaction = event.getNewFaction();
EventFactionsChunkChangeType type = event.getType();
Boolean remove = MConf.get().lwcRemoveOnChange.get(type); Boolean remove = MConf.get().lwcRemoveOnChange.get(type);
if (remove == null) return; if (remove == null) return;
if (remove == false) return; if (remove == false) return;
@ -72,7 +72,26 @@ public class EngineLwc extends EngineAbstract
// ... then remove for all other factions than the new one. // ... then remove for all other factions than the new one.
// First we wait one tick to make sure the chunk ownership changes have been applied. // First we wait one tick to make sure the chunk ownership changes have been applied.
// Then we remove the protections but we do it asynchronously to not lock the main thread. // Then we remove the protections but we do it asynchronously to not lock the main thread.
removeAlienProtectionsAsyncNextTick(event.getChunk(), newFaction); for (PS chunk : chunks)
{
removeAlienProtectionsAsyncNextTick(chunk, newFaction);
}
}
public void removeProtectionsOnChunkChange(Faction newFaction, Map<EventFactionsChunkChangeType, Set<PS>> typeChunks)
{
for (Entry<EventFactionsChunkChangeType, Set<PS>> typeChunk : typeChunks.entrySet())
{
final EventFactionsChunkChangeType type = typeChunk.getKey();
final Set<PS> chunks = typeChunk.getValue();
removeProtectionsOnChunkChange(newFaction, type, chunks);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void removeProtectionsOnChunkChange(EventFactionsChunksChange event)
{
removeProtectionsOnChunkChange(event.getNewFaction(), event.getTypeChunks());
} }
// -------------------------------------------- // // -------------------------------------------- //

View File

@ -1,216 +0,0 @@
package com.massivecraft.factions.task;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import com.massivecraft.factions.Factions;
import com.massivecraft.massivecore.ps.PS;
/*
* reference diagram, task should move in this pattern out from chunk 0 in the center.
* 8 [>][>][>][>][>] etc.
* [^][6][>][>][>][>][>][6]
* [^][^][4][>][>][>][4][v]
* [^][^][^][2][>][2][v][v]
* [^][^][^][^][0][v][v][v]
* [^][^][^][1][1][v][v][v]
* [^][^][3][<][<][3][v][v]
* [^][5][<][<][<][<][5][v]
* [7][<][<][<][<][<][<][7]
*/
public abstract class SpiralTask implements Runnable
{
// general task-related reference data
private transient World world = null;
private transient boolean readyToGo = false;
private transient int taskID = -1;
private transient int limit = 0;
// values for the spiral pattern routine
private transient int x = 0;
private transient int z = 0;
private transient boolean isZLeg = false;
private transient boolean isNeg = false;
private transient int length = -1;
private transient int current = 0;
// @SuppressWarnings("LeakingThisInConstructor") This actually triggers a warning in Eclipse xD Could we find another way to suppress the error please? :)
public SpiralTask(PS chunk, int radius)
{
chunk = chunk.getChunk(true);
// limit is determined based on spiral leg length for given radius; see insideRadius()
this.limit = (radius - 1) * 2;
this.world = Bukkit.getWorld(chunk.getWorld());
if (this.world == null)
{
Factions.get().log(Level.WARNING, "[SpiralTask] A valid world must be specified!");
this.stop();
return;
}
this.x = (int)chunk.getChunkX();
this.z = (int)chunk.getChunkZ();
this.readyToGo = true;
// get this party started
this.setTaskID(Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(Factions.get(), this, 2, 2));
}
/*
* This is where the necessary work is done; you'll need to override this method with whatever you want
* done at each chunk in the spiral pattern.
* Return false if the entire task needs to be aborted, otherwise return true to continue.
*/
public abstract boolean work();
/*
* Returns a PS pointing at the current chunk X and Z values.
*/
public final PS currentChunk()
{
return PS.valueOf(this.world.getName(), null, null, null, null, null, null, this.x, this.z, null, null, null, null, null);
}
/*
* Returns a Location pointing at the current chunk X and Z values.
* note that the Location is at the corner of the chunk, not the center.
*/
public final Location currentLocation()
{
return new Location(world, this.x * 16, 65.0, this.z * 16);
}
/*
* Returns current chunk X and Z values.
*/
public final int getX()
{
return x;
}
public final int getZ()
{
return z;
}
/*
* Below are the guts of the class, which you normally wouldn't need to mess with.
*/
public final void setTaskID(int ID)
{
if (ID == -1)
this.stop();
taskID = ID;
}
public final void run()
{
if (!this.valid() || !readyToGo) return;
// this is set so it only does one iteration at a time, no matter how frequently the timer fires
readyToGo = false;
// make sure we're still inside the specified radius
if ( ! this.insideRadius()) return;
// track this to keep one iteration from dragging on too long and possibly choking the system
long loopStartTime = now();
// keep going until the task has been running for 20ms or more, then stop to take a breather
while (now() < loopStartTime + 20)
{
// run the primary task on the current X/Z coordinates
if ( ! this.work())
{
this.finish();
return;
}
// move on to next chunk in spiral
if ( ! this.moveToNext())
return;
}
// ready for the next iteration to run
readyToGo = true;
}
// step through chunks in spiral pattern from center; returns false if we're done, otherwise returns true
public final boolean moveToNext()
{
if ( ! this.valid()) return false;
// make sure we don't need to turn down the next leg of the spiral
if (current < length)
{
current++;
// if we're outside the radius, we're done
if ( ! this.insideRadius()) return false;
}
else
{ // one leg/side of the spiral down...
current = 0;
isZLeg ^= true;
// every second leg (between X and Z legs, negative or positive), length increases
if (isZLeg)
{
isNeg ^= true;
length++;
}
}
// move one chunk further in the appropriate direction
if (isZLeg)
z += (isNeg) ? -1 : 1;
else
x += (isNeg) ? -1 : 1;
return true;
}
public final boolean insideRadius()
{
boolean inside = current < limit;
if (!inside)
this.finish();
return inside;
}
// for successful completion
public void finish()
{
// P.p.log("SpiralTask successfully completed!");
this.stop();
}
// we're done, whether finished or cancelled
public final void stop()
{
if (!this.valid()) return;
readyToGo = false;
Bukkit.getServer().getScheduler().cancelTask(taskID);
taskID = -1;
}
// is this task still valid/workable?
public final boolean valid()
{
return taskID != -1;
}
private static long now()
{
return System.currentTimeMillis();
}
}