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
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.ItemStack;
import net.tfminecraft.rpcharacters.lifecycle.CharacterActivatedEvent;
import net.tfminecraft.rpcharacters.permadeath.CharacterPermakillEvent;
import net.tfminecraft.surgery.managers.SurgeryMenuManager;

public class PlayerListener implements Listener {
Expand All @@ -18,7 +21,9 @@ public PlayerListener(SurgeryMenuManager menuManager) {
this.menuManager = menuManager;
}

@EventHandler
// LOW runs before RPCharacters unloads the quitting player's characters,
// so the failure command and penalty still find the same character
@EventHandler(priority = EventPriority.LOW)
public void onPlayerQuit(PlayerQuitEvent event) {
// Fail an in-progress surgery (quitting is not a free escape from the
// failure command) and remove leftover state so nothing leaks
Expand All @@ -32,6 +37,20 @@ public void onPlayerQuit(PlayerQuitEvent event) {
menuManager.getRequestManager().forget(event.getPlayer().getUniqueId());
}

@EventHandler
public void onCharacterActivated(CharacterActivatedEvent event) {
// The operation belongs to the characters who began it; a switch or
// permakill ends it before the result can land on another character
menuManager.handleCharacterActivated(event.getOwnerUuid(), event.getCharacter().getId());
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onCharacterPermakill(CharacterPermakillEvent event) {
// The kill is certain once no listener cancelled it; the last
// character's death fires no activation, so it is handled here
menuManager.handleCharacterPermakilled(event.getPlayer().getUniqueId(), event.getCharacter().getId());
}

@EventHandler
public void onInventoryDrag(InventoryDragEvent event) {
// Block drags entirely while the surgery menu is open; drag events are not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,16 +185,46 @@ public void handleQuit(Player player) {
stateManager.cleanup(playerId);
}

// ==============================================
// The patient, or null once they have left or their character was
// switched away or permakilled, so a cure or penalty never lands on
// a different character
// ==============================================
private Player patientOf(UUID surgeonId) {
UUID patientId = stateManager.getPatientUuid(surgeonId);
return patientId == null ? null : Bukkit.getPlayer(patientId);
Player patient = patientId == null ? null : Bukkit.getPlayer(patientId);
if (patient == null || !stateManager.isOriginalPatient(surgeonId, Ailments.characterId(patient))) {
return null;
}
return patient;
}

// ==============================================
// Whether both participants are still the characters the operation began
// on; a patient who logged off counts as unchanged
// ==============================================
private boolean sameCharacters(Player surgeon) {
UUID surgeonId = surgeon.getUniqueId();
if (!stateManager.isOriginalSurgeon(surgeonId, Ailments.characterId(surgeon))) {
return false;
}
UUID patientId = stateManager.getPatientUuid(surgeonId);
Player patient = patientId == null ? null : Bukkit.getPlayer(patientId);
return patient == null
? !stateManager.hasPatientChanged(surgeonId)
: stateManager.isOriginalPatient(surgeonId, Ailments.characterId(patient));
}

// ==============================================
// Executes the configured command for surgery completion (success or failure)
// Commands name accounts, so they are skipped once either participant has
// changed character; they would land on whoever is now active
// ==============================================
private void executeCompletionCommand(Player surgeon, boolean success) {
UUID surgeonId = surgeon.getUniqueId();
if (!sameCharacters(surgeon)) {
return;
}
String patientName = stateManager.getPatientName(surgeonId);

String configKey = success ? "commands.surgery-success" : "commands.surgery-failure";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.tfminecraft.surgery.managers;

import net.tfminecraft.surgery.procedures.Ailments;
import net.tfminecraft.surgery.procedures.Complication;
import net.tfminecraft.surgery.procedures.Procedure;
import net.tfminecraft.tlibs.objects.api.ItemAPI;
Expand Down Expand Up @@ -182,7 +183,8 @@ public void processMoveEffects(Player player, SurgeryTool clickedTool) {
}

// ==============================================
// Checks the patient is still online, in the same world, and within range
// Checks the patient is still online, alive, the same character, in the
// same world, and within range
// Range gets a configurable multiplier so normal fidgeting doesn't end the surgery
// ==============================================
private boolean isPatientPresent(Player surgeon) {
Expand All @@ -191,7 +193,10 @@ private boolean isPatientPresent(Player surgeon) {
return true;
}
Player patient = Bukkit.getPlayer(patientUuid);
if (patient == null || !patient.isOnline()) {
if (patient == null || !patient.isOnline() || patient.isDead()) {
return false;
}
if (!stateManager.isOriginalPatient(surgeon.getUniqueId(), Ailments.characterId(patient))) {
return false;
}
if (!patient.getWorld().equals(surgeon.getWorld())) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package net.tfminecraft.surgery.managers;

import net.tfminecraft.rpcharacters.api.HealingInjuries.HealingInjury;
import net.tfminecraft.surgery.procedures.Ailments;
import net.tfminecraft.surgery.procedures.ProcedureRegistry;
import net.tfminecraft.tlibs.objects.api.ItemAPI;
import net.tfminecraft.tlibs.TLibs;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;

import java.util.Objects;
import java.util.UUID;

// ==============================================
// Main file for the surgery menu system
// Delegates manager files for different responsibilities
Expand Down Expand Up @@ -66,6 +70,8 @@ public void initialize() {
public void openSurgeryMenu(Player surgeon, Player patient, HealingInjury ailment) {
stateManager.setPatientName(surgeon.getUniqueId(), patient.getName());
stateManager.setPatientUuid(surgeon.getUniqueId(), patient.getUniqueId());
stateManager.setSurgeonCharacterId(surgeon.getUniqueId(), Ailments.characterId(surgeon));
stateManager.setPatientCharacterId(surgeon.getUniqueId(), Ailments.characterId(patient));
stateManager.setAilment(surgeon.getUniqueId(), ailment.traitId(), ailment.displayName(),
procedures.get(ailment.traitId()));
menuBuilder.buildAndOpenMenu(surgeon);
Expand Down Expand Up @@ -108,10 +114,62 @@ public void handleSurgeonQuit(Player player) {
// their next move to notice the patient is gone
// ==============================================
public void handlePatientQuit(Player patient) {
java.util.UUID surgeonId = stateManager.findSurgeonForPatient(patient.getUniqueId());
UUID surgeonId = stateManager.findSurgeonForPatient(patient.getUniqueId());
if (surgeonId == null) {
return;
}
failForMissingPatient(surgeonId);
}

// ==============================================
// Handles a player's active character changing
// A surgeon who changes character gives up; a patient who changes
// character has left the table, the same as quitting
// ==============================================
public void handleCharacterActivated(UUID ownerId, String characterId) {
if (stateManager.hasSession(ownerId)) {
endForSurgeonChange(ownerId);
}
UUID surgeonId = stateManager.findSurgeonForPatient(ownerId);
if (surgeonId != null && !Objects.equals(characterId, stateManager.getPatientCharacterId(surgeonId))) {
endForPatientChange(surgeonId);
}
}

// ==============================================
// Handles a character being permakilled
// Ends any operation that character is part of, including when it was
// the account's last character and no other character takes over
// ==============================================
public void handleCharacterPermakilled(UUID ownerId, String characterId) {
if (stateManager.hasSession(ownerId) && Objects.equals(characterId, stateManager.getSurgeonCharacterId(ownerId))) {
endForSurgeonChange(ownerId);
}
UUID surgeonId = stateManager.findSurgeonForPatient(ownerId);
if (surgeonId != null && Objects.equals(characterId, stateManager.getPatientCharacterId(surgeonId))) {
endForPatientChange(surgeonId);
}
}

// The surgeon gives up; an unchanged patient still takes the failure penalty
private void endForSurgeonChange(UUID surgeonId) {
stateManager.markSurgeonChanged(surgeonId);
Player surgeon = plugin.getServer().getPlayer(surgeonId);
if (surgeon == null) {
stateManager.cleanup(surgeonId);
return;
}
completionHandler.handleAbandonment(surgeon);
surgeon.closeInventory();
}

// The patient's character is gone, so nothing lands on the account
private void endForPatientChange(UUID surgeonId) {
stateManager.markPatientChanged(surgeonId);
failForMissingPatient(surgeonId);
}

private void failForMissingPatient(UUID surgeonId) {
Player surgeon = plugin.getServer().getPlayer(surgeonId);
if (surgeon != null && surgeon.isOnline()) {
completionHandler.failSurgery(surgeon, uiUpdater.getMessage("failure-patient-left",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

// ==============================================
Expand All @@ -19,6 +20,12 @@ public class SurgeryStateManager {
private static class SurgerySession {
String patientName;
UUID patientUuid;
// Both participants' characters when the operation began
String surgeonCharacterId;
String patientCharacterId;
// Set once that participant's character was switched away or permakilled
Boolean surgeonChanged;
Boolean patientChanged;
String traitId;
String ailmentName;
Procedure procedure;
Expand Down Expand Up @@ -89,6 +96,8 @@ private SurgerySession get(UUID playerId) {
public int getRedTempCounter(UUID playerId) { SurgerySession s = get(playerId); return s == null || s.redTempCounter == null ? 0 : s.redTempCounter; }
public String getPatientName(UUID playerId) { SurgerySession s = get(playerId); return s == null || s.patientName == null ? "Unknown" : s.patientName; }
public UUID getPatientUuid(UUID playerId) { SurgerySession s = get(playerId); return s == null ? null : s.patientUuid; }
public String getPatientCharacterId(UUID playerId) { SurgerySession s = get(playerId); return s == null ? null : s.patientCharacterId; }
public String getSurgeonCharacterId(UUID playerId) { SurgerySession s = get(playerId); return s == null ? null : s.surgeonCharacterId; }
public boolean hasSession(UUID playerId) { return sessions.containsKey(playerId); }

// ==============================================
Expand All @@ -107,6 +116,22 @@ public UUID findSurgeonForPatient(UUID patientUuid) {
return null;
}

// ==============================================
// Whether a participant is still the character the operation began on,
// given their current character ID
// ==============================================
public boolean isOriginalSurgeon(UUID playerId, String characterId) {
SurgerySession s = get(playerId);
return s != null && !Boolean.TRUE.equals(s.surgeonChanged) && Objects.equals(s.surgeonCharacterId, characterId);
}

public boolean isOriginalPatient(UUID playerId, String characterId) {
SurgerySession s = get(playerId);
return s != null && !Boolean.TRUE.equals(s.patientChanged) && Objects.equals(s.patientCharacterId, characterId);
}

public boolean hasPatientChanged(UUID playerId) { SurgerySession s = get(playerId); return s != null && Boolean.TRUE.equals(s.patientChanged); }

// ==============================================
// Setters
// ==============================================
Expand Down Expand Up @@ -141,6 +166,10 @@ public void setAilment(UUID playerId, String traitId, String ailmentName, Proced
public void setRedTempCounter(UUID playerId, int count) { session(playerId).redTempCounter = count; }
public void setPatientName(UUID playerId, String name) { session(playerId).patientName = name; }
public void setPatientUuid(UUID playerId, UUID patientUuid) { session(playerId).patientUuid = patientUuid; }
public void setPatientCharacterId(UUID playerId, String characterId) { session(playerId).patientCharacterId = characterId; }
public void setSurgeonCharacterId(UUID playerId, String characterId) { session(playerId).surgeonCharacterId = characterId; }
public void markSurgeonChanged(UUID playerId) { SurgerySession s = get(playerId); if (s != null) { s.surgeonChanged = true; } }
public void markPatientChanged(UUID playerId) { SurgerySession s = get(playerId); if (s != null) { s.patientChanged = true; } }
public void removeCollapseCountdown(UUID playerId) { SurgerySession s = get(playerId); if (s != null) { s.collapseCountdown = null; } }

// ==============================================
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/net/tfminecraft/surgery/procedures/Ailments.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import net.tfminecraft.rpcharacters.api.HealingInjuries;
import net.tfminecraft.rpcharacters.api.HealingInjuries.HealingInjury;
import net.tfminecraft.rpcharacters.managers.PlayerManager;
import net.tfminecraft.rpcharacters.objects.PlayerData;
import net.tfminecraft.rpcharacters.objects.RPCharacter;
import org.bukkit.entity.Player;

import java.util.Comparator;
Expand Down Expand Up @@ -41,4 +44,11 @@ public static boolean cure(Player patient, String traitId) {
public static long extend(Player patient, String traitId, long extraMs) {
return HealingInjuries.extend(patient, traitId, extraMs);
}

// The patient's active character ID, or null without one
public static String characterId(Player patient) {
PlayerData data = PlayerManager.get(patient);
RPCharacter character = data == null ? null : data.getActiveCharacter();
return character == null ? null : character.getId();
}
}
Loading