From 36e8bc1467adaecc85b83e09241bfbc51a7ec793 Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Wed, 15 Apr 2026 06:38:45 -0700 Subject: [PATCH] Implement trekker honor titles - Setting affinity via commands will not add new honor titles --- src/main/java/emu/nebula/GameConstants.java | 4 +- .../java/emu/nebula/command/CommandArgs.java | 3 +- .../data/resources/AffinityLevelDef.java | 12 ++++- .../nebula/data/resources/CharacterDef.java | 6 +++ .../emu/nebula/data/resources/HonorDef.java | 48 ++++++++++++++----- .../nebula/game/character/GameCharacter.java | 18 +++++-- .../emu/nebula/game/inventory/Inventory.java | 48 ++++++++++++++++++- .../java/emu/nebula/game/player/Player.java | 2 +- 8 files changed, 119 insertions(+), 22 deletions(-) diff --git a/src/main/java/emu/nebula/GameConstants.java b/src/main/java/emu/nebula/GameConstants.java index 1737d53..fbd354b 100644 --- a/src/main/java/emu/nebula/GameConstants.java +++ b/src/main/java/emu/nebula/GameConstants.java @@ -26,7 +26,7 @@ public class GameConstants { public static final int JOINT_DRILL_TICKET_ID = 36; public static final int MAX_ENERGY = 240; - public static final int ENERGY_REGEN_TIME = 360; // Seconds + public static final int ENERGY_REGEN_TIME = 360; // Timeunit = Seconds public static final int CHARACTER_MAX_GEMS_PER_SLOT = 4; public static final int CHARACTER_MAX_GEM_PRESETS = 3; @@ -36,6 +36,8 @@ public class GameConstants { public static final int CHARACTER_TAG_VERSATILE = 102; public static final int CHARACTER_TAG_SUPPORT = 103; + public static final int AFFINITY_HONOR_UNLOCK_LEVEL = 10; + public static final int MAX_FORMATIONS = 6; public static final int MAX_SHOWCASE_IDS = 5; diff --git a/src/main/java/emu/nebula/command/CommandArgs.java b/src/main/java/emu/nebula/command/CommandArgs.java index 4db613b..bc56b9c 100644 --- a/src/main/java/emu/nebula/command/CommandArgs.java +++ b/src/main/java/emu/nebula/command/CommandArgs.java @@ -3,6 +3,7 @@ package emu.nebula.command; import java.util.List; import emu.nebula.Nebula; +import emu.nebula.data.resources.AffinityLevelDef; import emu.nebula.game.character.GameCharacter; import emu.nebula.game.character.GameDisc; import emu.nebula.game.player.Player; @@ -188,7 +189,7 @@ public class CommandArgs { if (this.getAffinity() >= 0) { int target = this.getAffinity(); - if (target > 50) target = 50; + if (target > AffinityLevelDef.getMaxLevel()) target = AffinityLevelDef.getMaxLevel(); if (target < 0) target = 0; if (character.getAffinityLevel() != target) { character.setAffinityLevel(target); diff --git a/src/main/java/emu/nebula/data/resources/AffinityLevelDef.java b/src/main/java/emu/nebula/data/resources/AffinityLevelDef.java index 218a1e3..49a74f4 100644 --- a/src/main/java/emu/nebula/data/resources/AffinityLevelDef.java +++ b/src/main/java/emu/nebula/data/resources/AffinityLevelDef.java @@ -11,9 +11,19 @@ public class AffinityLevelDef extends BaseDef { private int AffinityLevel; private int NeedExp; + @Getter + private static transient int maxLevel; + @Override public int getId() { return AffinityLevel; } - + + @Override + public void onLoad() { + // Calculate max affinity level + if (this.AffinityLevel > maxLevel) { + maxLevel = this.AffinityLevel; + } + } } diff --git a/src/main/java/emu/nebula/data/resources/CharacterDef.java b/src/main/java/emu/nebula/data/resources/CharacterDef.java index a0ffab8..cfdb69f 100644 --- a/src/main/java/emu/nebula/data/resources/CharacterDef.java +++ b/src/main/java/emu/nebula/data/resources/CharacterDef.java @@ -31,7 +31,9 @@ public class CharacterDef extends BaseDef { private int[] GemSlots; + // Cached data private transient CharacterDesDef des; + private transient HonorDef honor; private transient ElementType elementType; private transient List chats; @@ -43,6 +45,10 @@ public class CharacterDef extends BaseDef { protected void setDes(CharacterDesDef des) { this.des = des; } + + protected void setHonor(HonorDef honor) { + this.honor = honor; + } public int getSkillsUpgradeGroup(int index) { if (index < 0 || index >= this.SkillsUpgradeGroup.length) { diff --git a/src/main/java/emu/nebula/data/resources/HonorDef.java b/src/main/java/emu/nebula/data/resources/HonorDef.java index 39faa80..e64dac8 100644 --- a/src/main/java/emu/nebula/data/resources/HonorDef.java +++ b/src/main/java/emu/nebula/data/resources/HonorDef.java @@ -1,36 +1,60 @@ package emu.nebula.data.resources; +import emu.nebula.Nebula; import emu.nebula.data.BaseDef; import emu.nebula.data.GameData; import emu.nebula.data.ResourceType; +import emu.nebula.data.ResourceType.LoadPriority; import lombok.Getter; @Getter -@ResourceType(name = "Honor.json") +@ResourceType(name = "Honor.json", loadPriority = LoadPriority.LOW) public class HonorDef extends BaseDef { private int Id; private int Type; private int[] Params; + private transient boolean valid; + @Override public int getId() { return Id; } - public boolean isValid() { - if (this.Type == 2) { - if (this.Params.length < 1) { - return false; + @Override + public void onLoad() { + // Run AFTER character data has been loaded + switch (this.Type) { + case 1 -> { + // Normal + this.valid = true; } - - int charId = this.Params[0]; - var charData = GameData.getCharacterDataTable().get(charId); - - if (charData == null || !charData.isAvailable()) { - return false; + case 2 -> { + // Character + if (this.Params.length < 1) { + break; + } + + int charId = this.Params[0]; + var charData = GameData.getCharacterDataTable().get(charId); + + if (charData == null || !charData.isAvailable()) { + break; + } + + // Cache honor data for character data and set to valid + charData.setHonor(this); + this.valid = true; } + case 3 -> { + // Group + this.valid = true; + } + default -> { + // Unknown honor type + Nebula.getLogger().warn("Honor " + this.getId() + " has an unknown type (" + this.getType() + ")"); + } } - return true; } } diff --git a/src/main/java/emu/nebula/game/character/GameCharacter.java b/src/main/java/emu/nebula/game/character/GameCharacter.java index a23894a..1ca3052 100644 --- a/src/main/java/emu/nebula/game/character/GameCharacter.java +++ b/src/main/java/emu/nebula/game/character/GameCharacter.java @@ -30,6 +30,7 @@ import emu.nebula.proto.Public.AffinityInfo; import emu.nebula.proto.Public.Char; import emu.nebula.proto.Public.CharGemPreset; import emu.nebula.proto.Public.CharGemSlot; +import emu.nebula.proto.Public.Honor; import emu.nebula.proto.Public.UI32; import emu.nebula.proto.PublicStarTower.StarTowerChar; import emu.nebula.proto.PublicStarTower.StarTowerCharGem; @@ -411,7 +412,7 @@ public class GameCharacter implements GameDatabaseObject { return data != null ? data.getNeedExp() : 0; } - public void addAffinityExp(int amount) { + public void addAffinityExp(int amount, PlayerChangeInfo change) { // Setup int expRequired = this.getMaxAffinityExp(); @@ -420,10 +421,17 @@ public class GameCharacter implements GameDatabaseObject { // Check for level ups while (this.affinityExp >= expRequired && expRequired > 0) { + // Level up affinity and reset exp required this.affinityLevel += 1; this.affinityExp -= expRequired; expRequired = this.getMaxAffinityExp(); + + // Add honor title if we meet the requirements + if (this.affinityLevel == GameConstants.AFFINITY_HONOR_UNLOCK_LEVEL && this.getData().getHonor() != null) { + var proto = Honor.newInstance().setNewId(this.getData().getHonor().getId()); + change.add(proto); + } } // Clamp exp @@ -472,15 +480,15 @@ public class GameCharacter implements GameDatabaseObject { return null; } + // Remove items + var change = this.getPlayer().getInventory().removeItems(items); + // Add affinity exp - this.addAffinityExp(exp); + this.addAffinityExp(exp, change); // Trigger quest/achievement this.getPlayer().trigger(QuestCondition.GiftGiveTotal, count); - // Remove items - var change = this.getPlayer().getInventory().removeItems(items); - // Success return change; } diff --git a/src/main/java/emu/nebula/game/inventory/Inventory.java b/src/main/java/emu/nebula/game/inventory/Inventory.java index de2cb6f..31efd12 100644 --- a/src/main/java/emu/nebula/game/inventory/Inventory.java +++ b/src/main/java/emu/nebula/game/inventory/Inventory.java @@ -247,6 +247,32 @@ public class Inventory extends PlayerManager implements GameDatabaseObject { return true; } + public IntCollection getAllHonorTitles() { + // Setup int collection + var list = new IntOpenHashSet(); + + // Add character honor titles + for (var character : getPlayer().getCharacters().getCharacterCollection()) { + // Check affinity level + if (character.getAffinityLevel() < GameConstants.AFFINITY_HONOR_UNLOCK_LEVEL) { + continue; + } + + // Get honor data + var honor = character.getData().getHonor(); + if (honor == null) continue; + + // Add to honor list + list.add(honor.getId()); + } + + // Finally, add extra honor titles + list.addAll(this.getHonorList()); + + // Complete and return + return list; + } + public boolean addHonor(int id) { // Make sure we are not adding duplicates if (this.getHonorList().contains(id)) { @@ -264,7 +290,27 @@ public class Inventory extends PlayerManager implements GameDatabaseObject { } public boolean hasHonor(int id) { - return id == GameConstants.DEFAULT_HONOR_ID || this.getHonorList().contains(id); + // Check if honor title is default or if the player owns it + if (id == GameConstants.DEFAULT_HONOR_ID || this.getHonorList().contains(id)) { + return true; + } + + // Get honor data + var honor = GameData.getHonorDataTable().get(id); + if (honor == null) return false; + + // Check if honor title is a trekker affinity title + if (honor.getType() == 2 && honor.getParams().length >= 1) { + int charId = honor.getParams()[0]; + var character = this.getPlayer().getCharacters().getCharacterById(charId); + + if (character != null && character.getAffinityLevel() >= GameConstants.AFFINITY_HONOR_UNLOCK_LEVEL) { + return true; + } + } + + // Failure + return false; } // Resources diff --git a/src/main/java/emu/nebula/game/player/Player.java b/src/main/java/emu/nebula/game/player/Player.java index e09541b..6b88876 100644 --- a/src/main/java/emu/nebula/game/player/Player.java +++ b/src/main/java/emu/nebula/game/player/Player.java @@ -936,7 +936,7 @@ public class Player implements GameDatabaseObject { proto.addHonors(info); } - this.getInventory().getHonorList().forEach(proto::addHonorList); + this.getInventory().getAllHonorTitles().forEach(proto::addHonorList); // Set world class proto.getMutableWorldClass()