Implement setting headicons/skins

This commit is contained in:
Melledy
2025-11-02 19:04:23 -08:00
parent 88f3b246c8
commit 42f8233132
9 changed files with 244 additions and 26 deletions

View File

@@ -35,6 +35,8 @@ public class GameData {
@Getter private static DataTable<ItemDef> ItemDataTable = new DataTable<>();
@Getter private static DataTable<ProductionDef> ProductionDataTable = new DataTable<>();
@Getter private static DataTable<PlayerHeadDef> PlayerHeadDataTable = new DataTable<>();
@Getter private static DataTable<TitleDef> titleDataTable = new DataTable<>();
@Getter private static DataTable<MallMonthlyCardDef> MallMonthlyCardDataTable = new DataTable<>();
@Getter private static DataTable<MallPackageDef> MallPackageDataTable = new DataTable<>();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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