Refactor inventory system

This commit is contained in:
Melledy
2025-11-02 04:44:03 -08:00
parent bc7d5cfd73
commit 88f3b246c8
10 changed files with 185 additions and 39 deletions

View File

@@ -23,6 +23,7 @@ public class GameData {
@Getter private static DataTable<CharacterSkillUpgradeDef> CharacterSkillUpgradeDataTable = new DataTable<>(); @Getter private static DataTable<CharacterSkillUpgradeDef> CharacterSkillUpgradeDataTable = new DataTable<>();
@Getter private static DataTable<CharacterUpgradeDef> CharacterUpgradeDataTable = new DataTable<>(); @Getter private static DataTable<CharacterUpgradeDef> CharacterUpgradeDataTable = new DataTable<>();
@Getter private static DataTable<CharItemExpDef> CharItemExpDataTable = new DataTable<>(); @Getter private static DataTable<CharItemExpDef> CharItemExpDataTable = new DataTable<>();
@Getter private static DataTable<CharacterSkinDef> CharacterSkinDataTable = new DataTable<>();
@Getter private static DataTable<TalentGroupDef> TalentGroupDataTable = new DataTable<>(); @Getter private static DataTable<TalentGroupDef> TalentGroupDataTable = new DataTable<>();
@Getter private static DataTable<TalentDef> TalentDataTable = new DataTable<>(); @Getter private static DataTable<TalentDef> TalentDataTable = new DataTable<>();

View File

@@ -9,14 +9,17 @@ import lombok.Getter;
public class CharacterDef extends BaseDef { public class CharacterDef extends BaseDef {
private int Id; private int Id;
private String Name; private String Name;
private boolean Available;
private int Grade; private int Grade;
private int DefaultSkinId; private int DefaultSkinId;
private int AdvanceSkinId; private int AdvanceSkinId;
private int AdvanceSkinUnlockLevel;
private int AdvanceGroup; private int AdvanceGroup;
private int[] SkillsUpgradeGroup;
private int FragmentsId; private int FragmentsId;
private int TransformQty; private int TransformQty;
private int[] SkillsUpgradeGroup;
@Override @Override
public int getId() { public int getId() {

View File

@@ -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;
}
}

View File

@@ -16,9 +16,4 @@ public class HandbookDef extends BaseDef {
public int getId() { public int getId() {
return Id; return Id;
} }
@Override
public void onLoad() {
}
} }

View File

@@ -289,6 +289,51 @@ public class Character implements GameDatabaseObject {
return changes.setSuccess(true); 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 // Proto
public Char toProto() { public Char toProto() {

View File

@@ -73,9 +73,9 @@ public class CharacterStorage extends PlayerManager {
public HandbookInfo getCharacterHandbook() { public HandbookInfo getCharacterHandbook() {
var bitset = new Bitset(); var bitset = new Bitset();
for (var character : this.getCharacterCollection()) { for (var skinId : getPlayer().getInventory().getAllSkinIds()) {
// Get handbook // Get handbook data
var data = GameData.getHandbookDataTable().get(400000 + character.getSkin()); var data = GameData.getHandbookDataTable().get(400000 + skinId);
if (data == null) continue; if (data == null) continue;
// Set flag // Set flag

View File

@@ -2,9 +2,12 @@ package emu.nebula.game.inventory;
import java.util.List; import java.util.List;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import emu.nebula.GameConstants; import emu.nebula.GameConstants;
import emu.nebula.Nebula; import emu.nebula.Nebula;
import emu.nebula.data.GameData; import emu.nebula.data.GameData;
import emu.nebula.database.GameDatabaseObject;
import emu.nebula.game.player.PlayerManager; import emu.nebula.game.player.PlayerManager;
import emu.nebula.proto.Public.Item; import emu.nebula.proto.Public.Item;
import emu.nebula.proto.Public.Res; import emu.nebula.proto.Public.Res;
@@ -12,20 +15,73 @@ import emu.nebula.game.player.Player;
import emu.nebula.game.player.PlayerChangeInfo; import emu.nebula.game.player.PlayerChangeInfo;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; 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; import lombok.Getter;
@Getter @Getter
public class Inventory extends PlayerManager { @Entity(value = "inventory", useDiscriminator = false)
private final Int2ObjectMap<GameResource> resources; public class Inventory extends PlayerManager implements GameDatabaseObject {
private final Int2ObjectMap<GameItem> items; @Id
private int uid;
public Inventory(Player player) { // Database persistent data
super(player); private IntSet extraSkins;
private IntSet headIcons;
private IntSet titles;
// Items/resources
private transient Int2ObjectMap<GameResource> resources;
private transient Int2ObjectMap<GameItem> items;
public Inventory() {
this.resources = new Int2ObjectOpenHashMap<>(); this.resources = new Int2ObjectOpenHashMap<>();
this.items = 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 // Resources
public synchronized int getResourceCount(int id) { public synchronized int getResourceCount(int id) {

View File

@@ -12,12 +12,12 @@ public enum ItemType {
WorldRankExp (5), WorldRankExp (5),
RogueItem (6), RogueItem (6),
Disc (7), Disc (7),
Equipment (8), Equipment (9),
CharacterSkin (9), CharacterSkin (10),
MonthlyCard (10), MonthlyCard (11),
Title (11), Title (12),
Honor (12), Honor (13),
HeadItem (13); HeadItem (14);
@Getter @Getter
private final int value; private final int value;

View File

@@ -31,9 +31,6 @@ import emu.nebula.proto.Public.Story;
import emu.nebula.proto.Public.WorldClass; import emu.nebula.proto.Public.WorldClass;
import emu.nebula.proto.Public.Title; import emu.nebula.proto.Public.Title;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import lombok.Getter; import lombok.Getter;
import us.hebi.quickbuf.RepeatedInt; import us.hebi.quickbuf.RepeatedInt;
@@ -56,22 +53,19 @@ public class Player implements GameDatabaseObject {
private int titleSuffix; private int titleSuffix;
private int level; private int level;
private int exp; private int exp;
private int[] boards;
private int energy; private int energy;
private long energyLastUpdate; private long energyLastUpdate;
private int[] boards;
private IntSet headIcons;
private IntSet titles;
private long createTime; private long createTime;
// Managers // Managers
private final transient CharacterStorage characters; private final transient CharacterStorage characters;
private final transient Inventory inventory;
private transient GachaManager gachaManager; private transient GachaManager gachaManager;
// Referenced data // Referenced data
private transient Inventory inventory;
private transient FormationManager formations; private transient FormationManager formations;
private transient Mailbox mailbox; private transient Mailbox mailbox;
private transient StarTowerManager starTowerManager; private transient StarTowerManager starTowerManager;
@@ -82,7 +76,6 @@ public class Player implements GameDatabaseObject {
public Player() { public Player() {
this.sessions = new HashSet<>(); this.sessions = new HashSet<>();
this.characters = new CharacterStorage(this); this.characters = new CharacterStorage(this);
this.inventory = new Inventory(this);
this.gachaManager = new GachaManager(this); this.gachaManager = new GachaManager(this);
} }
@@ -110,8 +103,9 @@ public class Player implements GameDatabaseObject {
this.energy = 240; this.energy = 240;
this.energyLastUpdate = this.createTime; this.energyLastUpdate = this.createTime;
this.boards = new int[] {410301}; this.boards = new int[] {410301};
this.headIcons = new IntOpenHashSet();
this.titles = new IntOpenHashSet(); // Setup inventory
this.inventory = new Inventory(this);
// Add starter characters // Add starter characters
this.getCharacters().addCharacter(103); this.getCharacters().addCharacter(103);
@@ -123,10 +117,6 @@ public class Player implements GameDatabaseObject {
this.getCharacters().addDisc(211005); this.getCharacters().addDisc(211005);
this.getCharacters().addDisc(211007); this.getCharacters().addDisc(211007);
this.getCharacters().addDisc(211008); this.getCharacters().addDisc(211008);
// Add titles
this.getTitles().add(this.getTitlePrefix());
this.getTitles().add(this.getTitleSuffix());
} }
public Account getAccount() { public Account getAccount() {
@@ -190,7 +180,7 @@ public class Player implements GameDatabaseObject {
public boolean editTitle(int prefix, int suffix) { public boolean editTitle(int prefix, int suffix) {
// Check to make sure we own these titles // 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; return false;
} }
@@ -404,6 +394,11 @@ public class Player implements GameDatabaseObject {
public void onLoad() { public void onLoad() {
// Load from database // Load from database
this.getCharacters().loadFromDatabase(); this.getCharacters().loadFromDatabase();
// Load inventory first
if (this.inventory == null) {
this.inventory = this.loadManagerFromDatabase(Inventory.class);
}
this.getInventory().loadFromDatabase(); this.getInventory().loadFromDatabase();
// Load referenced classes // Load referenced classes
@@ -501,7 +496,7 @@ public class Player implements GameDatabaseObject {
} }
// Add titles // Add titles
for (int titleId : this.getTitles()) { for (int titleId : this.getInventory().getTitles()) {
var titleProto = Title.newInstance() var titleProto = Title.newInstance()
.setTitleId(titleId); .setTitleId(titleId);

View File

@@ -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);
}
}