This commit is contained in:
Ryan Hamshire 2012-04-30 20:11:39 -07:00
parent 5037847814
commit 0bed62739c
8 changed files with 148 additions and 21 deletions

View File

@ -1,7 +1,7 @@
name: GriefPrevention
main: me.ryanhamshire.GriefPrevention.GriefPrevention
softdepend: [Vault]
version: 3.3.2
softdepend: [Vault, Multiverse-Core]
version: 3.4.1
commands:
abandonclaim:
description: Deletes a claim.
@ -89,7 +89,11 @@ commands:
deletealladminclaims:
description: Deletes all administrative claims.
usage: /DeleteAllAdminClaims
permission: adminclaims
permission: griefprevention.adminclaims
transferclaim:
description: Converts an administrative claim to a private claim.
usage: /TransferClaim <player>
permission: griefprevention.adjustclaimblocks
permissions:
griefprevention.createclaims:
description: Grants permission to create claims.

View File

@ -31,7 +31,7 @@ import org.bukkit.entity.Player;
//represents a player claim
//creating an instance doesn't make an effective claim
//only claims which have been added to the datastore have any effect
public class Claim
public class Claim
{
//two locations, which together define the boundaries of the claim
//note that the upper Y value is always ignored, because claims ALWAYS extend up to the sky
@ -553,4 +553,21 @@ public class Claim
return false;
}
//implements a strict ordering of claims, used to keep the claims collection sorted for faster searching
boolean greaterThan(Claim otherClaim)
{
Location thisCorner = this.getLesserBoundaryCorner();
Location otherCorner = otherClaim.getLesserBoundaryCorner();
if(thisCorner.getBlockX() > otherCorner.getBlockX()) return true;
if(thisCorner.getBlockX() < otherCorner.getBlockX()) return false;
if(thisCorner.getBlockZ() > otherCorner.getBlockZ()) return true;
if(thisCorner.getBlockZ() < otherCorner.getBlockZ()) return false;
return thisCorner.getWorld().getName().compareTo(otherCorner.getWorld().getName()) < 0;
}
}

View File

@ -26,6 +26,7 @@ import java.util.*;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
//singleton class which manages all GriefPrevention data (except for config options)
public class DataStore
@ -125,8 +126,13 @@ public class DataStore
else
{
topLevelClaim.modifiedDate = new Date(files[i].lastModified());
this.claims.add(topLevelClaim);
topLevelClaim.inDataStore = true;
int j = 0;
while(j < this.claims.size() && !this.claims.get(j).greaterThan(topLevelClaim)) j++;
if(j < this.claims.size())
this.claims.add(j, topLevelClaim);
else
this.claims.add(this.claims.size(), topLevelClaim);
topLevelClaim.inDataStore = true;
}
}
@ -277,7 +283,12 @@ public class DataStore
}
//add it and mark it as added
this.claims.add(newClaim);
int j = 0;
while(j < this.claims.size() && !this.claims.get(j).greaterThan(newClaim)) j++;
if(j < this.claims.size())
this.claims.add(j, newClaim);
else
this.claims.add(this.claims.size(), newClaim);
newClaim.inDataStore = true;
//except for administrative claims (which have no owner), update the owner's playerData with the new claim
@ -619,11 +630,20 @@ public class DataStore
//check cachedClaim guess first. if it's in the datastore and the location is inside it, we're done
if(cachedClaim != null && cachedClaim.inDataStore && cachedClaim.contains(location, ignoreHeight, true)) return cachedClaim;
//the claims list is ordered by greater boundary corner
//create a temporary "fake" claim in memory for comparison purposes
Claim tempClaim = new Claim();
tempClaim.lesserBoundaryCorner = location;
//otherwise, search all existing claims until we find the right claim
for(int i = 0; i < this.claims.size(); i++)
{
Claim claim = this.claims.get(i);
//if we reach a claim which is greater than the temp claim created above, there's definitely no claim
//in the collection which includes our location
if(claim.greaterThan(tempClaim)) return null;
//find a top level claim
if(claim.contains(location, ignoreHeight, false))
{
@ -848,7 +868,7 @@ public class DataStore
//ends a siege
//either winnerName or loserName can be null, but not both
public void endSiege(SiegeData siegeData, String winnerName, String loserName)
public void endSiege(SiegeData siegeData, String winnerName, String loserName, boolean death)
{
boolean grantAccess = false;
@ -930,6 +950,36 @@ public class DataStore
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 20L * 60 * 5);
}
}
//if the siege ended due to death, transfer inventory to winner
if(death)
{
Player winner = GriefPrevention.instance.getServer().getPlayer(winnerName);
Player loser = GriefPrevention.instance.getServer().getPlayer(loserName);
if(winner != null && loser != null)
{
//get loser's inventory, then clear it
ItemStack [] loserItems = loser.getInventory().getContents();
loser.getInventory().clear();
//try to add it to the winner's inventory
for(int j = 0; j < loserItems.length; j++)
{
if(loserItems[j] == null || loserItems[j].getType() == Material.AIR || loserItems[j].getAmount() == 0) continue;
HashMap<Integer, ItemStack> wontFitItems = winner.getInventory().addItem(loserItems[j]);
//drop any remainder on the ground at his feet
Object [] keys = wontFitItems.keySet().toArray();
Location winnerLocation = winner.getLocation();
for(int i = 0; i < keys.length; i++)
{
Integer key = (Integer)keys[i];
winnerLocation.getWorld().dropItemNaturally(winnerLocation, wontFitItems.get(key));
}
}
}
}
}
//timestamp for each siege cooldown to end

View File

@ -112,8 +112,11 @@ class EntityEventHandler implements Listener
//if involved in a siege
if(playerData.siegeData != null)
{
//don't drop items as usual, they will be sent to the siege winner
event.getDrops().clear();
//end it, with the dieing player being the loser
this.dataStore.endSiege(playerData.siegeData, null, player.getName());
this.dataStore.endSiege(playerData.siegeData, null, player.getName(), true /*ended due to death*/);
}
}

View File

@ -469,6 +469,56 @@ public class GriefPrevention extends JavaPlugin
return true;
}
//transferclaim <player>
else if(cmd.getName().equalsIgnoreCase("transferclaim") && player != null)
{
//requires exactly one parameter, the other player's name
if(args.length != 1) return false;
//check additional permission
if(!player.hasPermission("griefprevention.adminclaims"))
{
GriefPrevention.sendMessage(player, TextMode.Err, "That command requires the administrative claims permission.");
return true;
}
//which claim is the user in?
Claim claim = this.dataStore.getClaimAt(player.getLocation(), true, null);
if(claim == null)
{
GriefPrevention.sendMessage(player, TextMode.Instr, "There's no claim here. Stand in the administrative claim you want to transfer.");
return true;
}
else if(!claim.isAdminClaim())
{
GriefPrevention.sendMessage(player, TextMode.Err, "Only administrative claims may be transferred to a player.");
return true;
}
OfflinePlayer targetPlayer = this.resolvePlayer(args[0]);
if(targetPlayer == null)
{
GriefPrevention.sendMessage(player, TextMode.Err, "Player not found.");
return true;
}
//change ownerhsip
try
{
this.dataStore.changeClaimOwner(claim, targetPlayer.getName());
}
catch(Exception e)
{
GriefPrevention.sendMessage(player, TextMode.Instr, "Only top level claims (not subdivisions) may be transferred. Stand outside of the subdivision and try again.");
return true;
}
//confirm
GriefPrevention.sendMessage(player, TextMode.Success, "Claim transferred.");
return true;
}
//trustlist
else if(cmd.getName().equalsIgnoreCase("trustlist") && player != null)
{

View File

@ -73,6 +73,12 @@ class PlayerEventHandler implements Listener
if(!GriefPrevention.instance.config_spam_enabled) return;
//remedy any CAPS SPAM without bothering to fault the player for it
if(message.length() > 4 && !player.hasPermission("griefprevention.spam") && message.toUpperCase().equals(message))
{
event.setMessage(message.toLowerCase());
}
PlayerData playerData = this.dataStore.getPlayerData(player.getName());
boolean spam = false;
@ -166,7 +172,8 @@ class PlayerEventHandler implements Listener
{
GriefPrevention.sendMessage(player, TextMode.Warn, GriefPrevention.instance.config_spam_warningMessage);
event.setCancelled(true);
GriefPrevention.AddLogEntry("Muted spam from " + player.getName() + ".");
GriefPrevention.AddLogEntry("Warned " + player.getName() + " about spam penalties.");
GriefPrevention.AddLogEntry("Muted spam from " + player.getName() + ": " + message);
}
}
@ -273,17 +280,12 @@ class PlayerEventHandler implements Listener
player.setHealth(0);
}
//FEATURE: on logout or kick, give player the any claim blocks he may have earned for this play session
//NOTE: not all kicks are bad, for example an AFK kick or a kick to make room for an admin to log in
//that's why even kicked players get their claim blocks
//FEATURE: during a siege, any player who logs out dies and forfeits the siege
//if player was involved in a siege, he forfeits
if(playerData.siegeData != null)
{
player.setHealth(0);
this.dataStore.endSiege(playerData.siegeData, null, player.getName());
if(player.getHealth() > 0) player.setHealth(0); //might already be zero from above, this avoids a double death message
}
//disable ignore claims mode
@ -773,7 +775,7 @@ class PlayerEventHandler implements Listener
if(playerData.shovelMode == ShovelMode.RestoreNature)
{
//if the clicked block is in a claim, visualize that claim and deliver an error message
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), true, playerData.lastClaim);
Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim);
if(claim != null)
{
GriefPrevention.sendMessage(player, TextMode.Err, claim.getOwnerName() + " claimed that block.");

View File

@ -62,6 +62,7 @@ class RestoreNatureProcessingTask implements Runnable
this.notAllowedToHang = new ArrayList<Integer>();
this.notAllowedToHang.add(Material.DIRT.getId());
this.notAllowedToHang.add(Material.GRASS.getId());
this.notAllowedToHang.add(Material.LONG_GRASS.getId());
this.notAllowedToHang.add(Material.SNOW.getId());
this.notAllowedToHang.add(Material.LOG.getId());
@ -454,7 +455,7 @@ class RestoreNatureProcessingTask implements Runnable
private int highestY(int x, int z)
{
int y;
for(y = snapshots[0].length - 1; y >= 0; y--)
for(y = snapshots[0].length - 1; y > 0; y--)
{
BlockSnapshot block = this.snapshots[x][y][z];
if(block.typeId != Material.AIR.getId() &&

View File

@ -65,13 +65,13 @@ class SiegeCheckupTask implements Runnable
//otherwise attacker wins if the defender runs away
else if(attackerRemains && !defenderRemains)
{
dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName());
dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName(), false);
}
//or defender wins if the attacker leaves
else if(!attackerRemains && defenderRemains)
{
dataStore.endSiege(this.siegeData, defender.getName(), attacker.getName());
dataStore.endSiege(this.siegeData, defender.getName(), attacker.getName(), false);
}
//if they both left, but are still close together, the battle continues (check again later)
@ -83,7 +83,7 @@ class SiegeCheckupTask implements Runnable
//otherwise they both left and aren't close to each other, so call the attacker the winner (defender escaped, possibly after a chase)
else
{
dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName());
dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName(), false);
}
}