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
12 changes: 12 additions & 0 deletions src/main/java/net/tfminecraft/rpcharacters/database/Database.java
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,13 @@ private JSONObject toAccountProfessionPointsJson(Map<String, Integer> points) {
}

private void loadProfessionFields(RPCharacter character, JSONObject characterJson) {
if (characterJson.get("forfeited-profession-points") instanceof JSONObject forfeitedJson) {
for (Object key : forfeitedJson.keySet()) {
if (forfeitedJson.get(key) instanceof Number amount) {
character.addForfeitedProfessionPoints(key.toString(), amount.intValue());
}
}
}
if (!characterJson.containsKey("profession-upgrades")) {
return;
}
Expand All @@ -923,6 +930,11 @@ private void loadProfessionFields(RPCharacter character, JSONObject characterJso

@SuppressWarnings("unchecked")
private void saveProfessionFields(HashMap<String, Object> defaults, RPCharacter character) {
if (!character.getForfeitedProfessionPoints().isEmpty()) {
JSONObject forfeitedJson = new JSONObject();
forfeitedJson.putAll(character.getForfeitedProfessionPoints());
defaults.put("forfeited-profession-points", forfeitedJson);
}
if (character.getProfessionUpgrades().isEmpty()) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ public class RPCharacter {
private String birthday;

private final Set<String> professionUpgrades = new LinkedHashSet<>();
/** Points lost by removing upgrades, per lowercase profession id. They stay spent. */
private final Map<String, Integer> forfeitedProfessionPoints = new HashMap<>();
private final Map<String, Integer> extraAttributeAllocation = new HashMap<>();

private String lastLocationWorld;
Expand Down Expand Up @@ -868,8 +870,41 @@ public List<ProfessionUpgradeDefinition> resolveProfessionUpgrades() {
return resolved;
}

public Map<String, Integer> getForfeitedProfessionPoints() {
return Collections.unmodifiableMap(forfeitedProfessionPoints);
}

public void setForfeitedProfessionPoints(Map<String, Integer> points) {
forfeitedProfessionPoints.clear();
if (points != null) {
for (Map.Entry<String, Integer> entry : points.entrySet()) {
addForfeitedProfessionPoints(entry.getKey(), entry.getValue() != null ? entry.getValue() : 0);
}
}
}

public void addForfeitedProfessionPoints(String professionId, int amount) {
if (professionId == null || professionId.isBlank() || amount <= 0) {
return;
}
forfeitedProfessionPoints.merge(professionId.toLowerCase(), amount, Integer::sum);
}

public void clearForfeitedProfessionPoints() {
forfeitedProfessionPoints.clear();
}

/** Removes a held upgrade without giving its cost back to the profession's free points. */
public void forfeitProfessionUpgrade(ProfessionUpgradeDefinition upgrade) {
if (upgrade == null || !professionUpgrades.remove(upgrade.getId())) {
return;
}
addForfeitedProfessionPoints(upgrade.getProfessionId(), upgrade.getCost());
}

/** Held upgrade costs plus forfeited points; what the profession's lifetime points pay for. */
public int getSpentPointsOnProfession(String professionId) {
int spent = 0;
int spent = professionId != null ? forfeitedProfessionPoints.getOrDefault(professionId.toLowerCase(), 0) : 0;
for (ProfessionUpgradeDefinition upgrade : resolveProfessionUpgrades()) {
if (upgrade.getProfessionId().equalsIgnoreCase(professionId)) {
spent += upgrade.getCost();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St
RPTexts.send(sender, RPTexts.ERROR + "Invalid player or upgrade.");
return true;
}
removeUpgradeFromActiveCharacter(target, upgrade);
removeUpgradeFromActiveCharacter(target, upgrade, false);
RPTexts.send(sender, RPTexts.ERROR + "Removed upgrade " + upgrade.getId() + " from " + target.getName());
return true;
}
Expand Down Expand Up @@ -176,7 +176,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St
RPTexts.send(player, RPTexts.ERROR + "Nothing to confirm.");
return true;
}
removeUpgradeFromActiveCharacter(player, upgrade);
removeUpgradeFromActiveCharacter(player, upgrade, true);
RPTexts.send(player, RPTexts.ERROR + "Lost the " + RPTexts.WARN
+ upgrade.getMenuItem().getItemMeta().getDisplayName() + RPTexts.ERROR + " upgrade!");
return true;
Expand Down Expand Up @@ -207,7 +207,12 @@ public static void reapplyActiveCharacterPerms() {
}
}

public static void removeUpgradeFromActiveCharacter(Player player, ProfessionUpgradeDefinition upgrade) {
/**
* @param forfeitPoints true for a player's own removal, which keeps the upgrade's cost spent so
* removing and re-buying cannot be used to respec
*/
public static void removeUpgradeFromActiveCharacter(Player player, ProfessionUpgradeDefinition upgrade,
boolean forfeitPoints) {
PlayerData pd = PlayerManager.get(player);
if (pd == null) {
return;
Expand All @@ -217,7 +222,11 @@ public static void removeUpgradeFromActiveCharacter(Player player, ProfessionUpg
return;
}
ProfessionIntegrator.removeUpgrade(player, upgrade);
character.removeProfessionUpgrade(upgrade.getId());
if (forfeitPoints) {
character.forfeitProfessionUpgrade(upgrade);
} else {
character.removeProfessionUpgrade(upgrade.getId());
}
RPCharacters.getPlayerManager().savePlayer(player);
}

Expand Down Expand Up @@ -248,6 +257,10 @@ public static void refundActiveCharacter(Player player) {
resetActiveCharacterUpgrades(player, true);
PlayerData pd = PlayerManager.get(player);
if (pd != null) {
RPCharacter character = pd.getActiveCharacter();
if (character != null) {
character.clearForfeitedProfessionPoints();
}
pd.clearAccountProfessionPoints();
pd.setProfessionPointsInitialized(false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package net.tfminecraft.rpcharacters.professions;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import net.tfminecraft.rpcharacters.objects.RPCharacter;

class ProfessionUpgradeForfeitTest {

private final ProfessionUpgradeDefinition apprentice = upgrade("smith_1", "Blacksmith", 3);
private final ProfessionUpgradeDefinition journeyman = upgrade("smith_2", "Blacksmith", 5);

@BeforeEach
void registerUpgrades() {
ProfessionRegistry.setUpgrades(List.of(apprentice, journeyman));
}

@AfterEach
void clearRegistry() {
ProfessionRegistry.clear();
}

@Test
void aRemovedUpgradeStaysSpentOnItsProfession() {
RPCharacter character = new RPCharacter(null);
character.addProfessionUpgrade(apprentice.getId());
character.addProfessionUpgrade(journeyman.getId());

character.forfeitProfessionUpgrade(journeyman);

assertFalse(character.hasProfessionUpgrade(journeyman.getId()));
assertEquals(8, character.getSpentPointsOnProfession("blacksmith"));
assertEquals(3, character.getTotalSpentPoints(), "forfeits must not count towards the upgrade cap");
}

@Test
void rebuyingAForfeitedUpgradeCostsItsPointsAgain() {
RPCharacter character = new RPCharacter(null);
character.addProfessionUpgrade(apprentice.getId());
character.forfeitProfessionUpgrade(apprentice);
character.addProfessionUpgrade(apprentice.getId());

assertEquals(6, character.getSpentPointsOnProfession("Blacksmith"));
}

@Test
void forfeitingAnUpgradeThatIsNotHeldCostsNothing() {
RPCharacter character = new RPCharacter(null);

character.forfeitProfessionUpgrade(apprentice);

assertEquals(0, character.getSpentPointsOnProfession("blacksmith"));
assertEquals(Map.of(), character.getForfeitedProfessionPoints());
}

@Test
void anAdminRemovalStillGivesThePointsBack() {
RPCharacter character = new RPCharacter(null);
character.addProfessionUpgrade(apprentice.getId());

character.removeProfessionUpgrade(apprentice.getId());

assertEquals(0, character.getSpentPointsOnProfession("blacksmith"));
}

@Test
void clearingForfeitsRestoresTheFullRefund() {
RPCharacter character = new RPCharacter(null);
character.setForfeitedProfessionPoints(Map.of("Blacksmith", 4, "chef", 0));

assertEquals(Map.of("blacksmith", 4), character.getForfeitedProfessionPoints());
character.clearForfeitedProfessionPoints();
assertEquals(0, character.getSpentPointsOnProfession("blacksmith"));
}

private static ProfessionUpgradeDefinition upgrade(String id, String professionId, int cost) {
return new ProfessionUpgradeDefinition(id, professionId, null, cost, "perk", List.of(), List.of());
}
}
Loading