From e5fe89ebc9cddf5aada427afdafb4d4bbc5c162c Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Fri, 21 Nov 2025 04:46:11 -0800 Subject: [PATCH] Implement sending gifts --- src/main/java/emu/nebula/data/GameData.java | 3 + .../data/resources/AffinityGiftDef.java | 20 +++++ .../data/resources/AffinityLevelDef.java | 19 ++++ .../nebula/data/resources/CharacterDef.java | 5 ++ .../data/resources/CharacterDesDef.java | 60 +++++++++++++ .../nebula/game/character/GameCharacter.java | 86 +++++++++++++++++++ .../HandlerCharAffinityGiftSendReq.java | 48 +++++++++++ 7 files changed, 241 insertions(+) create mode 100644 src/main/java/emu/nebula/data/resources/AffinityGiftDef.java create mode 100644 src/main/java/emu/nebula/data/resources/AffinityLevelDef.java create mode 100644 src/main/java/emu/nebula/data/resources/CharacterDesDef.java create mode 100644 src/main/java/emu/nebula/server/handlers/HandlerCharAffinityGiftSendReq.java diff --git a/src/main/java/emu/nebula/data/GameData.java b/src/main/java/emu/nebula/data/GameData.java index abd75fb..0154e07 100644 --- a/src/main/java/emu/nebula/data/GameData.java +++ b/src/main/java/emu/nebula/data/GameData.java @@ -28,6 +28,9 @@ public class GameData { @Getter private static DataTable TalentGroupDataTable = new DataTable<>(); @Getter private static DataTable TalentDataTable = new DataTable<>(); + @Getter private static DataTable AffinityLevelDataTable = new DataTable<>(); + @Getter private static DataTable AffinityGiftDataTable = new DataTable<>(); + @Getter private static DataTable CharGemDataTable = new DataTable<>(); @Getter private static DataTable CharGemSlotControlDataTable = new DataTable<>(); @Getter private static DataTable CharGemAttrGroupDataTable = new DataTable<>(); diff --git a/src/main/java/emu/nebula/data/resources/AffinityGiftDef.java b/src/main/java/emu/nebula/data/resources/AffinityGiftDef.java new file mode 100644 index 0000000..8cbbbd6 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/AffinityGiftDef.java @@ -0,0 +1,20 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; + +import lombok.Getter; + +@Getter +@ResourceType(name = "AffinityGift.json") +public class AffinityGiftDef extends BaseDef { + private int Id; + private int BaseAffinity; + private int[] Tags; + + @Override + public int getId() { + return Id; + } + +} diff --git a/src/main/java/emu/nebula/data/resources/AffinityLevelDef.java b/src/main/java/emu/nebula/data/resources/AffinityLevelDef.java new file mode 100644 index 0000000..218a1e3 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/AffinityLevelDef.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 = "AffinityLevel.json") +public class AffinityLevelDef extends BaseDef { + private int AffinityLevel; + private int NeedExp; + + @Override + public int getId() { + return AffinityLevel; + } + +} diff --git a/src/main/java/emu/nebula/data/resources/CharacterDef.java b/src/main/java/emu/nebula/data/resources/CharacterDef.java index 4bdd42b..a0ffab8 100644 --- a/src/main/java/emu/nebula/data/resources/CharacterDef.java +++ b/src/main/java/emu/nebula/data/resources/CharacterDef.java @@ -31,6 +31,7 @@ public class CharacterDef extends BaseDef { private int[] GemSlots; + private transient CharacterDesDef des; private transient ElementType elementType; private transient List chats; @@ -38,6 +39,10 @@ public class CharacterDef extends BaseDef { public int getId() { return Id; } + + protected void setDes(CharacterDesDef des) { + this.des = des; + } public int getSkillsUpgradeGroup(int index) { if (index < 0 || index >= this.SkillsUpgradeGroup.length) { diff --git a/src/main/java/emu/nebula/data/resources/CharacterDesDef.java b/src/main/java/emu/nebula/data/resources/CharacterDesDef.java new file mode 100644 index 0000000..d4bd807 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/CharacterDesDef.java @@ -0,0 +1,60 @@ +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 it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import lombok.Getter; + +@Getter +@ResourceType(name = "CharacterDes.json", loadPriority = LoadPriority.LOW) +public class CharacterDesDef extends BaseDef { + private int Id; + private int[] Tag; + private IntOpenHashSet PreferTags; + private IntOpenHashSet HateTags; + + @Override + public int getId() { + return Id; + } + + public boolean isPreferGift(AffinityGiftDef gift) { + if (this.getPreferTags() == null) { + return false; + } + + for (int i = 0; i < gift.getTags().length; i++) { + int tag = gift.getTags()[i]; + if (this.getPreferTags().contains(tag)) { + return true; + } + } + + return false; + } + + public boolean isHateGift(AffinityGiftDef gift) { + if (this.getHateTags() == null) { + return false; + } + + for (int i = 0; i < gift.getTags().length; i++) { + int tag = gift.getTags()[i]; + if (this.getHateTags().contains(tag)) { + return true; + } + } + + return false; + } + + @Override + public void onLoad() { + var character = GameData.getCharacterDataTable().get(this.getId()); + if (character != null) { + character.setDes(this); + } + } +} diff --git a/src/main/java/emu/nebula/game/character/GameCharacter.java b/src/main/java/emu/nebula/game/character/GameCharacter.java index fa413e6..7d13cd2 100644 --- a/src/main/java/emu/nebula/game/character/GameCharacter.java +++ b/src/main/java/emu/nebula/game/character/GameCharacter.java @@ -53,6 +53,8 @@ public class GameCharacter implements GameDatabaseObject { private int advance; private int level; private int exp; + private int affinityLevel; + private int affinityExp; private int skin; private int[] skills; private Bitset talents; @@ -378,6 +380,87 @@ public class GameCharacter implements GameDatabaseObject { return true; } + // Affinity + + public int getMaxAffinityExp() { + var data = GameData.getAffinityLevelDataTable().get(this.affinityLevel + 1); + return data != null ? data.getNeedExp() : 0; + } + + public void addAffinityExp(int amount) { + // Setup + int expRequired = this.getMaxAffinityExp(); + + // Add exp + this.affinityExp += amount; + + // Check for level ups + while (this.affinityExp >= expRequired && expRequired > 0) { + this.affinityLevel += 1; + this.affinityExp -= expRequired; + + expRequired = this.getMaxAffinityExp(); + } + + // Clamp exp + if (expRequired <= 0) { + this.affinityExp = 0; + } + + // Save to database + this.save(); + } + + public PlayerChangeInfo sendGift(ItemParamMap items) { + // Verify that the player has the items + if (!this.getPlayer().getInventory().hasItems(items)) { + return null; + } + + // Caluclate amount of exp to gain + var charDescription = this.getData().getDes(); + int exp = 0; + int count = 0; + + for (var item : items) { + // Get data + var gift = GameData.getAffinityGiftDataTable().get(item.getIntKey()); + if (gift == null) { + return null; + } + + // Calculate amount + double amount = gift.getBaseAffinity() * item.getIntValue(); + + if (charDescription.isPreferGift(gift)) { + amount = amount * 1.5D; + } else if (charDescription.isHateGift(gift)) { + amount = amount * 0.8D; + } + + // Add + exp += (int) amount; + count += item.getIntValue(); + } + + // Sanity check + if (exp <= 0) { + return null; + } + + // Add affinity exp + this.addAffinityExp(exp); + + // Trigger quest + this.getPlayer().triggerQuest(QuestCondType.GiftGiveTotal, count); + + // Remove items + var change = this.getPlayer().getInventory().removeItems(items); + + // Success + return change; + } + // Gems public boolean hasGemPreset(int index) { @@ -662,6 +745,8 @@ public class GameCharacter implements GameDatabaseObject { .setLevel(this.getLevel()) .setSkin(this.getSkin()) .setAdvance(this.getAdvance()) + .setAffinityLevel(this.getAffinityLevel()) + .setAffinityExp(this.getAffinityExp()) .setTalentNodes(this.getTalents().toByteArray()) .addAllSkillLvs(this.getSkills()) .setCreateTime(this.getCreateTime()); @@ -705,6 +790,7 @@ public class GameCharacter implements GameDatabaseObject { .setId(this.getCharId()) .setAdvance(this.getAdvance()) .setLevel(this.getLevel()) + .setAffinityLevel(this.getAffinityLevel()) .setTalentNodes(this.getTalents().toByteArray()) .addAllSkillLvs(this.getSkills()); diff --git a/src/main/java/emu/nebula/server/handlers/HandlerCharAffinityGiftSendReq.java b/src/main/java/emu/nebula/server/handlers/HandlerCharAffinityGiftSendReq.java new file mode 100644 index 0000000..38e8129 --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerCharAffinityGiftSendReq.java @@ -0,0 +1,48 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.CharAffinityGiftSend.CharAffinityGiftSendReq; +import emu.nebula.proto.CharAffinityGiftSend.CharAffinityGiftSendResp; +import emu.nebula.net.HandlerId; +import emu.nebula.game.inventory.ItemParamMap; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.char_affinity_gift_send_req) +public class HandlerCharAffinityGiftSendReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse request + var req = CharAffinityGiftSendReq.parseFrom(message); + + // Get character + var character = session.getPlayer().getCharacters().getCharacterById(req.getCharId()); + if (character == null) { + return session.encodeMsg(NetMsgId.char_affinity_gift_send_failed_ack); + } + + // Parse item templates + var items = ItemParamMap.fromTemplates(req.getItems()); + + // Send gifts + var change = character.sendGift(items); + + if (change == null) { + return session.encodeMsg(NetMsgId.char_affinity_gift_send_failed_ack); + } + + // Build response + var rsp = CharAffinityGiftSendResp.newInstance() + .setChange(change.toProto()); + + rsp.getMutableInfo() + .setCharId(character.getCharId()) + .setAffinityLevel(character.getAffinityLevel()) + .setAffinityExp(character.getAffinityExp()); + + // Encode and send + return session.encodeMsg(NetMsgId.char_affinity_gift_send_succeed_ack, rsp); + } + +}