mirror of
https://github.com/MikuLeaks/MikuSB.git
synced 2026-06-04 03:03:58 +00:00
Compare commits
2 Commits
d6f57053dd
...
ecf2446598
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ecf2446598 | ||
|
|
75d840974b |
@@ -6,6 +6,7 @@ namespace MikuSB.Data.Excel;
|
||||
public class RecycleExcel : ExcelResource
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public JToken? RecycleReward { get; set; }
|
||||
public JToken? RecycleBase { get; set; }
|
||||
public JToken? RecycleRatio { get; set; }
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ public class SupportCardExcel : ExcelResource
|
||||
public uint Icon { get; set; }
|
||||
public uint ProvideExp { get; set; }
|
||||
public uint Color { get; set; }
|
||||
[JsonProperty("RecycleID")] public int RecycleID { get; set; }
|
||||
[JsonProperty("LevelLimitID")] public int LevelLimitId { get; set; }
|
||||
[JsonProperty("AffixPool")] public List<int> AffixPool { get; set; } = [];
|
||||
[JsonProperty("AffixCost")] public JToken? AffixCostRaw { get; set; }
|
||||
|
||||
316
GameServer/Server/CallGS/Handlers/Inventory/Item_Recycle.cs
Normal file
316
GameServer/Server/CallGS/Handlers/Inventory/Item_Recycle.cs
Normal file
@@ -0,0 +1,316 @@
|
||||
using MikuSB.Data;
|
||||
using MikuSB.Data.Excel;
|
||||
using MikuSB.Database;
|
||||
using MikuSB.Database.Inventory;
|
||||
using MikuSB.Enums.Item;
|
||||
using MikuSB.Proto;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MikuSB.GameServer.Server.CallGS.Handlers.Inventory;
|
||||
|
||||
[CallGSApi("Item_Recycle")]
|
||||
public class Item_Recycle : ICallGSHandler
|
||||
{
|
||||
public async Task Handle(Connection connection, string param, ushort seqNo)
|
||||
{
|
||||
var player = connection.Player!;
|
||||
var req = JsonSerializer.Deserialize<ItemRecycleParam>(param);
|
||||
if (req?.TbItems == null || req.TbItems.Count == 0)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "Item_Recycle", "{\"sErr\":\"error.BadParam\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var config = RecycleConfig.Load();
|
||||
|
||||
var itemsToRecycle = new List<(BaseGameItemInfo Item, int RecycleId)>();
|
||||
foreach (var uniqueId in req.TbItems)
|
||||
{
|
||||
BaseGameItemInfo? item = player.InventoryManager.GetWeaponItem((uint)uniqueId)
|
||||
?? (BaseGameItemInfo?)player.InventoryManager.GetSupportCardItem((uint)uniqueId);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "Item_Recycle", "{\"sErr\":\"error.Recycle.ItemNotExists\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var recycleId = GetRecycleId(item);
|
||||
if (recycleId <= 0 || !config.HasConfig(recycleId))
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "Item_Recycle", "{\"sErr\":\"error.Recycle.ItemCanNotRecycle\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
itemsToRecycle.Add((item, recycleId));
|
||||
}
|
||||
|
||||
var sync = new NtfSyncPlayer();
|
||||
|
||||
foreach (var (item, recycleId) in itemsToRecycle)
|
||||
{
|
||||
var rewards = config.CalcRewards(item, recycleId);
|
||||
foreach (var reward in rewards)
|
||||
await GrantRewardAsync(player, sync, reward);
|
||||
|
||||
RemoveItem(player.InventoryManager.InventoryData, item, sync);
|
||||
}
|
||||
|
||||
DatabaseHelper.SaveDatabaseType(player.InventoryManager.InventoryData);
|
||||
|
||||
await CallGSRouter.SendScript(connection, "Item_Recycle", "{}", sync);
|
||||
}
|
||||
|
||||
private static int GetRecycleId(BaseGameItemInfo item)
|
||||
{
|
||||
if (item.ItemType == ItemTypeEnum.TYPE_WEAPON)
|
||||
{
|
||||
var t = GameData.WeaponData.Values.FirstOrDefault(x =>
|
||||
GameResourceTemplateId.FromGdpl(x.Genre, x.Detail, x.Particular, x.Level) == item.TemplateId);
|
||||
return t?.RecycleID ?? 0;
|
||||
}
|
||||
if (item.ItemType == ItemTypeEnum.TYPE_SUPPORT)
|
||||
{
|
||||
var t = GameData.SupportCardData.FirstOrDefault(x => x.TemplateId == item.TemplateId);
|
||||
return t?.RecycleID ?? 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void RemoveItem(InventoryData inventory, BaseGameItemInfo item, NtfSyncPlayer sync)
|
||||
{
|
||||
var removed = item.ToProto();
|
||||
removed.Count = 0;
|
||||
sync.Items.Add(removed);
|
||||
|
||||
if (item.ItemType == ItemTypeEnum.TYPE_WEAPON)
|
||||
inventory.Weapons.Remove(item.UniqueId);
|
||||
else
|
||||
inventory.SupportCards.Remove(item.UniqueId);
|
||||
}
|
||||
|
||||
private static async Task GrantRewardAsync(GameServer.Game.Player.PlayerInstance player, NtfSyncPlayer sync, IReadOnlyList<uint> reward)
|
||||
{
|
||||
if (reward.Count < 5) return;
|
||||
|
||||
var itemType = (ItemTypeEnum)reward[0];
|
||||
var detail = reward[1];
|
||||
var particular = reward[2];
|
||||
var level = reward[3];
|
||||
var count = Math.Max(1u, reward[4]);
|
||||
|
||||
switch (itemType)
|
||||
{
|
||||
case ItemTypeEnum.TYPE_SUPPLIES:
|
||||
{
|
||||
var templateId = (uint)GameResourceTemplateId.FromGdpl(reward[0], detail, particular, level);
|
||||
if (!GameData.SuppliesData.TryGetValue(templateId, out var supplies)) break;
|
||||
var item = await player.InventoryManager.AddSuppliesItem(supplies, count, sendPacket: false);
|
||||
if (item != null) sync.Items.Add(item.ToProto());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class RecycleConfig
|
||||
{
|
||||
private readonly Dictionary<int, RecycleEntry> _entries;
|
||||
private readonly List<SupplyTemplate> _weaponSupplies;
|
||||
private readonly List<SupplyTemplate> _supportSupplies;
|
||||
private readonly Dictionary<int, ulong> _weaponLevelExp;
|
||||
private readonly Dictionary<int, ulong> _supportLevelExp;
|
||||
|
||||
private readonly Dictionary<int, ulong> _weaponLevelExpSsr;
|
||||
private readonly Dictionary<int, ulong> _supportLevelExpSsr;
|
||||
|
||||
private RecycleConfig(
|
||||
Dictionary<int, RecycleEntry> entries,
|
||||
List<SupplyTemplate> weaponSupplies,
|
||||
List<SupplyTemplate> supportSupplies,
|
||||
Dictionary<int, ulong> weaponLevelExp,
|
||||
Dictionary<int, ulong> weaponLevelExpSsr,
|
||||
Dictionary<int, ulong> supportLevelExp,
|
||||
Dictionary<int, ulong> supportLevelExpSsr)
|
||||
{
|
||||
_entries = entries;
|
||||
_weaponSupplies = weaponSupplies;
|
||||
_supportSupplies = supportSupplies;
|
||||
_weaponLevelExp = weaponLevelExp;
|
||||
_weaponLevelExpSsr = weaponLevelExpSsr;
|
||||
_supportLevelExp = supportLevelExp;
|
||||
_supportLevelExpSsr = supportLevelExpSsr;
|
||||
}
|
||||
|
||||
public static RecycleConfig Load()
|
||||
{
|
||||
var entries = new Dictionary<int, RecycleEntry>();
|
||||
foreach (var row in GameData.RecycleData.Values)
|
||||
{
|
||||
var fixedRewards = ParseRewards(row.RecycleReward);
|
||||
var recycleBase = GetUInt(row.RecycleBase);
|
||||
var recycleRatio = GetDecimal(row.RecycleRatio);
|
||||
entries[row.ID] = new RecycleEntry(fixedRewards, recycleBase, recycleRatio);
|
||||
}
|
||||
|
||||
var weaponSupplies = new List<SupplyTemplate>();
|
||||
var supportSupplies = new List<SupplyTemplate>();
|
||||
foreach (var s in GameData.AllSuppliesData)
|
||||
{
|
||||
if (s.ProvideExp == 0) continue;
|
||||
if (s.Genre == 5 && s.Detail == 2)
|
||||
weaponSupplies.Add(new SupplyTemplate(s.Genre, s.Detail, s.Particular, s.Level, s.ProvideExp));
|
||||
else if (s.Genre == 5 && s.Detail == 3)
|
||||
supportSupplies.Add(new SupplyTemplate(s.Genre, s.Detail, s.Particular, s.Level, s.ProvideExp));
|
||||
}
|
||||
weaponSupplies.Sort((a, b) => b.ProvideExp.CompareTo(a.ProvideExp));
|
||||
supportSupplies.Sort((a, b) => b.ProvideExp.CompareTo(a.ProvideExp));
|
||||
|
||||
var weaponLevelExp = BuildLevelExpTable(GameData.UpgradeExpData.Values.Select(x => (x.Lv, x.WeaponNeedExp)));
|
||||
var weaponLevelExpSsr = BuildLevelExpTable(GameData.UpgradeExpData.Values.Select(x => (x.Lv, x.SSRWeaponNeedExp)));
|
||||
var supportLevelExp = BuildLevelExpTable(GameData.UpgradeExpData.Values.Select(x => (x.Lv, x.SusNeedExp)));
|
||||
var supportLevelExpSsr = BuildLevelExpTable(GameData.UpgradeExpData.Values.Select(x => (x.Lv, x.SSRSusNeedExp)));
|
||||
|
||||
return new RecycleConfig(entries, weaponSupplies, supportSupplies, weaponLevelExp, weaponLevelExpSsr, supportLevelExp, supportLevelExpSsr);
|
||||
}
|
||||
|
||||
public bool HasConfig(int recycleId) => _entries.ContainsKey(recycleId);
|
||||
|
||||
public List<IReadOnlyList<uint>> CalcRewards(BaseGameItemInfo item, int recycleId)
|
||||
{
|
||||
if (!_entries.TryGetValue(recycleId, out var entry))
|
||||
return [];
|
||||
|
||||
var rewards = new List<IReadOnlyList<uint>>(entry.FixedRewards);
|
||||
|
||||
var expRewards = CalcExpRewards(item, entry);
|
||||
rewards.AddRange(expRewards);
|
||||
|
||||
return rewards;
|
||||
}
|
||||
|
||||
private List<IReadOnlyList<uint>> CalcExpRewards(BaseGameItemInfo item, RecycleEntry entry)
|
||||
{
|
||||
if (entry.RecycleRatio == 0) return [];
|
||||
|
||||
List<SupplyTemplate> supplies;
|
||||
Dictionary<int, ulong> levelExp;
|
||||
|
||||
if (item.ItemType == ItemTypeEnum.TYPE_WEAPON)
|
||||
{
|
||||
supplies = _weaponSupplies;
|
||||
var color = GetItemColor(item);
|
||||
levelExp = color == 5 ? _weaponLevelExpSsr : _weaponLevelExp;
|
||||
}
|
||||
else if (item.ItemType == ItemTypeEnum.TYPE_SUPPORT)
|
||||
{
|
||||
supplies = _supportSupplies;
|
||||
var color = GetItemColor(item);
|
||||
levelExp = color == 5 ? _supportLevelExpSsr : _supportLevelExp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var baseExp = (ulong)entry.RecycleBase;
|
||||
var levelAccum = levelExp.GetValueOrDefault((int)item.Level);
|
||||
var totalExp = (ulong)Math.Floor((baseExp + levelAccum + item.Exp) * (double)entry.RecycleRatio);
|
||||
|
||||
if (totalExp == 0 || supplies.Count == 0) return [];
|
||||
|
||||
var rewards = new List<IReadOnlyList<uint>>();
|
||||
var remaining = totalExp;
|
||||
foreach (var supply in supplies)
|
||||
{
|
||||
if (remaining == 0) break;
|
||||
var count = remaining / supply.ProvideExp;
|
||||
if (count == 0) continue;
|
||||
remaining -= count * supply.ProvideExp;
|
||||
rewards.Add([supply.Genre, supply.Detail, supply.Particular, supply.Level, (uint)Math.Min(count, 99999)]);
|
||||
}
|
||||
return rewards;
|
||||
}
|
||||
|
||||
private static List<IReadOnlyList<uint>> ParseRewards(JToken? token)
|
||||
{
|
||||
if (token == null) return [];
|
||||
|
||||
if (token is JArray outerArray)
|
||||
{
|
||||
var rewards = new List<IReadOnlyList<uint>>();
|
||||
foreach (var element in outerArray)
|
||||
{
|
||||
if (element is JArray inner && inner.Count >= 4)
|
||||
{
|
||||
var reward = inner.Select(x => x.Value<uint>()).ToArray();
|
||||
if (reward.Length < 5)
|
||||
reward = [.. reward, 1];
|
||||
rewards.Add(reward);
|
||||
}
|
||||
}
|
||||
return rewards;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private static int GetItemColor(BaseGameItemInfo item)
|
||||
{
|
||||
if (item.ItemType == ItemTypeEnum.TYPE_WEAPON)
|
||||
{
|
||||
var t = GameData.WeaponData.Values.FirstOrDefault(x =>
|
||||
GameResourceTemplateId.FromGdpl(x.Genre, x.Detail, x.Particular, x.Level) == item.TemplateId);
|
||||
return t?.Color ?? 0;
|
||||
}
|
||||
if (item.ItemType == ItemTypeEnum.TYPE_SUPPORT)
|
||||
{
|
||||
var t = GameData.SupportCardData.FirstOrDefault(x => x.TemplateId == item.TemplateId);
|
||||
return (int)(t?.Color ?? 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static Dictionary<int, ulong> BuildLevelExpTable(IEnumerable<(int Lv, uint NeedExp)> source)
|
||||
{
|
||||
var table = new Dictionary<int, ulong>();
|
||||
ulong accumulated = 0;
|
||||
foreach (var (lv, needExp) in source.OrderBy(x => x.Lv))
|
||||
{
|
||||
table[lv] = accumulated;
|
||||
accumulated += needExp;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
private static uint GetUInt(JToken? token) => token?.Type switch
|
||||
{
|
||||
JTokenType.Integer => token.Value<uint>(),
|
||||
JTokenType.Float => (uint)Math.Max(0, token.Value<decimal>()),
|
||||
JTokenType.String when uint.TryParse(token.Value<string>(), out var r) => r,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
private static decimal GetDecimal(JToken? token) => token?.Type switch
|
||||
{
|
||||
JTokenType.Integer => token.Value<decimal>(),
|
||||
JTokenType.Float => token.Value<decimal>(),
|
||||
JTokenType.String when decimal.TryParse(token.Value<string>(), out var r) => r,
|
||||
_ => 0m
|
||||
};
|
||||
}
|
||||
|
||||
internal readonly record struct RecycleEntry(
|
||||
List<IReadOnlyList<uint>> FixedRewards,
|
||||
uint RecycleBase,
|
||||
decimal RecycleRatio);
|
||||
|
||||
internal readonly record struct SupplyTemplate(uint Genre, uint Detail, uint Particular, uint Level, uint ProvideExp);
|
||||
|
||||
internal sealed class ItemRecycleParam
|
||||
{
|
||||
[JsonPropertyName("tbItems")]
|
||||
public List<int> TbItems { get; set; } = [];
|
||||
}
|
||||
183
GameServer/Server/CallGS/Handlers/Weapon/Weapon_OneKeyToMax.cs
Normal file
183
GameServer/Server/CallGS/Handlers/Weapon/Weapon_OneKeyToMax.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using MikuSB.Data;
|
||||
using MikuSB.Database;
|
||||
using MikuSB.Database.Inventory;
|
||||
using MikuSB.Proto;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MikuSB.GameServer.Server.CallGS.Handlers.Weapon;
|
||||
|
||||
[CallGSApi("Weapon_OneKeyToMax")]
|
||||
public class Weapon_OneKeyToMax : ICallGSHandler
|
||||
{
|
||||
private const uint MaxBreak = 6;
|
||||
|
||||
public async Task Handle(Connection connection, string param, ushort seqNo)
|
||||
{
|
||||
var player = connection.Player!;
|
||||
var req = JsonSerializer.Deserialize<OneKeyToMaxParam>(param);
|
||||
if (req == null || req.Id <= 0 || req.TbBreakUpgradeMat == null || req.TbBreakUpgradeMat.Count == 0)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "Weapon_OneKeyToMax", "{\"sErr\":\"error.BadParam\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var weapon = player.InventoryManager.GetWeaponItem((uint)req.Id);
|
||||
if (weapon == null)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "Weapon_OneKeyToMax", "{\"sErr\":\"error.BadParam\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var config = WeaponUpgradeConfig.Load();
|
||||
if (!config.TryGetWeaponTemplate(weapon.TemplateId, out var targetTemplate))
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "Weapon_OneKeyToMax", "{\"sErr\":\"error.BadParam\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var inventory = player.InventoryManager.InventoryData;
|
||||
var equippedWeaponIds = player.CharacterManager.CharacterData.Characters
|
||||
.Select(x => x.WeaponUniqueId)
|
||||
.Where(x => x != 0)
|
||||
.ToHashSet();
|
||||
|
||||
// Validate all materials upfront before making any changes
|
||||
foreach (var stage in req.TbBreakUpgradeMat)
|
||||
{
|
||||
if (stage == null || stage.Count < 3) continue;
|
||||
var matList = stage[1].Deserialize<List<List<int>>>();
|
||||
if (matList == null) continue;
|
||||
|
||||
foreach (var entry in matList)
|
||||
{
|
||||
if (entry == null || entry.Count < 2) continue;
|
||||
var itemId = (uint)Math.Max(0, entry[0]);
|
||||
var count = (uint)Math.Max(0, entry[1]);
|
||||
if (itemId == 0 || count == 0) continue;
|
||||
|
||||
if (itemId == weapon.UniqueId)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "Weapon_OneKeyToMax", "{\"sErr\":\"tip.material_not_enough\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var material = FindInventoryItem(inventory, itemId);
|
||||
if (material == null || material.ItemCount < count)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "Weapon_OneKeyToMax", "{\"sErr\":\"tip.material_not_enough\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (material is GameWeaponInfo materialWeapon &&
|
||||
(materialWeapon.EquipAvatarId != 0 || equippedWeaponIds.Contains(materialWeapon.UniqueId)))
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "Weapon_OneKeyToMax", "{\"sErr\":\"tip.material_not_enough\"}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var syncItems = new List<Item>();
|
||||
var weaponLevel = weapon.Level == 0 ? 1u : weapon.Level;
|
||||
weapon.Level = weaponLevel;
|
||||
|
||||
// Process each break stage
|
||||
foreach (var stage in req.TbBreakUpgradeMat)
|
||||
{
|
||||
if (stage == null || stage.Count < 3) continue;
|
||||
|
||||
var matList = stage[1].Deserialize<List<List<int>>>();
|
||||
var doBreak = stage[2].GetInt32() == 1;
|
||||
|
||||
if (matList != null && matList.Count > 0)
|
||||
{
|
||||
// Aggregate materials, skip duplicates
|
||||
var materialsUsed = new Dictionary<uint, uint>();
|
||||
foreach (var entry in matList)
|
||||
{
|
||||
if (entry == null || entry.Count < 2) continue;
|
||||
var itemId = (uint)Math.Max(0, entry[0]);
|
||||
var count = (uint)Math.Max(0, entry[1]);
|
||||
if (itemId == 0 || count == 0) continue;
|
||||
materialsUsed[itemId] = materialsUsed.GetValueOrDefault(itemId) + count;
|
||||
}
|
||||
|
||||
ulong totalExp = 0;
|
||||
foreach (var (itemId, count) in materialsUsed)
|
||||
{
|
||||
var material = FindInventoryItem(inventory, itemId)!;
|
||||
if (config.TryGetMaterialGain(material, out var gainExp))
|
||||
totalExp += gainExp * count;
|
||||
}
|
||||
|
||||
// Consume materials
|
||||
foreach (var (itemId, count) in materialsUsed)
|
||||
{
|
||||
var material = FindInventoryItem(inventory, itemId)!;
|
||||
material.ItemCount -= count;
|
||||
if (material.ItemCount == 0)
|
||||
{
|
||||
RemoveInventoryItem(inventory, itemId);
|
||||
var proto = material.ToProto();
|
||||
proto.Count = 0;
|
||||
syncItems.Add(proto);
|
||||
}
|
||||
else
|
||||
{
|
||||
syncItems.Add(material.ToProto());
|
||||
}
|
||||
}
|
||||
|
||||
// Apply exp to weapon
|
||||
if (totalExp > 0)
|
||||
{
|
||||
var maxLevel = config.GetWeaponMaxLevel(targetTemplate.BreakLimitId, weapon.Break);
|
||||
var (newLevel, newExp) = config.ApplyWeaponExp(weapon.Level, weapon.Exp, totalExp, targetTemplate.Color, maxLevel);
|
||||
weapon.Level = newLevel;
|
||||
weapon.Exp = newExp;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform break
|
||||
if (doBreak && weapon.Break < MaxBreak)
|
||||
weapon.Break++;
|
||||
}
|
||||
|
||||
syncItems.Add(weapon.ToProto());
|
||||
DatabaseHelper.SaveDatabaseType(inventory);
|
||||
|
||||
var finalMaxLevel = config.GetWeaponMaxLevel(targetTemplate.BreakLimitId, weapon.Break);
|
||||
var bMaxUnlock = finalMaxLevel > 0 && weapon.Level >= finalMaxLevel;
|
||||
|
||||
var sync = new NtfSyncPlayer();
|
||||
sync.Items.AddRange(syncItems);
|
||||
|
||||
await CallGSRouter.SendScript(connection, "Weapon_OneKeyToMax",
|
||||
$"{{\"bMaxUnLock\":{(bMaxUnlock ? "true" : "false")}}}", sync);
|
||||
}
|
||||
|
||||
private static BaseGameItemInfo? FindInventoryItem(InventoryData inventory, uint itemId)
|
||||
{
|
||||
if (inventory.Weapons.TryGetValue(itemId, out var weapon)) return weapon;
|
||||
if (inventory.Skins.TryGetValue(itemId, out var skin)) return skin;
|
||||
if (inventory.Items.TryGetValue(itemId, out var item)) return item;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void RemoveInventoryItem(InventoryData inventory, uint itemId)
|
||||
{
|
||||
inventory.Weapons.Remove(itemId);
|
||||
inventory.Skins.Remove(itemId);
|
||||
inventory.Items.Remove(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class OneKeyToMaxParam
|
||||
{
|
||||
[JsonPropertyName("Id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonPropertyName("tbBreakUpgradeMat")]
|
||||
public List<List<System.Text.Json.JsonElement>> TbBreakUpgradeMat { get; set; } = [];
|
||||
}
|
||||
@@ -258,9 +258,9 @@ internal sealed class WeaponUpgradeConfig
|
||||
var weaponTemplates = GameData.WeaponData.Values.ToDictionary(
|
||||
x => GameResourceTemplateId.FromGdpl(x.Genre, x.Detail, x.Particular, x.Level),
|
||||
x => new MaterialTemplate(x.Color, x.ProvideExp, x.ConsumeGold, x.RecycleID, x.BreakLimitID));
|
||||
var suppliesTemplates = GameData.SuppliesData.Values.ToDictionary(
|
||||
x => GameResourceTemplateId.FromGdpl(x.Genre, x.Detail, x.Particular, x.Level),
|
||||
x => new MaterialTemplate(x.Color, x.ProvideExp, x.ConsumeGold, 0, 0));
|
||||
var suppliesTemplates = GameData.AllSuppliesData
|
||||
.GroupBy(x => GameResourceTemplateId.FromGdpl(x.Genre, x.Detail, x.Particular, x.Level))
|
||||
.ToDictionary(g => g.Key, g => new MaterialTemplate(g.First().Color, g.First().ProvideExp, g.First().ConsumeGold, 0, 0));
|
||||
|
||||
return new WeaponUpgradeConfig(normalExp, ssrExp, breakLimits, recycleById, weaponTemplates, suppliesTemplates);
|
||||
}
|
||||
@@ -271,6 +271,24 @@ internal sealed class WeaponUpgradeConfig
|
||||
public bool TryGetSuppliesTemplate(ulong templateId, out MaterialTemplate template) =>
|
||||
_suppliesTemplates.TryGetValue(templateId, out template!);
|
||||
|
||||
public bool TryGetMaterialGain(BaseGameItemInfo item, out ulong exp)
|
||||
{
|
||||
exp = 0;
|
||||
if (TryGetWeaponTemplate(item.TemplateId, out var weaponTemplate))
|
||||
{
|
||||
exp = weaponTemplate.ProvideExp;
|
||||
if (item is GameWeaponInfo weapon && weapon.Level > 1)
|
||||
exp += GetWeaponRecycleExp(weaponTemplate, weapon.Level);
|
||||
return true;
|
||||
}
|
||||
if (TryGetSuppliesTemplate(item.TemplateId, out var suppliesTemplate))
|
||||
{
|
||||
exp = suppliesTemplate.ProvideExp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ulong GetWeaponRecycleExp(MaterialTemplate template, uint level)
|
||||
{
|
||||
if (template.RecycleId <= 0 || !_recycleById.TryGetValue(template.RecycleId, out var recycle))
|
||||
|
||||
Reference in New Issue
Block a user