avatar & weapon level slider

This commit is contained in:
DismissedLight
2023-02-24 13:54:43 +08:00
parent 6d66af6c84
commit ece2737633
45 changed files with 1065 additions and 53 deletions

View File

@@ -12,6 +12,7 @@ using Snap.Hutao.Service.Navigation;
using System.Diagnostics;
using System.IO;
using System.Security.Principal;
using Windows.ApplicationModel;
namespace Snap.Hutao.Core.LifeCycle;
@@ -53,11 +54,6 @@ internal static class Activation
/// <returns>是否提升了权限</returns>
public static bool GetElevated()
{
if (Debugger.IsAttached)
{
return true;
}
using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
{
WindowsPrincipal principal = new(identity);
@@ -65,6 +61,19 @@ internal static class Activation
}
}
/// <summary>
/// 以管理员模式重启
/// </summary>
/// <returns>任务</returns>
public static async ValueTask RestartAsElevatedAsync()
{
if (GetElevated())
{
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
Process.GetCurrentProcess().Kill();
}
}
/// <summary>
/// 响应激活事件
/// 激活事件一般不会在UI线程上触发

View File

@@ -192,11 +192,6 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<BackdropTypeChangedMe
// 48 is the navigation button leftInset
RectInt32 dragRect = StructMarshal.RectInt32(new(48, 0), titleBar.ActualSize).Scale(scale);
appTitleBar.SetDragRectangles(dragRect.Enumerate().ToArray());
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/2976
SizeInt32 size = appWindow.ClientSize;
size.Height -= (int)(31 * scale);
appWindow.ResizeClient(size);
}
}
}

View File

@@ -18,7 +18,6 @@
<cwuc:SwitchPresenter.ContentTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
<ContentThemeTransition/>
</TransitionCollection>
</cwuc:SwitchPresenter.ContentTransitions>
<cwuc:Case>

View File

@@ -0,0 +1,167 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.ComponentModel;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata;
namespace Snap.Hutao.Model.Binding.BaseValue;
/// <summary>
/// 基础数值信息
/// </summary>
internal sealed class BaseValueInfo : ObservableObject
{
private readonly List<PropertyCurveValue> propValues;
private readonly Dictionary<int, Dictionary<GrowCurveType, float>> growCurveMap;
private readonly Dictionary<int, Promote>? promoteMap;
private int currentLevel;
private List<NameValue<string>> values = default!;
private bool promoted = true;
/// <summary>
/// 构造一个新的基础数值信息
/// </summary>
/// <param name="maxLevel">最大等级</param>
/// <param name="propValues">属性与初始值</param>
/// <param name="growCurveMap">生长曲线</param>
/// <param name="promoteMap">突破加成</param>
public BaseValueInfo(int maxLevel, List<PropertyCurveValue> propValues, Dictionary<int, Dictionary<GrowCurveType, float>> growCurveMap, Dictionary<int, Promote>? promoteMap = null)
{
this.propValues = propValues;
this.growCurveMap = growCurveMap;
this.promoteMap = promoteMap;
MaxLevel = maxLevel;
CurrentLevel = maxLevel;
}
/// <summary>
/// 最大等级
/// </summary>
public int MaxLevel { get; }
/// <summary>
/// 对应的基础值
/// </summary>
public List<NameValue<string>> Values { get => values; set => SetProperty(ref values, value); }
/// <summary>
/// 当前等级
/// </summary>
public int CurrentLevel
{
get => currentLevel;
set
{
if (SetProperty(ref currentLevel, value))
{
OnPropertyChanged(nameof(CurrentLevelFormatted));
UpdateValues(value, promoted);
}
}
}
/// <summary>
/// 格式化当前等级
/// </summary>
public string CurrentLevelFormatted
{
get => $"Lv.{CurrentLevel}";
}
/// <summary>
/// 是否突破
/// </summary>
public bool Promoted
{
get => promoted;
set
{
if (SetProperty(ref promoted, value))
{
UpdateValues(currentLevel, value);
}
}
}
private void UpdateValues(int level, bool promoted)
{
List<NameValue<string>> values = new(propValues.Count);
foreach (PropertyCurveValue propValue in propValues)
{
float value = propValue.Value * growCurveMap[level].GetValueOrDefault(propValue.Type);
if (promoteMap != null)
{
int promoteLevel = GetPromoteLevel(level, promoted);
float addValue = promoteMap[promoteLevel].AddProperties.GetValueOrDefault(propValue.Property, 0F);
value += addValue;
}
values.Add(Metadata.Converter.PropertyDescriptor.FormatNameValue(propValue.Property, value));
}
Values = values;
}
private int GetPromoteLevel(int level, bool promoted)
{
if (MaxLevel <= 70)
{
if (promoted)
{
return level switch
{
>= 60 => 4,
>= 50 => 3,
>= 40 => 2,
>= 20 => 1,
_ => 0,
};
}
else
{
return level switch
{
> 60 => 4,
> 50 => 3,
> 40 => 2,
> 20 => 1,
_ => 0,
};
}
}
else
{
if (promoted)
{
return level switch
{
>= 80 => 6,
>= 70 => 5,
>= 60 => 4,
>= 50 => 3,
>= 40 => 2,
>= 20 => 1,
_ => 0,
};
}
else
{
return level switch
{
> 80 => 6,
> 70 => 5,
> 60 => 4,
> 50 => 3,
> 40 => 2,
> 20 => 1,
_ => 0,
};
}
}
}
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
namespace Snap.Hutao.Model.Binding.BaseValue;
/// <summary>
/// 战斗属性与初始值
/// </summary>
internal sealed class PropertyCurveValue
{
/// <summary>
/// 构造一个新的战斗属性与初始值
/// </summary>
/// <param name="property">战斗属性</param>
/// <param name="type">类型</param>
/// <param name="value">初始值</param>
public PropertyCurveValue(FightProperty property, GrowCurveType type, float value)
{
Property = property;
Type = type;
Value = value;
}
/// <summary>
/// 战斗属性值
/// </summary>
public FightProperty Property { get; }
/// <summary>
/// 曲线类型
/// </summary>
public GrowCurveType Type { get; }
/// <summary>
/// 初始值
/// </summary>
public float Value { get; }
}

View File

@@ -25,7 +25,7 @@ internal sealed class CalculableAvatar : ObservableObject, ICalculableAvatar
{
AvatarId = avatar.Id;
LevelMin = 1;
LevelMax = int.Parse(avatar.Property.Parameters.Last().Level);
LevelMax = 90;
Skills = avatar.SkillDepot.EnumerateCompositeSkillsNoInherents().Select(p => p.ToCalculable()).ToList();
Name = avatar.Name;
Icon = AvatarIconConverter.IconNameToUri(avatar.Icon);

View File

@@ -25,7 +25,7 @@ internal class CalculableWeapon : ObservableObject, ICalculableWeapon
{
WeaponId = weapon.Id;
LevelMin = 1;
LevelMax = int.Parse(weapon.Property.Parameters.Last().Level);
LevelMax = (int)weapon.Quality >= 3 ? 90 : 70;
Name = weapon.Name;
Icon = EquipIconConverter.IconNameToUri(weapon.Icon);
Quality = weapon.RankLevel;

View File

@@ -0,0 +1,74 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
/// <summary>
/// 生长曲线
/// </summary>
[HighQuality]
[SuppressMessage("", "SA1602")]
internal enum GrowCurveType
{
GROW_CURVE_NONE = 0,
GROW_CURVE_HP = 1,
GROW_CURVE_ATTACK = 2,
GROW_CURVE_STAMINA = 3,
GROW_CURVE_STRIKE = 4,
GROW_CURVE_ANTI_STRIKE = 5,
GROW_CURVE_ANTI_STRIKE1 = 6,
GROW_CURVE_ANTI_STRIKE2 = 7,
GROW_CURVE_ANTI_STRIKE3 = 8,
GROW_CURVE_STRIKE_HURT = 9,
GROW_CURVE_ELEMENT = 10,
GROW_CURVE_KILL_EXP = 11,
GROW_CURVE_DEFENSE = 12,
GROW_CURVE_ATTACK_BOMB = 13,
GROW_CURVE_HP_LITTLEMONSTER = 14,
GROW_CURVE_ELEMENT_MASTERY = 15,
GROW_CURVE_PROGRESSION = 16,
GROW_CURVE_DEFENDING = 17,
GROW_CURVE_MHP = 18,
GROW_CURVE_MATK = 19,
GROW_CURVE_TOWERATK = 20,
GROW_CURVE_HP_S5 = 21,
GROW_CURVE_HP_S4 = 22,
GROW_CURVE_HP_2 = 23,
GROW_CURVE_ATTACK_2 = 24,
GROW_CURVE_ATTACK_S5 = 31,
GROW_CURVE_ATTACK_S4 = 32,
GROW_CURVE_ATTACK_S3 = 33,
GROW_CURVE_STRIKE_S5 = 34,
GROW_CURVE_DEFENSE_S5 = 41,
GROW_CURVE_DEFENSE_S4 = 42,
GROW_CURVE_ATTACK_101 = 1101,
GROW_CURVE_ATTACK_102 = 1102,
GROW_CURVE_ATTACK_103 = 1103,
GROW_CURVE_ATTACK_104 = 1104,
GROW_CURVE_ATTACK_105 = 1105,
GROW_CURVE_ATTACK_201 = 1201,
GROW_CURVE_ATTACK_202 = 1202,
GROW_CURVE_ATTACK_203 = 1203,
GROW_CURVE_ATTACK_204 = 1204,
GROW_CURVE_ATTACK_205 = 1205,
GROW_CURVE_ATTACK_301 = 1301,
GROW_CURVE_ATTACK_302 = 1302,
GROW_CURVE_ATTACK_303 = 1303,
GROW_CURVE_ATTACK_304 = 1304,
GROW_CURVE_ATTACK_305 = 1305,
GROW_CURVE_CRITICAL_101 = 2101,
GROW_CURVE_CRITICAL_102 = 2102,
GROW_CURVE_CRITICAL_103 = 2103,
GROW_CURVE_CRITICAL_104 = 2104,
GROW_CURVE_CRITICAL_105 = 2105,
GROW_CURVE_CRITICAL_201 = 2201,
GROW_CURVE_CRITICAL_202 = 2202,
GROW_CURVE_CRITICAL_203 = 2203,
GROW_CURVE_CRITICAL_204 = 2204,
GROW_CURVE_CRITICAL_205 = 2205,
GROW_CURVE_CRITICAL_301 = 2301,
GROW_CURVE_CRITICAL_302 = 2302,
GROW_CURVE_CRITICAL_303 = 2303,
GROW_CURVE_CRITICAL_304 = 2304,
GROW_CURVE_CRITICAL_305 = 2305,
}

View File

@@ -0,0 +1,20 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
/// <summary>
/// 怪物种类
/// </summary>
[HighQuality]
[SuppressMessage("", "SA1602")]
internal enum MonsterType
{
MONSTER_NONE = 0,
MONSTER_ORDINARY = 1,
MONSTER_BOSS = 2,
MONSTER_ENV_ANIMAL = 3,
MONSTER_LITTLE_MONSTER = 4,
MONSTER_FISH = 5,
MONSTER_PARTNER = 6,
}

View File

@@ -32,6 +32,11 @@ internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IName
/// </summary>
public List<Material>? CultivationItemsView { get; set; }
/// <summary>
/// 最大等级
/// </summary>
public int MaxLevel { get => 90; }
/// <inheritdoc/>
public ICalculableAvatar ToCalculable()
{

View File

@@ -18,6 +18,11 @@ internal partial class Avatar
/// </summary>
public AvatarId Id { get; set; }
/// <summary>
/// 突破提升 Id 外键
/// </summary>
public PromoteId PromoteId { get; set; }
/// <summary>
/// 排序号
/// </summary>
@@ -65,9 +70,15 @@ internal partial class Avatar
public WeaponType Weapon { get; set; }
/// <summary>
/// 属性
/// 基础数值
/// </summary>
public PropertiesParameters Property { get; set; } = default!;
public BaseValue BaseValue { get; set; } = default!;
/// <summary>
/// 生长曲线
/// </summary>
[JsonConverter(typeof(Core.Json.Converter.StringEnumKeyDictionaryConverter))]
public Dictionary<FightProperty, GrowCurveType> GrowCurves { get; set; } = default!;
/// <summary>
/// 技能

View File

@@ -12,7 +12,6 @@ namespace Snap.Hutao.Model.Metadata;
[SuppressMessage("", "SA1600")]
internal static class AvatarIds
{
public static readonly AvatarId Kate = 10000001;
public static readonly AvatarId Ayaka = 10000002;
public static readonly AvatarId Qin = 10000003;
@@ -107,6 +106,7 @@ internal static class AvatarIds
SideIcon = "UI_AvatarIcon_Side_PlayerBoy",
Quality = Intrinsic.ItemQuality.QUALITY_ORANGE,
},
[PlayerGirl] = new()
{
Name = "旅行者",

View File

@@ -0,0 +1,25 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Metadata;
/// <summary>
/// 基础数值
/// </summary>
internal class BaseValue
{
/// <summary>
/// 基础生命值
/// </summary>
public float HpBase { get; set; }
/// <summary>
/// 基础攻击力
/// </summary>
public float AttackBase { get; set; }
/// <summary>
/// 基础防御力
/// </summary>
public float DefenseBase { get; set; }
}

View File

@@ -15,7 +15,18 @@ namespace Snap.Hutao.Model.Metadata.Converter;
internal sealed class PropertyDescriptor : ValueConverter<PropertiesParameters, List<LevelParameters<string, ParameterDescription>>?>
{
/// <summary>
/// 格式化
/// 格式化名称与值
/// </summary>
/// <param name="property">属性</param>
/// <param name="value">值</param>
/// <returns>对</returns>
public static NameValue<string> FormatNameValue(FightProperty property, float value)
{
return new(property.GetLocalizedDescription(), FormatValue(property, value));
}
/// <summary>
/// 格式化名称与描述
/// </summary>
/// <param name="property">属性</param>
/// <param name="value">值</param>

View File

@@ -0,0 +1,24 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
namespace Snap.Hutao.Model.Metadata;
/// <summary>
/// 生长曲线
/// </summary>
[HighQuality]
internal sealed class GrowCurve
{
/// <summary>
/// 等级
/// </summary>
public int Level { get; set; }
/// <summary>
/// 曲线 值相乘
/// </summary>
[JsonConverter(typeof(Core.Json.Converter.StringEnumKeyDictionaryConverter))]
public Dictionary<GrowCurveType, float> Curves { get; set; } = default!;
}

View File

@@ -0,0 +1,64 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Monster;
/// <summary>
/// 敌对生物
/// </summary>
internal sealed class Monster
{
/// <summary>
/// Id
/// </summary>
public MonsterId Id { get; set; }
/// <summary>
/// 内部代号
/// </summary>
public string MonsterName { get; set; } = default!;
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; } = default!;
/// <summary>
/// 描述
/// </summary>
public string Description { get; set; } = default!;
/// <summary>
/// 图标
/// </summary>
public string Icon { get; set; } = default!;
/// <summary>
/// 怪物种类
/// </summary>
public MonsterType Type { get; set; }
/// <summary>
/// 强化标签
/// </summary>
public List<string>? Affixes { get; set; } = default!;
/// <summary>
/// 掉落物 Id
/// </summary>
public IEnumerable<MaterialId>? Drops { get; set; } = default!;
/// <summary>
/// 基本属性
/// </summary>
public MonsterBaseValue BaseValue { get; set; } = default!;
/// <summary>
/// 生长曲线
/// </summary>
[JsonConverter(typeof(Core.Json.Converter.StringEnumKeyDictionaryConverter))]
public Dictionary<FightProperty, GrowCurveType> GrowCurves { get; set; } = default!;
}

View File

@@ -0,0 +1,50 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Metadata.Monster;
/// <summary>
/// 怪物基本属性
/// </summary>
internal sealed class MonsterBaseValue : BaseValue
{
/// <summary>
/// 火抗
/// </summary>
public float FireSubHurt { get; set; }
/// <summary>
/// 草抗
/// </summary>
public float GrassSubHurt { get; set; }
/// <summary>
/// 水抗
/// </summary>
public float WaterSubHurt { get; set; }
/// <summary>
/// 雷抗
/// </summary>
public float ElecSubHurt { get; set; }
/// <summary>
/// 风抗
/// </summary>
public float WindSubHurt { get; set; }
/// <summary>
/// 冰抗
/// </summary>
public float IceSubHurt { get; set; }
/// <summary>
/// 岩抗
/// </summary>
public float RockSubHurt { get; set; }
/// <summary>
/// 物抗
/// </summary>
public float PhysicalSubHurt { get; set; }
}

View File

@@ -0,0 +1,30 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata;
/// <summary>
/// 突破加成
/// </summary>
[HighQuality]
internal sealed class Promote
{
/// <summary>
/// Id
/// </summary>
public PromoteId Id { get; set; }
/// <summary>
/// 突破等级
/// </summary>
public int Level { get; set; }
/// <summary>
/// 增加的属性
/// </summary>
[JsonConverter(typeof(Core.Json.Converter.StringEnumKeyDictionaryConverter))]
public Dictionary<FightProperty, float> AddProperties { get; set; } = default!;
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
namespace Snap.Hutao.Model.Metadata.Weapon;
/// <summary>
/// 生长曲线与值
/// </summary>
[HighQuality]
internal sealed class GrowCurveTypeValue
{
/// <summary>
/// 类型
/// </summary>
public GrowCurveType Type { get; set; }
/// <summary>
/// 值
/// </summary>
public float Value { get; set; }
}

View File

@@ -28,6 +28,11 @@ internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource
get => RankLevel;
}
/// <summary>
/// 最大等级
/// </summary>
public int MaxLevel { get => ((int)Quality) >= 3 ? 90 : 70; }
/// <inheritdoc/>
public ICalculableWeapon ToCalculable()
{

View File

@@ -17,6 +17,11 @@ internal sealed partial class Weapon
/// </summary>
public WeaponId Id { get; set; }
/// <summary>
/// 突破 Id
/// </summary>
public PromoteId PromoteId { get; set; }
/// <summary>
/// 武器类型
/// </summary>
@@ -48,9 +53,10 @@ internal sealed partial class Weapon
public string AwakenIcon { get; set; } = default!;
/// <summary>
/// 属性
/// 生长曲线
/// </summary>
public PropertiesParameters Property { get; set; } = default!;
[JsonConverter(typeof(Core.Json.Converter.StringEnumKeyDictionaryConverter))]
public Dictionary<FightProperty, GrowCurveTypeValue> GrowCurves { get; set; } = default!;
/// <summary>
/// 被动信息, 无被动的武器为 <see langword="null"/>

View File

@@ -0,0 +1,66 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 8位 怪物Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<MonsterId>))]
internal readonly struct MonsterId : IEquatable<MonsterId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="MonsterId"/> struct.
/// </summary>
/// <param name="value">value</param>
public MonsterId(int value)
{
Value = value;
}
public static implicit operator int(MonsterId value)
{
return value.Value;
}
public static implicit operator MonsterId(int value)
{
return new(value);
}
public static bool operator ==(MonsterId left, MonsterId right)
{
return left.Value == right.Value;
}
public static bool operator !=(MonsterId left, MonsterId right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals(MonsterId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is MonsterId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -0,0 +1,66 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
namespace Snap.Hutao.Model.Primitive;
/// <summary>
/// 1-5位 角色突破提升Id
/// </summary>
[HighQuality]
[JsonConverter(typeof(IdentityConverter<PromoteId>))]
internal readonly struct PromoteId : IEquatable<PromoteId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="PromoteId"/> struct.
/// </summary>
/// <param name="value">value</param>
public PromoteId(int value)
{
Value = value;
}
public static implicit operator int(PromoteId value)
{
return value.Value;
}
public static implicit operator PromoteId(int value)
{
return new(value);
}
public static bool operator ==(PromoteId left, PromoteId right)
{
return left.Value == right.Value;
}
public static bool operator !=(PromoteId left, PromoteId right)
{
return !(left == right);
}
/// <inheritdoc/>
public bool Equals(PromoteId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is PromoteId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -1428,6 +1428,24 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 等级 的本地化字符串。
/// </summary>
internal static string ViewControlBaseValueSliderLevel {
get {
return ResourceManager.GetString("ViewControlBaseValueSliderLevel", resourceCulture);
}
}
/// <summary>
/// 查找类似 突破后 的本地化字符串。
/// </summary>
internal static string ViewControlBaseValueSliderPromoted {
get {
return ResourceManager.GetString("ViewControlBaseValueSliderPromoted", resourceCulture);
}
}
/// <summary>
/// 查找类似 加载中,请稍候 的本地化字符串。
/// </summary>

View File

@@ -573,6 +573,12 @@
<data name="ViewAvatarPropertyHeader" xml:space="preserve">
<value>我的角色</value>
</data>
<data name="ViewControlBaseValueSliderLevel" xml:space="preserve">
<value>等级</value>
</data>
<data name="ViewControlBaseValueSliderPromoted" xml:space="preserve">
<value>突破后</value>
</data>
<data name="ViewControlLoadingText" xml:space="preserve">
<value>加载中,请稍候</value>
</data>

View File

@@ -24,7 +24,6 @@ internal static class ReliquaryWeightConfiguration
new AffixWeight(AvatarIds.Qin, 0, 75, 0, 100, 100, 0, 55, 100).Anemo().Phyiscal(),
new AffixWeight(AvatarIds.Lisa, 0, 75, 0, 100, 100, 75, 0, 0).Electro(),
new AffixWeight(AvatarIds.Barbara, 100, 50, 0, 50, 50, 0, 55, 100).Hydro(80),
new AffixWeight(AvatarIds.Barbara, 50, 75, 0, 100, 100, 0, 55, 100, "暴力奶妈").Hydro(),
new AffixWeight(AvatarIds.Kaeya, 0, 75, 0, 100, 100, 0, 30, 0).Cryo().Phyiscal(),
new AffixWeight(AvatarIds.Diluc, 0, 75, 0, 100, 100, 75, 0, 0).Pyro(),
new AffixWeight(AvatarIds.Razor, 0, 75, 0, 100, 100, 0, 0, 0).Electro().Phyiscal(),
@@ -37,16 +36,13 @@ internal static class ReliquaryWeightConfiguration
new AffixWeight(AvatarIds.Ningguang, 0, 75, 0, 100, 100, 0, 30, 0).Geo(),
new AffixWeight(AvatarIds.Klee, 0, 75, 0, 100, 100, 75, 30, 0).Pyro(),
new AffixWeight(AvatarIds.Zhongli, 80, 75, 0, 100, 100, 0, 55, 0).Geo().Phyiscal(50),
new AffixWeight(AvatarIds.Zhongli, 100, 55, 0, 100, 100, 0, 55, 0, "血牛钟离").Geo(75),
new AffixWeight(AvatarIds.Zhongli, 100, 55, 0, 100, 100, 0, 75, 0, "血牛钟离2命+").Geo(75),
new AffixWeight(AvatarIds.Fischl, 0, 75, 0, 100, 100, 75, 0, 0).Electro().Phyiscal(60),
new AffixWeight(AvatarIds.Bennett, 100, 50, 0, 100, 100, 0, 55, 100).Pyro(80),
new AffixWeight(AvatarIds.Tartaglia, 0, 75, 0, 100, 100, 75, 30, 0).Hydro(),
new AffixWeight(AvatarIds.Noel, 0, 50, 90, 100, 100, 0, 70, 0).Geo(),
new AffixWeight(AvatarIds.Qiqi, 0, 75, 0, 100, 100, 0, 55, 100).Cryo().Phyiscal(),
new AffixWeight(AvatarIds.Chongyun, 0, 75, 0, 100, 100, 75, 55, 0).Cryo(),
new AffixWeight(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 75, 0, 0, "融化流").Cryo(),
new AffixWeight(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 0, 55, 0, "永冻流").Cryo(),
new AffixWeight(AvatarIds.Ganyu, 0, 75, 0, 100, 100, 75, 0, 0).Cryo(),
new AffixWeight(AvatarIds.Albedo, 0, 0, 100, 100, 100, 0, 0, 0).Geo(),
new AffixWeight(AvatarIds.Diona, 100, 50, 0, 50, 50, 0, 90, 100).Cryo(),
new AffixWeight(AvatarIds.Mona, 0, 75, 0, 100, 100, 75, 75, 0).Hydro(),

View File

@@ -72,10 +72,10 @@ internal static class GachaStatisticsExtension
.ToList();
}
[SuppressMessage("", "IDE0057")]
private static Color GetColorByName(string name)
{
Span<byte> codes = MD5.HashData(Encoding.UTF8.GetBytes(name));
Color color = Color.FromArgb(255, codes.Slice(0, 5).Average(), codes.Slice(5, 5).Average(), codes.Slice(10, 5).Average());
return color;
return Color.FromArgb(255, codes.Slice(0, 5).Average(), codes.Slice(5, 5).Average(), codes.Slice(10, 5).Average());
}
}

View File

@@ -198,6 +198,8 @@ internal sealed class GachaLogService : IGachaLogService
.OrderBy(i => i.Id)
.FirstOrDefault()?.Id ?? long.MaxValue;
logger.LogInformation("Last Id to trim with [{id}]", trimId);
IEnumerable<GachaItem> toAdd = list
.OrderByDescending(i => i.Id)
.Where(i => i.Id < trimId)

View File

@@ -5,6 +5,7 @@ using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Achievement;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Monster;
using Snap.Hutao.Model.Metadata.Reliquary;
using Snap.Hutao.Model.Metadata.Weapon;
using Snap.Hutao.Model.Primitive;
@@ -154,5 +155,47 @@ internal interface IMetadataService : ICastableService
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>材料列表</returns>
ValueTask<List<Material>> GetMaterialsAsync(CancellationToken token = default(CancellationToken));
ValueTask<List<Material>> GetMaterialsAsync(CancellationToken token = default);
/// <summary>
/// 异步获取怪物列表
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>怪物列表</returns>
ValueTask<List<Monster>> GetMonstersAsync(CancellationToken token = default);
/// <summary>
/// 异步获取等级角色曲线映射
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>等级角色曲线映射</returns>
ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToAvatarCurveMapAsync(CancellationToken token = default);
/// <summary>
/// 异步获取等级怪物曲线映射
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>等级怪物曲线映射</returns>
ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToMonsterCurveMapAsync(CancellationToken token = default);
/// <summary>
/// 异步获取等级武器曲线映射
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>等级武器曲线映射</returns>
ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToWeaponCurveMapAsync(CancellationToken token = default);
/// <summary>
/// 异步获取角色突破列表
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>角色突破列表</returns>
ValueTask<List<Promote>> GetAvatarPromotesAsync(CancellationToken token = default);
/// <summary>
/// 异步获取武器突破列表
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>武器突破列表</returns>
ValueTask<List<Promote>> GetWeaponPromotesAsync(CancellationToken token = default(CancellationToken));
}

View File

@@ -4,6 +4,7 @@
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Achievement;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Monster;
using Snap.Hutao.Model.Metadata.Reliquary;
using Snap.Hutao.Model.Metadata.Weapon;
@@ -32,6 +33,18 @@ internal sealed partial class MetadataService
return FromCacheOrFileAsync<List<Avatar>>("Avatar", token);
}
/// <inheritdoc/>
public ValueTask<List<Promote>> GetAvatarPromotesAsync(CancellationToken token = default)
{
return FromCacheOrFileAsync<List<Promote>>("AvatarPromote", token);
}
/// <inheritdoc/>
public ValueTask<List<Promote>> GetWeaponPromotesAsync(CancellationToken token = default)
{
return FromCacheOrFileAsync<List<Promote>>("WeaponPromote", token);
}
/// <inheritdoc/>
public ValueTask<List<GachaEvent>> GetGachaEventsAsync(CancellationToken token = default)
{
@@ -44,6 +57,12 @@ internal sealed partial class MetadataService
return FromCacheOrFileAsync<List<Material>>("Material", token);
}
/// <inheritdoc/>
public ValueTask<List<Monster>> GetMonstersAsync(CancellationToken token = default)
{
return FromCacheOrFileAsync<List<Monster>>("Monster", token);
}
/// <inheritdoc/>
public ValueTask<List<Reliquary>> GetReliquariesAsync(CancellationToken token = default)
{

View File

@@ -51,6 +51,24 @@ internal sealed partial class MetadataService
return FromCacheAsDictionaryAsync<WeaponId, Weapon>("Weapon", w => w.Id, token);
}
/// <inheritdoc/>
public ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToAvatarCurveMapAsync(CancellationToken token = default)
{
return FromCacheAsDictionaryAsync<int, Dictionary<GrowCurveType, float>, GrowCurve>("AvatarCurve", a => a.Level, a => a.Curves, token);
}
/// <inheritdoc/>
public ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToMonsterCurveMapAsync(CancellationToken token = default)
{
return FromCacheAsDictionaryAsync<int, Dictionary<GrowCurveType, float>, GrowCurve>("MonsterCurve", m => m.Level, m => m.Curves, token);
}
/// <inheritdoc/>
public ValueTask<Dictionary<int, Dictionary<GrowCurveType, float>>> GetLevelToWeaponCurveMapAsync(CancellationToken token = default)
{
return FromCacheAsDictionaryAsync<int, Dictionary<GrowCurveType, float>, GrowCurve>("WeaponCurve", w => w.Level, w => w.Curves, token);
}
/// <inheritdoc/>
public ValueTask<Dictionary<string, Avatar>> GetNameToAvatarMapAsync(CancellationToken token = default)
{

View File

@@ -7,6 +7,7 @@ using Snap.Hutao.Core.Diagnostics;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Core.IO;
using Snap.Hutao.Service.Abstraction;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Net.Http.Json;
@@ -29,6 +30,7 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
private readonly JsonSerializerOptions options;
private readonly ILogger<MetadataService> logger;
private readonly IMemoryCache memoryCache;
private readonly string locale;
/// <summary>
/// 用于指示初始化是否完成
@@ -58,7 +60,8 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
this.memoryCache = memoryCache;
httpClient = httpClientFactory.CreateClient(nameof(MetadataService));
metadataFolderPath = Path.Combine(Core.CoreEnvironment.DataFolder, "Metadata");
locale = GetTextMapLocale();
metadataFolderPath = Path.Combine(Core.CoreEnvironment.DataFolder, "Metadata", locale);
Directory.CreateDirectory(metadataFolderPath);
}
@@ -86,6 +89,41 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
logger.LogInformation("Metadata initializaion completed in {time}ms", stopwatch.GetElapsedTime().TotalMilliseconds);
}
private static string GetTextMapLocale()
{
CultureInfo cultureInfo = CultureInfo.CurrentCulture;
while (true)
{
if (cultureInfo == CultureInfo.InvariantCulture)
{
// Fallback to Chinese.
return "CHS";
}
switch (cultureInfo.Name)
{
case "de": return "DE"; // German
case "en": return "EN"; // English
case "es": return "ES"; // Spanish
case "fr": return "FR"; // French
case "id": return "ID"; // Indonesian
case "it": return "IT"; // Italian
case "ja": return "JP"; // Japanese
case "kr": return "JP"; // Japanese
case "ko": return "KR"; // Korean
case "pt": return "PT"; // Portuguese
case "ru": return "RU"; // Russian
case "th": return "TH"; // Thai
case "tr": return "TR"; // Turkish
case "vi": return "TR"; // Vietnamese
case "zh-CHS": return "CHS"; // Chinese (Simplified) Legacy
case "zh-CHT": return "CHT"; // Chinese (Traditional) Legacy
default: cultureInfo = cultureInfo.Parent; break;
}
}
}
private async Task<bool> TryUpdateMetadataAsync(CancellationToken token)
{
IDictionary<string, string>? metaMd5Map;
@@ -93,7 +131,7 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
{
// download meta check file
metaMd5Map = await httpClient
.GetFromJsonAsync<IDictionary<string, string>>(Web.HutaoEndpoints.HutaoMetadataFile(MetaFileName), options, token)
.GetFromJsonAsync<IDictionary<string, string>>(Web.HutaoEndpoints.HutaoMetadataFile(locale, MetaFileName), options, token)
.ConfigureAwait(false);
if (metaMd5Map is null)
@@ -162,7 +200,7 @@ internal sealed partial class MetadataService : IMetadataService, IMetadataServi
private async Task DownloadMetadataAsync(string fileFullName, CancellationToken token)
{
Stream sourceStream = await httpClient
.GetStreamAsync(Web.HutaoEndpoints.HutaoMetadataFile(fileFullName), token)
.GetStreamAsync(Web.HutaoEndpoints.HutaoMetadataFile(locale, fileFullName), token)
.ConfigureAwait(false);
// Write stream while convert LF to CRLF

View File

@@ -71,6 +71,7 @@
<None Remove="Resource\Segoe Fluent Icons.ttf" />
<None Remove="Resource\WelcomeView_Background.png" />
<None Remove="stylecop.json" />
<None Remove="View\Control\BaseValueSlider.xaml" />
<None Remove="View\Control\BottomTextControl.xaml" />
<None Remove="View\Control\DescParamComboBox.xaml" />
<None Remove="View\Control\ItemIcon.xaml" />
@@ -178,7 +179,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.230118.102" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.230217.4" />
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.435">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -442,4 +443,9 @@
<LastGenOutput>SH.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Page Update="View\Control\BaseValueSlider.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,57 @@
<UserControl
x:Class="Snap.Hutao.View.Control.BaseValueSlider"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shcm="using:Snap.Hutao.Control.Markup"
xmlns:wsc="using:WinUICommunity.SettingsUI.Controls"
mc:Ignorable="d">
<StackPanel>
<wsc:SettingsGroup Margin="0,-64,0,0" VerticalAlignment="Top">
<wsc:Setting Padding="12,0,6,0" Header="{shcm:ResourceString Name=ViewControlBaseValueSliderLevel}">
<wsc:Setting.ActionContent>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="8,0"
VerticalAlignment="Center"
Text="{x:Bind BaseValueInfo.CurrentLevelFormatted, Mode=OneWay}"/>
<CheckBox
MinWidth="0"
Margin="16,0,8,0"
Padding="8,0,0,0"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Content="{shcm:ResourceString Name=ViewControlBaseValueSliderPromoted}"
IsChecked="{x:Bind BaseValueInfo.Promoted, Mode=TwoWay}"/>
<Slider
MinWidth="240"
Margin="16,0,8,0"
VerticalAlignment="Center"
Maximum="{x:Bind BaseValueInfo.MaxLevel, Mode=OneWay}"
Minimum="1"
StepFrequency="1"
Value="{x:Bind BaseValueInfo.CurrentLevel, Mode=TwoWay}"/>
</StackPanel>
</wsc:Setting.ActionContent>
</wsc:Setting>
</wsc:SettingsGroup>
<ItemsControl VerticalAlignment="Top" ItemsSource="{x:Bind BaseValueInfo.Values, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<wsc:Setting
Margin="0,2,0,0"
Padding="12,0"
Header="{Binding Name}">
<wsc:Setting.ActionContent>
<TextBlock Text="{Binding Value}"/>
</wsc:Setting.ActionContent>
</wsc:Setting>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,34 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control;
using Snap.Hutao.Model.Binding.BaseValue;
namespace Snap.Hutao.View.Control;
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
internal sealed partial class BaseValueSlider : UserControl
{
private static readonly DependencyProperty BaseValueInfoProperty = Property<BaseValueSlider>.Depend<BaseValueInfo>(nameof(BaseValueInfo));
/// <summary>
/// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>µĻ<C2B5><C4BB><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
public BaseValueSlider()
{
InitializeComponent();
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5>Ϣ
/// </summary>
public BaseValueInfo BaseValueInfo
{
get => (BaseValueInfo)GetValue(BaseValueInfoProperty);
set => SetValue(BaseValueInfoProperty, value);
}
}

View File

@@ -289,13 +289,11 @@
</StackPanel>
</Grid>
<!-- 属性 -->
<ContentControl
<shvc:BaseValueSlider
Margin="16,16,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Content="{Binding Selected.Property, Mode=OneWay}"
ContentTemplate="{StaticResource PropertyDataTemplate}"/>
BaseValueInfo="{Binding BaseValueInfo, Mode=OneWay}"/>
<TextBlock
Margin="16,32,0,0"
Style="{StaticResource BaseTextBlockStyle}"

View File

@@ -190,12 +190,11 @@
Margin="16,16,0,0"
Text="{Binding Selected.Description}"
TextWrapping="Wrap"/>
<ContentControl
<shvc:BaseValueSlider
Margin="16,16,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Content="{Binding Selected.Property, Mode=OneWay}"
ContentTemplate="{StaticResource PropertyDataTemplate}"/>
BaseValueInfo="{Binding BaseValueInfo, Mode=OneWay}"/>
<TextBlock
Margin="16,32,0,0"
Style="{StaticResource BaseTextBlockStyle}"

View File

@@ -184,7 +184,7 @@ internal sealed class SettingViewModel : Abstraction.ViewModel
if (SetProperty(ref selectedCulture, value))
{
SettingEntry entry = appDbContext.Settings.SingleOrAdd(SettingEntry.Culture, CultureInfo.CurrentCulture.Name);
entry.Value = selectedCulture.Value;
entry.Value = selectedCulture?.Value;
appDbContext.Settings.UpdateAndSave(entry);
AppInstance.Restart(string.Empty);
}

View File

@@ -5,8 +5,10 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI.UI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Snap.Hutao.Model.Binding.BaseValue;
using Snap.Hutao.Model.Binding.Hutao;
using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Intrinsic.Immutable;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Avatar;
@@ -42,6 +44,9 @@ internal sealed class WikiAvatarViewModel : Abstraction.ViewModel
private AdvancedCollectionView? avatars;
private Avatar? selected;
private string? filterText;
private BaseValueInfo? baseValueInfo;
private Dictionary<int, Dictionary<GrowCurveType, float>>? levelAvatarCurveMap;
private List<Promote>? promotes;
/// <summary>
/// 构造一个新的角色资料视图模型
@@ -66,7 +71,21 @@ internal sealed class WikiAvatarViewModel : Abstraction.ViewModel
/// <summary>
/// 选中的角色
/// </summary>
public Avatar? Selected { get => selected; set => SetProperty(ref selected, value); }
public Avatar? Selected
{
get => selected; set
{
if (SetProperty(ref selected, value))
{
UpdateBaseValueInfo(value);
}
}
}
/// <summary>
/// 基础数值信息
/// </summary>
public BaseValueInfo? BaseValueInfo { get => baseValueInfo; set => SetProperty(ref baseValueInfo, value); }
/// <summary>
/// 筛选文本
@@ -92,6 +111,9 @@ internal sealed class WikiAvatarViewModel : Abstraction.ViewModel
{
if (await metadataService.InitializeAsync().ConfigureAwait(false))
{
levelAvatarCurveMap = await metadataService.GetLevelToAvatarCurveMapAsync().ConfigureAwait(false);
promotes = await metadataService.GetAvatarPromotesAsync().ConfigureAwait(false);
Dictionary<MaterialId, Material> idMaterialMap = await metadataService.GetIdToMaterialMapAsync().ConfigureAwait(false);
List<Avatar> avatars = await metadataService.GetAvatarsAsync().ConfigureAwait(false);
List<Avatar> sorted = avatars
@@ -179,6 +201,30 @@ internal sealed class WikiAvatarViewModel : Abstraction.ViewModel
}
}
private void UpdateBaseValueInfo(Avatar? avatar)
{
if (avatar == null)
{
BaseValueInfo = null;
}
else
{
Dictionary<int, Promote> avatarPromoteMap = promotes!.Where(p => p.Id == avatar.PromoteId).ToDictionary(p => p.Level);
FightProperty promoteProperty = avatarPromoteMap[0].AddProperties.Keys.Last();
List<PropertyCurveValue> propertyCurveValues = new()
{
new(FightProperty.FIGHT_PROP_BASE_HP, avatar.GrowCurves[FightProperty.FIGHT_PROP_BASE_HP], avatar.BaseValue.HpBase),
new(FightProperty.FIGHT_PROP_BASE_ATTACK, avatar.GrowCurves[FightProperty.FIGHT_PROP_BASE_ATTACK], avatar.BaseValue.AttackBase),
new(FightProperty.FIGHT_PROP_BASE_DEFENSE, avatar.GrowCurves[FightProperty.FIGHT_PROP_BASE_DEFENSE], avatar.BaseValue.DefenseBase),
new(promoteProperty, GrowCurveType.GROW_CURVE_NONE, 0),
};
BaseValueInfo = new(avatar.MaxLevel, propertyCurveValues, levelAvatarCurveMap!, avatarPromoteMap);
}
}
private void ApplyFilter(string? input)
{
if (Avatars != null)

View File

@@ -5,9 +5,12 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI.UI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Snap.Hutao.Model.Binding.BaseValue;
using Snap.Hutao.Model.Binding.Hutao;
using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Intrinsic.Immutable;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Weapon;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Abstraction;
@@ -44,6 +47,9 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
private AdvancedCollectionView? weapons;
private Weapon? selected;
private string? filterText;
private BaseValueInfo? baseValueInfo;
private Dictionary<int, Dictionary<GrowCurveType, float>>? levelWeaponCurveMap;
private List<Promote>? promotes;
/// <summary>
/// 构造一个新的武器资料视图模型
@@ -68,7 +74,21 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
/// <summary>
/// 选中的角色
/// </summary>
public Weapon? Selected { get => selected; set => SetProperty(ref selected, value); }
public Weapon? Selected
{
get => selected; set
{
if (SetProperty(ref selected, value))
{
UpdateBaseValueInfo(value);
}
}
}
/// <summary>
/// 基础数值信息
/// </summary>
public BaseValueInfo? BaseValueInfo { get => baseValueInfo; set => SetProperty(ref baseValueInfo, value); }
/// <summary>
/// 筛选文本
@@ -94,6 +114,9 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
{
if (await metadataService.InitializeAsync().ConfigureAwait(false))
{
levelWeaponCurveMap = await metadataService.GetLevelToWeaponCurveMapAsync().ConfigureAwait(false);
promotes = await metadataService.GetWeaponPromotesAsync().ConfigureAwait(false);
List<Weapon> weapons = await metadataService.GetWeaponsAsync().ConfigureAwait(false);
List<Weapon> sorted = weapons
.Where(weapon => !skippedWeapons.Contains(weapon.Id))
@@ -175,6 +198,24 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
}
}
private void UpdateBaseValueInfo(Weapon? weapon)
{
if (weapon == null)
{
BaseValueInfo = null;
}
else
{
Dictionary<int, Promote> weaponPromoteMap = promotes!.Where(p => p.Id == weapon.PromoteId).ToDictionary(p => p.Level);
List<PropertyCurveValue> propertyCurveValues = weapon.GrowCurves
.Select(curveInfo => new PropertyCurveValue(curveInfo.Key, curveInfo.Value.Type, curveInfo.Value.Value))
.ToList();
BaseValueInfo = new(weapon.MaxLevel, propertyCurveValues, levelWeaponCurveMap!, weaponPromoteMap);
}
}
private void ApplyFilter(string? input)
{
if (Weapons != null)
@@ -237,7 +278,7 @@ internal class WikiWeaponViewModel : Abstraction.ViewModel
if (intrinsics.FightProperties.Contains(value))
{
matches.Add(weapon.Property.Properties.ElementAtOrDefault(1).GetLocalizedDescriptionOrDefault() == value);
matches.Add(weapon.GrowCurves.ElementAtOrDefault(1).Key.GetLocalizedDescriptionOrDefault() == value);
continue;
}
}

View File

@@ -89,11 +89,12 @@ internal static class HutaoEndpoints
/// <summary>
/// 胡桃元数据文件
/// </summary>
/// <param name="locale">语言</param>
/// <param name="fileName">文件名称</param>
/// <returns>路径</returns>
public static string HutaoMetadataFile(string fileName)
public static string HutaoMetadataFile(string locale, string fileName)
{
return $"{HutaoMetadataSnapGenshinApi}/{fileName}";
return $"{HutaoMetadataSnapGenshinApi}/{locale}/{fileName}";
}
#endregion
@@ -144,7 +145,12 @@ internal static class HutaoEndpoints
}
#endregion
#if DEBUG
private const string HutaoMetadataSnapGenshinApi = "http://hutao-metadata-pages.snapgenshin.cn";
#else
private const string HutaoMetadataSnapGenshinApi = "http://hutao-metadata.snapgenshin.com";
#endif
private const string HomaSnapGenshinApi = "https://homa.snapgenshin.com";
private const string PatcherDGPStudioApi = "https://patcher.dgp-studio.cn";
private const string StaticSnapGenshinApi = "https://static.snapgenshin.com";

View File

@@ -17,7 +17,7 @@ internal static class MemoryExtension
/// </summary>
/// <param name="char256">目标字符数组</param>
/// <returns>结果字符串</returns>
public static unsafe string AsString(this ref __CHAR_256 char256)
public static unsafe string AsString(this in __CHAR_256 char256)
{
fixed (CHAR* pszModule = &char256._0)
{

View File

@@ -4,12 +4,7 @@
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>