diff --git a/src/main/java/emu/nebula/GameConstants.java b/src/main/java/emu/nebula/GameConstants.java index ecfb4de..53bf7d6 100644 --- a/src/main/java/emu/nebula/GameConstants.java +++ b/src/main/java/emu/nebula/GameConstants.java @@ -31,6 +31,7 @@ public class GameConstants { public static final int CHARACTER_MAX_GEMS_PER_SLOT = 4; public static final int CHARACTER_MAX_GEM_PRESETS = 3; public static final int CHARACTER_MAX_GEM_SLOTS = 3; + public static final int CHARACTER_MAX_OVERLOCK_COUNT = 2; public static final int CHARACTER_TAG_VANGUARD = 101; public static final int CHARACTER_TAG_VERSATILE = 102; diff --git a/src/main/java/emu/nebula/data/resources/CharGemAttrValueDef.java b/src/main/java/emu/nebula/data/resources/CharGemAttrValueDef.java index b4365e1..f79de9b 100644 --- a/src/main/java/emu/nebula/data/resources/CharGemAttrValueDef.java +++ b/src/main/java/emu/nebula/data/resources/CharGemAttrValueDef.java @@ -13,6 +13,7 @@ public class CharGemAttrValueDef extends BaseDef { private int TypeId; private int AttrType; private int AttrTypeFirstSubtype; + private int OverlockCount; private int Rarity; @Override diff --git a/src/main/java/emu/nebula/data/resources/CharGemDef.java b/src/main/java/emu/nebula/data/resources/CharGemDef.java index 8006ebd..aa84b3f 100644 --- a/src/main/java/emu/nebula/data/resources/CharGemDef.java +++ b/src/main/java/emu/nebula/data/resources/CharGemDef.java @@ -10,6 +10,7 @@ public class CharGemDef extends BaseDef { private int Id; private int GenerateCostTid; private int RefreshCostTid; + private int OverlockCostTid; private int Type; @Override diff --git a/src/main/java/emu/nebula/data/resources/CharGemSlotControlDef.java b/src/main/java/emu/nebula/data/resources/CharGemSlotControlDef.java index 6bd8899..2b2a86f 100644 --- a/src/main/java/emu/nebula/data/resources/CharGemSlotControlDef.java +++ b/src/main/java/emu/nebula/data/resources/CharGemSlotControlDef.java @@ -21,6 +21,8 @@ public class CharGemSlotControlDef extends BaseDef { private int GeneratenCostQty; private int RefreshCostQty; + private int OverlockCostQty; + private int OverlockDoraCostQty; private int LockableNum; private int LockItemTid; diff --git a/src/main/java/emu/nebula/game/character/CharacterGem.java b/src/main/java/emu/nebula/game/character/CharacterGem.java index 9f03acf..a1d7f9b 100644 --- a/src/main/java/emu/nebula/game/character/CharacterGem.java +++ b/src/main/java/emu/nebula/game/character/CharacterGem.java @@ -3,6 +3,7 @@ package emu.nebula.game.character; import dev.morphia.annotations.Entity; import emu.nebula.proto.Public.CharGem; import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntSet; import lombok.Getter; @Getter @@ -10,7 +11,9 @@ import lombok.Getter; public class CharacterGem { private boolean locked; private int[] attributes; + private int[] overlock; private int[] alterAttributes; + private int[] alterOverlock; @Deprecated // Morphia only public CharacterGem() { @@ -19,7 +22,9 @@ public class CharacterGem { public CharacterGem(IntList attributes) { this.attributes = attributes.toIntArray(); + this.overlock = new int[this.attributes.length]; this.alterAttributes = new int[4]; + this.alterOverlock = new int[this.alterAttributes.length]; } public void setLocked(boolean locked) { @@ -30,8 +35,18 @@ public class CharacterGem { this.attributes = attributes.toIntArray(); } - public void setNewAttributes(IntList attributes) { + public void setNewAttributes(IntList attributes, IntSet locked) { this.alterAttributes = attributes.toIntArray(); + this.alterOverlock = new int[this.getOverlock().length]; + + // Copy over locked attributes + if (locked.size() > 0) { + for (int i = 0; i < this.getOverlock().length; i++) { + if (locked.contains(this.alterAttributes[i])) { + this.getAlterOverlock()[i] = this.getOverlock()[i]; + } + } + } } public boolean hasAlterAttributes() { @@ -52,12 +67,40 @@ public class CharacterGem { // Replace attributes this.attributes = this.alterAttributes; + this.overlock = this.alterOverlock; this.alterAttributes = new int[4]; + this.alterOverlock = new int[this.alterAttributes.length]; // Success return true; } + public int[] getOverlock() { + if (this.overlock == null) { + this.overlock = new int[this.attributes.length]; + } + + return this.overlock; + } + + public int[] getAlterOverlock() { + if (this.alterOverlock == null) { + this.alterOverlock = new int[this.attributes.length]; + } + + return this.alterOverlock; + } + + public int getOverlockCount() { + int count = 0; + + for (int i = 0; i < this.getOverlock().length; i++) { + count += this.getOverlock()[i]; + } + + return count; + } + // Proto public CharGem toProto() { @@ -65,8 +108,8 @@ public class CharacterGem { .setLock(this.isLocked()) .addAllAttributes(this.getAttributes()) .addAllAlterAttributes(this.getAlterAttributes()) - .addAllOverlockCount(new int[this.getAttributes().length]) - .addAllAlterOverlockCount(new int[this.getAlterAttributes().length]); + .addAllOverlockCount(this.getOverlock()) + .addAllAlterOverlockCount(this.getAlterOverlock()); return proto; } diff --git a/src/main/java/emu/nebula/game/character/GameCharacter.java b/src/main/java/emu/nebula/game/character/GameCharacter.java index 60bd431..e1b5253 100644 --- a/src/main/java/emu/nebula/game/character/GameCharacter.java +++ b/src/main/java/emu/nebula/game/character/GameCharacter.java @@ -769,10 +769,10 @@ public class GameCharacter implements GameDatabaseObject { // Create base list of attributes var list = new CustomIntArray(); + var locked = new IntOpenHashSet(); // Add locked attributes to list if (lockedAttributes.length() != 0) { - var locked = new IntOpenHashSet(); lockedAttributes.forEach(locked::add); for (int i = 0; i < gem.getAttributes().length; i++) { @@ -786,7 +786,7 @@ public class GameCharacter implements GameDatabaseObject { // Generate attributes and create gem var attributes = gemControl.generateAttributes(this, list); - gem.setNewAttributes(attributes); + gem.setNewAttributes(attributes, locked); // Save to database this.save(); @@ -818,6 +818,92 @@ public class GameCharacter implements GameDatabaseObject { return success; } + public PlayerChangeInfo overlockGem(int slotId, int gemIndex, int attrIndex) { + // Get gem from slot + var gem = this.getGemFromSlot(slotId, gemIndex); + if (gem == null) return null; + + // Sanity check attr index + if (attrIndex < 0 || attrIndex >= gem.getAttributes().length) { + return null; + } + + // Get total overlock count + int count = gem.getOverlockCount(); + + if (count >= GameConstants.CHARACTER_MAX_OVERLOCK_COUNT) { + return null; + } + + // Get gem data + var gemData = this.getData().getCharGemData(slotId); + var gemControl = GameData.getCharGemSlotControlDataTable().get(slotId); + + if (gemControl == null) { + return null; + } + + // Calculate the materials we need + var materials = new ItemParamMap(); + materials.add(gemData.getOverlockCostTid(), gemControl.getOverlockCostQty()); + materials.add(GameConstants.GOLD_ITEM_ID, gemControl.getOverlockDoraCostQty()); + + // Make sure the player has the materials to craft the emblem + if (!getPlayer().getInventory().hasItems(materials)) { + return null; + } + + // Get attribute + int attrId = gem.getAttributes()[attrIndex]; + var attr = GameData.getCharGemAttrValueDataTable().get(attrId); + + if (attr == null) { + return null; + } + + // Check if we can upgrade + if (gem.getOverlock()[attrIndex] >= attr.getOverlockCount()) { + return null; + } + + // Upgrade overlock + gem.getOverlock()[attrIndex]++; + + // Consume materials + var change = getPlayer().getInventory().removeItems(materials); + + // Save + this.save(); + + // Success + return change; + } + + public PlayerChangeInfo revertOverlockGem(int slotId, int gemIndex, int attrIndex) { + // Get gem from slot + var gem = this.getGemFromSlot(slotId, gemIndex); + if (gem == null) return null; + + // Sanity check attr index + if (attrIndex < 0 || attrIndex >= gem.getAttributes().length) { + return null; + } + + // Check if we can revert + if (gem.getOverlock()[attrIndex] <= 0) { + return null; + } + + // Revert overlock + gem.getOverlock()[attrIndex]--; + + // Save + this.save(); + + // Success + return new PlayerChangeInfo(); + } + // Proto public Char toProto() { @@ -892,7 +978,7 @@ public class GameCharacter implements GameDatabaseObject { if (gem != null) { info.addAllAttributes(gem.getAttributes()); - info.addAllOverlockCount(new int[gem.getAttributes().length]); + info.addAllOverlockCount(gem.getOverlock()); } else { info.addAllAttributes(new int[4]); info.addAllOverlockCount(new int[4]); diff --git a/src/main/java/emu/nebula/server/handlers/HandlerCharGemOverlockReq.java b/src/main/java/emu/nebula/server/handlers/HandlerCharGemOverlockReq.java new file mode 100644 index 0000000..9340681 --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerCharGemOverlockReq.java @@ -0,0 +1,35 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.CharGemUseOverlock.CharGemOverlockReq; +import emu.nebula.net.HandlerId; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.char_gem_overlock_req) +public class HandlerCharGemOverlockReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse req + var req = CharGemOverlockReq.parseFrom(message); + + // Get character + var character = session.getPlayer().getCharacters().getCharacterById(req.getCharId()); + + if (character == null) { + return session.encodeMsg(NetMsgId.char_gem_overlock_failed_ack); + } + + // Overlock gem + var change = character.overlockGem(req.getSlotId(), req.getGemIndex(), req.getAttrIndex()); + + if (change == null) { + return session.encodeMsg(NetMsgId.char_gem_overlock_failed_ack); + } + + // Encode and send + return session.encodeMsg(NetMsgId.char_gem_overlock_succeed_ack, change.toProto()); + } + +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerCharGemOverlockRevertReq.java b/src/main/java/emu/nebula/server/handlers/HandlerCharGemOverlockRevertReq.java new file mode 100644 index 0000000..48dd1e3 --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerCharGemOverlockRevertReq.java @@ -0,0 +1,35 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.CharGemUseOverlockRevert.CharGemOverlockRevertReq; +import emu.nebula.net.HandlerId; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.char_gem_overlock_revert_req) +public class HandlerCharGemOverlockRevertReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse req + var req = CharGemOverlockRevertReq.parseFrom(message); + + // Get character + var character = session.getPlayer().getCharacters().getCharacterById(req.getCharId()); + + if (character == null) { + return session.encodeMsg(NetMsgId.char_gem_overlock_revert_failed_ack); + } + + // Overlock gem + var change = character.revertOverlockGem(req.getSlotId(), req.getGemIndex(), req.getAttrIndex()); + + if (change == null) { + return session.encodeMsg(NetMsgId.char_gem_overlock_revert_failed_ack); + } + + // Encode and send + return session.encodeMsg(NetMsgId.char_gem_overlock_revert_succeed_ack, change.toProto()); + } + +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerCharGemRefreshReq.java b/src/main/java/emu/nebula/server/handlers/HandlerCharGemRefreshReq.java index 9cac1cc..7dfea1d 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerCharGemRefreshReq.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerCharGemRefreshReq.java @@ -39,7 +39,8 @@ public class HandlerCharGemRefreshReq extends NetHandler { // Build response var rsp = CharGemRefreshResp.newInstance() .setChangeInfo(change.toProto()) - .addAllAttributes(gem.getAlterAttributes()); + .addAllAttributes(gem.getAlterAttributes()) + .addAllOverlockCount(gem.getAlterOverlock()); // Encode and send return session.encodeMsg(NetMsgId.char_gem_refresh_succeed_ack, rsp);