Implement item use (energy potions)

- Selectors don't work yet
This commit is contained in:
Melledy
2025-11-01 08:08:31 -07:00
parent a00dffe2e4
commit b4e008fb9a
7 changed files with 202 additions and 38 deletions

View File

@@ -2,6 +2,7 @@ package emu.nebula.data.resources;
import emu.nebula.data.BaseDef; import emu.nebula.data.BaseDef;
import emu.nebula.data.ResourceType; import emu.nebula.data.ResourceType;
import emu.nebula.game.inventory.ItemParamMap;
import emu.nebula.game.inventory.ItemSubType; import emu.nebula.game.inventory.ItemSubType;
import emu.nebula.game.inventory.ItemType; import emu.nebula.game.inventory.ItemType;
import lombok.Getter; import lombok.Getter;
@@ -16,6 +17,11 @@ public class ItemDef extends BaseDef {
private int Rarity; private int Rarity;
private boolean Stack; private boolean Stack;
private int UseMode;
private int UseAction;
private String UseArgs;
private transient ItemParamMap useParams;
private transient ItemType itemType; private transient ItemType itemType;
private transient ItemSubType itemSubType; private transient ItemSubType itemSubType;
@@ -28,5 +34,9 @@ public class ItemDef extends BaseDef {
public void onLoad() { public void onLoad() {
this.itemType = ItemType.getByValue(this.Type); this.itemType = ItemType.getByValue(this.Type);
this.itemSubType = ItemSubType.getByValue(this.Stype); this.itemSubType = ItemSubType.getByValue(this.Stype);
if (this.UseArgs != null) {
this.useParams = ItemParamMap.fromJsonString(this.UseArgs);
}
} }
} }

View File

@@ -39,27 +39,27 @@ public class Inventory extends PlayerManager {
return item != null ? item.getCount() : 0; return item != null ? item.getCount() : 0;
} }
// // Add/Remove items
public PlayerChangeInfo addItem(int id, int count) { public PlayerChangeInfo addItem(int id, int count) {
return this.addItem(id, count, null); 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 // Changes
if (changes == null) { if (change == null) {
changes = new PlayerChangeInfo(); change = new PlayerChangeInfo();
} }
// Sanity // Sanity
if (count == 0) { if (count == 0) {
return changes; return change;
} }
// Get game data // Get game data
var data = GameData.getItemDataTable().get(id); var data = GameData.getItemDataTable().get(id);
if (data == null) { if (data == null) {
return changes; return change;
} }
// Set amount // Set amount
@@ -98,11 +98,11 @@ public class Inventory extends PlayerManager {
} }
if (diff != 0) { if (diff != 0) {
var change = Res.newInstance() var proto = Res.newInstance()
.setTid(id) .setTid(id)
.setQty(diff); .setQty(diff);
changes.add(change); change.add(proto);
} }
} }
case Item -> { case Item -> {
@@ -136,11 +136,11 @@ public class Inventory extends PlayerManager {
} }
if (diff != 0) { if (diff != 0) {
var change = Item.newInstance() var proto = Item.newInstance()
.setTid(id) .setTid(id)
.setQty(diff); .setQty(diff);
changes.add(change); change.add(proto);
} }
} }
case Disc -> { case Disc -> {
@@ -151,7 +151,7 @@ public class Inventory extends PlayerManager {
var disc = getPlayer().getCharacters().addDisc(id); var disc = getPlayer().getCharacters().addDisc(id);
if (disc != null) { if (disc != null) {
changes.add(disc.toProto()); change.add(disc.toProto());
} }
} }
case Char -> { case Char -> {
@@ -162,11 +162,14 @@ public class Inventory extends PlayerManager {
var character = getPlayer().getCharacters().addCharacter(id); var character = getPlayer().getCharacters().addCharacter(id);
if (character != null) { if (character != null) {
changes.add(character.toProto()); change.add(character.toProto());
} }
} }
case Energy -> {
this.getPlayer().addEnergy(amount, change);
}
case WorldRankExp -> { case WorldRankExp -> {
this.getPlayer().addExp(amount, changes); this.getPlayer().addExp(amount, change);
} }
default -> { default -> {
// Not implemented // Not implemented
@@ -174,79 +177,79 @@ public class Inventory extends PlayerManager {
} }
// //
return changes; return change;
} }
@Deprecated @Deprecated
public synchronized PlayerChangeInfo addItems(List<ItemParam> params, PlayerChangeInfo changes) { public synchronized PlayerChangeInfo addItems(List<ItemParam> params, PlayerChangeInfo change) {
// Changes // Changes
if (changes == null) { if (change == null) {
changes = new PlayerChangeInfo(); change = new PlayerChangeInfo();
} }
for (ItemParam param : params) { 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) { public synchronized PlayerChangeInfo addItems(ItemParamMap params) {
return this.addItems(params, null); return this.addItems(params, null);
} }
public synchronized PlayerChangeInfo addItems(ItemParamMap params, PlayerChangeInfo changes) { public synchronized PlayerChangeInfo addItems(ItemParamMap params, PlayerChangeInfo change) {
// Changes // Changes
if (changes == null) { if (change == null) {
changes = new PlayerChangeInfo(); change = new PlayerChangeInfo();
} }
// Sanity // Sanity
if (params == null || params.isEmpty()) { if (params == null || params.isEmpty()) {
return changes; return change;
} }
// Add items // Add items
for (var param : params.entries()) { 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) { public PlayerChangeInfo removeItem(int id, int count) {
return this.removeItem(id, count, null); 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) { if (count > 0) {
count = -count; count = -count;
} }
return this.addItem(id, count, changes); return this.addItem(id, count, change);
} }
public synchronized PlayerChangeInfo removeItems(ItemParamMap params) { public synchronized PlayerChangeInfo removeItems(ItemParamMap params) {
return this.removeItems(params, null); return this.removeItems(params, null);
} }
public synchronized PlayerChangeInfo removeItems(ItemParamMap params, PlayerChangeInfo changes) { public synchronized PlayerChangeInfo removeItems(ItemParamMap params, PlayerChangeInfo change) {
// Changes // Changes
if (changes == null) { if (change == null) {
changes = new PlayerChangeInfo(); change = new PlayerChangeInfo();
} }
// Sanity // Sanity
if (params == null || params.isEmpty()) { if (params == null || params.isEmpty()) {
return changes; return change;
} }
// Remove items // Remove items
for (var param : params.entries()) { 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 // Create change info
var changes = new PlayerChangeInfo(); var change = new PlayerChangeInfo();
// Remove items // Remove items
this.removeItems(materials, changes); this.removeItems(materials, change);
// Add produced items // Add produced items
this.addItem(data.getProductionId(), data.getProductionPerBatch() * num, changes); this.addItem(data.getProductionId(), data.getProductionPerBatch() * num, change);
// Success // 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 // Database

View File

@@ -7,7 +7,7 @@ import java.util.stream.Stream;
import emu.nebula.proto.Public.Item; import emu.nebula.proto.Public.Item;
import emu.nebula.proto.Public.ItemInfo; import emu.nebula.proto.Public.ItemInfo;
import emu.nebula.proto.Public.ItemTpl; import emu.nebula.proto.Public.ItemTpl;
import emu.nebula.util.JsonUtils;
import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
import us.hebi.quickbuf.RepeatedMessage; import us.hebi.quickbuf.RepeatedMessage;
@@ -112,4 +112,15 @@ public class ItemParamMap extends Int2IntLinkedOpenHashMap {
return map; 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;
}
} }

View File

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

View File

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

View File

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

View File

@@ -77,6 +77,10 @@ public class JsonUtils {
return null; return null;
} }
} }
public static <T1, T2> Map<T1, T2> decodeMap(String jsonData, Class<T1> keyType, Class<T2> valueType) {
return gson.fromJson(jsonData, TypeToken.getParameterized(LinkedHashMap.class, keyType, valueType).getType());
}
public static <T> T loadToClass(InputStreamReader fileReader, Class<T> classType) throws IOException { public static <T> T loadToClass(InputStreamReader fileReader, Class<T> classType) throws IOException {
return gson.fromJson(fileReader, classType); return gson.fromJson(fileReader, classType);