Files
Grasscutter/src/main/java/emu/grasscutter/game/systems/InventorySystem.java
T
jacky14 ccf182d692 Medicine revival&Login white screen (#1883)
* Update InventorySystem.java

-:[fix] Medicine revival

* Update GameMainQuest.java

-:[fix] Login white screen
2022-10-22 16:51:33 +10:30

837 lines
32 KiB
Java

package emu.grasscutter.game.systems;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.AvatarPromoteData;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.WeaponPromoteData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.inventory.MaterialType;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.ItemUseOp;
import emu.grasscutter.game.props.ItemUseAction.ItemUseAddExp;
import emu.grasscutter.game.props.ItemUseAction.ItemUseAddReliquaryExp;
import emu.grasscutter.game.props.ItemUseAction.ItemUseAddWeaponExp;
import emu.grasscutter.game.props.ItemUseAction.UseItemParams;
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
import emu.grasscutter.net.proto.MaterialInfoOuterClass.MaterialInfo;
import emu.grasscutter.server.event.player.PlayerUseFoodEvent;
import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2FloatArrayMap;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntRBTreeMap;
import lombok.val;
public class InventorySystem extends BaseGameSystem {
private static final Int2IntMap weaponRefundMaterials = new Int2IntArrayMap();
{
// Use a sorted map, use exp as key to sort by exp
// We want to have weaponRefundMaterials as (id, exp) in descending exp order
var temp = new Int2IntRBTreeMap(Collections.reverseOrder());
GameData.getItemDataMap().forEach((id, data) -> {
if (data == null) return;
if (data.getMaterialType() != MaterialType.MATERIAL_WEAPON_EXP_STONE) return;
var actions = data.getItemUseActions();
if (actions == null) return;
for (var action : actions) {
if (action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_WEAPON_EXP) {
temp.putIfAbsent((int) ((ItemUseAddWeaponExp) action).getExp(), (int) id);
return;
}
}
});
temp.forEach((exp, id) -> weaponRefundMaterials.putIfAbsent((int) id, (int) exp));
}
public InventorySystem(GameServer server) {
super(server);
}
public void lockEquip(Player player, long targetEquipGuid, boolean isLocked) {
GameItem equip = player.getInventory().getItemByGuid(targetEquipGuid);
if (equip == null || !equip.getItemData().isEquip()) {
return;
}
equip.setLocked(isLocked);
equip.save();
player.sendPacket(new PacketStoreItemChangeNotify(equip));
player.sendPacket(new PacketSetEquipLockStateRsp(equip));
}
public void upgradeRelic(Player player, long targetGuid, List<Long> foodRelicList, List<ItemParam> list) {
GameItem relic = player.getInventory().getItemByGuid(targetGuid);
if (relic == null || relic.getItemType() != ItemType.ITEM_RELIQUARY) {
return;
}
int moraCost = 0;
int expGain = 0;
List<GameItem> foodRelics = new ArrayList<GameItem>();
for (long guid : foodRelicList) {
// Add to delete queue
GameItem food = player.getInventory().getItemByGuid(guid);
if (food == null || !food.isDestroyable()) {
continue;
}
// Calculate mora cost
int exp = food.getItemData().getBaseConvExp();
moraCost += exp;
expGain += exp;
// Feeding artifact with exp already
if (food.getTotalExp() > 0) {
expGain += (food.getTotalExp() * 4) / 5;
}
foodRelics.add(food);
}
List<ItemParamData> payList = new ArrayList<ItemParamData>();
for (ItemParam itemParam : list) {
int amount = itemParam.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order
int gain = 0;
var data = GameData.getItemDataMap().get(itemParam.getItemId());
if (data != null) {
var actions = data.getItemUseActions();
if (actions != null) {
for (var action : actions) {
if (action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_RELIQUARY_EXP) {
gain += ((ItemUseAddReliquaryExp) action).getExp();
}
}
}
}
gain *= amount;
expGain += gain;
moraCost += gain;
payList.add(new ItemParamData(itemParam.getItemId(), itemParam.getCount()));
}
// Make sure exp gain is valid
if (expGain <= 0) {
return;
}
// Confirm payment of materials and mora (assume food relics are payable afterwards)
payList.add(new ItemParamData(202, moraCost));
if (!player.getInventory().payItems(payList)) {
return;
}
// Consume food relics
player.getInventory().removeItems(foodRelics);
// Implement random rate boost
int rate = 1;
int boost = Utils.randomRange(1, 100);
if (boost == 100) {
rate = 5;
} else if (boost <= 9) {
rate = 2;
}
expGain *= rate;
// Now we upgrade
int level = relic.getLevel();
int oldLevel = level;
int exp = relic.getExp();
int totalExp = relic.getTotalExp();
int reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level);
int upgrades = 0;
List<Integer> oldAppendPropIdList = new ArrayList<>(relic.getAppendPropIdList());
while (expGain > 0 && reqExp > 0 && level < relic.getItemData().getMaxLevel()) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
totalExp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// On relic levelup
if (relic.getItemData().getAddPropLevelSet() != null && relic.getItemData().getAddPropLevelSet().contains(level)) {
upgrades += 1;
}
// Set req exp
reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level);
}
}
relic.addAppendProps(upgrades);
// Save
relic.setLevel(level);
relic.setExp(exp);
relic.setTotalExp(totalExp);
relic.save();
// Avatar
if (oldLevel != level) {
Avatar avatar = relic.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(relic.getEquipCharacter()) : null;
if (avatar != null) {
avatar.recalcStats();
}
}
// Packet
player.sendPacket(new PacketStoreItemChangeNotify(relic));
player.sendPacket(new PacketReliquaryUpgradeRsp(relic, rate, oldLevel, oldAppendPropIdList));
}
public List<ItemParam> calcWeaponUpgradeReturnItems(Player player, long targetGuid, List<Long> foodWeaponGuidList, List<ItemParam> itemParamList) {
GameItem weapon = player.getInventory().getItemByGuid(targetGuid);
// Sanity checks
if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) {
return null;
}
WeaponPromoteData promoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel());
if (promoteData == null) {
return null;
}
// Get exp gain
int expGain = foodWeaponGuidList.stream()
.map(player.getInventory()::getItemByGuid)
.filter(Objects::nonNull)
.mapToInt(food -> food.getItemData().getWeaponBaseExp() + ((food.getTotalExp() * 4) / 5))
.sum();
// Stream::ofNullable version
expGain += itemParamList.stream()
.mapToInt(param -> {
int exp = Stream.ofNullable(GameData.getItemDataMap().get(param.getItemId()))
.map(ItemData::getItemUseActions)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(action -> action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_WEAPON_EXP)
.mapToInt(action -> ((ItemUseAddWeaponExp) action).getExp())
.sum();
return exp * param.getCount();
})
.sum();
// Optional::ofNullable version
// expGain += itemParamList.stream()
// .mapToInt(param -> {
// int exp = Optional.ofNullable(GameData.getItemDataMap().get(param.getItemId()))
// .map(ItemData::getItemUseActions)
// .map(actions -> {
// return actions.stream()
// .filter(action -> action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_WEAPON_EXP)
// .mapToInt(action -> ((ItemUseAddWeaponExp) action).getExp())
// .sum();
// })
// .orElse(0);
// return exp * param.getCount();
// })
// .sum();
// Try
int maxLevel = promoteData.getUnlockMaxLevel();
int level = weapon.getLevel();
int exp = weapon.getExp();
int reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// Set req exp
reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level);
}
}
return getLeftoverOres(expGain);
}
public void upgradeWeapon(Player player, long targetGuid, List<Long> foodWeaponGuidList, List<ItemParam> itemParamList) {
GameItem weapon = player.getInventory().getItemByGuid(targetGuid);
// Sanity checks
if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) {
return;
}
WeaponPromoteData promoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel());
if (promoteData == null) {
return;
}
// Get exp gain
int expGain = 0, expGainFree = 0;
List<GameItem> foodWeapons = new ArrayList<GameItem>();
for (long guid : foodWeaponGuidList) {
GameItem food = player.getInventory().getItemByGuid(guid);
if (food == null || !food.isDestroyable()) {
continue;
}
expGain += food.getItemData().getWeaponBaseExp();
if (food.getTotalExp() > 0) {
expGainFree += (food.getTotalExp() * 4) / 5; // No tax :D
}
foodWeapons.add(food);
}
List<ItemParamData> payList = new ArrayList<ItemParamData>();
for (ItemParam param : itemParamList) {
int amount = param.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order
var data = GameData.getItemDataMap().get(param.getItemId());
if (data != null) {
var actions = data.getItemUseActions();
if (actions != null) {
for (var action : actions) {
if (action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_WEAPON_EXP) {
expGain += ((ItemUseAddWeaponExp) action).getExp() * amount;
}
}
}
}
payList.add(new ItemParamData(param.getItemId(), amount));
}
// Make sure exp gain is valid
int moraCost = expGain / 10;
expGain += expGainFree;
if (expGain <= 0) {
return;
}
// Confirm payment of materials and mora (assume food weapons are payable afterwards)
payList.add(new ItemParamData(202, moraCost));
if (!player.getInventory().payItems(payList)) {
return;
}
player.getInventory().removeItems(foodWeapons);
// Level up
int maxLevel = promoteData.getUnlockMaxLevel();
int level = weapon.getLevel();
int oldLevel = level;
int exp = weapon.getExp();
int totalExp = weapon.getTotalExp();
int reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
totalExp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// Set req exp
reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level);
}
}
List<ItemParam> leftovers = getLeftoverOres(expGain);
player.getInventory().addItemParams(leftovers);
weapon.setLevel(level);
weapon.setExp(exp);
weapon.setTotalExp(totalExp);
weapon.save();
// Avatar
if (oldLevel != level) {
Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null;
if (avatar != null) {
avatar.recalcStats();
}
}
// Packets
player.sendPacket(new PacketStoreItemChangeNotify(weapon));
player.sendPacket(new PacketWeaponUpgradeRsp(weapon, oldLevel, leftovers));
}
private List<ItemParam> getLeftoverOres(int leftover) {
List<ItemParam> leftoverOreList = new ArrayList<>(3);
for (var e : weaponRefundMaterials.int2IntEntrySet()) {
int exp = e.getIntValue();
int ores = leftover / exp;
leftover = leftover % exp;
if (ores > 0)
leftoverOreList.add(ItemParam.newBuilder().setItemId(e.getIntKey()).setCount(ores).build());
}
return leftoverOreList;
}
public void refineWeapon(Player player, long targetGuid, long feedGuid) {
GameItem weapon = player.getInventory().getItemByGuid(targetGuid);
GameItem feed = player.getInventory().getItemByGuid(feedGuid);
// Sanity checks
if (weapon == null || feed == null || !feed.isDestroyable()) {
return;
}
if (weapon.getItemData().getAwakenMaterial() == 0) {
if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemId() != feed.getItemId()) {
return;
}
} else {
if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemData().getAwakenMaterial() != feed.getItemId()) {
return;
}
}
if (weapon.getRefinement() >= 4 || weapon.getAffixes() == null || weapon.getAffixes().size() == 0) {
return;
}
// Calculate
int oldRefineLevel = weapon.getRefinement();
int targetRefineLevel = Math.min(oldRefineLevel + feed.getRefinement() + 1, 4);
int moraCost = 0;
try {
moraCost = weapon.getItemData().getAwakenCosts()[weapon.getRefinement()];
} catch (Exception e) {
return;
}
// Mora check
if (player.getMora() >= moraCost) {
player.setMora(player.getMora() - moraCost);
} else {
return;
}
// Consume weapon
player.getInventory().removeItem(feed, 1);
// Get
weapon.setRefinement(targetRefineLevel);
weapon.save();
// Avatar
Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null;
if (avatar != null) {
avatar.recalcStats();
}
// Packets
player.sendPacket(new PacketStoreItemChangeNotify(weapon));
player.sendPacket(new PacketWeaponAwakenRsp(avatar, weapon, feed, oldRefineLevel));
}
public void promoteWeapon(Player player, long targetGuid) {
GameItem weapon = player.getInventory().getItemByGuid(targetGuid);
if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) {
return;
}
int nextPromoteLevel = weapon.getPromoteLevel() + 1;
WeaponPromoteData currentPromoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel());
WeaponPromoteData nextPromoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), nextPromoteLevel);
if (currentPromoteData == null || nextPromoteData == null) {
return;
}
// Level check
if (weapon.getLevel() != currentPromoteData.getUnlockMaxLevel()) {
return;
}
// Pay materials and mora if possible
ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null?
if (nextPromoteData.getCoinCost() > 0) {
costs = Arrays.copyOf(costs, costs.length + 1);
costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost());
}
if (!player.getInventory().payItems(costs)) {
return;
}
int oldPromoteLevel = weapon.getPromoteLevel();
weapon.setPromoteLevel(nextPromoteLevel);
weapon.save();
// Avatar
Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null;
if (avatar != null) {
avatar.recalcStats();
}
// Packets
player.sendPacket(new PacketStoreItemChangeNotify(weapon));
player.sendPacket(new PacketWeaponPromoteRsp(weapon, oldPromoteLevel));
}
public void promoteAvatar(Player player, long guid) {
Avatar avatar = player.getAvatars().getAvatarByGuid(guid);
// Sanity checks
if (avatar == null) {
return;
}
int nextPromoteLevel = avatar.getPromoteLevel() + 1;
AvatarPromoteData currentPromoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel());
AvatarPromoteData nextPromoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), nextPromoteLevel);
if (currentPromoteData == null || nextPromoteData == null) {
return;
}
// Level check
if (avatar.getLevel() != currentPromoteData.getUnlockMaxLevel()) {
return;
}
// Pay materials and mora if possible
ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null?
if (nextPromoteData.getCoinCost() > 0) {
costs = Arrays.copyOf(costs, costs.length + 1);
costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost());
}
if (!player.getInventory().payItems(costs)) {
return;
}
// Update promote level
avatar.setPromoteLevel(nextPromoteLevel);
// Update proud skills
Optional.ofNullable(GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId()))
.map(AvatarSkillDepotData::getInherentProudSkillOpens)
.ifPresent(d -> d.stream()
.filter(openData -> openData.getProudSkillGroupId() > 0)
.filter(openData -> openData.getNeedAvatarPromoteLevel() == avatar.getPromoteLevel())
.mapToInt(openData -> (openData.getProudSkillGroupId() * 100) + 1)
.filter(GameData.getProudSkillDataMap()::containsKey)
.forEach(proudSkillId -> {
avatar.getProudSkillList().add(proudSkillId);
player.sendPacket(new PacketProudSkillChangeNotify(avatar));
})
);
// Packets
player.sendPacket(new PacketAvatarPropNotify(avatar));
player.sendPacket(new PacketAvatarPromoteRsp(avatar));
// TODO Send entity prop update packet to world
avatar.recalcStats(true);
avatar.save();
}
public void upgradeAvatar(Player player, long guid, int itemId, int count) {
Avatar avatar = player.getAvatars().getAvatarByGuid(guid);
// Sanity checks
if (avatar == null) {
return;
}
AvatarPromoteData promoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel());
if (promoteData == null) {
return;
}
// Calc exp
int expGain = 0;
var data = GameData.getItemDataMap().get(itemId);
if (data != null) {
var actions = data.getItemUseActions();
if (actions != null) {
for (var action : actions) {
if (action.getItemUseOp() == ItemUseOp.ITEM_USE_ADD_EXP) {
expGain += ((ItemUseAddExp) action).getExp() * count;
}
}
}
}
// Sanity check
if (expGain <= 0) {
return;
}
// Payment check
int moraCost = expGain / 5;
ItemParamData[] costItems = new ItemParamData[] {new ItemParamData(itemId, count), new ItemParamData(202, moraCost)};
if (!player.getInventory().payItems(costItems)) {
return;
}
// Level up
upgradeAvatar(player, avatar, promoteData, expGain);
}
public void upgradeAvatar(Player player, Avatar avatar, int expGain) {
AvatarPromoteData promoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel());
if (promoteData == null) {
return;
}
upgradeAvatar(player, avatar, promoteData, expGain);
}
public void upgradeAvatar(Player player, Avatar avatar, AvatarPromoteData promoteData, int expGain) {
int maxLevel = promoteData.getUnlockMaxLevel();
int level = avatar.getLevel();
int oldLevel = level;
int exp = avatar.getExp();
int reqExp = GameData.getAvatarLevelExpRequired(level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// Set req exp
reqExp = GameData.getAvatarLevelExpRequired(level);
}
}
// Old map for packet
Map<Integer, Float> oldPropMap = avatar.getFightProperties();
if (oldLevel != level) {
// Deep copy if level has changed
oldPropMap = new Int2FloatArrayMap(avatar.getFightProperties());
}
// Done
avatar.setLevel(level);
avatar.setExp(exp);
avatar.recalcStats();
avatar.save();
// TODO Send entity prop update packet to world
// Packets
player.sendPacket(new PacketAvatarPropNotify(avatar));
player.sendPacket(new PacketAvatarUpgradeRsp(avatar, oldLevel, oldPropMap));
}
public void upgradeAvatarFetterLevel(Player player, Avatar avatar, int expGain) {
// May work. Not test.
int maxLevel = 10; // Keep it until I think of a more "elegant" way
int level = avatar.getFetterLevel();
int exp = avatar.getFetterExp();
int reqExp = GameData.getAvatarFetterLevelExpRequired(level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
expGain -= toGain;
if (exp >= reqExp) {
exp = 0;
level += 1;
reqExp = GameData.getAvatarFetterLevelExpRequired(level);
}
}
avatar.setFetterLevel(level);
avatar.setFetterExp(exp);
avatar.save();
player.sendPacket(new PacketAvatarPropNotify(avatar));
player.sendPacket(new PacketAvatarFetterDataNotify(avatar));
}
@Deprecated(forRemoval = true)
public void upgradeAvatarSkill(Player player, long guid, int skillId) {
// Sanity checks
Avatar avatar = player.getAvatars().getAvatarByGuid(guid);
if (avatar == null) return;
avatar.upgradeSkill(skillId);
}
@Deprecated(forRemoval = true)
public void unlockAvatarConstellation(Player player, long guid) {
// Sanity check
Avatar avatar = player.getAvatars().getAvatarByGuid(guid);
if (avatar == null) return;
avatar.unlockConstellation();
}
public void destroyMaterial(Player player, List<MaterialInfo> list) {
// Return materials
val returnMaterialMap = new Int2IntOpenHashMap();
val inventory = player.getInventory();
for (MaterialInfo info : list) {
// Sanity check
if (info.getCount() <= 0) {
continue;
}
GameItem item = inventory.getItemByGuid(info.getGuid());
if (item == null || !item.isDestroyable()) {
continue;
}
// Remove
int removeAmount = Math.min(info.getCount(), item.getCount());
inventory.removeItem(item, removeAmount);
// Delete material return items
val data = item.getItemData();
if (data.getDestroyReturnMaterial().length > 0) {
for (int i = 0; i < data.getDestroyReturnMaterial().length; i++) {
returnMaterialMap.addTo(data.getDestroyReturnMaterial()[i], data.getDestroyReturnMaterialCount()[i]);
}
}
}
// Give back items
if (returnMaterialMap.size() > 0) {
returnMaterialMap.forEach((id, count) -> inventory.addItem(new GameItem(id, count)));
}
// Packets
player.sendPacket(new PacketDestroyMaterialRsp(returnMaterialMap));
}
// Uses an item from the player's inventory.
public synchronized GameItem useItem(Player player, long targetGuid, long itemGuid, int count, int optionId, boolean isEnterMpDungeonTeam) {
Grasscutter.getLogger().debug("Attempting to use item from inventory");
Avatar target = player.getAvatars().getAvatarByGuid(targetGuid);
GameItem item = player.getInventory().getItemByGuid(itemGuid);
if (item == null) return null;
if (item.getCount() < count) return null;
ItemData itemData = item.getItemData();
if (itemData == null) return null;
var params = new UseItemParams(player, itemData.getUseTarget(), target, count, optionId, isEnterMpDungeonTeam);
if (useItemDirect(itemData, params)) {
player.getInventory().removeItem(item, count);
var actions = itemData.getItemUseActions();
if (actions != null)
actions.forEach(use -> use.postUseItem(params));
Grasscutter.getLogger().debug("Item use succeeded!");
return item;
} else {
Grasscutter.getLogger().debug("Item use failed!");
return null;
}
}
// Uses an item without checking the player's inventory.
public synchronized boolean useItemDirect(ItemData itemData, UseItemParams params) {
if (itemData == null) return false;
// Ensure targeting conditions are satisfied
val target = Optional.ofNullable(params.targetAvatar);
switch (params.itemUseTarget) {
case ITEM_USE_TARGET_NONE -> {}
case ITEM_USE_TARGET_SPECIFY_AVATAR -> {
if (target.isEmpty()) return false;
}
case ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR -> {
if (target.map(a -> !a.getAsEntity().isAlive()).orElse(true)) return false;
}
case ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR -> {
if (target.map(a -> a.getAsEntity().isAlive()).orElse(true)) return false;
}
case ITEM_USE_TARGET_CUR_AVATAR -> {}
case ITEM_USE_TARGET_CUR_TEAM -> {}
}
int[] satiationParams = itemData.getSatiationParams();
if (satiationParams != null && satiationParams.length > 0 && target.isPresent()) {
// Invoke and call player use food event.
var event = new PlayerUseFoodEvent(params.player, itemData, params.targetAvatar.getAsEntity()); event.call();
if (event.isCanceled()) return false;
float satiationIncrease = satiationParams[0] + ((float)satiationParams[1])/params.targetAvatar.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
if (!params.targetAvatar.addSatiation(satiationIncrease)) { // Make sure avatar can eat
return false;
}
}
// Use
var actions = itemData.getItemUseActions();
Grasscutter.getLogger().debug("Using - actions - {}", actions);
if (actions == null) return true; // Maybe returning false would be more appropriate?
return actions.stream()
.map(use -> use.useItem(params))
.reduce(false, (a,b) -> a || b); // Don't short-circuit!!!
}
public static synchronized int checkPlayerAvatarConstellationLevel(Player player, int id) {
// Try to accept itemId OR avatarId
int avatarId = 0;
if (GameData.getAvatarDataMap().containsKey(id)) {
avatarId = id;
} else {
avatarId = Optional.ofNullable(GameData.getItemDataMap().get(id))
.map(itemData -> itemData.getItemUseActions())
.flatMap(actions ->
actions.stream()
.filter(action -> action.getItemUseOp() == ItemUseOp.ITEM_USE_GAIN_AVATAR)
.map(action -> ((emu.grasscutter.game.props.ItemUseAction.ItemUseGainAvatar) action).getI())
.findFirst())
.orElse(0);
}
if (avatarId == 0)
return -2; // Not an Avatar
Avatar avatar = player.getAvatars().getAvatarById(avatarId);
if (avatar == null)
return -1; // Doesn't have
// Constellation
int constLevel = avatar.getCoreProudSkillLevel();
val avatarData = avatar.getSkillDepot();
if (avatarData == null) {
Grasscutter.getLogger().error("Attempted to check constellation level for UID"+player.getUid()+"'s avatar "+avatarId+" but avatar has no skillDepot!");
return 0;
}
int constItemId = avatarData.getTalentCostItemId();
GameItem constItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(constItemId);
constLevel += Optional.ofNullable(constItem).map(GameItem::getCount).orElse(0);
return constLevel;
}
}