mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
cultivation wip [skip ci]
This commit is contained in:
34
src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/LinqTest.cs
Normal file
34
src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/LinqTest.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Snap.Hutao.Test.BaseClassLibrary;
|
||||
|
||||
[TestClass]
|
||||
public sealed class LinqTest
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void LinqOrderByWithWrapperStruct()
|
||||
{
|
||||
List<MyUInt32> list = [1, 5, 2, 6, 3, 7, 4, 8];
|
||||
string result = string.Join(", ", list.OrderBy(i => i).Select(i => i.Value));
|
||||
|
||||
Console.WriteLine(result);
|
||||
}
|
||||
|
||||
private readonly struct MyUInt32
|
||||
{
|
||||
public readonly uint Value;
|
||||
|
||||
public MyUInt32(uint value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator MyUInt32(uint value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,4 +207,4 @@ internal static partial class EnumerableExtension
|
||||
list.Sort((left, right) => keySelector(right).CompareTo(keySelector(left)));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Extension;
|
||||
|
||||
@@ -75,9 +74,4 @@ internal static class SpanExtension
|
||||
|
||||
return unchecked((byte)(sum / count));
|
||||
}
|
||||
|
||||
public static Span<T> AsSpan<T>(this List<T> list)
|
||||
{
|
||||
return CollectionsMarshal.AsSpan(list);
|
||||
}
|
||||
}
|
||||
612
src/Snap.Hutao/Snap.Hutao/Migrations/20231207085530_AddCultivateEntryLevelInformation.Designer.cs
generated
Normal file
612
src/Snap.Hutao/Snap.Hutao/Migrations/20231207085530_AddCultivateEntryLevelInformation.Designer.cs
generated
Normal file
@@ -0,0 +1,612 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20231207085530_AddCultivateEntryLevelInformation")]
|
||||
partial class AddCultivateEntryLevelInformation
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.0");
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Current")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("achievements");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("achievement_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("CalculatorRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("GameRecordRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Info")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("ShowcaseRefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("avatar_infos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("cultivate_entries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("AvatarLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("AvatarLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("SkillALevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillALevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_entry_level_informations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsFinished")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachedUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("cultivate_projects");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DailyNote")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("DailyTaskNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("DailyTaskNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HomeCoinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HomeCoinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("RefreshTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ResinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ResinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("daily_notes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("gacha_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("GachaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("QueryType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("gacha_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MihoyoSDK")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("game_accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AppendPropIdList")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MainPropId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_reliquaries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PromoteLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ProjectId");
|
||||
|
||||
b.ToTable("inventory_weapons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("ExpireTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("object_cache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("ScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SpiralAbyss")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("spiral_abysses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Aid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CookieToken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("CookieTokenLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Fingerprint")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("FingerprintLastUpdateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsOversea")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Ltoken");
|
||||
|
||||
b.Property<string>("Mid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("Stoken");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithMany()
|
||||
.HasForeignKey("EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithMany()
|
||||
.HasForeignKey("EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
|
||||
.WithMany()
|
||||
.HasForeignKey("ArchiveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Archive");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddCultivateEntryLevelInformation : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "cultivate_entry_level_informations",
|
||||
columns: table => new
|
||||
{
|
||||
InnerId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
EntryId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
AvatarLevelFrom = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
AvatarLevelTo = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
SkillALevelFrom = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
SkillALevelTo = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
SkillELevelFrom = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
SkillELevelTo = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
SkillQLevelFrom = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
SkillQLevelTo = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
WeaponLevelFrom = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
WeaponLevelTo = table.Column<uint>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_cultivate_entry_level_informations", x => x.InnerId);
|
||||
table.ForeignKey(
|
||||
name: "FK_cultivate_entry_level_informations_cultivate_entries_EntryId",
|
||||
column: x => x.EntryId,
|
||||
principalTable: "cultivate_entries",
|
||||
principalColumn: "InnerId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_cultivate_entry_level_informations_EntryId",
|
||||
table: "cultivate_entry_level_informations",
|
||||
column: "EntryId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "cultivate_entry_level_informations");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,13 +113,59 @@ namespace Snap.Hutao.Migrations
|
||||
b.ToTable("cultivate_entries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("AvatarLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("AvatarLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("SkillALevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillALevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillELevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SkillQLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelFrom")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("WeaponLevelTo")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("EntryId");
|
||||
|
||||
b.ToTable("cultivate_entry_level_informations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Count")
|
||||
b.Property<uint>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("EntryId")
|
||||
@@ -128,7 +174,7 @@ namespace Snap.Hutao.Migrations
|
||||
b.Property<bool>("IsFinished")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
b.Property<uint>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
@@ -481,6 +527,17 @@ namespace Snap.Hutao.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
.WithMany()
|
||||
.HasForeignKey("EntryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Entry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||
{
|
||||
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
using Snap.Hutao.Service.Cultivation;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Snap.Hutao.Model.Entity;
|
||||
|
||||
[Table("cultivate_entry_level_informations")]
|
||||
internal sealed class CultivateEntryLevelInformation : IMappingFrom<CultivateEntryLevelInformation, Guid, LevelInformation>
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid InnerId { get; set; }
|
||||
|
||||
public Guid EntryId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(EntryId))]
|
||||
public CultivateEntry? Entry { get; set; }
|
||||
|
||||
public uint AvatarLevelFrom { get; set; }
|
||||
|
||||
public uint AvatarLevelTo { get; set; }
|
||||
|
||||
public uint SkillALevelFrom { get; set; }
|
||||
|
||||
public uint SkillALevelTo { get; set; }
|
||||
|
||||
public uint SkillELevelFrom { get; set; }
|
||||
|
||||
public uint SkillELevelTo { get; set; }
|
||||
|
||||
public uint SkillQLevelFrom { get; set; }
|
||||
|
||||
public uint SkillQLevelTo { get; set; }
|
||||
|
||||
public uint WeaponLevelFrom { get; set; }
|
||||
|
||||
public uint WeaponLevelTo { get; set; }
|
||||
|
||||
public static CultivateEntryLevelInformation From(Guid entryId, LevelInformation source)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
EntryId = entryId,
|
||||
AvatarLevelFrom = source.AvatarLevelFrom,
|
||||
AvatarLevelTo = source.AvatarLevelTo,
|
||||
SkillALevelFrom = source.SkillALevelFrom,
|
||||
SkillALevelTo = source.SkillALevelTo,
|
||||
SkillELevelFrom = source.SkillELevelFrom,
|
||||
SkillELevelTo = source.SkillELevelTo,
|
||||
SkillQLevelFrom = source.SkillQLevelFrom,
|
||||
SkillQLevelTo = source.SkillQLevelTo,
|
||||
WeaponLevelFrom = source.WeaponLevelFrom,
|
||||
WeaponLevelTo = source.WeaponLevelTo,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -35,12 +35,12 @@ internal sealed class CultivateItem : IDbMappingForeignKeyFrom<CultivateItem, We
|
||||
/// <summary>
|
||||
/// 物品 Id
|
||||
/// </summary>
|
||||
public int ItemId { get; set; }
|
||||
public uint ItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品个数
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
public uint Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否完成此项
|
||||
|
||||
@@ -37,89 +37,40 @@ internal sealed class AppDbContext : DbContext
|
||||
logger.LogInformation("{Name}[{Id}] created", nameof(AppDbContext), ContextId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置
|
||||
/// </summary>
|
||||
public DbSet<SettingEntry> Settings { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 用户
|
||||
/// </summary>
|
||||
public DbSet<User> Users { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 成就
|
||||
/// </summary>
|
||||
public DbSet<Achievement> Achievements { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 成就存档
|
||||
/// </summary>
|
||||
public DbSet<AchievementArchive> AchievementArchives { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 卡池数据
|
||||
/// </summary>
|
||||
public DbSet<GachaItem> GachaItems { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 卡池存档
|
||||
/// </summary>
|
||||
public DbSet<GachaArchive> GachaArchives { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 角色信息
|
||||
/// </summary>
|
||||
public DbSet<AvatarInfo> AvatarInfos { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏内账号
|
||||
/// </summary>
|
||||
public DbSet<GameAccount> GameAccounts { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 实时便笺
|
||||
/// </summary>
|
||||
public DbSet<DailyNoteEntry> DailyNotes { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 对象缓存
|
||||
/// </summary>
|
||||
public DbSet<ObjectCacheEntry> ObjectCache { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 培养计划
|
||||
/// </summary>
|
||||
public DbSet<CultivateProject> CultivateProjects { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 培养入口点
|
||||
/// </summary>
|
||||
public DbSet<CultivateEntry> CultivateEntries { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 培养消耗物品
|
||||
/// </summary>
|
||||
public DbSet<CultivateEntryLevelInformation> LevelInformations { get; set; } = default!;
|
||||
|
||||
public DbSet<CultivateItem> CultivateItems { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 背包内物品
|
||||
/// </summary>
|
||||
public DbSet<InventoryItem> InventoryItems { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 背包内武器
|
||||
/// </summary>
|
||||
public DbSet<InventoryWeapon> InventoryWeapons { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 背包内圣遗物
|
||||
/// </summary>
|
||||
public DbSet<InventoryReliquary> InventoryReliquaries { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 深渊记录
|
||||
/// </summary>
|
||||
public DbSet<SpiralAbyssEntry> SpiralAbysses { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -161,4 +161,22 @@ internal sealed partial class CultivationDbService : ICultivationDbService
|
||||
return appDbContext.CultivateProjects.AsNoTracking().ToObservableCollection();
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask RemoveLevelInformationByEntryIdAsync(Guid entryId)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
await appDbContext.LevelInformations.ExecuteDeleteWhereAsync(l => l.EntryId == entryId).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask AddLevelInformationAsync(CultivateEntryLevelInformation levelInformation)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
await appDbContext.LevelInformations.AddAndSaveAsync(levelInformation).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Service.Cultivation;
|
||||
|
||||
internal sealed class CultivationMetadataContext : ICultivationMetadataContext
|
||||
{
|
||||
public List<Material> Materials { get; set; } = default!;
|
||||
|
||||
public Dictionary<MaterialId, Material> IdMaterialMap { get; set; } = default!;
|
||||
|
||||
public Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> IdAvatarMap { get; set; } = default!;
|
||||
|
||||
public Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> IdWeaponMap { get; set; } = default!;
|
||||
}
|
||||
@@ -7,8 +7,8 @@ using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Database;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Inventroy;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.ViewModel.Cultivation;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
@@ -29,7 +29,7 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public List<InventoryItemView> GetInventoryItemViews(CultivateProject cultivateProject, List<Material> metadata, ICommand saveCommand)
|
||||
public List<InventoryItemView> GetInventoryItemViews(CultivateProject cultivateProject, ICultivationMetadataContext context, ICommand saveCommand)
|
||||
{
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
@@ -39,7 +39,7 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
List<InventoryItem> entities = cultivationDbService.GetInventoryItemListByProjectId(projectId);
|
||||
|
||||
List<InventoryItemView> results = [];
|
||||
foreach (Material meta in metadata.Where(m => m.IsInventoryItem()).OrderBy(m => m.Id.Value))
|
||||
foreach (Material meta in context.EnumerateInventroyMaterial())
|
||||
{
|
||||
InventoryItem entity = entities.SingleOrDefault(e => e.ItemId == meta.Id) ?? InventoryItem.From(projectId, meta.Id);
|
||||
results.Add(new(entity, meta, saveCommand));
|
||||
@@ -50,11 +50,7 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<ObservableCollection<CultivateEntryView>> GetCultivateEntriesAsync(
|
||||
CultivateProject cultivateProject,
|
||||
List<Material> materials,
|
||||
Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap,
|
||||
Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> idWeaponMap)
|
||||
public async ValueTask<ObservableCollection<CultivateEntryView>> GetCultivateEntriesAsync(CultivateProject cultivateProject, ICultivationMetadataContext context)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
List<CultivateEntry> entries = await cultivationDbService
|
||||
@@ -67,13 +63,13 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
List<CultivateItemView> entryItems = [];
|
||||
foreach (CultivateItem item in await cultivationDbService.GetCultivateItemListByEntryIdAsync(entry.InnerId).ConfigureAwait(false))
|
||||
{
|
||||
entryItems.Add(new(item, materials.Single(m => m.Id == item.ItemId)));
|
||||
entryItems.Add(new(item, context.GetMaterial(item.ItemId)));
|
||||
}
|
||||
|
||||
Item itemBase = entry.Type switch
|
||||
{
|
||||
CultivateType.AvatarAndSkill => idAvatarMap[entry.Id].ToItem(),
|
||||
CultivateType.Weapon => idWeaponMap[entry.Id].ToItem(),
|
||||
CultivateType.AvatarAndSkill => context.GetAvatar(entry.Id).ToItem(),
|
||||
CultivateType.Weapon => context.GetWeapon(entry.Id).ToItem(),
|
||||
|
||||
// TODO: support furniture calc
|
||||
_ => default!,
|
||||
@@ -89,9 +85,7 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<ObservableCollection<StatisticsCultivateItem>> GetStatisticsCultivateItemCollectionAsync(
|
||||
CultivateProject cultivateProject,
|
||||
List<Material> materials,
|
||||
CancellationToken token)
|
||||
CultivateProject cultivateProject, ICultivationMetadataContext context, CancellationToken token)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
List<StatisticsCultivateItem> resultItems = [];
|
||||
@@ -115,7 +109,7 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
}
|
||||
else
|
||||
{
|
||||
resultItems.Add(new(materials.Single(m => m.Id == item.ItemId), item));
|
||||
resultItems.Add(new(context.GetMaterial(item.ItemId), item));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,7 +152,7 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<bool> SaveConsumptionAsync(CultivateType type, uint itemId, List<Web.Hoyolab.Takumi.Event.Calculate.Item> items)
|
||||
public async ValueTask<bool> SaveConsumptionAsync(CultivateType type, uint itemId, List<Web.Hoyolab.Takumi.Event.Calculate.Item> items, LevelInformation levelInformation)
|
||||
{
|
||||
if (items.Count == 0)
|
||||
{
|
||||
@@ -188,8 +182,12 @@ internal sealed partial class CultivationService : ICultivationService
|
||||
}
|
||||
|
||||
Guid entryId = entry.InnerId;
|
||||
await cultivationDbService.RemoveCultivateItemRangeByEntryIdAsync(entryId).ConfigureAwait(false);
|
||||
|
||||
await cultivationDbService.RemoveLevelInformationByEntryIdAsync(entryId).ConfigureAwait(false);
|
||||
CultivateEntryLevelInformation entryLevelInformation = CultivateEntryLevelInformation.From(entryId, levelInformation);
|
||||
await cultivationDbService.AddLevelInformationAsync(entryLevelInformation).ConfigureAwait(false);
|
||||
|
||||
await cultivationDbService.RemoveCultivateItemRangeByEntryIdAsync(entryId).ConfigureAwait(false);
|
||||
IEnumerable<CultivateItem> toAdd = items.Select(item => CultivateItem.From(entryId, item));
|
||||
await cultivationDbService.AddCultivateItemRangeAsync(toAdd).ConfigureAwait(false);
|
||||
|
||||
|
||||
@@ -35,4 +35,6 @@ internal interface ICultivationDbService
|
||||
void UpdateCultivateItem(CultivateItem item);
|
||||
|
||||
ValueTask UpdateCultivateItemAsync(CultivateItem item);
|
||||
ValueTask RemoveLevelInformationByEntryIdAsync(Guid entryId);
|
||||
ValueTask AddLevelInformationAsync(CultivateEntryLevelInformation levelInformation);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
namespace Snap.Hutao.Service.Cultivation;
|
||||
|
||||
internal interface ICultivationMetadataContext : IMetadataContext,
|
||||
IMetadataListMaterialSource,
|
||||
IMetadataDictionaryIdMaterialSource,
|
||||
IMetadataDictionaryIdAvatarSource,
|
||||
IMetadataDictionaryIdWeaponSource
|
||||
{
|
||||
}
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Primitive;
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.ViewModel.Cultivation;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -27,38 +25,12 @@ internal interface ICultivationService
|
||||
/// </summary>
|
||||
ObservableCollection<CultivateProject> ProjectCollection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取绑定用的养成列表
|
||||
/// </summary>
|
||||
/// <param name="cultivateProject">养成计划</param>
|
||||
/// <param name="materials">材料</param>
|
||||
/// <param name="idAvatarMap">Id 角色映射</param>
|
||||
/// <param name="idWeaponMap">Id 武器映射</param>
|
||||
/// <returns>绑定用的养成列表</returns>
|
||||
ValueTask<ObservableCollection<CultivateEntryView>> GetCultivateEntriesAsync(
|
||||
CultivateProject cultivateProject,
|
||||
List<Material> materials,
|
||||
Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap,
|
||||
Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> idWeaponMap);
|
||||
ValueTask<ObservableCollection<CultivateEntryView>> GetCultivateEntriesAsync(CultivateProject cultivateProject, ICultivationMetadataContext context);
|
||||
|
||||
/// <summary>
|
||||
/// 获取物品列表
|
||||
/// </summary>
|
||||
/// <param name="cultivateProject">养成计划</param>
|
||||
/// <param name="metadata">元数据</param>
|
||||
/// <param name="saveCommand">保存命令</param>
|
||||
/// <returns>物品列表</returns>
|
||||
List<InventoryItemView> GetInventoryItemViews(CultivateProject cultivateProject, List<Material> metadata, ICommand saveCommand);
|
||||
List<InventoryItemView> GetInventoryItemViews(CultivateProject cultivateProject, ICultivationMetadataContext context, ICommand saveCommand);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取统计物品列表
|
||||
/// </summary>
|
||||
/// <param name="cultivateProject">养成计划</param>
|
||||
/// <param name="materials">材料</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>统计物品列表</returns>
|
||||
ValueTask<ObservableCollection<StatisticsCultivateItem>> GetStatisticsCultivateItemCollectionAsync(
|
||||
CultivateProject cultivateProject, List<Material> materials, CancellationToken token);
|
||||
CultivateProject cultivateProject, ICultivationMetadataContext context, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// 删除养成清单
|
||||
@@ -74,14 +46,7 @@ internal interface ICultivationService
|
||||
/// <returns>任务</returns>
|
||||
ValueTask RemoveProjectAsync(CultivateProject project);
|
||||
|
||||
/// <summary>
|
||||
/// 异步保存养成物品
|
||||
/// </summary>
|
||||
/// <param name="type">类型</param>
|
||||
/// <param name="itemId">主Id</param>
|
||||
/// <param name="items">待存物品</param>
|
||||
/// <returns>是否保存成功</returns>
|
||||
ValueTask<bool> SaveConsumptionAsync(CultivateType type, uint itemId, List<Item> items);
|
||||
ValueTask<bool> SaveConsumptionAsync(CultivateType type, uint itemId, List<Item> items, LevelInformation levelInformation);
|
||||
|
||||
/// <summary>
|
||||
/// 保存养成物品状态
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Abstraction;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
namespace Snap.Hutao.Service.Cultivation;
|
||||
|
||||
internal sealed class LevelInformation : IMappingFrom<LevelInformation, AvatarPromotionDelta>
|
||||
{
|
||||
public uint AvatarLevelFrom { get; private set; }
|
||||
|
||||
public uint AvatarLevelTo { get; private set; }
|
||||
|
||||
public uint SkillALevelFrom { get; private set; }
|
||||
|
||||
public uint SkillALevelTo { get; private set; }
|
||||
|
||||
public uint SkillELevelFrom { get; private set; }
|
||||
|
||||
public uint SkillELevelTo { get; private set; }
|
||||
|
||||
public uint SkillQLevelFrom { get; private set; }
|
||||
|
||||
public uint SkillQLevelTo { get; private set; }
|
||||
|
||||
public uint WeaponLevelFrom { get; private set; }
|
||||
|
||||
public uint WeaponLevelTo { get; private set; }
|
||||
|
||||
public static LevelInformation From(AvatarPromotionDelta delta)
|
||||
{
|
||||
LevelInformation levelInformation = new();
|
||||
|
||||
if (delta.AvatarId != 0U)
|
||||
{
|
||||
levelInformation.AvatarLevelFrom = delta.AvatarLevelCurrent;
|
||||
levelInformation.AvatarLevelTo = delta.AvatarLevelTarget;
|
||||
}
|
||||
|
||||
if (delta.SkillList is [PromotionDelta skillA, PromotionDelta skillE, PromotionDelta skillQ, ..])
|
||||
{
|
||||
levelInformation.SkillALevelFrom = skillA.LevelCurrent;
|
||||
levelInformation.SkillALevelTo = skillA.LevelTarget;
|
||||
levelInformation.SkillELevelFrom = skillE.LevelCurrent;
|
||||
levelInformation.SkillELevelTo = skillE.LevelTarget;
|
||||
levelInformation.SkillQLevelFrom = skillQ.LevelCurrent;
|
||||
levelInformation.SkillQLevelTo = skillQ.LevelTarget;
|
||||
}
|
||||
|
||||
if (delta.Weapon is { } weapon)
|
||||
{
|
||||
levelInformation.WeaponLevelFrom = weapon.LevelCurrent;
|
||||
levelInformation.WeaponLevelTo = weapon.LevelTarget;
|
||||
}
|
||||
|
||||
return levelInformation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataContext;
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdAvatarSource
|
||||
{
|
||||
public Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> IdAvatarMap { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdMaterialSource
|
||||
{
|
||||
public Dictionary<MaterialId, Material> IdMaterialMap { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataDictionaryIdWeaponSource
|
||||
{
|
||||
public Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> IdWeaponMap { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal interface IMetadataListMaterialSource
|
||||
{
|
||||
public List<Material> Materials { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
|
||||
internal static class MetadataServiceContextExtension
|
||||
{
|
||||
public static async ValueTask<TContext> GetContextAsync<TContext>(this IMetadataService metadataService, CancellationToken token = default)
|
||||
where TContext : IMetadataContext, new()
|
||||
{
|
||||
TContext context = new();
|
||||
|
||||
// List
|
||||
{
|
||||
if (context is IMetadataListMaterialSource listMaterialSource)
|
||||
{
|
||||
listMaterialSource.Materials = await metadataService.GetMaterialListAsync(token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Dictionary
|
||||
{
|
||||
if (context is IMetadataDictionaryIdAvatarSource dictionaryAvatarSource)
|
||||
{
|
||||
dictionaryAvatarSource.IdAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (context is IMetadataDictionaryIdMaterialSource dictionaryMaterialSource)
|
||||
{
|
||||
dictionaryMaterialSource.IdMaterialMap = await metadataService.GetIdToMaterialMapAsync(token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (context is IMetadataDictionaryIdWeaponSource dictionaryWeaponSource)
|
||||
{
|
||||
dictionaryWeaponSource.IdWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
#pragma warning disable SH002
|
||||
public static IEnumerable<Material> EnumerateInventroyMaterial(this IMetadataListMaterialSource context)
|
||||
{
|
||||
return context.Materials.Where(m => m.IsInventoryItem()).OrderBy(m => m.Id.Value);
|
||||
}
|
||||
|
||||
public static Avatar GetAvatar(this IMetadataDictionaryIdAvatarSource context, AvatarId id)
|
||||
{
|
||||
return context.IdAvatarMap[id];
|
||||
}
|
||||
|
||||
public static Material GetMaterial(this IMetadataDictionaryIdMaterialSource context, MaterialId id)
|
||||
{
|
||||
return context.IdMaterialMap[id];
|
||||
}
|
||||
|
||||
public static Weapon GetWeapon(this IMetadataDictionaryIdWeaponSource context, WeaponId id)
|
||||
{
|
||||
return context.IdWeaponMap[id];
|
||||
}
|
||||
#pragma warning restore SH002
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
namespace Snap.Hutao.View.Dialog;
|
||||
@@ -25,6 +26,30 @@ internal sealed partial class CultivatePromotionDeltaBatchDialog : ContentDialog
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
ContentDialogResult result = await ShowAsync();
|
||||
|
||||
return new(result == ContentDialogResult.Primary, PromotionDelta);
|
||||
if (result is not ContentDialogResult.Primary)
|
||||
{
|
||||
return new(false, default!);
|
||||
}
|
||||
|
||||
LocalSetting.Set(SettingKeys.CultivationAvatarLevelCurrent, PromotionDelta.AvatarLevelCurrent);
|
||||
LocalSetting.Set(SettingKeys.CultivationAvatarLevelTarget, PromotionDelta.AvatarLevelTarget);
|
||||
|
||||
if (PromotionDelta.SkillList is [PromotionDelta skillA, PromotionDelta skillE, PromotionDelta skillQ, ..])
|
||||
{
|
||||
LocalSetting.Set(SettingKeys.CultivationAvatarSkillACurrent, skillA.LevelCurrent);
|
||||
LocalSetting.Set(SettingKeys.CultivationAvatarSkillATarget, skillA.LevelTarget);
|
||||
LocalSetting.Set(SettingKeys.CultivationAvatarSkillECurrent, skillE.LevelCurrent);
|
||||
LocalSetting.Set(SettingKeys.CultivationAvatarSkillETarget, skillE.LevelTarget);
|
||||
LocalSetting.Set(SettingKeys.CultivationAvatarSkillQCurrent, skillQ.LevelCurrent);
|
||||
LocalSetting.Set(SettingKeys.CultivationAvatarSkillQTarget, skillQ.LevelTarget);
|
||||
}
|
||||
|
||||
if (PromotionDelta.Weapon is { } weapon)
|
||||
{
|
||||
LocalSetting.Set(SettingKeys.CultivationWeapon90LevelCurrent, weapon.LevelCurrent);
|
||||
LocalSetting.Set(SettingKeys.CultivationWeapon90LevelTarget, weapon.LevelTarget);
|
||||
}
|
||||
|
||||
return new(true, PromotionDelta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,61 +169,61 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
|
||||
if (userService.Current is null)
|
||||
{
|
||||
infoBarService.Warning(SH.MustSelectUserAndUid);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (avatar.Weapon is null)
|
||||
{
|
||||
if (avatar.Weapon is null)
|
||||
{
|
||||
infoBarService.Warning(SH.ViewModelAvatarPropertyCalculateWeaponNull);
|
||||
return;
|
||||
}
|
||||
infoBarService.Warning(SH.ViewModelAvatarPropertyCalculateWeaponNull);
|
||||
return;
|
||||
}
|
||||
|
||||
CalculableOptions options = new(avatar.ToCalculable(), avatar.Weapon.ToCalculable());
|
||||
CultivatePromotionDeltaDialog dialog = await contentDialogFactory.CreateInstanceAsync<CultivatePromotionDeltaDialog>(options).ConfigureAwait(false);
|
||||
(bool isOk, CalculatorAvatarPromotionDelta delta) = await dialog.GetPromotionDeltaAsync().ConfigureAwait(false);
|
||||
CalculableOptions options = new(avatar.ToCalculable(), avatar.Weapon.ToCalculable());
|
||||
CultivatePromotionDeltaDialog dialog = await contentDialogFactory.CreateInstanceAsync<CultivatePromotionDeltaDialog>(options).ConfigureAwait(false);
|
||||
(bool isOk, CalculatorAvatarPromotionDelta delta) = await dialog.GetPromotionDeltaAsync().ConfigureAwait(false);
|
||||
|
||||
if (!isOk)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!isOk)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Response<CalculatorConsumption> consumptionResponse = await calculatorClient
|
||||
.ComputeAsync(userService.Current.Entity, delta)
|
||||
Response<CalculatorConsumption> consumptionResponse = await calculatorClient
|
||||
.ComputeAsync(userService.Current.Entity, delta)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!consumptionResponse.IsOk())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CalculatorConsumption consumption = consumptionResponse.Data;
|
||||
LevelInformation levelInformation = LevelInformation.From(delta);
|
||||
|
||||
List<CalculatorItem> items = CalculatorItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
|
||||
bool avatarSaved = await cultivationService
|
||||
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items, levelInformation)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
// Take a hot path if avatar is not saved.
|
||||
bool avatarAndWeaponSaved = avatarSaved && await cultivationService
|
||||
.SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull(), levelInformation)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!consumptionResponse.IsOk())
|
||||
if (avatarAndWeaponSaved)
|
||||
{
|
||||
return;
|
||||
infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess);
|
||||
}
|
||||
|
||||
CalculatorConsumption consumption = consumptionResponse.Data;
|
||||
|
||||
List<CalculatorItem> items = CalculatorItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
|
||||
bool avatarSaved = await cultivationService
|
||||
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
try
|
||||
else
|
||||
{
|
||||
// take a hot path if avatar is not saved.
|
||||
bool avatarAndWeaponSaved = avatarSaved && await cultivationService
|
||||
.SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull())
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (avatarAndWeaponSaved)
|
||||
{
|
||||
infoBarService.Success(SH.ViewModelCultivationEntryAddSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
|
||||
}
|
||||
}
|
||||
catch (Core.ExceptionService.UserdataCorruptedException ex)
|
||||
{
|
||||
infoBarService.Error(ex, SH.ViewModelCultivationAddWarning);
|
||||
infoBarService.Warning(SH.ViewModelCultivationEntryAddWarning);
|
||||
}
|
||||
}
|
||||
catch (Core.ExceptionService.UserdataCorruptedException ex)
|
||||
{
|
||||
infoBarService.Error(ex, SH.ViewModelCultivationAddWarning);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("BatchCultivateCommand")]
|
||||
@@ -274,8 +274,11 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: handle this correctly.
|
||||
// We should always create a new baseline each time.
|
||||
baseline.Weapon.Id = avatar.Weapon.Id;
|
||||
baseline.Weapon.LevelCurrent = Math.Min(avatar.Weapon.LevelNumber, baseline.Weapon.LevelTarget);
|
||||
baseline.Weapon.LevelTarget = Math.Min(avatar.Weapon.MaxLevel, baseline.Weapon.LevelTarget);
|
||||
|
||||
Response<CalculatorConsumption> consumptionResponse = await calculatorClient
|
||||
.ComputeAsync(userService.Current.Entity, baseline)
|
||||
@@ -289,17 +292,18 @@ internal sealed partial class AvatarPropertyViewModel : Abstraction.ViewModel, I
|
||||
else
|
||||
{
|
||||
CalculatorConsumption consumption = consumptionResponse.Data;
|
||||
LevelInformation levelInformation = LevelInformation.From(baseline);
|
||||
|
||||
List<CalculatorItem> items = CalculatorItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
|
||||
bool avatarSaved = await cultivationService
|
||||
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
|
||||
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items, levelInformation)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
// take a hot path if avatar is not saved.
|
||||
bool avatarAndWeaponSaved = avatarSaved && await cultivationService
|
||||
.SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull())
|
||||
.SaveConsumptionAsync(CultivateType.Weapon, avatar.Weapon.Id, consumption.WeaponConsume.EmptyIfNull(), levelInformation)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (avatarAndWeaponSaved)
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
|
||||
using Snap.Hutao.Factory.ContentDialog;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Metadata.Item;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Cultivation;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.Service.Navigation;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using Snap.Hutao.View.Dialog;
|
||||
@@ -38,14 +37,8 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
|
||||
private ObservableCollection<CultivateEntryView>? cultivateEntries;
|
||||
private ObservableCollection<StatisticsCultivateItem>? statisticsItems;
|
||||
|
||||
/// <summary>
|
||||
/// 项目
|
||||
/// </summary>
|
||||
public ObservableCollection<CultivateProject>? Projects { get => projects; set => SetProperty(ref projects, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 当前选中的计划
|
||||
/// </summary>
|
||||
public CultivateProject? SelectedProject
|
||||
{
|
||||
get => selectedProject; set
|
||||
@@ -58,19 +51,10 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 物品列表
|
||||
/// </summary>
|
||||
public List<InventoryItemView>? InventoryItems { get => inventoryItems; set => SetProperty(ref inventoryItems, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 养成列表
|
||||
/// </summary>
|
||||
public ObservableCollection<CultivateEntryView>? CultivateEntries { get => cultivateEntries; set => SetProperty(ref cultivateEntries, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 统计列表
|
||||
/// </summary>
|
||||
public ObservableCollection<StatisticsCultivateItem>? StatisticsItems { get => statisticsItems; set => SetProperty(ref statisticsItems, value); }
|
||||
|
||||
protected override async ValueTask<bool> InitializeUIAsync()
|
||||
@@ -95,60 +79,61 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
|
||||
CultivateProjectDialog dialog = await contentDialogFactory.CreateInstanceAsync<CultivateProjectDialog>().ConfigureAwait(false);
|
||||
(bool isOk, CultivateProject project) = await dialog.CreateProjectAsync().ConfigureAwait(false);
|
||||
|
||||
if (isOk)
|
||||
if (!isOk)
|
||||
{
|
||||
ProjectAddResult result = await cultivationService.TryAddProjectAsync(project).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case ProjectAddResult.Added:
|
||||
infoBarService.Success(SH.ViewModelCultivationProjectAdded);
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
SelectedProject = project;
|
||||
break;
|
||||
case ProjectAddResult.InvalidName:
|
||||
infoBarService.Information(SH.ViewModelCultivationProjectInvalidName);
|
||||
break;
|
||||
case ProjectAddResult.AlreadyExists:
|
||||
infoBarService.Information(SH.ViewModelCultivationProjectAlreadyExists);
|
||||
break;
|
||||
default:
|
||||
throw Must.NeverHappen();
|
||||
}
|
||||
switch (await cultivationService.TryAddProjectAsync(project).ConfigureAwait(false))
|
||||
{
|
||||
case ProjectAddResult.Added:
|
||||
infoBarService.Success(SH.ViewModelCultivationProjectAdded);
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
SelectedProject = project;
|
||||
break;
|
||||
case ProjectAddResult.InvalidName:
|
||||
infoBarService.Information(SH.ViewModelCultivationProjectInvalidName);
|
||||
break;
|
||||
case ProjectAddResult.AlreadyExists:
|
||||
infoBarService.Information(SH.ViewModelCultivationProjectAlreadyExists);
|
||||
break;
|
||||
default:
|
||||
throw Must.NeverHappen();
|
||||
}
|
||||
}
|
||||
|
||||
[Command("RemoveProjectCommand")]
|
||||
private async Task RemoveProjectAsync(CultivateProject? project)
|
||||
{
|
||||
if (project is not null)
|
||||
if (project is null)
|
||||
{
|
||||
await cultivationService.RemoveProjectAsync(project).ConfigureAwait(false);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
ArgumentNullException.ThrowIfNull(Projects);
|
||||
SelectedProject = Projects.FirstOrDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
await cultivationService.RemoveProjectAsync(project).ConfigureAwait(false);
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
ArgumentNullException.ThrowIfNull(Projects);
|
||||
SelectedProject = Projects.FirstOrDefault();
|
||||
}
|
||||
|
||||
private async ValueTask UpdateEntryCollectionAsync(CultivateProject? project)
|
||||
{
|
||||
if (project is not null)
|
||||
if (project is null)
|
||||
{
|
||||
List<Material> materials = await metadataService.GetMaterialListAsync().ConfigureAwait(false);
|
||||
Dictionary<AvatarId, Model.Metadata.Avatar.Avatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false);
|
||||
Dictionary<WeaponId, Model.Metadata.Weapon.Weapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false);
|
||||
|
||||
ObservableCollection<CultivateEntryView> entries = await cultivationService
|
||||
.GetCultivateEntriesAsync(project, materials, idAvatarMap, idWeaponMap)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
CultivateEntries = entries;
|
||||
InventoryItems = cultivationService.GetInventoryItemViews(project, materials, SaveInventoryItemCommand);
|
||||
|
||||
await UpdateStatisticsItemsAsync().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
CultivationMetadataContext context = await metadataService.GetContextAsync<CultivationMetadataContext>().ConfigureAwait(false);
|
||||
|
||||
ObservableCollection<CultivateEntryView> entries = await cultivationService
|
||||
.GetCultivateEntriesAsync(project, context)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
CultivateEntries = entries;
|
||||
InventoryItems = cultivationService.GetInventoryItemViews(project, context, SaveInventoryItemCommand);
|
||||
|
||||
await UpdateStatisticsItemsAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Command("RemoveEntryCommand")]
|
||||
@@ -194,8 +179,8 @@ internal sealed partial class CultivationViewModel : Abstraction.ViewModel
|
||||
ObservableCollection<StatisticsCultivateItem> statistics;
|
||||
try
|
||||
{
|
||||
List<Material> materials = await metadataService.GetMaterialListAsync(token).ConfigureAwait(false);
|
||||
statistics = await cultivationService.GetStatisticsCultivateItemCollectionAsync(SelectedProject, materials, token).ConfigureAwait(false);
|
||||
CultivationMetadataContext context = await metadataService.GetContextAsync<CultivationMetadataContext>().ConfigureAwait(false);
|
||||
statistics = await cultivationService.GetStatisticsCultivateItemCollectionAsync(SelectedProject, context, token).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@ internal sealed class StatisticsCultivateItem
|
||||
/// <summary>
|
||||
/// 对应背包物品的个数
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
public uint Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 对应背包物品的个数
|
||||
|
||||
@@ -16,10 +16,10 @@ namespace Snap.Hutao.ViewModel;
|
||||
[Injection(InjectAs.Scoped)]
|
||||
internal sealed partial class TestViewModel : Abstraction.ViewModel
|
||||
{
|
||||
private readonly MainWindow mainWindow;
|
||||
private readonly HutaoAsAServiceClient homaAsAServiceClient;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly HutaoAsAServiceClient homaAsAServiceClient;
|
||||
private readonly MainWindow mainWindow;
|
||||
|
||||
private UploadAnnouncement announcement = new();
|
||||
|
||||
@@ -39,15 +39,10 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel
|
||||
set => LocalSetting.Set(SettingKeys.OverrideElevationRequirement, value);
|
||||
}
|
||||
|
||||
protected override ValueTask<bool> InitializeUIAsync()
|
||||
{
|
||||
return ValueTask.FromResult(true);
|
||||
}
|
||||
|
||||
[Command("ResetGuideStateCommand")]
|
||||
private static void ResetGuideState()
|
||||
{
|
||||
LocalSetting.Set(SettingKeys.Major1Minor7Revision0GuideState, (uint)GuideState.Language);
|
||||
UnsafeLocalSetting.Set(SettingKeys.Major1Minor7Revision0GuideState, GuideState.Language);
|
||||
}
|
||||
|
||||
[Command("ExceptionCommand")]
|
||||
@@ -59,11 +54,12 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel
|
||||
[Command("ResetMainWindowSizeCommand")]
|
||||
private void ResetMainWindowSize()
|
||||
{
|
||||
mainWindow.AppWindow.Resize(new(1280, 720));
|
||||
double scale = mainWindow.WindowOptions.GetRasterizationScale();
|
||||
mainWindow.AppWindow.Resize(new Windows.Graphics.SizeInt32(1280, 720).Scale(scale));
|
||||
}
|
||||
|
||||
[Command("UploadAnnouncementCommand")]
|
||||
private async void UploadAnnouncementAsync()
|
||||
private async Task UploadAnnouncementAsync()
|
||||
{
|
||||
Web.Response.Response response = await homaAsAServiceClient.UploadAnnouncementAsync(Announcement).ConfigureAwait(false);
|
||||
if (response.IsOk())
|
||||
@@ -75,7 +71,7 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel
|
||||
}
|
||||
|
||||
[Command("CompensationGachaLogServiceTimeCommand")]
|
||||
private async void CompensationGachaLogServiceTimeAsync()
|
||||
private async Task CompensationGachaLogServiceTimeAsync()
|
||||
{
|
||||
Web.Response.Response response = await homaAsAServiceClient.GachaLogCompensationAsync(15).ConfigureAwait(false);
|
||||
if (response.IsOk())
|
||||
|
||||
@@ -135,8 +135,6 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
|
||||
return;
|
||||
}
|
||||
|
||||
// ContentDialog must be created by main thread.
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
CalculableOptions options = new(avatar.ToCalculable(), null);
|
||||
CultivatePromotionDeltaDialog dialog = await contentDialogFactory.CreateInstanceAsync<CultivatePromotionDeltaDialog>(options).ConfigureAwait(false);
|
||||
(bool isOk, CalculateAvatarPromotionDelta delta) = await dialog.GetPromotionDeltaAsync().ConfigureAwait(false);
|
||||
@@ -156,11 +154,12 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
|
||||
}
|
||||
|
||||
CalculateConsumption consumption = consumptionResponse.Data;
|
||||
LevelInformation levelInformation = LevelInformation.From(delta);
|
||||
List<CalculateItem> items = CalculateItemHelper.Merge(consumption.AvatarConsume, consumption.AvatarSkillConsume);
|
||||
try
|
||||
{
|
||||
bool saved = await cultivationService
|
||||
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items)
|
||||
.SaveConsumptionAsync(CultivateType.AvatarAndSkill, avatar.Id, items, levelInformation)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (saved)
|
||||
|
||||
@@ -148,10 +148,11 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
|
||||
}
|
||||
|
||||
CalculateConsumption consumption = consumptionResponse.Data;
|
||||
LevelInformation levelInformation = LevelInformation.From(delta);
|
||||
try
|
||||
{
|
||||
bool saved = await cultivationService
|
||||
.SaveConsumptionAsync(CultivateType.Weapon, weapon.Id, consumption.WeaponConsume.EmptyIfNull())
|
||||
.SaveConsumptionAsync(CultivateType.Weapon, weapon.Id, consumption.WeaponConsume.EmptyIfNull(), levelInformation)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (saved)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.ViewModel.AvatarProperty;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
@@ -62,4 +63,41 @@ internal sealed class AvatarPromotionDelta
|
||||
Weapon = new() { LevelTarget = LocalSetting.Get(SettingKeys.CultivationWeapon90LevelTarget, 90U), },
|
||||
};
|
||||
}
|
||||
|
||||
public static bool TryGetNonErrorCopy(AvatarPromotionDelta source, AvatarView avatar, out AvatarPromotionDelta? copy)
|
||||
{
|
||||
copy = new()
|
||||
{
|
||||
AvatarId = avatar.Id,
|
||||
AvatarLevelCurrent = Math.Min(avatar.LevelNumber, source.AvatarLevelTarget),
|
||||
};
|
||||
|
||||
if (avatar.Skills is [SkillView skillA, SkillView skillE, SkillView skillQ, ..])
|
||||
{
|
||||
copy.SkillList = new(3);
|
||||
copy.SkillList.Add(new()
|
||||
{
|
||||
Id = skillA.GroupId,
|
||||
LevelCurrent = Math.Min(skillA.LevelNumber, skillA.LevelTarget),
|
||||
});
|
||||
copy.SkillList[0].Id = skillA.GroupId;
|
||||
copy.SkillList[0].LevelCurrent = Math.Min(skillA.LevelNumber, copy.SkillList[0].LevelTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
target = new()
|
||||
{
|
||||
AvatarId = source.AvatarId,
|
||||
AvatarLevelCurrent = source.AvatarLevelCurrent,
|
||||
AvatarLevelTarget = source.AvatarLevelTarget,
|
||||
ElementAttrId = source.ElementAttrId,
|
||||
SkillList = source.SkillList?.Select(i => i.Clone()).ToList(),
|
||||
Weapon = source.Weapon?.Clone(),
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -187,9 +187,9 @@ internal sealed partial class CalculateClient
|
||||
private class IdCount
|
||||
{
|
||||
[JsonPropertyName("cnt")]
|
||||
public int Count { get; set; }
|
||||
public uint Count { get; set; }
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
public uint Id { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ internal sealed class Item
|
||||
/// Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
public uint Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
@@ -33,7 +33,7 @@ internal sealed class Item
|
||||
/// 数量
|
||||
/// </summary>
|
||||
[JsonPropertyName("num")]
|
||||
public int Num { get; set; }
|
||||
public uint Num { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品星级 仅有家具为有效值
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate;
|
||||
|
||||
/// <summary>
|
||||
/// 物品帮助类
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal static class ItemHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 合并两个物品列表
|
||||
/// </summary>
|
||||
/// <param name="left">左列表</param>
|
||||
/// <param name="right">右列表</param>
|
||||
/// <returns>合并且排序好的列表</returns>
|
||||
public static List<Item> Merge(List<Item>? left, List<Item>? right)
|
||||
{
|
||||
if (left.IsNullOrEmpty() && right.IsNullOrEmpty())
|
||||
{
|
||||
return new(0);
|
||||
return [];
|
||||
}
|
||||
|
||||
if (left.IsNullOrEmpty() && !right.IsNullOrEmpty())
|
||||
@@ -38,9 +31,10 @@ internal static class ItemHelper
|
||||
List<Item> result = new(left.Count + right.Count);
|
||||
result.AddRange(left);
|
||||
|
||||
foreach (Item item in right)
|
||||
foreach (ref readonly Item item in CollectionsMarshal.AsSpan(right))
|
||||
{
|
||||
if (result.SingleOrDefault(i => i.Id == item.Id) is { } existed)
|
||||
uint id = item.Id;
|
||||
if (result.SingleOrDefault(i => i.Id == id) is { } existed)
|
||||
{
|
||||
existed.Num += item.Num;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user