diff --git a/src/main/java/emu/nebula/data/GameData.java b/src/main/java/emu/nebula/data/GameData.java index c736490..6038bda 100644 --- a/src/main/java/emu/nebula/data/GameData.java +++ b/src/main/java/emu/nebula/data/GameData.java @@ -35,6 +35,8 @@ public class GameData { @Getter private static DataTable ItemDataTable = new DataTable<>(); @Getter private static DataTable ProductionDataTable = new DataTable<>(); + @Getter private static DataTable PlayerHeadDataTable = new DataTable<>(); + @Getter private static DataTable titleDataTable = new DataTable<>(); @Getter private static DataTable MallMonthlyCardDataTable = new DataTable<>(); @Getter private static DataTable MallPackageDataTable = new DataTable<>(); diff --git a/src/main/java/emu/nebula/data/resources/ItemDef.java b/src/main/java/emu/nebula/data/resources/ItemDef.java index ef06fe6..482d4b6 100644 --- a/src/main/java/emu/nebula/data/resources/ItemDef.java +++ b/src/main/java/emu/nebula/data/resources/ItemDef.java @@ -6,6 +6,7 @@ import emu.nebula.game.inventory.ItemParamMap; import emu.nebula.game.inventory.ItemSubType; import emu.nebula.game.inventory.ItemType; import lombok.Getter; +import lombok.Setter; @Getter @ResourceType(name = "Item.json") @@ -25,6 +26,9 @@ public class ItemDef extends BaseDef { private transient ItemType itemType; private transient ItemSubType itemSubType; + @Setter + private transient TitleDef titleData; + @Override public int getId() { return Id; diff --git a/src/main/java/emu/nebula/data/resources/PlayerHeadDef.java b/src/main/java/emu/nebula/data/resources/PlayerHeadDef.java new file mode 100644 index 0000000..2f2f644 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/PlayerHeadDef.java @@ -0,0 +1,19 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; +import lombok.Getter; + +@Getter +@ResourceType(name = "PlayerHead.json") +public class PlayerHeadDef extends BaseDef { + private int Id; + private int HeadType; + private int UnlockChar; + private int UnlockSkin; + + @Override + public int getId() { + return Id; + } +} diff --git a/src/main/java/emu/nebula/data/resources/TitleDef.java b/src/main/java/emu/nebula/data/resources/TitleDef.java new file mode 100644 index 0000000..20274fc --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/TitleDef.java @@ -0,0 +1,28 @@ +package emu.nebula.data.resources; + +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 = "Title.json", loadPriority = LoadPriority.LOW) +public class TitleDef extends BaseDef { + private int Id; + private int ItemId; + private int TitleType; + + @Override + public int getId() { + return Id; + } + + @Override + public void onLoad() { + var item = GameData.getItemDataTable().get(this.getItemId()); + if (item != null) { + item.setTitleData(this); + } + } +} diff --git a/src/main/java/emu/nebula/game/character/Character.java b/src/main/java/emu/nebula/game/character/Character.java index 3d71336..b45b423 100644 --- a/src/main/java/emu/nebula/game/character/Character.java +++ b/src/main/java/emu/nebula/game/character/Character.java @@ -295,35 +295,11 @@ public class Character implements GameDatabaseObject { return false; } - // - var skinData = GameData.getCharacterSkinDataTable().get(skinId); - if (skinData == null) { - return false; - } - // Make sure we have the skin - if (skinData.getCharId() != this.getCharId()) { + if (!getPlayer().getInventory().hasSkin(skinId)) { 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; diff --git a/src/main/java/emu/nebula/game/inventory/Inventory.java b/src/main/java/emu/nebula/game/inventory/Inventory.java index 405af06..be116d7 100644 --- a/src/main/java/emu/nebula/game/inventory/Inventory.java +++ b/src/main/java/emu/nebula/game/inventory/Inventory.java @@ -82,6 +82,125 @@ public class Inventory extends PlayerManager implements GameDatabaseObject { return skins; } + public boolean hasSkin(int id) { + // Get skin data + var skinData = GameData.getCharacterSkinDataTable().get(id); + if (skinData == null) { + return false; + } + + // Get character + var character = getPlayer().getCharacters().getCharacterById(skinData.getCharId()); + if (character == null) { + return false; + } + + // Check + 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 (character.getAdvance() < character.getData().getAdvanceSkinUnlockLevel()) { + return false; + } + break; + default: + // Extra skin, only allow if we have the skin unlocked + if (!getPlayer().getInventory().getExtraSkins().contains(id)) { + return false; + } + break; + } + + // Unknown + return true; + } + + public void addSkin(int id) { + // Make sure we are not adding duplicates + if (this.getExtraSkins().contains(id)) { + return; + } + + // Add + this.getExtraSkins().add(id); + + // Save to database + Nebula.getGameDatabase().addToList(this, this.getUid(), "extraSkins", id); + } + + public IntCollection getAllHeadIcons() { + // Setup int collection + var icons = new IntOpenHashSet(); + + // Add character skins + for (var character : getPlayer().getCharacters().getCharacterCollection()) { + // Add default skin id + icons.add(character.getData().getDefaultSkinId()); + + // Add advance skin + if (character.getAdvance() >= character.getData().getAdvanceSkinUnlockLevel()) { + icons.add(character.getData().getAdvanceSkinId()); + } + } + + // Finally, add extra skins + icons.addAll(this.getHeadIcons()); + + // Complete and return + return icons; + } + + public boolean hasHeadIcon(int id) { + // Get head icon data + var data = GameData.getPlayerHeadDataTable().get(id); + if (data == null) { + return false; + } + + // Check to make sure we own this head icon + if (data.getHeadType() == 3) { + // Character skin icon + return this.hasSkin(id); + } else { + // Extra head icon + if (!this.getHeadIcons().contains(id)) { + return false; + } + } + + // Unknown + return true; + } + + public void addHeadIcon(int id) { + // Make sure we are not adding duplicates + if (this.getHeadIcons().contains(id)) { + return; + } + + // Add + this.getHeadIcons().add(id); + + // Save to database + Nebula.getGameDatabase().addToList(this, this.getUid(), "headIcons", id); + } + + public void addTitle(int id) { + // Make sure we are not adding duplicates + if (this.getTitles().contains(id)) { + return; + } + + // Add + this.getTitles().add(id); + + // Save to database + Nebula.getGameDatabase().addToList(this, this.getUid(), "titles", id); + } + // Resources public synchronized int getResourceCount(int id) { @@ -228,6 +347,23 @@ public class Inventory extends PlayerManager implements GameDatabaseObject { case WorldRankExp -> { this.getPlayer().addExp(amount, change); } + case CharacterSkin -> { + // Cannot remove skins + if (amount <= 0) { + break; + } + + // Get skin data + var skinData = GameData.getCharacterSkinDataTable().get(id); + if (skinData == null) { + break; + } + + // Check + if (skinData.getType() >= 3) { + this.addSkin(id); + } + } default -> { // Not implemented } diff --git a/src/main/java/emu/nebula/game/player/Player.java b/src/main/java/emu/nebula/game/player/Player.java index 76cf3ee..c0c43e0 100644 --- a/src/main/java/emu/nebula/game/player/Player.java +++ b/src/main/java/emu/nebula/game/player/Player.java @@ -199,6 +199,27 @@ public class Player implements GameDatabaseObject { return true; } + public boolean editHeadIcon(int id) { + // Skip if we are not changing head icon + if (this.headIcon == id) { + return true; + } + + // Make sure we own the head icon + if (!getInventory().hasHeadIcon(id)) { + return false; + } + + // Set + this.headIcon = id; + + // Update in database + Nebula.getGameDatabase().update(this, this.getUid(), "headIcon", this.getHeadIcon()); + + // Success + return true; + } + public boolean editSignature(String signature) { // Sanity check if (signature == null) { diff --git a/src/main/java/emu/nebula/server/handlers/HandlerPlayerHeadIconInfoReq.java b/src/main/java/emu/nebula/server/handlers/HandlerPlayerHeadIconInfoReq.java index 89f300e..de6d7ef 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerPlayerHeadIconInfoReq.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerPlayerHeadIconInfoReq.java @@ -11,8 +11,16 @@ public class HandlerPlayerHeadIconInfoReq extends NetHandler { @Override public byte[] handle(GameSession session, byte[] message) throws Exception { + // Build response var rsp = PlayerHeadIconInfoResp.newInstance(); - + + var icons = session.getPlayer().getInventory().getAllHeadIcons(); + + for (int id : icons) { + rsp.addList(id); + } + + // Encode and send return session.encodeMsg(NetMsgId.player_head_icon_info_succeed_ack, rsp); } diff --git a/src/main/java/emu/nebula/server/handlers/HandlerPlayerHeadIconSetReq.java b/src/main/java/emu/nebula/server/handlers/HandlerPlayerHeadIconSetReq.java new file mode 100644 index 0000000..a272333 --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerPlayerHeadIconSetReq.java @@ -0,0 +1,24 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.PlayerHeadiconSet.PlayerHeadIconSetReq; +import emu.nebula.net.HandlerId; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.player_head_icon_set_req) +public class HandlerPlayerHeadIconSetReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse req + var req = PlayerHeadIconSetReq.parseFrom(message); + + // Set head icon + boolean result = session.getPlayer().editHeadIcon(req.getHeadIcon()); + + // Encode and send + return session.encodeMsg(result ? NetMsgId.player_head_icon_set_succeed_ack : NetMsgId.player_head_icon_set_failed_ack); + } + +}