Practice battles work
This commit is contained in:
34644
SVSim.Database/Migrations/20260524022437_PracticeOpponents.Designer.cs
generated
Normal file
34644
SVSim.Database/Migrations/20260524022437_PracticeOpponents.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SVSim.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class PracticeOpponents : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PracticeOpponents",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false),
|
||||
PracticeId = table.Column<int>(type: "integer", nullable: false),
|
||||
TextId = table.Column<string>(type: "text", nullable: false),
|
||||
ClassId = table.Column<int>(type: "integer", nullable: false),
|
||||
CharaId = table.Column<int>(type: "integer", nullable: false),
|
||||
DegreeId = table.Column<int>(type: "integer", nullable: false),
|
||||
AiDeckLevel = table.Column<int>(type: "integer", nullable: false),
|
||||
AiLogicLevel = table.Column<int>(type: "integer", nullable: false),
|
||||
AiMaxLife = table.Column<int>(type: "integer", nullable: false),
|
||||
Battle3dFieldId = table.Column<string>(type: "text", nullable: false),
|
||||
IsMaintenance = table.Column<bool>(type: "boolean", nullable: false),
|
||||
IsCampaignPractice = table.Column<bool>(type: "boolean", nullable: false),
|
||||
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
DateUpdated = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PracticeOpponents", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "PracticeOpponents");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25486,6 +25486,57 @@ namespace SVSim.Database.Migrations
|
||||
b.ToTable("PaymentItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.PracticeOpponentEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("AiDeckLevel")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("AiLogicLevel")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("AiMaxLife")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Battle3dFieldId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("CharaId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ClassId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("DateCreated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DateUpdated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("DegreeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("IsCampaignPractice")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("IsMaintenance")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("PracticeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("TextId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PracticeOpponents");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.PreReleaseInfo", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
||||
49
SVSim.Database/Models/PracticeOpponentEntry.cs
Normal file
49
SVSim.Database/Models/PracticeOpponentEntry.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
/// <summary>
|
||||
/// One row per AI opponent shown on the practice (solo-play) opponent select screen.
|
||||
/// Populated from prod /practice/info captures by SVSim.Bootstrap.GlobalsImporter.
|
||||
///
|
||||
/// The (<see cref="ClassId"/>, <see cref="AiDeckLevel"/>) pair MUST exist in the client's
|
||||
/// baked-in master CSV `ai/practice_ai_setting`; if it doesn't, the client's
|
||||
/// PracticeAISettingDataSet.GetSettingData throws InvalidOperationException and the
|
||||
/// difficulty-select dialog crashes BEFORE /practice/start is sent. Prod's catalog is
|
||||
/// the safe source of truth — we can't see the CSV directly.
|
||||
/// </summary>
|
||||
public class PracticeOpponentEntry : BaseEntity<int>
|
||||
{
|
||||
/// <summary>Practice slot id (Id = practice_id from the wire; also unique).</summary>
|
||||
public int PracticeId { get => Id; set => Id = value; }
|
||||
|
||||
/// <summary>Text-table key resolved client-side via Data.Master.GetPracticeText.</summary>
|
||||
public string TextId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Class (leader) id the AI plays.</summary>
|
||||
public int ClassId { get; set; }
|
||||
|
||||
/// <summary>Portrait / character id (leader art).</summary>
|
||||
public int CharaId { get; set; }
|
||||
|
||||
/// <summary>Title-degree id shown next to the AI name. -1 when unset.</summary>
|
||||
public int DegreeId { get; set; }
|
||||
|
||||
/// <summary>AI deck-strength tier; key into the client's practice_ai_setting CSV.</summary>
|
||||
public int AiDeckLevel { get; set; }
|
||||
|
||||
/// <summary>AI decision-making tier.</summary>
|
||||
public int AiLogicLevel { get; set; }
|
||||
|
||||
/// <summary>Starting HP for the AI side (typically 20; 10 for the easiest "tutorial" rows).</summary>
|
||||
public int AiMaxLife { get; set; }
|
||||
|
||||
/// <summary>3D battlefield asset id (string on the wire; client int.TryParse's it).</summary>
|
||||
public string Battle3dFieldId { get; set; } = "1";
|
||||
|
||||
/// <summary>true => entry shown but disabled with a maintenance suffix.</summary>
|
||||
public bool IsMaintenance { get; set; }
|
||||
|
||||
/// <summary>true => entry is a special event-tied "campaign" practice.</summary>
|
||||
public bool IsCampaignPractice { get; set; }
|
||||
}
|
||||
@@ -111,4 +111,7 @@ public class GlobalsRepository : IGlobalsRepository
|
||||
|
||||
public Task<List<ShadowverseCardSetEntry>> GetRotationCardSets() =>
|
||||
_dbContext.CardSets.AsNoTracking().Where(s => s.IsInRotation).ToListAsync();
|
||||
|
||||
public Task<List<PracticeOpponentEntry>> GetPracticeOpponents() =>
|
||||
_dbContext.PracticeOpponents.AsNoTracking().OrderBy(e => e.ClassId).ThenBy(e => e.Id).ToListAsync();
|
||||
}
|
||||
|
||||
@@ -32,4 +32,5 @@ public interface IGlobalsRepository
|
||||
Task<List<FeatureMaintenanceEntry>> GetFeatureMaintenances();
|
||||
Task<PreReleaseInfo?> GetPreReleaseInfo();
|
||||
Task<List<ShadowverseCardSetEntry>> GetRotationCardSets();
|
||||
Task<List<PracticeOpponentEntry>> GetPracticeOpponents();
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public class SVSimDbContext : DbContext
|
||||
public DbSet<MaintenanceCardEntry> MaintenanceCards => Set<MaintenanceCardEntry>();
|
||||
public DbSet<FeatureMaintenanceEntry> FeatureMaintenances => Set<FeatureMaintenanceEntry>();
|
||||
public DbSet<PreReleaseInfo> PreReleaseInfos => Set<PreReleaseInfo>();
|
||||
public DbSet<PracticeOpponentEntry> PracticeOpponents => Set<PracticeOpponentEntry>();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
Reference in New Issue
Block a user