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

View File

@@ -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<ItemParam> params, PlayerChangeInfo changes) {
public synchronized PlayerChangeInfo addItems(List<ItemParam> 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

View File

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

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;
}
}
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 {
return gson.fromJson(fileReader, classType);