Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/net/tfminecraft/rpcharacters/RPCharacters.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
import net.tfminecraft.rpcharacters.pvp.ArmourDonning;
import net.tfminecraft.rpcharacters.pvp.DuelCommand;
import net.tfminecraft.rpcharacters.pvp.Duels;
import net.tfminecraft.rpcharacters.pvp.KnockoutRestrictions;
import net.tfminecraft.rpcharacters.pvp.PvpCommand;
import net.tfminecraft.rpcharacters.pvp.PvpKnockoutManager;
import net.tfminecraft.rpcharacters.pvp.PvpStrikeService;
Expand Down Expand Up @@ -442,6 +443,7 @@ public void registerListeners() {
getServer().getPluginManager().registerEvents(wardrobeListener, this);
getServer().getPluginManager().registerEvents(rpInjureListener, this);
getServer().getPluginManager().registerEvents(pvpKnockoutManager, this);
getServer().getPluginManager().registerEvents(new KnockoutRestrictions(pvpKnockoutManager::isDown), this);
getServer().getPluginManager().registerEvents(pvpCommand, this);
getServer().getPluginManager().registerEvents(duelCommand, this);
getServer().getPluginManager().registerEvents(new ArmourDonning(), this);
Expand Down
16 changes: 0 additions & 16 deletions src/main/java/net/tfminecraft/rpcharacters/database/Database.java
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,6 @@ private void loadCharacter(PlayerData pd, SavedCharacter saved) {
loadExtraAttributeAllocation(c, json);
loadTraitState(c, json);
loadLastLocation(c, json);
loadPvpLethal(c, json);
CharacterEvilRpFields.load(c, json);
CharacterStageChangeFields.load(c, json);
CharacterClassPickFields.load(c, json);
Expand Down Expand Up @@ -594,7 +593,6 @@ private boolean trySaveCharacter(PlayerData pd, RPCharacter c, boolean durable)
saveExtraAttributeAllocation(defaults, c);
saveTraitState(defaults, c);
saveLastLocation(defaults, c);
defaults.put("pvp-lethal", String.valueOf(c.isPvpLethal()));
CharacterEvilRpFields.save(defaults, c);
CharacterStageChangeFields.save(defaults, c);
CharacterClassPickFields.save(defaults, c);
Expand Down Expand Up @@ -796,20 +794,6 @@ private void loadLastLocation(RPCharacter character, JSONObject characterJson) {
((Number) zRaw).doubleValue());
}

private void loadPvpLethal(RPCharacter character, JSONObject characterJson) {
if (characterJson == null || !characterJson.containsKey("pvp-lethal")) {
return;
}
Object raw = characterJson.get("pvp-lethal");
if (raw instanceof Boolean bool) {
character.setPvpLethal(bool.booleanValue());
return;
}
if (raw != null) {
character.setPvpLethal(Boolean.parseBoolean(String.valueOf(raw)));
}
}

private void loadNutritionFields(RPCharacter character, JSONObject characterJson) {
if (characterJson.containsKey("food-value")) {
character.setFoodValue(((Number) characterJson.get("food-value")).intValue());
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/net/tfminecraft/rpcharacters/grave/Grave.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public final class Grave {
private String killerDisplay;
private boolean protect;
private boolean locked;
/** Left by a character a strike killed: only the killer (or an admin) may open it. */
private boolean killerOnly;
private final long created;
private int experience;
private final ItemStack[] storage;
Expand Down Expand Up @@ -153,6 +155,14 @@ public void setLocked(boolean locked) {
this.locked = locked;
}

public boolean isKillerOnly() {
return killerOnly;
}

public void setKillerOnly(boolean killerOnly) {
this.killerOnly = killerOnly;
}

public long getCreated() {
return created;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public boolean onCommand(CommandSender sender, Command command, String label, St
GraveRecover.sendMessage(player, GraveLoader.getMessageUnlockNone());
return true;
}
if (grave.isKillerOnly()) {
GraveRecover.sendMessage(player, GraveLoader.getMessageUnlockKillerOnly());
return true;
}
if (!grave.isLocked()) {
GraveRecover.sendMessage(player, GraveLoader.getMessageUnlockAlready());
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ public void onPlayerDeath(PlayerDeathEvent event) {
Player killerPlayer = killerId != null ? Bukkit.getPlayer(killerId) : null;
boolean killedByOther = killerId != null && !killerId.equals(victim.getUniqueId());
boolean killerCanLoot = killerPlayer != null && killedByOther && GraveLootRules.canLootGrave(killerPlayer);
boolean unlocked = context.inPvpStart() && killedByOther && context.evilVictim();
if (PvpStartDeathPolicy.keepInventory(context.inPvpStart(), killedByOther, killerCanLoot, unlocked)) {
// A strike killed the character: everything goes in a grave only the killer can open.
boolean killerOnly = context.characterDies() && killedByOther;
boolean unlocked = !killerOnly && context.inPvpStart() && killedByOther && context.evilVictim();
if (!killerOnly && PvpStartDeathPolicy.keepInventory(context.inPvpStart(), killedByOther, killerCanLoot, unlocked)) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
// Set on MONITOR, after DenarEconomy's NORMAL handler has dropped the pouch.
keepInventory.add(victim.getUniqueId());
return;
Expand All @@ -66,7 +68,8 @@ public void onPlayerDeath(PlayerDeathEvent event) {
offhand = stripKept(storage, armor, offhand, stash);
ItemStack[] offhandSlot = new ItemStack[] { offhand };
ItemStack bundledTicket = null;
if (GraveLoader.isInsuranceEnabled() && GraveInsuranceTickets.bindableSlot(stash) == null) {
// Insurance can't recover a killer-only grave, so a ticket in a bundle stays an ordinary item there.
if (!killerOnly && GraveLoader.isInsuranceEnabled() && GraveInsuranceTickets.bindableSlot(stash) == null) {
bundledTicket = GraveInsuranceExtract.pull(storage, armor, offhandSlot, stash.values());
offhand = offhandSlot[0];
}
Expand Down Expand Up @@ -97,13 +100,19 @@ public void onPlayerDeath(PlayerDeathEvent event) {
if (grave == null) {
return;
}
if (unlocked) {
if (killerOnly) {
grave.setKillerOnly(true);
grave.flush();
KillerOnlyGraves.remember(victim.getUniqueId(), grave.getId());
} else if (unlocked) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
// Evil RP victims killed in a PvP start leave their items for anyone to take.
grave.setLocked(false);
grave.flush();
}

GraveInsuranceTickets.Binding binding = GraveInsuranceTickets.bind(stash, grave);
// Loose tickets stay unbound and come back at respawn rather than tying up a grave its owner can't open.
GraveInsuranceTickets.Binding binding = killerOnly ? GraveInsuranceTickets.Binding.NONE
: GraveInsuranceTickets.bind(stash, grave);
if (!binding.bound() && bundledTicket != null) {
ItemStack bound = GraveInsuranceTickets.bindTicket(bundledTicket, grave);
if (bound != null) {
Expand All @@ -116,7 +125,8 @@ public void onPlayerDeath(PlayerDeathEvent event) {
if (!stash.isEmpty()) {
excludedStash.put(victim.getUniqueId(), stash);
}
sendPlaced(victim, chestBlock, unlocked, binding.bound());
sendPlaced(victim, chestBlock, killerOnly ? GraveLoader.getMessageKillerOnly()
: unlocked ? GraveLoader.getMessageUnlockedByStrike() : GraveLoader.getMessageUnlockHint(), binding.bound());
event.setDroppedExp(0);
event.getDrops().clear();
}
Expand All @@ -136,6 +146,7 @@ public void keepItemsAfterPouchDrop(PlayerDeathEvent event) {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerRespawn(PlayerRespawnEvent event) {
Player player = event.getPlayer();
KillerOnlyGraves.forget(player.getUniqueId());
Map<Integer, ItemStack> stashed = excludedStash.remove(player.getUniqueId());
if (stashed != null && !stashed.isEmpty()) {
PlayerInventory inventory = player.getInventory();
Expand Down Expand Up @@ -246,7 +257,7 @@ private static ItemStack cloneItem(ItemStack item) {
return item != null ? item.clone() : null;
}

private void sendPlaced(Player player, Block chest, boolean unlocked, boolean insured) {
private void sendPlaced(Player player, Block chest, String hint, boolean insured) {
List<String> notices = new ArrayList<>();
String template = GraveLoader.getMessagePlaced();
if (template != null && !template.isBlank()) {
Expand All @@ -258,7 +269,6 @@ private void sendPlaced(Player player, Block chest, boolean unlocked, boolean in
.replace("{world}", world);
notices.add(StringFormatter.formatHex(text.replace('&', '\u00A7')));
}
String hint = unlocked ? GraveLoader.getMessageUnlockedByStrike() : GraveLoader.getMessageUnlockHint();
if (hint != null && !hint.isBlank()) {
notices.add(StringFormatter.formatHex(hint.replace('&', '\u00A7')));
}
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/net/tfminecraft/rpcharacters/grave/GraveLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public final class GraveLoader implements LoaderInterface {
private static String messageUnlockedByStrike = "&cYou were in an evil RP session, so your grave is unlocked. Anyone can loot it.";
private static String messageUnlockNone = "&eYou have no grave to unlock.";
private static String messageUnlockAlready = "&eYour grave is already unlocked.";
private static String messageKillerOnly = "&cYour character died from a strike. Only your killer can open this grave.";
private static String messageUnlockKillerOnly = "&cOnly the killer can open this grave. It can't be unlocked.";
private static String messageUnlockSuccess = "&aYour grave is now unlocked. Anyone can loot it.";
private static String messageRobHint = "&aRight click to rob";

Expand Down Expand Up @@ -100,6 +102,8 @@ public void load(File configFile) {
messageUnlockedByStrike = config.getString("messages.unlocked-evil-rp", "&cYou were in an evil RP session, so your grave is unlocked. Anyone can loot it.");
messageUnlockNone = config.getString("messages.unlock-none", "&eYou have no grave to unlock.");
messageUnlockAlready = config.getString("messages.unlock-already", "&eYour grave is already unlocked.");
messageKillerOnly = config.getString("messages.killer-only", "&cYour character died from a strike. Only your killer can open this grave.");
messageUnlockKillerOnly = config.getString("messages.unlock-killer-only", "&cOnly the killer can open this grave. It can't be unlocked.");
messageUnlockSuccess = config.getString("messages.unlock-success", "&aYour grave is now unlocked. Anyone can loot it.");
messageRobHint = config.getString("messages.rob-hint", "&aRight click to rob");
}
Expand Down Expand Up @@ -254,6 +258,14 @@ public static String getMessageUnlockedByStrike() {
return messageUnlockedByStrike;
}

public static String getMessageKillerOnly() {
return messageKillerOnly;
}

public static String getMessageUnlockKillerOnly() {
return messageUnlockKillerOnly;
}

public static String getMessageUnlockHint() {
return messageUnlockHint;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ public static boolean canRecover(Player player, Grave grave) {
if (player == null || grave == null) {
return false;
}
if (grave.isOwner(player.getUniqueId()) || player.hasPermission(ADMIN_PERMISSION)) {
if (player.hasPermission(ADMIN_PERMISSION)) {
return true;
}
return !grave.isLocked();
if (grave.isKillerOnly()) {
return player.getUniqueId().equals(grave.getKiller());
}
return grave.isOwner(player.getUniqueId()) || !grave.isLocked();
}

/** Whether this player could take items from a grave they had just made by killing someone. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ private static GraveRecord toRecord(Grave grave) throws IOException {
record.killerDisplay = grave.getKillerDisplay();
record.protect = grave.isProtected();
record.locked = grave.isLocked();
record.killerOnly = grave.isKillerOnly() ? Boolean.TRUE : null;
record.created = grave.getCreated();
record.experience = grave.getExperience();
record.hologramUuid = null;
Expand All @@ -615,7 +616,7 @@ private static Grave fromRecord(GraveRecord record) {
return null;
}
Location block = new Location(world, record.x, record.y, record.z);
return new Grave(
Grave grave = new Grave(
UUID.fromString(record.id),
UUID.fromString(record.owner),
record.characterOwner != null ? UUID.fromString(record.characterOwner) : null,
Expand All @@ -631,6 +632,8 @@ private static Grave fromRecord(GraveRecord record) {
decodeList(record.extras),
parseUuid(record.hologramUuid),
block);
grave.setKillerOnly(Boolean.TRUE.equals(record.killerOnly));
return grave;
}

private static UUID parseUuid(String raw) {
Expand Down Expand Up @@ -717,6 +720,7 @@ private static final class GraveRecord {
String killerDisplay;
boolean protect;
Boolean locked;
Boolean killerOnly;
long created;
int experience;
String hologramUuid;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package net.tfminecraft.rpcharacters.grave;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
* The grave listener runs before the strike, so it marks a grave killer-only from the
* strike it expects to kill. The strike then settles it: if the kill did not happen (a
* cancelled permakill), the grave goes back to an ordinary locked grave its owner can open.
*/
public final class KillerOnlyGraves {

private static final Map<UUID, UUID> awaitingStrike = new ConcurrentHashMap<>();

private KillerOnlyGraves() {
}

static void remember(UUID victimId, UUID graveId) {
awaitingStrike.put(victimId, graveId);
}

/** Called once the strike for this death has landed. */
public static void settle(UUID victimId, boolean characterDied) {
UUID graveId = awaitingStrike.remove(victimId);
if (graveId == null || characterDied) {
return;
}
Grave grave = GraveManager.get().getById(graveId);
if (grave == null) {
return;
}
grave.setKillerOnly(false);
grave.flush();
}

/** A respawn ends the death; a strike that never came leaves the grave as it is. */
static void forget(UUID victimId) {
awaitingStrike.remove(victimId);
}
}
Loading
Loading