mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
AvatarViewBuilder
This commit is contained in:
@@ -11,11 +11,6 @@ namespace Snap.Hutao.Model.Metadata.Converter;
|
||||
[HighQuality]
|
||||
internal sealed class AvatarNameCardPicConverter : ValueConverter<Avatar.Avatar?, Uri>
|
||||
{
|
||||
/// <summary>
|
||||
/// 从角色转换到名片
|
||||
/// </summary>
|
||||
/// <param name="avatar">角色</param>
|
||||
/// <returns>名片</returns>
|
||||
public static Uri AvatarToUri(Avatar.Avatar? avatar)
|
||||
{
|
||||
if (avatar is null)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.AvatarInfo.Factory.Builder;
|
||||
|
||||
internal sealed class AvatarViewBuilder : IAvatarViewBuilder
|
||||
{
|
||||
public ViewModel.AvatarProperty.AvatarView AvatarView { get; } = new();
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Abstraction.Extension;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.ViewModel.AvatarProperty;
|
||||
|
||||
namespace Snap.Hutao.Service.AvatarInfo.Factory.Builder;
|
||||
|
||||
internal static class AvatarViewBuilderExtension
|
||||
{
|
||||
public static TBuilder ApplyCostumeIconOrDefault<TBuilder>(this TBuilder builder, Web.Enka.Model.AvatarInfo avatarInfo, Avatar avatar)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
if (avatarInfo.CostumeId.TryGetValue(out CostumeId id))
|
||||
{
|
||||
Costume costume = avatar.Costumes.Single(c => c.Id == id);
|
||||
|
||||
// Set to costume icon
|
||||
builder.AvatarView.Icon = AvatarIconConverter.IconNameToUri(costume.FrontIcon);
|
||||
builder.AvatarView.SideIcon = AvatarIconConverter.IconNameToUri(costume.SideIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AvatarView.Icon = AvatarIconConverter.IconNameToUri(avatar.Icon);
|
||||
builder.AvatarView.SideIcon = AvatarIconConverter.IconNameToUri(avatar.SideIcon);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TBuilder SetCalculatorRefreshTimeFormat<TBuilder>(this TBuilder builder, DateTimeOffset refreshTime, Func<object?, string> format, string defaultValue)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.SetCalculatorRefreshTimeFormat(refreshTime == DateTimeOffsetExtension.DatebaseDefaultTime ? defaultValue : format(refreshTime.ToLocalTime()));
|
||||
}
|
||||
|
||||
public static TBuilder SetCalculatorRefreshTimeFormat<TBuilder>(this TBuilder builder, string calculatorRefreshTimeFormat)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.CalculatorRefreshTimeFormat = calculatorRefreshTimeFormat);
|
||||
}
|
||||
|
||||
public static TBuilder SetConstellations<TBuilder>(this TBuilder builder, List<Skill> talents, List<SkillId>? talentIds)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.SetConstellations(CreateConstellations(talents, talentIds.EmptyIfNull()));
|
||||
|
||||
static List<ConstellationView> CreateConstellations(List<Skill> talents, List<SkillId> talentIds)
|
||||
{
|
||||
// TODO: use builder here
|
||||
return talents.SelectList(talent => new ViewModel.AvatarProperty.ConstellationView()
|
||||
{
|
||||
Name = talent.Name,
|
||||
Icon = SkillIconConverter.IconNameToUri(talent.Icon),
|
||||
Description = talent.Description,
|
||||
IsActivated = talentIds.Contains(talent.Id),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static TBuilder SetConstellations<TBuilder>(this TBuilder builder, List<ConstellationView> constellations)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.Constellations = constellations);
|
||||
}
|
||||
|
||||
public static TBuilder SetCritScore<TBuilder>(this TBuilder builder, Dictionary<FightProperty, float>? fightPropMap)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.SetCritScore(ScoreCrit(fightPropMap));
|
||||
|
||||
static float ScoreCrit(Dictionary<FightProperty, float>? fightPropMap)
|
||||
{
|
||||
if (fightPropMap.IsNullOrEmpty())
|
||||
{
|
||||
return 0F;
|
||||
}
|
||||
|
||||
float cr = fightPropMap[FightProperty.FIGHT_PROP_CRITICAL];
|
||||
float cd = fightPropMap[FightProperty.FIGHT_PROP_CRITICAL_HURT];
|
||||
|
||||
return 100 * ((cr * 2) + cd);
|
||||
}
|
||||
}
|
||||
|
||||
public static TBuilder SetCritScore<TBuilder>(this TBuilder builder, float critScore)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.SetCritScore($"{critScore:F2}");
|
||||
}
|
||||
|
||||
public static TBuilder SetCritScore<TBuilder>(this TBuilder builder, string critScore)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.CritScore = critScore);
|
||||
}
|
||||
|
||||
public static TBuilder SetElement<TBuilder>(this TBuilder builder, ElementType element)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.Element = element);
|
||||
}
|
||||
|
||||
public static TBuilder SetFetterLevel<TBuilder>(this TBuilder builder, FetterLevel? level)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
if (level.TryGetValue(out FetterLevel value))
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.FetterLevel = value);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TBuilder SetFetterLevel<TBuilder>(this TBuilder builder, uint level)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.FetterLevel = level);
|
||||
}
|
||||
|
||||
public static TBuilder SetGameRecordRefreshTimeFormat<TBuilder>(this TBuilder builder, DateTimeOffset refreshTime, Func<object?, string> format, string defaultValue)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.SetGameRecordRefreshTimeFormat(refreshTime == DateTimeOffsetExtension.DatebaseDefaultTime ? defaultValue : format(refreshTime.ToLocalTime()));
|
||||
}
|
||||
|
||||
public static TBuilder SetGameRecordRefreshTimeFormat<TBuilder>(this TBuilder builder, string gameRecordRefreshTimeFormat)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.GameRecordRefreshTimeFormat = gameRecordRefreshTimeFormat);
|
||||
}
|
||||
|
||||
public static TBuilder SetId<TBuilder>(this TBuilder builder, AvatarId id)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.Id = id);
|
||||
}
|
||||
|
||||
public static TBuilder SetLevelNumber<TBuilder>(this TBuilder builder, uint? levelNumber)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
if (levelNumber.TryGetValue(out uint value))
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.LevelNumber = value);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TBuilder SetName<TBuilder>(this TBuilder builder, string name)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.Name = name);
|
||||
}
|
||||
|
||||
public static TBuilder SetNameCard<TBuilder>(this TBuilder builder, Uri nameCard)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.NameCard = nameCard);
|
||||
}
|
||||
|
||||
public static TBuilder SetProperties<TBuilder>(this TBuilder builder, List<AvatarProperty> properties)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.Properties = properties);
|
||||
}
|
||||
|
||||
public static TBuilder SetQuality<TBuilder>(this TBuilder builder, QualityType quality)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.Quality = quality);
|
||||
}
|
||||
|
||||
public static TBuilder SetReliquaries<TBuilder>(this TBuilder builder, List<ReliquaryView> reliquaries)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.Reliquaries = reliquaries);
|
||||
}
|
||||
|
||||
public static TBuilder SetScore<TBuilder>(this TBuilder builder, float score)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.SetScore($"{score:F2}");
|
||||
}
|
||||
|
||||
public static TBuilder SetScore<TBuilder>(this TBuilder builder, string score)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.Score = score);
|
||||
}
|
||||
|
||||
public static TBuilder SetShowcaseRefreshTimeFormat<TBuilder>(this TBuilder builder, DateTimeOffset refreshTime, Func<object?, string> format, string defaultValue)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.SetShowcaseRefreshTimeFormat(refreshTime == DateTimeOffsetExtension.DatebaseDefaultTime ? defaultValue : format(refreshTime.ToLocalTime()));
|
||||
}
|
||||
|
||||
public static TBuilder SetShowcaseRefreshTimeFormat<TBuilder>(this TBuilder builder, string showcaseRefreshTimeFormat)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.ShowcaseRefreshTimeFormat = showcaseRefreshTimeFormat);
|
||||
}
|
||||
|
||||
public static TBuilder SetSkills<TBuilder>(this TBuilder builder, Dictionary<SkillId, SkillLevel>? skillLevelMap, Dictionary<SkillGroupId, SkillLevel>? proudSkillExtraLevelMap, List<ProudableSkill> proudSkills)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.SetSkills(CreateSkills(skillLevelMap, proudSkillExtraLevelMap, proudSkills));
|
||||
|
||||
static List<SkillView> CreateSkills(Dictionary<SkillId, SkillLevel>? skillLevelMap, Dictionary<SkillGroupId, SkillLevel>? proudSkillExtraLevelMap, List<ProudableSkill> proudSkills)
|
||||
{
|
||||
if (skillLevelMap.IsNullOrEmpty())
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
Dictionary<SkillId, SkillLevel> skillExtraLeveledMap = new(skillLevelMap);
|
||||
|
||||
if (proudSkillExtraLevelMap is not null)
|
||||
{
|
||||
foreach ((SkillGroupId groupId, SkillLevel extraLevel) in proudSkillExtraLevelMap)
|
||||
{
|
||||
skillExtraLeveledMap.IncreaseValue(proudSkills.Single(p => p.GroupId == groupId).Id, extraLevel);
|
||||
}
|
||||
}
|
||||
|
||||
return proudSkills.SelectList(proudableSkill =>
|
||||
{
|
||||
SkillId skillId = proudableSkill.Id;
|
||||
|
||||
// TODO: use builder here
|
||||
return new SkillView()
|
||||
{
|
||||
Name = proudableSkill.Name,
|
||||
Icon = SkillIconConverter.IconNameToUri(proudableSkill.Icon),
|
||||
Description = proudableSkill.Description,
|
||||
|
||||
GroupId = proudableSkill.GroupId,
|
||||
LevelNumber = skillLevelMap[skillId],
|
||||
Info = DescriptionsParametersDescriptor.Convert(proudableSkill.Proud, skillExtraLeveledMap[skillId]),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static TBuilder SetSkills<TBuilder>(this TBuilder builder, List<SkillView> skills)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.Skills = skills);
|
||||
}
|
||||
|
||||
public static TBuilder SetWeapon<TBuilder>(this TBuilder builder, WeaponView? weapon)
|
||||
where TBuilder : IAvatarViewBuilder
|
||||
{
|
||||
return builder.Configure(b => b.AvatarView.Weapon = weapon);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
|
||||
namespace Snap.Hutao.Service.AvatarInfo.Factory.Builder;
|
||||
|
||||
internal interface IAvatarViewBuilder : IBuilder
|
||||
{
|
||||
ViewModel.AvatarProperty.AvatarView AvatarView { get; }
|
||||
}
|
||||
@@ -6,7 +6,9 @@ using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Intrinsic.Format;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.AvatarInfo.Factory.Builder;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
using System.Runtime.InteropServices;
|
||||
using EntityAvatarInfo = Snap.Hutao.Model.Entity.AvatarInfo;
|
||||
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
|
||||
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
|
||||
@@ -24,11 +26,11 @@ internal sealed class SummaryAvatarFactory
|
||||
private readonly DateTimeOffset showcaseRefreshTime;
|
||||
private readonly DateTimeOffset gameRecordRefreshTime;
|
||||
private readonly DateTimeOffset calculatorRefreshTime;
|
||||
private readonly SummaryFactoryMetadataContext metadataContext;
|
||||
private readonly SummaryFactoryMetadataContext context;
|
||||
|
||||
public SummaryAvatarFactory(SummaryFactoryMetadataContext metadataContext, EntityAvatarInfo avatarInfo)
|
||||
public SummaryAvatarFactory(SummaryFactoryMetadataContext context, EntityAvatarInfo avatarInfo)
|
||||
{
|
||||
this.metadataContext = metadataContext;
|
||||
this.context = context;
|
||||
this.avatarInfo = avatarInfo.Info;
|
||||
|
||||
showcaseRefreshTime = avatarInfo.ShowcaseRefreshTime;
|
||||
@@ -36,83 +38,51 @@ internal sealed class SummaryAvatarFactory
|
||||
calculatorRefreshTime = avatarInfo.CalculatorRefreshTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建角色
|
||||
/// </summary>
|
||||
/// <returns>角色</returns>
|
||||
public static PropertyAvatar Create(SummaryFactoryMetadataContext context, EntityAvatarInfo avatarInfo)
|
||||
{
|
||||
return new SummaryAvatarFactory(context, avatarInfo).Create();
|
||||
}
|
||||
|
||||
public PropertyAvatar Create()
|
||||
{
|
||||
ReliquaryAndWeapon reliquaryAndWeapon = ProcessEquip(avatarInfo.EquipList.EmptyIfNull());
|
||||
MetadataAvatar avatar = metadataContext.IdAvatarMap[avatarInfo.AvatarId];
|
||||
MetadataAvatar avatar = context.IdAvatarMap[avatarInfo.AvatarId];
|
||||
|
||||
PropertyAvatar propertyAvatar = new()
|
||||
{
|
||||
// metadata part
|
||||
Id = avatar.Id,
|
||||
Name = avatar.Name,
|
||||
Quality = avatar.Quality,
|
||||
NameCard = AvatarNameCardPicConverter.AvatarToUri(avatar),
|
||||
Element = ElementNameIconConverter.ElementNameToElementType(avatar.FetterInfo.VisionBefore),
|
||||
PropertyAvatar propertyAvatar = new AvatarViewBuilder()
|
||||
.SetId(avatar.Id)
|
||||
.SetName(avatar.Name)
|
||||
.SetQuality(avatar.Quality)
|
||||
.SetNameCard(AvatarNameCardPicConverter.AvatarToUri(avatar))
|
||||
.SetElement(ElementNameIconConverter.ElementNameToElementType(avatar.FetterInfo.VisionBefore))
|
||||
.SetConstellations(avatar.SkillDepot.Talents, avatarInfo.TalentIdList)
|
||||
.SetSkills(avatarInfo.SkillLevelMap, avatarInfo.ProudSkillExtraLevelMap, avatar.SkillDepot.CompositeSkillsNoInherents())
|
||||
.SetFetterLevel(avatarInfo.FetterInfo?.ExpLevel)
|
||||
.SetProperties(SummaryAvatarProperties.Create(avatarInfo.FightPropMap))
|
||||
.SetCritScore(avatarInfo.FightPropMap)
|
||||
.SetLevelNumber(avatarInfo.PropMap?[PlayerProperty.PROP_LEVEL].Value)
|
||||
.SetWeapon(reliquaryAndWeapon.Weapon)
|
||||
.SetReliquaries(reliquaryAndWeapon.Reliquaries)
|
||||
.SetScore(reliquaryAndWeapon.Reliquaries.Sum(r => r.Score))
|
||||
.SetShowcaseRefreshTimeFormat(showcaseRefreshTime, SH.FormatServiceAvatarInfoSummaryShowcaseRefreshTimeFormat, SH.ServiceAvatarInfoSummaryShowcaseNotRefreshed)
|
||||
.SetGameRecordRefreshTimeFormat(gameRecordRefreshTime, SH.FormatServiceAvatarInfoSummaryGameRecordRefreshTimeFormat, SH.ServiceAvatarInfoSummaryGameRecordNotRefreshed)
|
||||
.SetCalculatorRefreshTimeFormat(calculatorRefreshTime, SH.FormatServiceAvatarInfoSummaryCalculatorRefreshTimeFormat, SH.ServiceAvatarInfoSummaryCalculatorNotRefreshed)
|
||||
.ApplyCostumeIconOrDefault(avatarInfo, avatar)
|
||||
.AvatarView;
|
||||
|
||||
// webinfo & metadata mixed part
|
||||
Constellations = SummaryHelper.CreateConstellations(avatar.SkillDepot.Talents, avatarInfo.TalentIdList),
|
||||
Skills = SummaryHelper.CreateSkills(avatarInfo.SkillLevelMap, avatarInfo.ProudSkillExtraLevelMap, avatar.SkillDepot.CompositeSkillsNoInherents()),
|
||||
|
||||
// webinfo part
|
||||
FetterLevel = avatarInfo.FetterInfo?.ExpLevel ?? 0U,
|
||||
Properties = SummaryAvatarProperties.Create(avatarInfo.FightPropMap),
|
||||
CritScore = $"{SummaryHelper.ScoreCrit(avatarInfo.FightPropMap):F2}",
|
||||
LevelNumber = avatarInfo.PropMap?[PlayerProperty.PROP_LEVEL].Value ?? 0U,
|
||||
|
||||
// processed webinfo part
|
||||
Weapon = reliquaryAndWeapon.Weapon,
|
||||
Reliquaries = reliquaryAndWeapon.Reliquaries,
|
||||
Score = $"{reliquaryAndWeapon.Reliquaries.Sum(r => r.Score):F2}",
|
||||
|
||||
// times
|
||||
ShowcaseRefreshTimeFormat = showcaseRefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime
|
||||
? SH.ServiceAvatarInfoSummaryShowcaseNotRefreshed
|
||||
: SH.FormatServiceAvatarInfoSummaryShowcaseRefreshTimeFormat(showcaseRefreshTime.ToLocalTime()),
|
||||
GameRecordRefreshTimeFormat = gameRecordRefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime
|
||||
? SH.ServiceAvatarInfoSummaryGameRecordNotRefreshed
|
||||
: SH.FormatServiceAvatarInfoSummaryGameRecordRefreshTimeFormat(gameRecordRefreshTime.ToLocalTime()),
|
||||
CalculatorRefreshTimeFormat = calculatorRefreshTime == DateTimeOffsetExtension.DatebaseDefaultTime
|
||||
? SH.ServiceAvatarInfoSummaryCalculatorNotRefreshed
|
||||
: SH.FormatServiceAvatarInfoSummaryCalculatorRefreshTimeFormat(calculatorRefreshTime.ToLocalTime()),
|
||||
};
|
||||
|
||||
ApplyCostumeIconOrDefault(ref propertyAvatar, avatar);
|
||||
return propertyAvatar;
|
||||
}
|
||||
|
||||
private void ApplyCostumeIconOrDefault(ref PropertyAvatar propertyAvatar, MetadataAvatar avatar)
|
||||
{
|
||||
if (avatarInfo.CostumeId.TryGetValue(out CostumeId id))
|
||||
{
|
||||
Model.Metadata.Avatar.Costume costume = avatar.Costumes.Single(c => c.Id == id);
|
||||
|
||||
// Set to costume icon
|
||||
propertyAvatar.Icon = AvatarIconConverter.IconNameToUri(costume.FrontIcon);
|
||||
propertyAvatar.SideIcon = AvatarIconConverter.IconNameToUri(costume.SideIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyAvatar.Icon = AvatarIconConverter.IconNameToUri(avatar.Icon);
|
||||
propertyAvatar.SideIcon = AvatarIconConverter.IconNameToUri(avatar.SideIcon);
|
||||
}
|
||||
}
|
||||
|
||||
private ReliquaryAndWeapon ProcessEquip(List<Equip> equipments)
|
||||
{
|
||||
List<PropertyReliquary> reliquaryList = [];
|
||||
PropertyWeapon? weapon = null;
|
||||
|
||||
foreach (Equip equip in equipments)
|
||||
foreach (ref readonly Equip equip in CollectionsMarshal.AsSpan(equipments))
|
||||
{
|
||||
switch (equip.Flat.ItemType)
|
||||
{
|
||||
case ItemType.ITEM_RELIQUARY:
|
||||
SummaryReliquaryFactory summaryReliquaryFactory = new(metadataContext, avatarInfo, equip);
|
||||
SummaryReliquaryFactory summaryReliquaryFactory = new(context, avatarInfo, equip);
|
||||
reliquaryList.Add(summaryReliquaryFactory.CreateReliquary());
|
||||
break;
|
||||
case ItemType.ITEM_WEAPON:
|
||||
@@ -126,7 +96,7 @@ internal sealed class SummaryAvatarFactory
|
||||
|
||||
private PropertyWeapon CreateWeapon(Equip equip)
|
||||
{
|
||||
MetadataWeapon weapon = metadataContext.IdWeaponMap[equip.ItemId];
|
||||
MetadataWeapon weapon = context.IdWeaponMap[equip.ItemId];
|
||||
|
||||
// AffixMap can be null when it's a white weapon.
|
||||
ArgumentNullException.ThrowIfNull(equip.Weapon);
|
||||
@@ -160,7 +130,9 @@ internal sealed class SummaryAvatarFactory
|
||||
// EquipBase
|
||||
Level = $"Lv.{equip.Weapon.Level.Value}",
|
||||
Quality = weapon.Quality,
|
||||
MainProperty = mainStat is not null ? FightPropertyFormat.ToNameValue(mainStat.AppendPropId, mainStat.StatValue) : NameValueDefaults.String,
|
||||
MainProperty = mainStat is not null
|
||||
? FightPropertyFormat.ToNameValue(mainStat.AppendPropId, mainStat.StatValue)
|
||||
: NameValueDefaults.String,
|
||||
|
||||
// Weapon
|
||||
Id = weapon.Id,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.ViewModel.AvatarProperty;
|
||||
|
||||
namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
@@ -20,23 +21,18 @@ internal sealed partial class SummaryFactory : ISummaryFactory
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<Summary> CreateAsync(IEnumerable<Model.Entity.AvatarInfo> avatarInfos, CancellationToken token)
|
||||
{
|
||||
SummaryFactoryMetadataContext metadataContext = new()
|
||||
{
|
||||
IdAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false),
|
||||
IdWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false),
|
||||
IdReliquaryAffixWeightMap = await metadataService.GetIdToReliquaryAffixWeightMapAsync(token).ConfigureAwait(false),
|
||||
IdReliquaryMainPropertyMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync(token).ConfigureAwait(false),
|
||||
IdReliquarySubAffixMap = await metadataService.GetIdToReliquarySubAffixMapAsync(token).ConfigureAwait(false),
|
||||
ReliquaryLevels = await metadataService.GetReliquaryLevelListAsync(token).ConfigureAwait(false),
|
||||
Reliquaries = await metadataService.GetReliquaryListAsync(token).ConfigureAwait(false),
|
||||
};
|
||||
SummaryFactoryMetadataContext context = await metadataService
|
||||
.GetContextAsync<SummaryFactoryMetadataContext>(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
IOrderedEnumerable<AvatarView> avatars = avatarInfos
|
||||
.Where(a => !AvatarIds.IsPlayer(a.Info.AvatarId))
|
||||
.Select(a => new SummaryAvatarFactory(metadataContext, a).Create())
|
||||
.Select(a => SummaryAvatarFactory.Create(context, a))
|
||||
.OrderByDescending(a => a.LevelNumber)
|
||||
.ThenBy(a => a.Name);
|
||||
.ThenByDescending(a => a.FetterLevel)
|
||||
.ThenBy(a => a.Element);
|
||||
|
||||
// TODO: thenby weapon type
|
||||
return new()
|
||||
{
|
||||
Avatars = [.. avatars],
|
||||
|
||||
@@ -17,7 +17,8 @@ internal sealed class SummaryFactoryMetadataContext : IMetadataContext,
|
||||
IMetadataDictionaryIdWeaponSource,
|
||||
IMetadataDictionaryIdReliquaryAffixWeightSource,
|
||||
IMetadataDictionaryIdReliquaryMainPropertySource,
|
||||
IMetadataDictionaryIdReliquarySubAffixSource
|
||||
IMetadataDictionaryIdReliquarySubAffixSource,
|
||||
IMetadataListReliquaryMainAffixLevelSource
|
||||
{
|
||||
public Dictionary<AvatarId, MetadataAvatar> IdAvatarMap { get; set; } = default!;
|
||||
|
||||
@@ -29,7 +30,7 @@ internal sealed class SummaryFactoryMetadataContext : IMetadataContext,
|
||||
|
||||
public Dictionary<ReliquarySubAffixId, ReliquarySubAffix> IdReliquarySubAffixMap { get; set; } = default!;
|
||||
|
||||
public List<ReliquaryMainAffixLevel> ReliquaryLevels { get; set; } = default!;
|
||||
public List<ReliquaryMainAffixLevel> ReliquaryMainAffixLevels { get; set; } = default!;
|
||||
|
||||
public List<MetadataReliquary> Reliquaries { get; set; } = default!;
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.ViewModel.AvatarProperty;
|
||||
|
||||
namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
|
||||
@@ -15,66 +11,6 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
[HighQuality]
|
||||
internal static class SummaryHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建命之座
|
||||
/// </summary>
|
||||
/// <param name="talents">全部命座</param>
|
||||
/// <param name="talentIds">激活的命座列表</param>
|
||||
/// <returns>命之座</returns>
|
||||
public static List<ConstellationView> CreateConstellations(List<Skill> talents, List<SkillId>? talentIds)
|
||||
{
|
||||
talentIds ??= [];
|
||||
|
||||
return talents.SelectList(talent => new ConstellationView()
|
||||
{
|
||||
Name = talent.Name,
|
||||
Icon = SkillIconConverter.IconNameToUri(talent.Icon),
|
||||
Description = talent.Description,
|
||||
IsActivated = talentIds.Contains(talent.Id),
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建技能组
|
||||
/// </summary>
|
||||
/// <param name="skillLevelMap">技能等级映射</param>
|
||||
/// <param name="proudSkillExtraLevelMap">额外提升等级映射</param>
|
||||
/// <param name="proudSkills">技能列表</param>
|
||||
/// <returns>技能</returns>
|
||||
public static List<SkillView> CreateSkills(Dictionary<SkillId, SkillLevel>? skillLevelMap, Dictionary<SkillGroupId, SkillLevel>? proudSkillExtraLevelMap, List<ProudableSkill> proudSkills)
|
||||
{
|
||||
if (skillLevelMap.IsNullOrEmpty())
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
Dictionary<SkillId, SkillLevel> skillExtraLeveledMap = new(skillLevelMap);
|
||||
|
||||
if (proudSkillExtraLevelMap is not null)
|
||||
{
|
||||
foreach ((SkillGroupId groupId, SkillLevel extraLevel) in proudSkillExtraLevelMap)
|
||||
{
|
||||
skillExtraLeveledMap.IncreaseValue(proudSkills.Single(p => p.GroupId == groupId).Id, extraLevel);
|
||||
}
|
||||
}
|
||||
|
||||
return proudSkills.SelectList(proudableSkill =>
|
||||
{
|
||||
SkillId skillId = proudableSkill.Id;
|
||||
|
||||
return new SkillView()
|
||||
{
|
||||
Name = proudableSkill.Name,
|
||||
Icon = SkillIconConverter.IconNameToUri(proudableSkill.Icon),
|
||||
Description = proudableSkill.Description,
|
||||
|
||||
GroupId = proudableSkill.GroupId,
|
||||
LevelNumber = skillLevelMap[skillId],
|
||||
Info = DescriptionsParametersDescriptor.Convert(proudableSkill.Proud, skillExtraLeveledMap[skillId]),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取副属性对应的最大属性的Id
|
||||
/// </summary>
|
||||
@@ -132,22 +68,4 @@ internal static class SummaryHelper
|
||||
_ => throw Must.NeverHappen($"Unexpected AppendId: {appendId.Value} Delta: {delta}"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取双爆评分
|
||||
/// </summary>
|
||||
/// <param name="fightPropMap">属性</param>
|
||||
/// <returns>评分</returns>
|
||||
public static float ScoreCrit(Dictionary<FightProperty, float>? fightPropMap)
|
||||
{
|
||||
if (fightPropMap.IsNullOrEmpty())
|
||||
{
|
||||
return 0F;
|
||||
}
|
||||
|
||||
float cr = fightPropMap[FightProperty.FIGHT_PROP_CRITICAL];
|
||||
float cd = fightPropMap[FightProperty.FIGHT_PROP_CRITICAL_HURT];
|
||||
|
||||
return 100 * ((cr * 2) + cd);
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ internal sealed class SummaryReliquaryFactory
|
||||
ArgumentNullException.ThrowIfNull(equip.Flat.ReliquarySubstats);
|
||||
result.ComposedSubProperties = CreateComposedSubProperties(equip.Reliquary.AppendPropIdList);
|
||||
|
||||
ReliquaryMainAffixLevel relicLevel = metadataContext.ReliquaryLevels.Single(r => r.Level == equip.Reliquary.Level && r.Rank == reliquary.RankLevel);
|
||||
ReliquaryMainAffixLevel relicLevel = metadataContext.ReliquaryMainAffixLevels.Single(r => r.Level == equip.Reliquary.Level && r.Rank == reliquary.RankLevel);
|
||||
FightProperty property = metadataContext.IdReliquaryMainPropertyMap[equip.Reliquary.MainPropId];
|
||||
|
||||
result.MainProperty = FightPropertyFormat.ToNameValue(property, relicLevel.PropertyMap[property]);
|
||||
@@ -146,7 +146,7 @@ internal sealed class SummaryReliquaryFactory
|
||||
// 从喵插件抓取的圣遗物评分权重
|
||||
// 部分复杂的角色暂时使用了默认值
|
||||
ReliquaryAffixWeight affixWeight = metadataContext.IdReliquaryAffixWeightMap.GetValueOrDefault(avatarInfo.AvatarId, ReliquaryAffixWeight.Default);
|
||||
ReliquaryMainAffixLevel? maxRelicLevel = metadataContext.ReliquaryLevels.Where(r => r.Rank == reliquary.RankLevel).MaxBy(r => r.Level);
|
||||
ReliquaryMainAffixLevel? maxRelicLevel = metadataContext.ReliquaryMainAffixLevels.Where(r => r.Rank == reliquary.RankLevel).MaxBy(r => r.Level);
|
||||
ArgumentNullException.ThrowIfNull(maxRelicLevel);
|
||||
|
||||
float percent = relicLevel.PropertyMap[property] / maxRelicLevel.PropertyMap[property];
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataListReliquaryMainAffixLevelSource
|
||||
{
|
||||
public List<ReliquaryMainAffixLevel> ReliquaryMainAffixLevels { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataListReliquarySource
|
||||
{
|
||||
public List<Reliquary> Reliquaries { get; set; }
|
||||
}
|
||||
@@ -33,6 +33,16 @@ internal static class MetadataServiceContextExtension
|
||||
{
|
||||
listMaterialSource.Materials = await metadataService.GetMaterialListAsync(token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (context is IMetadataListReliquaryMainAffixLevelSource listReliquaryMainAffixLevelSource)
|
||||
{
|
||||
listReliquaryMainAffixLevelSource.ReliquaryMainAffixLevels = await metadataService.GetReliquaryMainAffixLevelListAsync(token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (context is IMetadataListReliquarySource listReliquarySource)
|
||||
{
|
||||
listReliquarySource.Reliquaries = await metadataService.GetReliquaryListAsync(token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Dictionary
|
||||
|
||||
@@ -80,7 +80,7 @@ internal static class MetadataServiceListExtension
|
||||
return metadataService.FromCacheOrFileAsync<List<ReliquaryMainAffix>>(FileNameReliquaryMainAffix, token);
|
||||
}
|
||||
|
||||
public static ValueTask<List<ReliquaryMainAffixLevel>> GetReliquaryLevelListAsync(this IMetadataService metadataService, CancellationToken token = default)
|
||||
public static ValueTask<List<ReliquaryMainAffixLevel>> GetReliquaryMainAffixLevelListAsync(this IMetadataService metadataService, CancellationToken token = default)
|
||||
{
|
||||
return metadataService.FromCacheOrFileAsync<List<ReliquaryMainAffixLevel>>(FileNameReliquaryMainAffixLevel, token);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user