From aacf013c065db9b2daa14be83e475a66dab9be0a Mon Sep 17 00:00:00 2001
From: AnimeGitB <AnimeGitB@bigblueball.in>
Date: Tue, 18 Oct 2022 15:11:10 +1030
Subject: [PATCH] Fix gacha avatars (fixes #1870)

---
 .../data/excels/AvatarSkillDepotData.java     |  9 +++++
 .../emu/grasscutter/data/excels/ItemData.java |  2 +
 .../grasscutter/game/gacha/GachaSystem.java   |  8 ++--
 .../ItemUseAction/ItemUseGainAvatar.java      |  7 +++-
 .../game/systems/InventorySystem.java         | 37 ++++++++++++++-----
 5 files changed, 49 insertions(+), 14 deletions(-)

diff --git a/src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java b/src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java
index cd4fcd27..30533d61 100644
--- a/src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java
+++ b/src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java
@@ -1,6 +1,7 @@
 package emu.grasscutter.data.excels;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.IntStream;
 
 import emu.grasscutter.data.GameData;
@@ -36,6 +37,7 @@ public class AvatarSkillDepotData extends GameResource {
     @Getter private AvatarSkillData energySkillData;
     @Getter private ElementType elementType;
     @Getter private IntList abilities;
+    @Getter private int talentCostItemId;
 
     @Override
     public int getId() {
@@ -66,6 +68,13 @@ public class AvatarSkillDepotData extends GameResource {
                 this.setAbilities(new AbilityEmbryoEntry(getSkillDepotAbilityGroup(), config.abilities.stream().map(Object::toString).toArray(String[]::new)));
             }
         }
+
+        // Get constellation item from GameData
+        Optional.ofNullable(this.talents)
+            .map(talents -> talents.get(0))
+            .map(i -> GameData.getAvatarTalentDataMap().get((int) i))
+            .map(talentData -> talentData.getMainCostItemId())
+            .ifPresent(itemId -> this.talentCostItemId = itemId);
     }
 
     public static class InherentProudSkillOpens {
diff --git a/src/main/java/emu/grasscutter/data/excels/ItemData.java b/src/main/java/emu/grasscutter/data/excels/ItemData.java
index 7293f8c1..1161e671 100644
--- a/src/main/java/emu/grasscutter/data/excels/ItemData.java
+++ b/src/main/java/emu/grasscutter/data/excels/ItemData.java
@@ -2,6 +2,7 @@ package emu.grasscutter.data.excels;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 import com.google.gson.annotations.SerializedName;
 import emu.grasscutter.data.GameResource;
@@ -133,6 +134,7 @@ public class ItemData extends GameResource {
             this.itemUseActions = this.itemUse.stream()
                                     .filter(x -> x.getUseOp() != ItemUseOp.ITEM_USE_NONE)
                                     .map(ItemUseAction::fromItemUseData)
+                                    .filter(Objects::nonNull)
                                     .toList();
         }
     }
diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java b/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java
index 69246696..6456d464 100644
--- a/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java
+++ b/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java
@@ -316,10 +316,10 @@ public class GachaSystem extends BaseGameSystem {
                             pools.removeFromAllPools(new int[] {itemId});
                         }
                         addStarglitter = (itemData.getRankLevel()==5)? 10 : 2;
-                        int constItemId = itemId + 100;
-                        GameItem constItem = inventory.getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(constItemId);
-                        gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(ItemParam.newBuilder().setItemId(constItemId).setCount(1)).setIsTransferItemNew(constItem == null));
-                        inventory.addItem(constItemId, 1);
+                        int constItemId = itemId + 100;  // This may not hold true for future characters. Examples of strictly correct constellation item lookup are elsewhere for now.
+                        boolean haveConstItem = inventory.getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(constItemId) == null;
+                        gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(ItemParam.newBuilder().setItemId(constItemId).setCount(1)).setIsTransferItemNew(haveConstItem));
+                        //inventory.addItem(constItemId, 1);  // This is now managed by the avatar card item itself
                     }
                     isTransferItem = true;
                     break;
diff --git a/src/main/java/emu/grasscutter/game/props/ItemUseAction/ItemUseGainAvatar.java b/src/main/java/emu/grasscutter/game/props/ItemUseAction/ItemUseGainAvatar.java
index fecd509a..bdfd39bc 100644
--- a/src/main/java/emu/grasscutter/game/props/ItemUseAction/ItemUseGainAvatar.java
+++ b/src/main/java/emu/grasscutter/game/props/ItemUseAction/ItemUseGainAvatar.java
@@ -1,5 +1,7 @@
 package emu.grasscutter.game.props.ItemUseAction;
 
+import java.util.Optional;
+
 import emu.grasscutter.game.avatar.Avatar;
 import emu.grasscutter.game.props.ItemUseOp;
 import emu.grasscutter.game.systems.InventorySystem;
@@ -36,7 +38,10 @@ public class ItemUseGainAvatar extends ItemUseInt {
             params.player.addAvatar(avatar);
             return true;
         } else {
-            int itemId = (this.i % 1000) + 100;
+            int itemId = Optional.ofNullable(params.player.getAvatars().getAvatarById(this.i))
+                .map(Avatar::getSkillDepot)
+                .map(depot -> depot.getTalentCostItemId())
+                .orElse((this.i % 1000) + 100);
             return params.player.getInventory().addItem(itemId);
         }
     }
diff --git a/src/main/java/emu/grasscutter/game/systems/InventorySystem.java b/src/main/java/emu/grasscutter/game/systems/InventorySystem.java
index baa8ba15..23876aa6 100644
--- a/src/main/java/emu/grasscutter/game/systems/InventorySystem.java
+++ b/src/main/java/emu/grasscutter/game/systems/InventorySystem.java
@@ -8,7 +8,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import emu.grasscutter.Grasscutter;
@@ -799,18 +798,38 @@ public class InventorySystem extends BaseGameSystem {
                 .reduce(false, (a,b) -> a || b);  // Don't short-circuit!!!
     }
 
-    public static synchronized int checkPlayerAvatarConstellationLevel(Player player, int itemId) {
-        ItemData itemData = GameData.getItemDataMap().get(itemId);
-        if ((itemData == null) || (itemData.getMaterialType() != MaterialType.MATERIAL_AVATAR)) {
-            return -2;  // Not an Avatar
+    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);
         }
-        Avatar avatar = player.getAvatars().getAvatarById((itemId % 1000) + 10000000);
-        if (avatar == null) {
+
+        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();
-        GameItem constItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId + 100);
+        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;
     }
-- 
GitLab