From 88f3b246c86d1594a410b5daacf88c5454c651a5 Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Sun, 2 Nov 2025 04:44:03 -0800 Subject: [PATCH] Refactor inventory system --- src/main/java/emu/nebula/data/GameData.java | 1 + .../nebula/data/resources/CharacterDef.java | 7 +- .../data/resources/CharacterSkinDef.java | 18 +++++ .../nebula/data/resources/HandbookDef.java | 5 -- .../emu/nebula/game/character/Character.java | 45 ++++++++++++ .../game/character/CharacterStorage.java | 6 +- .../emu/nebula/game/inventory/Inventory.java | 68 +++++++++++++++++-- .../emu/nebula/game/inventory/ItemType.java | 12 ++-- .../java/emu/nebula/game/player/Player.java | 29 ++++---- .../handlers/HandlerCharSkinSetReq.java | 33 +++++++++ 10 files changed, 185 insertions(+), 39 deletions(-) create mode 100644 src/main/java/emu/nebula/data/resources/CharacterSkinDef.java create mode 100644 src/main/java/emu/nebula/server/handlers/HandlerCharSkinSetReq.java diff --git a/src/main/java/emu/nebula/data/GameData.java b/src/main/java/emu/nebula/data/GameData.java index 540c6e6..c736490 100644 --- a/src/main/java/emu/nebula/data/GameData.java +++ b/src/main/java/emu/nebula/data/GameData.java @@ -23,6 +23,7 @@ public class GameData { @Getter private static DataTable CharacterSkillUpgradeDataTable = new DataTable<>(); @Getter private static DataTable CharacterUpgradeDataTable = new DataTable<>(); @Getter private static DataTable CharItemExpDataTable = new DataTable<>(); + @Getter private static DataTable CharacterSkinDataTable = new DataTable<>(); @Getter private static DataTable TalentGroupDataTable = new DataTable<>(); @Getter private static DataTable TalentDataTable = new DataTable<>(); diff --git a/src/main/java/emu/nebula/data/resources/CharacterDef.java b/src/main/java/emu/nebula/data/resources/CharacterDef.java index 5619cff..902101a 100644 --- a/src/main/java/emu/nebula/data/resources/CharacterDef.java +++ b/src/main/java/emu/nebula/data/resources/CharacterDef.java @@ -9,14 +9,17 @@ import lombok.Getter; public class CharacterDef extends BaseDef { private int Id; private String Name; - private boolean Available; private int Grade; + private int DefaultSkinId; private int AdvanceSkinId; + private int AdvanceSkinUnlockLevel; + private int AdvanceGroup; + private int[] SkillsUpgradeGroup; + private int FragmentsId; private int TransformQty; - private int[] SkillsUpgradeGroup; @Override public int getId() { diff --git a/src/main/java/emu/nebula/data/resources/CharacterSkinDef.java b/src/main/java/emu/nebula/data/resources/CharacterSkinDef.java new file mode 100644 index 0000000..8ea46ee --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/CharacterSkinDef.java @@ -0,0 +1,18 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; +import lombok.Getter; + +@Getter +@ResourceType(name = "CharacterSkin.json") +public class CharacterSkinDef extends BaseDef { + private int Id; + private int CharId; + private int Type; + + @Override + public int getId() { + return Id; + } +} diff --git a/src/main/java/emu/nebula/data/resources/HandbookDef.java b/src/main/java/emu/nebula/data/resources/HandbookDef.java index 170de2a..aa7523f 100644 --- a/src/main/java/emu/nebula/data/resources/HandbookDef.java +++ b/src/main/java/emu/nebula/data/resources/HandbookDef.java @@ -16,9 +16,4 @@ public class HandbookDef extends BaseDef { public int getId() { return Id; } - - @Override - public void onLoad() { - - } } diff --git a/src/main/java/emu/nebula/game/character/Character.java b/src/main/java/emu/nebula/game/character/Character.java index 9aff1dc..3d71336 100644 --- a/src/main/java/emu/nebula/game/character/Character.java +++ b/src/main/java/emu/nebula/game/character/Character.java @@ -289,6 +289,51 @@ public class Character implements GameDatabaseObject { return changes.setSuccess(true); } + public boolean setSkin(int skinId) { + // Sanity check + if (this.skin == skinId) { + return false; + } + + // + var skinData = GameData.getCharacterSkinDataTable().get(skinId); + if (skinData == null) { + return false; + } + + // Make sure we have the skin + if (skinData.getCharId() != this.getCharId()) { + return false; + } + + switch (skinData.getType()) { + case 1: + // Default skin, always allow + break; + case 2: + // Ascension skin, only allow if the character has the right ascension level + if (this.getAdvance() < this.getData().getAdvanceSkinUnlockLevel()) { + return false; + } + break; + default: + // Extra skin, only allow if we have the skin unlocked + if (!getPlayer().getInventory().getExtraSkins().contains(skinId)) { + return false; + } + break; + } + + // Set skin + this.skin = skinId; + + // Save + this.save(); + + // Success + return true; + } + // Proto public Char toProto() { diff --git a/src/main/java/emu/nebula/game/character/CharacterStorage.java b/src/main/java/emu/nebula/game/character/CharacterStorage.java index 51ae5a0..119e997 100644 --- a/src/main/java/emu/nebula/game/character/CharacterStorage.java +++ b/src/main/java/emu/nebula/game/character/CharacterStorage.java @@ -73,9 +73,9 @@ public class CharacterStorage extends PlayerManager { public HandbookInfo getCharacterHandbook() { var bitset = new Bitset(); - for (var character : this.getCharacterCollection()) { - // Get handbook - var data = GameData.getHandbookDataTable().get(400000 + character.getSkin()); + for (var skinId : getPlayer().getInventory().getAllSkinIds()) { + // Get handbook data + var data = GameData.getHandbookDataTable().get(400000 + skinId); if (data == null) continue; // Set flag diff --git a/src/main/java/emu/nebula/game/inventory/Inventory.java b/src/main/java/emu/nebula/game/inventory/Inventory.java index 07e90e9..405af06 100644 --- a/src/main/java/emu/nebula/game/inventory/Inventory.java +++ b/src/main/java/emu/nebula/game/inventory/Inventory.java @@ -2,9 +2,12 @@ package emu.nebula.game.inventory; import java.util.List; +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; import emu.nebula.GameConstants; import emu.nebula.Nebula; import emu.nebula.data.GameData; +import emu.nebula.database.GameDatabaseObject; import emu.nebula.game.player.PlayerManager; import emu.nebula.proto.Public.Item; import emu.nebula.proto.Public.Res; @@ -12,20 +15,73 @@ import emu.nebula.game.player.Player; import emu.nebula.game.player.PlayerChangeInfo; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntCollection; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; import lombok.Getter; @Getter -public class Inventory extends PlayerManager { - private final Int2ObjectMap resources; - private final Int2ObjectMap items; +@Entity(value = "inventory", useDiscriminator = false) +public class Inventory extends PlayerManager implements GameDatabaseObject { + @Id + private int uid; - public Inventory(Player player) { - super(player); - + // Database persistent data + private IntSet extraSkins; + private IntSet headIcons; + private IntSet titles; + + // Items/resources + private transient Int2ObjectMap resources; + private transient Int2ObjectMap items; + + public Inventory() { this.resources = new Int2ObjectOpenHashMap<>(); this.items = new Int2ObjectOpenHashMap<>(); } + public Inventory(Player player) { + this(); + this.setPlayer(player); + this.uid = player.getUid(); + + // Setup + this.extraSkins = new IntOpenHashSet(); + this.headIcons = new IntOpenHashSet(); + this.titles = new IntOpenHashSet(); + + // Add titles directly + this.getTitles().add(player.getTitlePrefix()); + this.getTitles().add(player.getTitleSuffix()); + + // Save to database + this.save(); + } + + // + + public IntCollection getAllSkinIds() { + // Setup int collection + var skins = new IntOpenHashSet(); + + // Add character skins + for (var character : getPlayer().getCharacters().getCharacterCollection()) { + // Add default skin id + skins.add(character.getData().getDefaultSkinId()); + + // Add advance skin + if (character.getAdvance() >= character.getData().getAdvanceSkinUnlockLevel()) { + skins.add(character.getData().getAdvanceSkinId()); + } + } + + // Finally, add extra skins + skins.addAll(this.getExtraSkins()); + + // Complete and return + return skins; + } + // Resources public synchronized int getResourceCount(int id) { diff --git a/src/main/java/emu/nebula/game/inventory/ItemType.java b/src/main/java/emu/nebula/game/inventory/ItemType.java index 5f23a4f..cdf2721 100644 --- a/src/main/java/emu/nebula/game/inventory/ItemType.java +++ b/src/main/java/emu/nebula/game/inventory/ItemType.java @@ -12,12 +12,12 @@ public enum ItemType { WorldRankExp (5), RogueItem (6), Disc (7), - Equipment (8), - CharacterSkin (9), - MonthlyCard (10), - Title (11), - Honor (12), - HeadItem (13); + Equipment (9), + CharacterSkin (10), + MonthlyCard (11), + Title (12), + Honor (13), + HeadItem (14); @Getter private final int value; diff --git a/src/main/java/emu/nebula/game/player/Player.java b/src/main/java/emu/nebula/game/player/Player.java index 1ad7e63..76cf3ee 100644 --- a/src/main/java/emu/nebula/game/player/Player.java +++ b/src/main/java/emu/nebula/game/player/Player.java @@ -31,9 +31,6 @@ import emu.nebula.proto.Public.Story; import emu.nebula.proto.Public.WorldClass; import emu.nebula.proto.Public.Title; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; - import lombok.Getter; import us.hebi.quickbuf.RepeatedInt; @@ -56,22 +53,19 @@ public class Player implements GameDatabaseObject { private int titleSuffix; private int level; private int exp; + private int[] boards; private int energy; private long energyLastUpdate; - private int[] boards; - private IntSet headIcons; - private IntSet titles; - private long createTime; // Managers private final transient CharacterStorage characters; - private final transient Inventory inventory; private transient GachaManager gachaManager; // Referenced data + private transient Inventory inventory; private transient FormationManager formations; private transient Mailbox mailbox; private transient StarTowerManager starTowerManager; @@ -82,7 +76,6 @@ public class Player implements GameDatabaseObject { public Player() { this.sessions = new HashSet<>(); this.characters = new CharacterStorage(this); - this.inventory = new Inventory(this); this.gachaManager = new GachaManager(this); } @@ -110,8 +103,9 @@ public class Player implements GameDatabaseObject { this.energy = 240; this.energyLastUpdate = this.createTime; this.boards = new int[] {410301}; - this.headIcons = new IntOpenHashSet(); - this.titles = new IntOpenHashSet(); + + // Setup inventory + this.inventory = new Inventory(this); // Add starter characters this.getCharacters().addCharacter(103); @@ -123,10 +117,6 @@ public class Player implements GameDatabaseObject { this.getCharacters().addDisc(211005); this.getCharacters().addDisc(211007); this.getCharacters().addDisc(211008); - - // Add titles - this.getTitles().add(this.getTitlePrefix()); - this.getTitles().add(this.getTitleSuffix()); } public Account getAccount() { @@ -190,7 +180,7 @@ public class Player implements GameDatabaseObject { public boolean editTitle(int prefix, int suffix) { // Check to make sure we own these titles - if (!getTitles().contains(prefix) || !getTitles().contains(suffix)) { + if (!getInventory().getTitles().contains(prefix) || !getInventory().getTitles().contains(suffix)) { return false; } @@ -404,6 +394,11 @@ public class Player implements GameDatabaseObject { public void onLoad() { // Load from database this.getCharacters().loadFromDatabase(); + + // Load inventory first + if (this.inventory == null) { + this.inventory = this.loadManagerFromDatabase(Inventory.class); + } this.getInventory().loadFromDatabase(); // Load referenced classes @@ -501,7 +496,7 @@ public class Player implements GameDatabaseObject { } // Add titles - for (int titleId : this.getTitles()) { + for (int titleId : this.getInventory().getTitles()) { var titleProto = Title.newInstance() .setTitleId(titleId); diff --git a/src/main/java/emu/nebula/server/handlers/HandlerCharSkinSetReq.java b/src/main/java/emu/nebula/server/handlers/HandlerCharSkinSetReq.java new file mode 100644 index 0000000..9efe6cf --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerCharSkinSetReq.java @@ -0,0 +1,33 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.CharSkinSet.CharSkinSetReq; +import emu.nebula.net.HandlerId; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.char_skin_set_req) +public class HandlerCharSkinSetReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse request + var req = CharSkinSetReq.parseFrom(message); + + // Get character + var character = session.getPlayer().getCharacters().getCharacterById(req.getCharId()); + if (character == null) { + return session.encodeMsg(NetMsgId.char_skin_set_failed_ack); + } + + // Set skin + var result = character.setSkin(req.getSkinId()); + if (!result) { + return session.encodeMsg(NetMsgId.char_skin_set_failed_ack); + } + + // Encode and send + return session.encodeMsg(NetMsgId.char_skin_set_succeed_ack); + } + +}