Implement trekker honor titles

- Setting affinity via commands will not add new honor titles
This commit is contained in:
Melledy
2026-04-15 06:38:45 -07:00
parent 9ba38d5a5e
commit 36e8bc1467
8 changed files with 119 additions and 22 deletions
+3 -1
View File
@@ -26,7 +26,7 @@ public class GameConstants {
public static final int JOINT_DRILL_TICKET_ID = 36; public static final int JOINT_DRILL_TICKET_ID = 36;
public static final int MAX_ENERGY = 240; 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_GEMS_PER_SLOT = 4;
public static final int CHARACTER_MAX_GEM_PRESETS = 3; 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_VERSATILE = 102;
public static final int CHARACTER_TAG_SUPPORT = 103; 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_FORMATIONS = 6;
public static final int MAX_SHOWCASE_IDS = 5; public static final int MAX_SHOWCASE_IDS = 5;
@@ -3,6 +3,7 @@ package emu.nebula.command;
import java.util.List; import java.util.List;
import emu.nebula.Nebula; import emu.nebula.Nebula;
import emu.nebula.data.resources.AffinityLevelDef;
import emu.nebula.game.character.GameCharacter; import emu.nebula.game.character.GameCharacter;
import emu.nebula.game.character.GameDisc; import emu.nebula.game.character.GameDisc;
import emu.nebula.game.player.Player; import emu.nebula.game.player.Player;
@@ -188,7 +189,7 @@ public class CommandArgs {
if (this.getAffinity() >= 0) { if (this.getAffinity() >= 0) {
int target = this.getAffinity(); int target = this.getAffinity();
if (target > 50) target = 50; if (target > AffinityLevelDef.getMaxLevel()) target = AffinityLevelDef.getMaxLevel();
if (target < 0) target = 0; if (target < 0) target = 0;
if (character.getAffinityLevel() != target) { if (character.getAffinityLevel() != target) {
character.setAffinityLevel(target); character.setAffinityLevel(target);
@@ -11,9 +11,19 @@ public class AffinityLevelDef extends BaseDef {
private int AffinityLevel; private int AffinityLevel;
private int NeedExp; private int NeedExp;
@Getter
private static transient int maxLevel;
@Override @Override
public int getId() { public int getId() {
return AffinityLevel; return AffinityLevel;
} }
@Override
public void onLoad() {
// Calculate max affinity level
if (this.AffinityLevel > maxLevel) {
maxLevel = this.AffinityLevel;
}
}
} }
@@ -31,7 +31,9 @@ public class CharacterDef extends BaseDef {
private int[] GemSlots; private int[] GemSlots;
// Cached data
private transient CharacterDesDef des; private transient CharacterDesDef des;
private transient HonorDef honor;
private transient ElementType elementType; private transient ElementType elementType;
private transient List<ChatDef> chats; private transient List<ChatDef> chats;
@@ -44,6 +46,10 @@ public class CharacterDef extends BaseDef {
this.des = des; this.des = des;
} }
protected void setHonor(HonorDef honor) {
this.honor = honor;
}
public int getSkillsUpgradeGroup(int index) { public int getSkillsUpgradeGroup(int index) {
if (index < 0 || index >= this.SkillsUpgradeGroup.length) { if (index < 0 || index >= this.SkillsUpgradeGroup.length) {
return -1; return -1;
@@ -1,36 +1,60 @@
package emu.nebula.data.resources; package emu.nebula.data.resources;
import emu.nebula.Nebula;
import emu.nebula.data.BaseDef; import emu.nebula.data.BaseDef;
import emu.nebula.data.GameData; import emu.nebula.data.GameData;
import emu.nebula.data.ResourceType; import emu.nebula.data.ResourceType;
import emu.nebula.data.ResourceType.LoadPriority;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
@ResourceType(name = "Honor.json") @ResourceType(name = "Honor.json", loadPriority = LoadPriority.LOW)
public class HonorDef extends BaseDef { public class HonorDef extends BaseDef {
private int Id; private int Id;
private int Type; private int Type;
private int[] Params; private int[] Params;
private transient boolean valid;
@Override @Override
public int getId() { public int getId() {
return Id; return Id;
} }
public boolean isValid() { @Override
if (this.Type == 2) { public void onLoad() {
if (this.Params.length < 1) { // Run AFTER character data has been loaded
return false; switch (this.Type) {
case 1 -> {
// Normal
this.valid = true;
} }
case 2 -> {
// Character
if (this.Params.length < 1) {
break;
}
int charId = this.Params[0]; int charId = this.Params[0];
var charData = GameData.getCharacterDataTable().get(charId); var charData = GameData.getCharacterDataTable().get(charId);
if (charData == null || !charData.isAvailable()) { if (charData == null || !charData.isAvailable()) {
return false; 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;
} }
} }
@@ -30,6 +30,7 @@ import emu.nebula.proto.Public.AffinityInfo;
import emu.nebula.proto.Public.Char; import emu.nebula.proto.Public.Char;
import emu.nebula.proto.Public.CharGemPreset; import emu.nebula.proto.Public.CharGemPreset;
import emu.nebula.proto.Public.CharGemSlot; import emu.nebula.proto.Public.CharGemSlot;
import emu.nebula.proto.Public.Honor;
import emu.nebula.proto.Public.UI32; import emu.nebula.proto.Public.UI32;
import emu.nebula.proto.PublicStarTower.StarTowerChar; import emu.nebula.proto.PublicStarTower.StarTowerChar;
import emu.nebula.proto.PublicStarTower.StarTowerCharGem; import emu.nebula.proto.PublicStarTower.StarTowerCharGem;
@@ -411,7 +412,7 @@ public class GameCharacter implements GameDatabaseObject {
return data != null ? data.getNeedExp() : 0; return data != null ? data.getNeedExp() : 0;
} }
public void addAffinityExp(int amount) { public void addAffinityExp(int amount, PlayerChangeInfo change) {
// Setup // Setup
int expRequired = this.getMaxAffinityExp(); int expRequired = this.getMaxAffinityExp();
@@ -420,10 +421,17 @@ public class GameCharacter implements GameDatabaseObject {
// Check for level ups // Check for level ups
while (this.affinityExp >= expRequired && expRequired > 0) { while (this.affinityExp >= expRequired && expRequired > 0) {
// Level up affinity and reset exp required
this.affinityLevel += 1; this.affinityLevel += 1;
this.affinityExp -= expRequired; this.affinityExp -= expRequired;
expRequired = this.getMaxAffinityExp(); 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 // Clamp exp
@@ -472,15 +480,15 @@ public class GameCharacter implements GameDatabaseObject {
return null; return null;
} }
// Remove items
var change = this.getPlayer().getInventory().removeItems(items);
// Add affinity exp // Add affinity exp
this.addAffinityExp(exp); this.addAffinityExp(exp, change);
// Trigger quest/achievement // Trigger quest/achievement
this.getPlayer().trigger(QuestCondition.GiftGiveTotal, count); this.getPlayer().trigger(QuestCondition.GiftGiveTotal, count);
// Remove items
var change = this.getPlayer().getInventory().removeItems(items);
// Success // Success
return change; return change;
} }
@@ -247,6 +247,32 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
return true; 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) { public boolean addHonor(int id) {
// Make sure we are not adding duplicates // Make sure we are not adding duplicates
if (this.getHonorList().contains(id)) { if (this.getHonorList().contains(id)) {
@@ -264,7 +290,27 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
} }
public boolean hasHonor(int id) { 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 // Resources
@@ -936,7 +936,7 @@ public class Player implements GameDatabaseObject {
proto.addHonors(info); proto.addHonors(info);
} }
this.getInventory().getHonorList().forEach(proto::addHonorList); this.getInventory().getAllHonorTitles().forEach(proto::addHonorList);
// Set world class // Set world class
proto.getMutableWorldClass() proto.getMutableWorldClass()