Skip to content
Closed
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
12 changes: 12 additions & 0 deletions plugins/RPCharacters/logs/m/Mailer.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[2026-10-11 12:38] Set the status of the character Recipient 7e4aaaef-10cb-44f8-97aa-032add923e6a to DEAD
[2026-10-11 12:38] Set the status of the character Recipient 7e4aaaef-10cb-44f8-97aa-032add923e6a to ALIVE
[2026-10-11 12:38] Set the status of the character New name to DEAD
[2026-10-11 12:38] Set the status of the character New name to ALIVE
[2026-10-11 13:26] Set the status of the character Recipient 111a7683-92d2-4940-ae3c-c0798ba43fcb to DEAD
[2026-10-11 13:26] Set the status of the character Recipient 111a7683-92d2-4940-ae3c-c0798ba43fcb to ALIVE
[2026-10-11 13:26] Set the status of the character New name to DEAD
[2026-10-11 13:26] Set the status of the character New name to ALIVE
[2026-10-11 13:31] Set the status of the character Recipient 5f350894-20c0-49fb-9f7d-ae0bd5c77069 to DEAD
[2026-10-11 13:31] Set the status of the character Recipient 5f350894-20c0-49fb-9f7d-ae0bd5c77069 to ALIVE
[2026-10-11 13:31] Set the status of the character New name to DEAD
[2026-10-11 13:31] Set the status of the character New name to ALIVE
Comment on lines +1 to +12

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

3 changes: 3 additions & 0 deletions plugins/RPCharacters/logs/p/Player.txt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we commiting this?

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[2026-10-11 12:38] Set the status of the character Character to DEAD
[2026-10-11 13:26] Set the status of the character Character to DEAD
[2026-10-11 13:31] Set the status of the character Character to DEAD
3 changes: 3 additions & 0 deletions plugins/RPCharacters/logs/p/Player0.txt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we commiting this?

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[2026-10-11 12:38] Added clue to Aria: A footprint
[2026-10-11 13:27] Added clue to Aria: A footprint
[2026-10-11 13:32] Added clue to Aria: A footprint
15 changes: 15 additions & 0 deletions plugins/RPCharacters/logs/s/Subject.txt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we commiting this?

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[2026-10-11 12:37] Set the status of the character Aria to DEAD
[2026-10-11 12:37] Set the status of the character Aria to ALIVE
[2026-10-11 12:37] Set the status of the character Aria to DEAD
[2026-10-11 12:37] Set the status of the character Aria to DEAD
[2026-10-11 12:37] Set the status of the character Dead to DEAD
[2026-10-11 13:26] Set the status of the character Aria to DEAD
[2026-10-11 13:26] Set the status of the character Aria to ALIVE
[2026-10-11 13:26] Set the status of the character Aria to DEAD
[2026-10-11 13:26] Set the status of the character Aria to DEAD
[2026-10-11 13:26] Set the status of the character Dead to DEAD
[2026-10-11 13:30] Set the status of the character Aria to DEAD
[2026-10-11 13:30] Set the status of the character Aria to ALIVE
[2026-10-11 13:30] Set the status of the character Aria to DEAD
[2026-10-11 13:30] Set the status of the character Aria to DEAD
[2026-10-11 13:30] Set the status of the character Dead to DEAD
30 changes: 30 additions & 0 deletions plugins/RPCharacters/logs/v/Victim.txt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we commiting this?

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[2026-10-11 12:38] Set the status of the character Aria to DEAD
[2026-10-11 12:38] Set the status of the character Aria to DEAD
[2026-10-11 12:38] Set the status of the character Aria to ALIVE
[2026-10-11 12:38] Set the status of the character Aria to DEAD
[2026-10-11 12:38] Set the status of the character Aria to DEAD
[2026-10-11 12:38] Set the status of the character Aria to ALIVE
[2026-10-11 12:38] Set the status of the character Aria to DEAD
[2026-10-11 12:38] Set the status of the character Aria to ALIVE
[2026-10-11 12:38] Set the status of the character Aria to DEAD
[2026-10-11 12:38] Set the status of the character Aria to DEAD
[2026-10-11 13:27] Set the status of the character Aria to DEAD
[2026-10-11 13:27] Set the status of the character Aria to DEAD
[2026-10-11 13:27] Set the status of the character Aria to ALIVE
[2026-10-11 13:27] Set the status of the character Aria to DEAD
[2026-10-11 13:27] Set the status of the character Aria to DEAD
[2026-10-11 13:27] Set the status of the character Aria to ALIVE
[2026-10-11 13:27] Set the status of the character Aria to DEAD
[2026-10-11 13:27] Set the status of the character Aria to ALIVE
[2026-10-11 13:27] Set the status of the character Aria to DEAD
[2026-10-11 13:27] Set the status of the character Aria to DEAD
[2026-10-11 13:31] Set the status of the character Aria to DEAD
[2026-10-11 13:31] Set the status of the character Aria to DEAD
[2026-10-11 13:31] Set the status of the character Aria to ALIVE
[2026-10-11 13:31] Set the status of the character Aria to DEAD
[2026-10-11 13:31] Set the status of the character Aria to DEAD
[2026-10-11 13:31] Set the status of the character Aria to ALIVE
[2026-10-11 13:31] Set the status of the character Aria to DEAD
[2026-10-11 13:31] Set the status of the character Aria to ALIVE
[2026-10-11 13:31] Set the status of the character Aria to DEAD
[2026-10-11 13:31] Set the status of the character Aria to DEAD
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<argLine></argLine>
<simplefactions.version>3.0.1</simplefactions.version>
<simplefactions.version>3.16.0</simplefactions.version>
<tlibs.version>2.0.0</tlibs.version>
<vehicleframework.version>2.7.0</vehicleframework.version>
<worldguard.version>7.0.9</worldguard.version>
Expand Down
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 @@ -287,9 +287,11 @@ public void onEnable() {
partyChatRecipientResolver);
realmChatDependencyListener.registerIfPresent();
registerPlaceholderApi();
net.tfminecraft.rpcharacters.plots.Plots.start(this);
}
@Override
public void onDisable() {
net.tfminecraft.rpcharacters.plots.Plots.stop();
CodexPopulationService.stop();
if (focusModule != null) focusModule.shutdown();
ChatRecipientResolverRegistry.unregister(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String
RPTexts.send(p, RPTexts.ERROR + "You don't have a free character slot!");
return true;
}
if (net.tfminecraft.rpcharacters.plots.PlotHunt.locked(p)) {
net.tfminecraft.rpcharacters.plots.PlotMenu.sendLocked(p);
return true;
}
CreationManager.initiateCreation(p);
return true;
} else if(cmd.getName().equalsIgnoreCase(cmd1) && args[0].equalsIgnoreCase("next") && args.length == 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public class InventoryManager {
private static final String SUMMARY_ACTION_KEY = "summary_action";
public static final String OPEN_CLASS_PICK_KEY = "open_class_pick";
public static final int CLASS_PICK_SLOT = 22;
public static final int SWITCH_SLOT = 6, INTRIGUE_SLOT = 20, PLOTS_SLOT = 24;

private static String t(String raw) {
return RPTexts.formatGui(raw);
Expand Down Expand Up @@ -99,6 +100,13 @@ public void characterView(Player p, RPCharacter c) {
if (c.getStatus().equals(Status.ALIVE) && c.isActive() && c.getOwner().equals(p) && ClassPickService.isEnabled()) {
i.setItem(CLASS_PICK_SLOT, getClassPickItem(c));
}
if (c.getStatus().equals(Status.ALIVE) && c.getOwner().equals(p)) {
i.setItem(INTRIGUE_SLOT, net.tfminecraft.rpcharacters.plots.PlotMenu.intrigueFor(p, c));
if (c.isActive()) i.setItem(PLOTS_SLOT, net.tfminecraft.rpcharacters.plots.PlotMenu.button());
// A character hunted by a plot they know of must face it: no switching away.
if (i.getItem(SWITCH_SLOT) != null && net.tfminecraft.rpcharacters.plots.PlotHunt.locked(p))
i.setItem(SWITCH_SLOT, net.tfminecraft.rpcharacters.plots.PlotMenu.lockedItem());
}
int slotn = 0;
while(slotn < i.getSize()) {
if(i.getItem(slotn) == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,11 @@ public void confirmClick(Player p, RPCharacter c, ConfirmType t) {
p.closeInventory();
return;
}
if (net.tfminecraft.rpcharacters.plots.PlotHunt.locked(p)) {
net.tfminecraft.rpcharacters.plots.PlotMenu.sendLocked(p);
p.closeInventory();
return;
}
PlayerData pd = get(p);
pd.setActiveCharacter(c);
net.tfminecraft.rpcharacters.clues.discovery.ClueDiscoveryVisualManager.get().refreshViewer(p);
Expand Down Expand Up @@ -587,6 +592,10 @@ public void selectionClick(InventoryClickEvent e) {
}
if(i.getType().equals(Material.YELLOW_CONCRETE)) {
if(!p.equals(o)) return;
if (net.tfminecraft.rpcharacters.plots.PlotHunt.locked(p)) {
net.tfminecraft.rpcharacters.plots.PlotMenu.sendLocked(p);
return;
}
if (!CharacterSlotService.isSlotUnlocked(o, slotIndex)) {
RPTexts.send(p, RPTexts.ERROR + "This character slot is locked.");
p.playSound(p.getLocation(), Sound.ENTITY_VILLAGER_NO, 1f, 1f);
Expand Down Expand Up @@ -679,6 +688,10 @@ public void selectionClick(InventoryClickEvent e) {
last.put(p, c);
InventoryManager inv = new InventoryManager();
inv.confirmView(p);
} else if (e.getSlot() == InventoryManager.PLOTS_SLOT) {
ItemStack clicked = e.getCurrentItem();
if (clicked == null || clicked.getType() != Material.IRON_SWORD || !p.equals(o)) return;
net.tfminecraft.rpcharacters.plots.PlotMenu.open(p);
} else if (e.getSlot() == InventoryManager.CLASS_PICK_SLOT) {
ItemStack clicked = e.getCurrentItem();
if (clicked == null || clicked.getItemMeta() == null || !p.equals(o) || !clicked.getItemMeta()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package net.tfminecraft.rpcharacters.plots;

/** The target's own Spymaster or confidant sitting inside the plot. */
public enum Betrayal { NONE, CONFIDANT, SPYMASTER }
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package net.tfminecraft.rpcharacters.plots;

/** What a collaborator works on, chosen by the plot master. */
public enum CollaboratorTask {
HIDE("hide", "Hide the plot", "Secrecy falls more slowly."),
PROGRESS("progress", "Progress the plot", "The plot bar fills faster."),
STRENGTH("strength", "Strengthen the plot", "Adds the most plot strength.");

private final String key, label, effect;

CollaboratorTask(String key, String label, String effect) {
this.key = key;
this.label = label;
this.effect = effect;
}

public String key() { return key; }
public String label() { return label; }
public String effect() { return effect; }

public static CollaboratorTask parse(String text) {
for (CollaboratorTask task : values()) if (task.key.equalsIgnoreCase(text)) return task;
return null;
}
}
14 changes: 14 additions & 0 deletions src/main/java/net/tfminecraft/rpcharacters/plots/Defender.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.tfminecraft.rpcharacters.plots;

/** Who rolls against plots aimed at a character: the faction Spymaster, a personal confidant, or themselves. */
public enum Defender {
SELF("Yourself"),
CONFIDANT("Your confidant"),
SPYMASTER("Your Spymaster");

private final String label;

Defender(String label) { this.label = label; }

public String label() { return label; }
}
93 changes: 93 additions & 0 deletions src/main/java/net/tfminecraft/rpcharacters/plots/Intrigue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package net.tfminecraft.rpcharacters.plots;

import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.random.RandomGenerator;
import java.util.function.Supplier;
import org.bukkit.entity.Player;

/**
* Every character's Intrigue: one roll for life with the Spymaster formula. Rolls a character already
* made as Spymaster in SimpleFactions are imported and kept.
*/
public final class Intrigue {
static final Map<String, Double> WEIGHTS = Map.of(
"intelligence", 3.0, "wisdom", 2.5, "charisma", 1.5, "dexterity", 1.0, "constitution", -2.0, "strength", -2.0);
static Supplier<RandomGenerator> random = ThreadLocalRandom::current;
private static final Map<String, Integer> values = new LinkedHashMap<>();
private static Path file;

private Intrigue() {}

/** Loads this plugin's rolls, then adds any Spymaster roll SimpleFactions has that this file lacks. */
public static void load(Path own, Path simpleFactions) throws IOException {
values.clear();
file = own;
values.putAll(read(own));
boolean imported = false;
if (simpleFactions != null && Files.exists(simpleFactions))
for (var entry : read(simpleFactions).entrySet()) imported |= values.putIfAbsent(entry.getKey(), entry.getValue()) == null;
if (imported) save();
}

private static Map<String, Integer> read(Path path) throws IOException {
Map<String, Integer> result = new LinkedHashMap<>();
if (!Files.exists(path)) return result;
Map<String, Integer> loaded = PlotStore.GSON.fromJson(Files.readString(path), new TypeToken<Map<String, Integer>>() {}.getType());
if (loaded == null) throw new IOException("Empty Intrigue file: " + path);
loaded.forEach((id, value) -> {
if (id != null && value != null) result.put(id, PlotMath.clamp(value));
});
return result;
}

static void save() throws IOException {
Files.createDirectories(file.toAbsolutePath().getParent());
Path pending = file.resolveSibling(file.getFileName() + ".tmp");
Files.writeString(pending, PlotStore.GSON.toJson(values));
try { Files.move(pending, file, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); }
catch (AtomicMoveNotSupportedException ignored) {
Files.move(pending, file, StandardCopyOption.REPLACE_EXISTING);
}
}

/** The character's Intrigue, or null until they have rolled. */
public static Integer known(String characterId) {
return characterId == null ? null : values.get(characterId);
}

/** The player's active character's Intrigue, rolled now if this is the first time. */
public static int of(Player player, String characterId) throws IOException {
Integer known = values.get(characterId);
if (known != null) return known;
int rolled = PlotMath.clamp(roll(PlotCharacters.attributes(player)));
values.put(characterId, rolled);
try {
save();
} catch (IOException error) {
values.remove(characterId);
throw error;
}
return rolled;
}

/** SimpleFactions' configured formula when it is installed, otherwise the same formula with its defaults. */
static int roll(Map<String, Integer> attributes) {
Integer configured = PlotFactions.intrigueRoll(attributes, random.get());
return configured != null ? configured : defaultRoll(attributes, random.get());
}

static int defaultRoll(Map<String, Integer> attributes, RandomGenerator random) {
double score = 50;
for (var weight : WEIGHTS.entrySet())
score += (Math.max(0, Math.min(16, attributes.getOrDefault(weight.getKey(), 0))) - 6) * weight.getValue();
return PlotMath.clamp((int) Math.round(score) + random.nextInt(-20, 21));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package net.tfminecraft.rpcharacters.plots;

import java.util.ArrayList;
import java.util.List;

/** A character's plot approach, personal confidant and chosen investigators. */
public final class IntrigueSettings {
public PlotApproach approach = PlotApproach.NEUTRAL;
public long approachChangedAt;
/** The character who watches over this one; they serve only one character. */
public PlotCharacter confidant;
public long confidantChangedAt;
/** Characters who investigate plots this character has learned of. */
public List<PlotCharacter> investigators = new ArrayList<>();

PlotApproach approach() { return approach == null ? PlotApproach.NEUTRAL : approach; }
}
Loading
Loading