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<CharacterUpgradeDef> CharacterUpgradeDataTable = 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<TalentDef> TalentDataTable = new DataTable<>();

View File

@@ -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() {

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() {
return Id;
}
@Override
public void onLoad() {
}
}

View File

@@ -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() {

View File

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

View File

@@ -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<GameResource> resources;
private final Int2ObjectMap<GameItem> 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<GameResource> resources;
private transient Int2ObjectMap<GameItem> 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) {

View File

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

View File

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

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