mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-22 11:54:39 +01:00
Initial commit
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class EquipInventoryTab implements InventoryTab {
|
||||
private final Set<GenshinItem> items;
|
||||
private final int maxCapacity;
|
||||
|
||||
public EquipInventoryTab(int maxCapacity) {
|
||||
this.items = new HashSet<GenshinItem>();
|
||||
this.maxCapacity = maxCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenshinItem getItemById(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddItem(GenshinItem item) {
|
||||
this.items.add(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveItem(GenshinItem item) {
|
||||
this.items.remove(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxCapacity() {
|
||||
return this.maxCapacity;
|
||||
}
|
||||
}
|
||||
45
src/main/java/emu/grasscutter/game/inventory/EquipType.java
Normal file
45
src/main/java/emu/grasscutter/game/inventory/EquipType.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public enum EquipType {
|
||||
EQUIP_NONE (0),
|
||||
EQUIP_BRACER (1),
|
||||
EQUIP_NECKLACE (2),
|
||||
EQUIP_SHOES (3),
|
||||
EQUIP_RING (4),
|
||||
EQUIP_DRESS (5),
|
||||
EQUIP_WEAPON (6);
|
||||
|
||||
private final int value;
|
||||
private static final Int2ObjectMap<EquipType> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, EquipType> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
Stream.of(values()).forEach(e -> {
|
||||
map.put(e.getValue(), e);
|
||||
stringMap.put(e.name(), e);
|
||||
});
|
||||
}
|
||||
|
||||
private EquipType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static EquipType getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, EQUIP_NONE);
|
||||
}
|
||||
|
||||
public static EquipType getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, EQUIP_NONE);
|
||||
}
|
||||
}
|
||||
430
src/main/java/emu/grasscutter/game/inventory/GenshinItem.java
Normal file
430
src/main/java/emu/grasscutter/game/inventory/GenshinItem.java
Normal file
@@ -0,0 +1,430 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import dev.morphia.annotations.PostLoad;
|
||||
import dev.morphia.annotations.Transient;
|
||||
|
||||
import emu.grasscutter.data.GenshinData;
|
||||
import emu.grasscutter.data.GenshinDepot;
|
||||
import emu.grasscutter.data.def.ItemData;
|
||||
import emu.grasscutter.data.def.ReliquaryAffixData;
|
||||
import emu.grasscutter.data.def.ReliquaryMainPropData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||
import emu.grasscutter.net.proto.EquipOuterClass.Equip;
|
||||
import emu.grasscutter.net.proto.FurnitureOuterClass.Furniture;
|
||||
import emu.grasscutter.net.proto.ItemHintOuterClass.ItemHint;
|
||||
import emu.grasscutter.net.proto.ItemOuterClass.Item;
|
||||
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
||||
import emu.grasscutter.net.proto.MaterialOuterClass.Material;
|
||||
import emu.grasscutter.net.proto.ReliquaryOuterClass.Reliquary;
|
||||
import emu.grasscutter.net.proto.SceneReliquaryInfoOuterClass.SceneReliquaryInfo;
|
||||
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
|
||||
import emu.grasscutter.net.proto.WeaponOuterClass.Weapon;
|
||||
import emu.grasscutter.utils.WeightedList;
|
||||
|
||||
@Entity(value = "items", noClassnameStored = true)
|
||||
public class GenshinItem {
|
||||
@Id private ObjectId id;
|
||||
@Indexed private int ownerId;
|
||||
private int itemId;
|
||||
private int count;
|
||||
|
||||
@Transient private long guid; // Player unique id
|
||||
@Transient private ItemData itemData;
|
||||
|
||||
// Equips
|
||||
private int level;
|
||||
private int exp;
|
||||
private int totalExp;
|
||||
private int promoteLevel;
|
||||
private boolean locked;
|
||||
|
||||
// Weapon
|
||||
private List<Integer> affixes;
|
||||
private int refinement = 0;
|
||||
|
||||
// Relic
|
||||
private int mainPropId;
|
||||
private List<Integer> appendPropIdList;
|
||||
|
||||
private int equipCharacter;
|
||||
@Transient private int weaponEntityId;
|
||||
|
||||
public GenshinItem() {
|
||||
// Morphia only
|
||||
}
|
||||
|
||||
public GenshinItem(int itemId) {
|
||||
this(GenshinData.getItemDataMap().get(itemId));
|
||||
}
|
||||
|
||||
public GenshinItem(int itemId, int count) {
|
||||
this(GenshinData.getItemDataMap().get(itemId), count);
|
||||
}
|
||||
|
||||
public GenshinItem(ItemData data) {
|
||||
this(data, 1);
|
||||
}
|
||||
|
||||
public GenshinItem(ItemData data, int count) {
|
||||
this.itemId = data.getId();
|
||||
this.itemData = data;
|
||||
|
||||
if (data.getItemType() == ItemType.ITEM_VIRTUAL) {
|
||||
this.count = count;
|
||||
} else {
|
||||
this.count = Math.min(count, data.getStackLimit());
|
||||
}
|
||||
|
||||
// Equip data
|
||||
if (getItemType() == ItemType.ITEM_WEAPON) {
|
||||
this.level = 1;
|
||||
this.affixes = new ArrayList<>(2);
|
||||
if (getItemData().getSkillAffix() != null) {
|
||||
for (int skillAffix : getItemData().getSkillAffix()) {
|
||||
if (skillAffix > 0) {
|
||||
this.affixes.add(skillAffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (getItemType() == ItemType.ITEM_RELIQUARY) {
|
||||
this.level = 1;
|
||||
this.appendPropIdList = new ArrayList<>();
|
||||
// Create main property
|
||||
ReliquaryMainPropData mainPropData = GenshinDepot.getRandomRelicMainProp(getItemData().getMainPropDepotId());
|
||||
if (mainPropData != null) {
|
||||
this.mainPropId = mainPropData.getId();
|
||||
}
|
||||
// Create extra stats
|
||||
if (getItemData().getAppendPropNum() > 0) {
|
||||
for (int i = 0; i < getItemData().getAppendPropNum(); i++) {
|
||||
this.addAppendProp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectId getObjectId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public void setOwner(GenshinPlayer player) {
|
||||
this.ownerId = player.getId();
|
||||
this.guid = player.getNextGuid();
|
||||
}
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public void setItemId(int itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
public long getGuid() {
|
||||
return guid;
|
||||
}
|
||||
|
||||
public ItemType getItemType() {
|
||||
return this.itemData.getItemType();
|
||||
}
|
||||
|
||||
public ItemData getItemData() {
|
||||
return itemData;
|
||||
}
|
||||
|
||||
public void setItemData(ItemData materialData) {
|
||||
this.itemData = materialData;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public int getExp() {
|
||||
return exp;
|
||||
}
|
||||
|
||||
public void setExp(int exp) {
|
||||
this.exp = exp;
|
||||
}
|
||||
|
||||
public int getTotalExp() {
|
||||
return totalExp;
|
||||
}
|
||||
|
||||
public void setTotalExp(int totalExp) {
|
||||
this.totalExp = totalExp;
|
||||
}
|
||||
|
||||
public int getPromoteLevel() {
|
||||
return promoteLevel;
|
||||
}
|
||||
|
||||
public void setPromoteLevel(int promoteLevel) {
|
||||
this.promoteLevel = promoteLevel;
|
||||
}
|
||||
|
||||
public int getEquipSlot() {
|
||||
return this.getItemData().getEquipType().getValue();
|
||||
}
|
||||
|
||||
public int getEquipCharacter() {
|
||||
return equipCharacter;
|
||||
}
|
||||
|
||||
public void setEquipCharacter(int equipCharacter) {
|
||||
this.equipCharacter = equipCharacter;
|
||||
}
|
||||
|
||||
public boolean isEquipped() {
|
||||
return this.getEquipCharacter() > 0;
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
public void setLocked(boolean locked) {
|
||||
this.locked = locked;
|
||||
}
|
||||
|
||||
public boolean isDestroyable() {
|
||||
return !this.isLocked() && !this.isEquipped();
|
||||
}
|
||||
|
||||
public int getWeaponEntityId() {
|
||||
return weaponEntityId;
|
||||
}
|
||||
|
||||
public void setWeaponEntityId(int weaponEntityId) {
|
||||
this.weaponEntityId = weaponEntityId;
|
||||
}
|
||||
|
||||
public List<Integer> getAffixes() {
|
||||
return affixes;
|
||||
}
|
||||
|
||||
public int getRefinement() {
|
||||
return refinement;
|
||||
}
|
||||
|
||||
public void setRefinement(int refinement) {
|
||||
this.refinement = refinement;
|
||||
}
|
||||
|
||||
public int getMainPropId() {
|
||||
return mainPropId;
|
||||
}
|
||||
|
||||
public List<Integer> getAppendPropIdList() {
|
||||
return appendPropIdList;
|
||||
}
|
||||
|
||||
public void addAppendProp() {
|
||||
if (this.getAppendPropIdList() == null) {
|
||||
this.appendPropIdList = new ArrayList<>();
|
||||
}
|
||||
|
||||
if (this.getAppendPropIdList().size() < 4) {
|
||||
addNewAppendProp();
|
||||
} else {
|
||||
upgradeRandomAppendProp();
|
||||
}
|
||||
}
|
||||
|
||||
private void addNewAppendProp() {
|
||||
List<ReliquaryAffixData> affixList = GenshinDepot.getRandomRelicAffixList(getItemData().getAppendPropDepotId());
|
||||
|
||||
if (affixList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build blacklist - Dont add same stat as main/sub stat
|
||||
Set<FightProperty> blacklist = new HashSet<>();
|
||||
ReliquaryMainPropData mainPropData = GenshinData.getReliquaryMainPropDataMap().get(this.getMainPropId());
|
||||
if (mainPropData != null) {
|
||||
blacklist.add(mainPropData.getFightProp());
|
||||
}
|
||||
int len = Math.min(4, this.getAppendPropIdList().size());
|
||||
for (int i = 0; i < len; i++) {
|
||||
ReliquaryAffixData affixData = GenshinData.getReliquaryAffixDataMap().get((int) this.getAppendPropIdList().get(i));
|
||||
if (affixData != null) {
|
||||
blacklist.add(affixData.getFightProp());
|
||||
}
|
||||
}
|
||||
|
||||
// Build random list
|
||||
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
|
||||
for (ReliquaryAffixData affix : affixList) {
|
||||
if (!blacklist.contains(affix.getFightProp())) {
|
||||
randomList.add(affix.getWeight(), affix);
|
||||
}
|
||||
}
|
||||
|
||||
if (randomList.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add random stat
|
||||
ReliquaryAffixData affixData = randomList.next();
|
||||
this.getAppendPropIdList().add(affixData.getId());
|
||||
}
|
||||
|
||||
private void upgradeRandomAppendProp() {
|
||||
List<ReliquaryAffixData> affixList = GenshinDepot.getRandomRelicAffixList(getItemData().getAppendPropDepotId());
|
||||
|
||||
if (affixList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build whitelist
|
||||
Set<FightProperty> whitelist = new HashSet<>();
|
||||
int len = Math.min(4, this.getAppendPropIdList().size());
|
||||
for (int i = 0; i < len; i++) {
|
||||
ReliquaryAffixData affixData = GenshinData.getReliquaryAffixDataMap().get((int) this.getAppendPropIdList().get(i));
|
||||
if (affixData != null) {
|
||||
whitelist.add(affixData.getFightProp());
|
||||
}
|
||||
}
|
||||
|
||||
// Build random list
|
||||
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
|
||||
for (ReliquaryAffixData affix : affixList) {
|
||||
if (whitelist.contains(affix.getFightProp())) {
|
||||
randomList.add(affix.getUpgradeWeight(), affix);
|
||||
}
|
||||
}
|
||||
|
||||
// Add random stat
|
||||
ReliquaryAffixData affixData = randomList.next();
|
||||
this.getAppendPropIdList().add(affixData.getId());
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
public void onLoad() {
|
||||
if (this.itemData == null) {
|
||||
this.itemData = GenshinData.getItemDataMap().get(getItemId());
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
if (this.count > 0 && this.ownerId > 0) {
|
||||
DatabaseHelper.saveItem(this);
|
||||
} else if (this.getObjectId() != null) {
|
||||
DatabaseHelper.deleteItem(this);
|
||||
}
|
||||
}
|
||||
|
||||
public SceneWeaponInfo createSceneWeaponInfo() {
|
||||
SceneWeaponInfo.Builder weaponInfo = SceneWeaponInfo.newBuilder()
|
||||
.setEntityId(this.getWeaponEntityId())
|
||||
.setItemId(this.getItemId())
|
||||
.setGuid(this.getGuid())
|
||||
.setLevel(this.getLevel())
|
||||
.setGadgetId(this.getItemData().getGadgetId())
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder().setIsInited(getAffixes().size() > 0));
|
||||
|
||||
if (this.getAffixes() != null && this.getAffixes().size() > 0) {
|
||||
for (int affix : this.getAffixes()) {
|
||||
weaponInfo.putAffixMap(affix, this.getRefinement());
|
||||
}
|
||||
}
|
||||
|
||||
return weaponInfo.build();
|
||||
}
|
||||
|
||||
public SceneReliquaryInfo createSceneReliquaryInfo() {
|
||||
SceneReliquaryInfo relicInfo = SceneReliquaryInfo.newBuilder()
|
||||
.setItemId(this.getItemId())
|
||||
.setGuid(this.getGuid())
|
||||
.setLevel(this.getLevel())
|
||||
.build();
|
||||
|
||||
return relicInfo;
|
||||
}
|
||||
|
||||
public Item toProto() {
|
||||
Item.Builder proto = Item.newBuilder()
|
||||
.setGuid(this.getGuid())
|
||||
.setItemId(this.getItemId());
|
||||
|
||||
switch (getItemType()) {
|
||||
case ITEM_WEAPON:
|
||||
Weapon.Builder weapon = Weapon.newBuilder()
|
||||
.setLevel(this.getLevel())
|
||||
.setExp(this.getExp())
|
||||
.setPromoteLevel(this.getPromoteLevel());
|
||||
|
||||
if (this.getAffixes() != null && this.getAffixes().size() > 0) {
|
||||
for (int affix : this.getAffixes()) {
|
||||
weapon.putAffixMap(affix, this.getRefinement());
|
||||
}
|
||||
}
|
||||
|
||||
proto.setEquip(Equip.newBuilder().setWeapon(weapon).setIsLocked(this.isLocked()).build());
|
||||
break;
|
||||
case ITEM_RELIQUARY:
|
||||
Reliquary relic = Reliquary.newBuilder()
|
||||
.setLevel(this.getLevel())
|
||||
.setExp(this.getExp())
|
||||
.setPromoteLevel(this.getPromoteLevel())
|
||||
.setMainPropId(this.getMainPropId())
|
||||
.addAllAppendPropIdList(this.getAppendPropIdList())
|
||||
.build();
|
||||
proto.setEquip(Equip.newBuilder().setReliquary(relic).setIsLocked(this.isLocked()).build());
|
||||
break;
|
||||
case ITEM_MATERIAL:
|
||||
Material material = Material.newBuilder()
|
||||
.setCount(getCount())
|
||||
.build();
|
||||
proto.setMaterial(material);
|
||||
break;
|
||||
case ITEM_FURNITURE:
|
||||
Furniture furniture = Furniture.newBuilder()
|
||||
.setCount(getCount())
|
||||
.build();
|
||||
proto.setFurniture(furniture);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
|
||||
public ItemHint toItemHintProto() {
|
||||
return ItemHint.newBuilder().setItemId(getItemId()).setCount(getCount()).setIsNew(false).build();
|
||||
}
|
||||
|
||||
public ItemParam toItemParam() {
|
||||
return ItemParam.newBuilder().setItemId(this.getItemId()).setCount(this.getCount()).build();
|
||||
}
|
||||
}
|
||||
353
src/main/java/emu/grasscutter/game/inventory/Inventory.java
Normal file
353
src/main/java/emu/grasscutter/game/inventory/Inventory.java
Normal file
@@ -0,0 +1,353 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.GenshinConstants;
|
||||
import emu.grasscutter.data.GenshinData;
|
||||
import emu.grasscutter.data.def.AvatarCostumeData;
|
||||
import emu.grasscutter.data.def.AvatarData;
|
||||
import emu.grasscutter.data.def.AvatarFlycloakData;
|
||||
import emu.grasscutter.data.def.ItemData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.avatar.AvatarStorage;
|
||||
import emu.grasscutter.game.avatar.GenshinAvatar;
|
||||
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketStoreItemChangeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketStoreItemDelNotify;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
|
||||
public class Inventory implements Iterable<GenshinItem> {
|
||||
private final GenshinPlayer player;
|
||||
|
||||
private final Long2ObjectMap<GenshinItem> store;
|
||||
private final Int2ObjectMap<InventoryTab> inventoryTypes;
|
||||
|
||||
public Inventory(GenshinPlayer player) {
|
||||
this.player = player;
|
||||
this.store = new Long2ObjectOpenHashMap<>();
|
||||
this.inventoryTypes = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(GenshinConstants.LIMIT_WEAPON));
|
||||
this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(GenshinConstants.LIMIT_RELIC));
|
||||
this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(GenshinConstants.LIMIT_MATERIAL));
|
||||
this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(GenshinConstants.LIMIT_FURNITURE));
|
||||
}
|
||||
|
||||
public GenshinPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public AvatarStorage getAvatarStorage() {
|
||||
return this.getPlayer().getAvatars();
|
||||
}
|
||||
|
||||
public Long2ObjectMap<GenshinItem> getItems() {
|
||||
return store;
|
||||
}
|
||||
|
||||
public Int2ObjectMap<InventoryTab> getInventoryTypes() {
|
||||
return inventoryTypes;
|
||||
}
|
||||
|
||||
public InventoryTab getInventoryTab(ItemType type) {
|
||||
return getInventoryTypes().get(type.getValue());
|
||||
}
|
||||
|
||||
public void createInventoryTab(ItemType type, InventoryTab tab) {
|
||||
this.getInventoryTypes().put(type.getValue(), tab);
|
||||
}
|
||||
|
||||
public GenshinItem getItemByGuid(long id) {
|
||||
return this.getItems().get(id);
|
||||
}
|
||||
|
||||
public boolean addItem(int itemId) {
|
||||
return addItem(itemId, 1);
|
||||
}
|
||||
|
||||
public boolean addItem(int itemId, int count) {
|
||||
ItemData itemData = GenshinData.getItemDataMap().get(itemId);
|
||||
|
||||
if (itemData == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GenshinItem item = new GenshinItem(itemData, count);
|
||||
|
||||
return addItem(item);
|
||||
}
|
||||
|
||||
public boolean addItem(GenshinItem item) {
|
||||
GenshinItem result = putItem(item);
|
||||
|
||||
if (result != null) {
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(result));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addItems(Collection<GenshinItem> items) {
|
||||
List<GenshinItem> changedItems = new LinkedList<>();
|
||||
|
||||
for (GenshinItem item : items) {
|
||||
GenshinItem result = putItem(item);
|
||||
if (result != null) {
|
||||
changedItems.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
|
||||
}
|
||||
|
||||
public void addItemParams(Collection<ItemParam> items) {
|
||||
List<GenshinItem> changedItems = new LinkedList<>();
|
||||
|
||||
for (ItemParam itemParam : items) {
|
||||
GenshinItem toAdd = new GenshinItem(itemParam.getItemId(), itemParam.getCount());
|
||||
GenshinItem result = putItem(toAdd);
|
||||
if (result != null) {
|
||||
changedItems.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
|
||||
}
|
||||
|
||||
private synchronized GenshinItem putItem(GenshinItem item) {
|
||||
// Dont add items that dont have a valid item definition.
|
||||
if (item.getItemData() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add item to inventory store
|
||||
ItemType type = item.getItemData().getItemType();
|
||||
InventoryTab tab = getInventoryTab(type);
|
||||
|
||||
// Add
|
||||
if (type == ItemType.ITEM_WEAPON || type == ItemType.ITEM_RELIQUARY) {
|
||||
if (tab.getSize() >= tab.getMaxCapacity()) {
|
||||
return null;
|
||||
}
|
||||
putItem(item, tab);
|
||||
} else if (type == ItemType.ITEM_VIRTUAL) {
|
||||
// Handle
|
||||
this.addVirtualItem(item.getItemId(), item.getCount());
|
||||
return null;
|
||||
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR) {
|
||||
// Get avatar id
|
||||
int avatarId = (item.getItemId() % 1000) + 10000000;
|
||||
// Dont let people give themselves extra main characters
|
||||
if (avatarId == GenshinConstants.MAIN_CHARACTER_MALE || avatarId == GenshinConstants.MAIN_CHARACTER_FEMALE) {
|
||||
return null;
|
||||
}
|
||||
// Add avatar
|
||||
AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarId);
|
||||
if (avatarData != null && !player.getAvatars().hasAvatar(avatarId)) {
|
||||
this.getPlayer().addAvatar(new GenshinAvatar(avatarData));
|
||||
}
|
||||
return null;
|
||||
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_FLYCLOAK) {
|
||||
AvatarFlycloakData flycloakData = GenshinData.getAvatarFlycloakDataMap().get(item.getItemId());
|
||||
if (flycloakData != null && !player.getFlyCloakList().contains(item.getItemId())) {
|
||||
getPlayer().addFlycloak(item.getItemId());
|
||||
}
|
||||
return null;
|
||||
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_COSTUME) {
|
||||
AvatarCostumeData costumeData = GenshinData.getAvatarCostumeDataItemIdMap().get(item.getItemId());
|
||||
if (costumeData != null && !player.getCostumeList().contains(costumeData.getId())) {
|
||||
getPlayer().addCostume(costumeData.getId());
|
||||
}
|
||||
return null;
|
||||
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_NAMECARD) {
|
||||
if (!player.getNameCardList().contains(item.getItemId())) {
|
||||
getPlayer().addNameCard(item.getItemId());
|
||||
}
|
||||
return null;
|
||||
} else if (tab != null) {
|
||||
GenshinItem existingItem = tab.getItemById(item.getItemId());
|
||||
if (existingItem == null) {
|
||||
// Item type didnt exist before, we will add it to main inventory map if there is enough space
|
||||
if (tab.getSize() >= tab.getMaxCapacity()) {
|
||||
return null;
|
||||
}
|
||||
putItem(item, tab);
|
||||
} else {
|
||||
// Add count
|
||||
existingItem.setCount(Math.min(existingItem.getCount() + item.getCount(), item.getItemData().getStackLimit()));
|
||||
existingItem.save();
|
||||
return existingItem;
|
||||
}
|
||||
}
|
||||
|
||||
// Set ownership and save to db
|
||||
item.save();
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private synchronized void putItem(GenshinItem item, InventoryTab tab) {
|
||||
// Set owner and guid FIRST!
|
||||
item.setOwner(getPlayer());
|
||||
// Put in item store
|
||||
getItems().put(item.getGuid(), item);
|
||||
if (tab != null) {
|
||||
tab.onAddItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void addVirtualItem(int itemId, int count) {
|
||||
switch (itemId) {
|
||||
case 102: // Adventure exp
|
||||
getPlayer().addExpDirectly(count);
|
||||
break;
|
||||
case 201: // Primogem
|
||||
getPlayer().setPrimogems(player.getPrimogems() + count);
|
||||
break;
|
||||
case 202: // Mora
|
||||
getPlayer().setMora(player.getMora() + count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeItems(List<GenshinItem> items) {
|
||||
// TODO Bulk delete
|
||||
for (GenshinItem item : items) {
|
||||
this.removeItem(item, item.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeItem(long guid) {
|
||||
return removeItem(guid, 1);
|
||||
}
|
||||
|
||||
public synchronized boolean removeItem(long guid, int count) {
|
||||
GenshinItem item = this.getItemByGuid(guid);
|
||||
|
||||
if (item == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return removeItem(item, count);
|
||||
}
|
||||
|
||||
public synchronized boolean removeItem(GenshinItem item) {
|
||||
return removeItem(item, item.getCount());
|
||||
}
|
||||
|
||||
public synchronized boolean removeItem(GenshinItem item, int count) {
|
||||
// Sanity check
|
||||
if (count <= 0 || item == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
item.setCount(item.getCount() - count);
|
||||
|
||||
if (item.getCount() <= 0) {
|
||||
// Remove from inventory tab too
|
||||
InventoryTab tab = null;
|
||||
if (item.getItemData() != null) {
|
||||
tab = getInventoryTab(item.getItemData().getItemType());
|
||||
}
|
||||
// Remove if less than 0
|
||||
deleteItem(item, tab);
|
||||
//
|
||||
getPlayer().sendPacket(new PacketStoreItemDelNotify(item));
|
||||
} else {
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(item));
|
||||
}
|
||||
|
||||
// Update in db
|
||||
item.save();
|
||||
|
||||
// Returns true on success
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deleteItem(GenshinItem item, InventoryTab tab) {
|
||||
getItems().remove(item.getGuid());
|
||||
if (tab != null) {
|
||||
tab.onRemoveItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equipItem(long avatarGuid, long equipGuid) {
|
||||
GenshinAvatar avatar = getPlayer().getAvatars().getAvatarByGuid(avatarGuid);
|
||||
GenshinItem item = this.getItemByGuid(equipGuid);
|
||||
|
||||
if (avatar != null && item != null) {
|
||||
return avatar.equipItem(item, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean unequipItem(long avatarGuid, int slot) {
|
||||
GenshinAvatar avatar = getPlayer().getAvatars().getAvatarByGuid(avatarGuid);
|
||||
EquipType equipType = EquipType.getTypeByValue(slot);
|
||||
|
||||
if (avatar != null && equipType != EquipType.EQUIP_WEAPON) {
|
||||
if (avatar.unequipItem(equipType)) {
|
||||
getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(avatar, equipType));
|
||||
avatar.recalcStats();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void loadFromDatabase() {
|
||||
List<GenshinItem> items = DatabaseHelper.getInventoryItems(getPlayer());
|
||||
|
||||
for (GenshinItem item : items) {
|
||||
// Should never happen
|
||||
if (item.getObjectId() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemData itemData = GenshinData.getItemDataMap().get(item.getItemId());
|
||||
if (itemData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
item.setItemData(itemData);
|
||||
|
||||
InventoryTab tab = null;
|
||||
if (item.getItemData() != null) {
|
||||
tab = getInventoryTab(item.getItemData().getItemType());
|
||||
}
|
||||
|
||||
putItem(item, tab);
|
||||
|
||||
// Equip to a character if possible
|
||||
if (item.isEquipped()) {
|
||||
GenshinAvatar avatar = getPlayer().getAvatars().getAvatarById(item.getEquipCharacter());
|
||||
boolean hasEquipped = false;
|
||||
|
||||
if (avatar != null) {
|
||||
hasEquipped = avatar.equipItem(item, false);
|
||||
}
|
||||
|
||||
if (!hasEquipped) {
|
||||
item.setEquipCharacter(0);
|
||||
item.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<GenshinItem> iterator() {
|
||||
return this.getItems().values().iterator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
public interface InventoryTab {
|
||||
public GenshinItem getItemById(int id);
|
||||
|
||||
public void onAddItem(GenshinItem item);
|
||||
|
||||
public void onRemoveItem(GenshinItem item);
|
||||
|
||||
public int getSize();
|
||||
|
||||
public int getMaxCapacity();
|
||||
}
|
||||
27
src/main/java/emu/grasscutter/game/inventory/ItemDef.java
Normal file
27
src/main/java/emu/grasscutter/game/inventory/ItemDef.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
public class ItemDef {
|
||||
private int itemId;
|
||||
private int count;
|
||||
|
||||
public ItemDef(int itemId, int count) {
|
||||
this.itemId = itemId;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public void setItemId(int itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public enum ItemQuality {
|
||||
QUALITY_NONE(0),
|
||||
QUALITY_WHITE(1),
|
||||
QUALITY_GREEN(2),
|
||||
QUALITY_BLUE(3),
|
||||
QUALITY_PURPLE(4),
|
||||
QUALITY_ORANGE(5),
|
||||
QUALITY_ORANGE_SP(105);
|
||||
|
||||
private final int value;
|
||||
private static final Int2ObjectMap<ItemQuality> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, ItemQuality> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
Stream.of(values()).forEach(e -> {
|
||||
map.put(e.getValue(), e);
|
||||
stringMap.put(e.name(), e);
|
||||
});
|
||||
}
|
||||
|
||||
private ItemQuality(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static ItemQuality getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, QUALITY_NONE);
|
||||
}
|
||||
|
||||
public static ItemQuality getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, QUALITY_NONE);
|
||||
}
|
||||
}
|
||||
45
src/main/java/emu/grasscutter/game/inventory/ItemType.java
Normal file
45
src/main/java/emu/grasscutter/game/inventory/ItemType.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public enum ItemType {
|
||||
ITEM_NONE (0),
|
||||
ITEM_VIRTUAL (1),
|
||||
ITEM_MATERIAL (2),
|
||||
ITEM_RELIQUARY (3),
|
||||
ITEM_WEAPON (4),
|
||||
ITEM_DISPLAY (5),
|
||||
ITEM_FURNITURE (6);
|
||||
|
||||
private final int value;
|
||||
private static final Int2ObjectMap<ItemType> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, ItemType> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
Stream.of(values()).forEach(e -> {
|
||||
map.put(e.getValue(), e);
|
||||
stringMap.put(e.name(), e);
|
||||
});
|
||||
}
|
||||
|
||||
private ItemType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static ItemType getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, ITEM_NONE);
|
||||
}
|
||||
|
||||
public static ItemType getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public class MaterialInventoryTab implements InventoryTab {
|
||||
private final Int2ObjectMap<GenshinItem> items;
|
||||
private final int maxCapacity;
|
||||
|
||||
public MaterialInventoryTab(int maxCapacity) {
|
||||
this.items = new Int2ObjectOpenHashMap<>();
|
||||
this.maxCapacity = maxCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenshinItem getItemById(int id) {
|
||||
return this.items.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddItem(GenshinItem item) {
|
||||
this.items.put(item.getItemId(), item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveItem(GenshinItem item) {
|
||||
this.items.remove(item.getItemId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxCapacity() {
|
||||
return this.maxCapacity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public enum MaterialType {
|
||||
MATERIAL_NONE (0),
|
||||
MATERIAL_FOOD (1),
|
||||
MATERIAL_QUEST (2),
|
||||
MATERIAL_EXCHANGE (4),
|
||||
MATERIAL_CONSUME (5),
|
||||
MATERIAL_EXP_FRUIT (6),
|
||||
MATERIAL_AVATAR (7),
|
||||
MATERIAL_ADSORBATE (8),
|
||||
MATERIAL_CRICKET (9),
|
||||
MATERIAL_ELEM_CRYSTAL (10),
|
||||
MATERIAL_WEAPON_EXP_STONE (11),
|
||||
MATERIAL_CHEST (12),
|
||||
MATERIAL_RELIQUARY_MATERIAL (13),
|
||||
MATERIAL_AVATAR_MATERIAL (14),
|
||||
MATERIAL_NOTICE_ADD_HP (15),
|
||||
MATERIAL_SEA_LAMP (16),
|
||||
MATERIAL_SELECTABLE_CHEST (17),
|
||||
MATERIAL_FLYCLOAK (18),
|
||||
MATERIAL_NAMECARD (19),
|
||||
MATERIAL_TALENT (20),
|
||||
MATERIAL_WIDGET (21),
|
||||
MATERIAL_CHEST_BATCH_USE (22),
|
||||
MATERIAL_FAKE_ABSORBATE (23),
|
||||
MATERIAL_CONSUME_BATCH_USE (24),
|
||||
MATERIAL_WOOD (25),
|
||||
MATERIAL_FURNITURE_FORMULA (27),
|
||||
MATERIAL_CHANNELLER_SLAB_BUFF (28),
|
||||
MATERIAL_FURNITURE_SUITE_FORMULA (29),
|
||||
MATERIAL_COSTUME (30);
|
||||
|
||||
private final int value;
|
||||
private static final Int2ObjectMap<MaterialType> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, MaterialType> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
Stream.of(values()).forEach(e -> {
|
||||
map.put(e.getValue(), e);
|
||||
stringMap.put(e.name(), e);
|
||||
});
|
||||
}
|
||||
|
||||
private MaterialType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static MaterialType getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, MATERIAL_NONE);
|
||||
}
|
||||
|
||||
public static MaterialType getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, MATERIAL_NONE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user