From b4e008fb9a5ef2259987113ae489d1b50e38f02f Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Sat, 1 Nov 2025 08:08:31 -0700 Subject: [PATCH] Implement item use (energy potions) - Selectors don't work yet --- .../emu/nebula/data/resources/ItemDef.java | 10 ++ .../emu/nebula/game/inventory/Inventory.java | 119 ++++++++++++------ .../nebula/game/inventory/ItemParamMap.java | 13 +- .../emu/nebula/game/inventory/UseAction.java | 30 +++++ .../emu/nebula/game/inventory/UseMode.java | 29 +++++ .../server/handlers/HandlerItemUseReq.java | 35 ++++++ src/main/java/emu/nebula/util/JsonUtils.java | 4 + 7 files changed, 202 insertions(+), 38 deletions(-) create mode 100644 src/main/java/emu/nebula/game/inventory/UseAction.java create mode 100644 src/main/java/emu/nebula/game/inventory/UseMode.java create mode 100644 src/main/java/emu/nebula/server/handlers/HandlerItemUseReq.java diff --git a/src/main/java/emu/nebula/data/resources/ItemDef.java b/src/main/java/emu/nebula/data/resources/ItemDef.java index d29b018..ef06fe6 100644 --- a/src/main/java/emu/nebula/data/resources/ItemDef.java +++ b/src/main/java/emu/nebula/data/resources/ItemDef.java @@ -2,6 +2,7 @@ package emu.nebula.data.resources; import emu.nebula.data.BaseDef; import emu.nebula.data.ResourceType; +import emu.nebula.game.inventory.ItemParamMap; import emu.nebula.game.inventory.ItemSubType; import emu.nebula.game.inventory.ItemType; import lombok.Getter; @@ -16,6 +17,11 @@ public class ItemDef extends BaseDef { private int Rarity; private boolean Stack; + private int UseMode; + private int UseAction; + private String UseArgs; + + private transient ItemParamMap useParams; private transient ItemType itemType; private transient ItemSubType itemSubType; @@ -28,5 +34,9 @@ public class ItemDef extends BaseDef { public void onLoad() { this.itemType = ItemType.getByValue(this.Type); this.itemSubType = ItemSubType.getByValue(this.Stype); + + if (this.UseArgs != null) { + this.useParams = ItemParamMap.fromJsonString(this.UseArgs); + } } } diff --git a/src/main/java/emu/nebula/game/inventory/Inventory.java b/src/main/java/emu/nebula/game/inventory/Inventory.java index 23dfdb4..2931d0e 100644 --- a/src/main/java/emu/nebula/game/inventory/Inventory.java +++ b/src/main/java/emu/nebula/game/inventory/Inventory.java @@ -39,27 +39,27 @@ public class Inventory extends PlayerManager { return item != null ? item.getCount() : 0; } - // + // Add/Remove items public PlayerChangeInfo addItem(int id, int count) { return this.addItem(id, count, null); } - public synchronized PlayerChangeInfo addItem(int id, int count, PlayerChangeInfo changes) { + public synchronized PlayerChangeInfo addItem(int id, int count, PlayerChangeInfo change) { // Changes - if (changes == null) { - changes = new PlayerChangeInfo(); + if (change == null) { + change = new PlayerChangeInfo(); } // Sanity if (count == 0) { - return changes; + return change; } // Get game data var data = GameData.getItemDataTable().get(id); if (data == null) { - return changes; + return change; } // Set amount @@ -98,11 +98,11 @@ public class Inventory extends PlayerManager { } if (diff != 0) { - var change = Res.newInstance() + var proto = Res.newInstance() .setTid(id) .setQty(diff); - changes.add(change); + change.add(proto); } } case Item -> { @@ -136,11 +136,11 @@ public class Inventory extends PlayerManager { } if (diff != 0) { - var change = Item.newInstance() + var proto = Item.newInstance() .setTid(id) .setQty(diff); - changes.add(change); + change.add(proto); } } case Disc -> { @@ -151,7 +151,7 @@ public class Inventory extends PlayerManager { var disc = getPlayer().getCharacters().addDisc(id); if (disc != null) { - changes.add(disc.toProto()); + change.add(disc.toProto()); } } case Char -> { @@ -162,11 +162,14 @@ public class Inventory extends PlayerManager { var character = getPlayer().getCharacters().addCharacter(id); if (character != null) { - changes.add(character.toProto()); + change.add(character.toProto()); } } + case Energy -> { + this.getPlayer().addEnergy(amount, change); + } case WorldRankExp -> { - this.getPlayer().addExp(amount, changes); + this.getPlayer().addExp(amount, change); } default -> { // Not implemented @@ -174,79 +177,79 @@ public class Inventory extends PlayerManager { } // - return changes; + return change; } @Deprecated - public synchronized PlayerChangeInfo addItems(List params, PlayerChangeInfo changes) { + public synchronized PlayerChangeInfo addItems(List params, PlayerChangeInfo change) { // Changes - if (changes == null) { - changes = new PlayerChangeInfo(); + if (change == null) { + change = new PlayerChangeInfo(); } for (ItemParam param : params) { - this.addItem(param.getId(), param.getCount(), changes); + this.addItem(param.getId(), param.getCount(), change); } - return changes; + return change; } public synchronized PlayerChangeInfo addItems(ItemParamMap params) { return this.addItems(params, null); } - public synchronized PlayerChangeInfo addItems(ItemParamMap params, PlayerChangeInfo changes) { + public synchronized PlayerChangeInfo addItems(ItemParamMap params, PlayerChangeInfo change) { // Changes - if (changes == null) { - changes = new PlayerChangeInfo(); + if (change == null) { + change = new PlayerChangeInfo(); } // Sanity if (params == null || params.isEmpty()) { - return changes; + return change; } // Add items for (var param : params.entries()) { - this.addItem(param.getIntKey(), param.getIntValue(), changes); + this.addItem(param.getIntKey(), param.getIntValue(), change); } - return changes; + return change; } public PlayerChangeInfo removeItem(int id, int count) { return this.removeItem(id, count, null); } - public synchronized PlayerChangeInfo removeItem(int id, int count, PlayerChangeInfo changes) { + public synchronized PlayerChangeInfo removeItem(int id, int count, PlayerChangeInfo change) { if (count > 0) { count = -count; } - return this.addItem(id, count, changes); + return this.addItem(id, count, change); } public synchronized PlayerChangeInfo removeItems(ItemParamMap params) { return this.removeItems(params, null); } - public synchronized PlayerChangeInfo removeItems(ItemParamMap params, PlayerChangeInfo changes) { + public synchronized PlayerChangeInfo removeItems(ItemParamMap params, PlayerChangeInfo change) { // Changes - if (changes == null) { - changes = new PlayerChangeInfo(); + if (change == null) { + change = new PlayerChangeInfo(); } // Sanity if (params == null || params.isEmpty()) { - return changes; + return change; } // Remove items for (var param : params.entries()) { - this.removeItem(param.getIntKey(), param.getIntValue(), changes); + this.removeItem(param.getIntKey(), param.getIntValue(), change); } - return changes; + return change; } /** @@ -322,16 +325,58 @@ public class Inventory extends PlayerManager { } // Create change info - var changes = new PlayerChangeInfo(); + var change = new PlayerChangeInfo(); // Remove items - this.removeItems(materials, changes); + this.removeItems(materials, change); // Add produced items - this.addItem(data.getProductionId(), data.getProductionPerBatch() * num, changes); + this.addItem(data.getProductionId(), data.getProductionPerBatch() * num, change); // Success - return changes.setSuccess(true); + return change.setSuccess(true); + } + + public PlayerChangeInfo useItem(int id, int count, PlayerChangeInfo change) { + // Changes + if (change == null) { + change = new PlayerChangeInfo(); + } + + // Get item data + var data = GameData.getItemDataTable().get(id); + if (data == null || data.getUseParams() == null) { + return change; + } + + // Make sure we have this item + if (!this.verifyItem(id, count)) { + return change; + } + + // Success + boolean success = false; + + // Apply use + switch (data.getUseAction()) { + case 2 -> { + // Add items + this.addItems(data.getUseParams(), change); + // Success + success = true; + } + default -> { + // Not implemented + } + } + + // Consume item if successful + if (success) { + this.removeItem(id, count, change); + } + + // Success + return change; } // Database diff --git a/src/main/java/emu/nebula/game/inventory/ItemParamMap.java b/src/main/java/emu/nebula/game/inventory/ItemParamMap.java index f4966e7..a875cdb 100644 --- a/src/main/java/emu/nebula/game/inventory/ItemParamMap.java +++ b/src/main/java/emu/nebula/game/inventory/ItemParamMap.java @@ -7,7 +7,7 @@ import java.util.stream.Stream; import emu.nebula.proto.Public.Item; import emu.nebula.proto.Public.ItemInfo; import emu.nebula.proto.Public.ItemTpl; - +import emu.nebula.util.JsonUtils; import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap; import us.hebi.quickbuf.RepeatedMessage; @@ -112,4 +112,15 @@ public class ItemParamMap extends Int2IntLinkedOpenHashMap { return map; } + + public static ItemParamMap fromJsonString(String jsonString) { + var map = new ItemParamMap(); + var json = JsonUtils.decodeMap(jsonString, Integer.class, Integer.class); + + for (var entry : json.entrySet()) { + map.add(entry.getKey(), entry.getValue()); + } + + return map; + } } diff --git a/src/main/java/emu/nebula/game/inventory/UseAction.java b/src/main/java/emu/nebula/game/inventory/UseAction.java new file mode 100644 index 0000000..46957b3 --- /dev/null +++ b/src/main/java/emu/nebula/game/inventory/UseAction.java @@ -0,0 +1,30 @@ +package emu.nebula.game.inventory; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import lombok.Getter; + +public enum UseAction { + Drop (1), + Item (2), + Pick (3), + No (4); + + @Getter + private final int value; + private final static Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); + + static { + for (UseAction type : UseAction.values()) { + map.put(type.getValue(), type); + } + } + + private UseAction(int value) { + this.value = value; + } + + public static UseAction getByValue(int value) { + return map.get(value); + } +} diff --git a/src/main/java/emu/nebula/game/inventory/UseMode.java b/src/main/java/emu/nebula/game/inventory/UseMode.java new file mode 100644 index 0000000..a307b54 --- /dev/null +++ b/src/main/java/emu/nebula/game/inventory/UseMode.java @@ -0,0 +1,29 @@ +package emu.nebula.game.inventory; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import lombok.Getter; + +public enum UseMode { + UseModeManual (1), + UseModeAuto (2), + UseModeNot (3); + + @Getter + private final int value; + private final static Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); + + static { + for (UseMode type : UseMode.values()) { + map.put(type.getValue(), type); + } + } + + private UseMode(int value) { + this.value = value; + } + + public static UseMode getByValue(int value) { + return map.get(value); + } +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerItemUseReq.java b/src/main/java/emu/nebula/server/handlers/HandlerItemUseReq.java new file mode 100644 index 0000000..5bde273 --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerItemUseReq.java @@ -0,0 +1,35 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.ItemUse.ItemUseReq; +import emu.nebula.net.HandlerId; +import emu.nebula.game.player.PlayerChangeInfo; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.item_use_req) +public class HandlerItemUseReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse request + var req = ItemUseReq.parseFrom(message); + + // Sanity check + if (req.getUse() == null) { + return session.encodeMsg(NetMsgId.item_use_failed_ack); + } + + // Create change info + var change = new PlayerChangeInfo(); + + // Use item + for (var param : req.getUse().getList()) { + session.getPlayer().getInventory().useItem(param.getTid(), param.getQty(), change); + } + + // Encode and send + return session.encodeMsg(NetMsgId.item_use_succeed_ack, change.toProto()); + } + +} diff --git a/src/main/java/emu/nebula/util/JsonUtils.java b/src/main/java/emu/nebula/util/JsonUtils.java index 1d03c08..238b2d0 100644 --- a/src/main/java/emu/nebula/util/JsonUtils.java +++ b/src/main/java/emu/nebula/util/JsonUtils.java @@ -77,6 +77,10 @@ public class JsonUtils { return null; } } + + public static Map decodeMap(String jsonData, Class keyType, Class valueType) { + return gson.fromJson(jsonData, TypeToken.getParameterized(LinkedHashMap.class, keyType, valueType).getType()); + } public static T loadToClass(InputStreamReader fileReader, Class classType) throws IOException { return gson.fromJson(fileReader, classType);