Files
SlaySharp/src/SlaySharp.Web/Pages/Index.razor
T
2026-09-16 23:37:16 +02:00

553 lines
26 KiB
Plaintext

@page "/"
@using SlaySharp.Core.Run
@using SlaySharp.Core.Combat
@using SlaySharp.Core.Models
@using SlaySharp.Core.Catalogs
@using SlaySharp.Core.Relics
@using SlaySharp.Web.Components
@inject IJSRuntime JS
<PageTitle>SlaySharp - Visual Studio Code Edition</PageTitle>
@if (_showIntroBoot)
{
<!-- Cyberpunk Roslyn CLR Intro Boot Sequence -->
<div class="intro-boot-screen @(_isGlitchTransitioning ? "intro-glitch-out" : "") font-mono" @onclick="EnterTitleScreenAsync">
<div class="intro-crt-lines"></div>
<div class="intro-content-box p-4 p-md-5">
<!-- Terminal Header -->
<div class="d-flex align-items-center justify-content-between mb-3 pb-2 border-bottom border-secondary">
<div class="d-flex align-items-center gap-2">
<span style="color: #e06c75; font-size: 0.9rem;">●</span>
<span style="color: #e5c07b; font-size: 0.9rem;">●</span>
<span style="color: #98c379; font-size: 0.9rem;">●</span>
<span class="text-muted ms-2" style="font-size: 0.78rem;">pwsh -- SlaySharp Roslyn CLR v8.0 Bootstrapper</span>
</div>
<span class="badge bg-dark text-info border border-info px-2 py-1" style="font-size: 0.7rem;">x64-wasm</span>
</div>
<!-- Animated Terminal Diagnostics Log -->
<div class="intro-term-body mb-4">
<div class="text-info">&gt; Microsoft (R) .NET Core CLR 8.0.0-rtm (WebAssembly)</div>
<div class="text-success">&gt; [OK] Roslyn C# AST &amp; JIT Tier-1 Optimization Engine loaded</div>
<div class="text-muted">&gt; [OK] Allocated 64MB managed memory with GC Generation 0/1/2</div>
<div class="text-warning">&gt; [ALERT] 3 Incident Branches detected on CI/CD pipeline!</div>
<div class="text-white">&gt; [READY] Principal .NET Architect credentials authenticated.</div>
</div>
<!-- Center ASCII / Neon Title Logo -->
<div class="intro-ascii-logo text-center my-3 my-md-4">
<pre class="neon-glow-text mb-0" style="color: #61afef; font-size: clamp(0.55rem, 1.4vw, 1.05rem); font-weight: 900; line-height: 1.15;">
____ _ ____ _
/ ___|| | __ _ _ _/ ___|| |__ __ _ _ __ _ __
\___ \| |/ _` | | | \___ \| '_ \ / _` | '__| '_ \
___) | | (_| | |_| |___) | | | | (_| | | | |_) |
|____/|_|\__,_|\__, |____/|_| |_|\__,_|_| | .__/
|___/ |_|
</pre>
<div class="text-info fw-bolder mt-3" style="font-size: clamp(0.9rem, 2vw, 1.25rem); letter-spacing: 2px;">
THE C# &amp; .NET RUNTIME ROGUELIKE DECKBUILDER
</div>
</div>
<!-- Glowing Launch CTA -->
<div class="text-center mt-4">
<button class="btn btn-success btn-lg font-mono fw-bolder px-5 py-3 shadow-lg intro-start-btn" @onclick="EnterTitleScreenAsync">
⚡ INITIALIZE WORKSPACE (PRESS ANYWHERE) ➔
</button>
</div>
<div class="text-muted text-center mt-3" style="font-size: 0.78rem;">
Click anywhere or press Enter to launch IDE Dashboard
</div>
</div>
</div>
}
else
{
@switch (_run.CurrentScreen)
{
case RunScreen.Title:
<!-- VS Code Welcome / Get Started Dashboard with Cinematic Intro -->
<div class="container-fluid h-100 p-4 d-flex flex-column overflow-y-auto font-mono position-relative title-screen-container"
style="background: linear-gradient(rgba(24, 26, 31, 0.78), rgba(24, 26, 31, 0.90)), url('images/title_screen_bg.jpg') no-repeat center center; background-size: cover;">
<!-- Animated Deployment Overlay -->
@if (_isDeploying)
{
<div class="deploying-overlay d-flex flex-column align-items-center justify-content-center">
<div class="spinner-border text-info mb-3" style="width: 4rem; height: 4rem;" role="status"></div>
<h2 class="text-white fw-bolder font-mono mb-2 neon-glow-text">DEPLOYING TO STAGING...</h2>
<div class="text-muted font-mono fs-6 mb-3">Compiling Roslyn IL modules & building Git topology graph...</div>
<div class="badge bg-dark border border-info text-info px-4 py-2 font-mono fs-6">
✓ 0 Errors | ✓ 0 Warnings | Production SLA 100%
</div>
</div>
}
<!-- Welcome Header with Intro Glow & Code Terminal Badge -->
<div class="mb-4 pb-3 border-bottom border-secondary d-flex justify-content-between align-items-center flex-wrap gap-3">
<div class="d-flex align-items-center gap-3">
<div class="csharp-logo-glow">
<span style="font-size: 3.2rem; color: #61afef; font-weight: 900;">C#</span>
</div>
<div>
<h1 class="text-white fw-bolder mb-0 neon-glow-text" style="font-size: 2.6rem; letter-spacing: -1px;">
SlaySharp <span style="color: #61afef; font-size: 1.3rem;">v1.3</span>
</h1>
<span class="text-info" style="font-size: 1rem; font-weight: 800;">
C# & .NET Runtime Roguelike Deckbuilder
</span>
</div>
</div>
<!-- Animated Terminal Boot Badge & Replay Intro CTA -->
<div class="d-flex align-items-center gap-3">
<button class="btn btn-outline-secondary btn-sm font-mono fw-bold px-3 py-2" @onclick="() => _showIntroBoot = true">
📺 Replay Boot Intro
</button>
<div class="card p-2 px-3 text-start shadow" style="background-color: rgba(24, 26, 31, 0.92); border: 1px solid #383e4a; border-radius: 8px;">
<div class="text-muted" style="font-size: 0.72rem; font-weight: 800;">RUNTIME ENVIRONMENT</div>
<div class="text-success font-mono" style="font-size: 0.85rem; font-weight: 800;">
🟢 .NET 8.0 CLR [AggressiveTieredOpt]
</div>
</div>
</div>
</div>
<div class="row g-4">
<!-- Left Column: Start Campaigns & Encounters -->
<div class="col-lg-7">
<!-- Main Hero CTA: Act 1 Roguelike Campaign -->
<div class="card p-4 mb-4 shadow-lg hero-campaign-card"
style="background: linear-gradient(135deg, rgba(27, 47, 35, 0.92) 0%, rgba(33, 37, 43, 0.95) 100%); border: 2px solid #98c379; border-radius: 14px; cursor: pointer; backdrop-filter: blur(8px);"
@onclick="TriggerCampaignLaunchAsync">
<div class="d-flex justify-content-between align-items-center mb-2">
<div class="d-flex align-items-center gap-3">
<span style="font-size: 2.5rem;" class="pulse-icon">🗺️</span>
<div>
<h3 class="text-success fw-bolder mb-0">START CAMPAIGN: ACT 1</h3>
<span class="text-muted" style="font-size: 0.82rem; font-weight: 800;">CI/CD Pipeline & Legacy Monolith Boss</span>
</div>
</div>
<span class="badge bg-success text-white fs-6 px-4 py-3 fw-bolder shadow launch-btn">
Launch Run (F5) ➔
</span>
</div>
<p class="text-white mb-0" style="font-size: 0.95rem; font-weight: 700; line-height: 1.5;">
Navigate the 7-floor procedural Git branch graph. Patch production bugs, resolve StackOverflow dilemmas, refactor modules at coffee breaks, and face the Legacy Monolith Boss!
</p>
</div>
<h4 class="text-white mb-3 d-flex align-items-center gap-2 fw-bold">
<span style="color: #61afef; font-size: 1.5rem;">⚡</span>
<span>Quick Incident Sandboxes (Single Encounters)</span>
</h4>
<div class="d-flex flex-column gap-3">
<!-- Config 1 -->
<div class="card p-3 shadow-sm sandbox-card" style="background-color: rgba(33, 37, 43, 0.92); border: 2px solid #383e4a; border-radius: 10px; cursor: pointer; backdrop-filter: blur(6px);"
@onclick="() => StartSandboxEncounter(1)">
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="text-danger fw-bolder fs-6">💥 [SEV-1] NullReference & Off-By-One</span>
<span class="badge bg-danger text-white fs-6 px-3 py-1">Quick Battle ➔</span>
</div>
<p class="text-white mb-0" style="font-size: 0.85rem; font-weight: 700;">
Patch NullReferenceException (45 HP) and Off-By-One Bug (22 HP).
</p>
</div>
<!-- Config 2 -->
<div class="card p-3 shadow-sm sandbox-card" style="background-color: rgba(33, 37, 43, 0.92); border: 2px solid #383e4a; border-radius: 10px; cursor: pointer; backdrop-filter: blur(6px);"
@onclick="() => StartSandboxEncounter(2)">
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="text-warning fw-bolder fs-6">👻 [SEV-2] Memory Leak Spectre</span>
<span class="badge bg-warning text-dark fs-6 px-3 py-1">Quick Battle ➔</span>
</div>
<p class="text-white mb-0" style="font-size: 0.85rem; font-weight: 700;">
Dispose unmanaged memory from Memory Leak Spectre (34 HP) with Shield gain.
</p>
</div>
<!-- Config 3 -->
<div class="card p-3 shadow-sm sandbox-card" style="background-color: rgba(33, 37, 43, 0.92); border: 2px solid #383e4a; border-radius: 10px; cursor: pointer; backdrop-filter: blur(6px);"
@onclick="() => StartSandboxEncounter(3)">
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="text-info fw-bolder fs-6">👑 [ACT-BOSS] Legacy Monolith (90 HP)</span>
<span class="badge bg-primary text-white fs-6 px-3 py-1">Boss Battle ➔</span>
</div>
<p class="text-white mb-0" style="font-size: 0.85rem; font-weight: 700;">
Battle the 90 HP Legacy Monolith Boss with spaghetti code and thread locks.
</p>
</div>
</div>
</div>
<!-- Right Column: Workspace & Diagnostics Configuration -->
<div class="col-lg-5">
<h4 class="text-white mb-3 d-flex align-items-center gap-2 fw-bold">
<span style="color: #56b6c2; font-size: 1.5rem;">⚙️</span>
<span>Active Profile</span>
</h4>
<div class="card p-4 mb-3" style="background-color: rgba(33, 37, 43, 0.92); border: 2px solid #383e4a; border-radius: 12px; backdrop-filter: blur(6px);">
<div class="d-flex align-items-center gap-2 mb-3">
<span style="font-size: 2.2rem;">👨‍💻</span>
<div>
<div class="text-info fw-bolder fs-5">Principal .NET Architect</div>
<div class="text-white" style="font-size: 0.85rem; font-weight: 800;">3 CPU Cycles / 70 SLA HP</div>
</div>
</div>
<div class="text-muted fw-bold mb-1" style="font-size: 0.78rem;">STARTER NUGET PACKAGES:</div>
<div class="d-flex flex-column gap-2 mb-2">
<div class="badge bg-dark border border-secondary text-white p-2 d-flex align-items-center justify-content-between" style="font-size: 0.85rem; font-weight: 800;">
<span>📜 Newtonsoft.Json</span>
<span class="text-info">+1 Card T1</span>
</div>
<div class="badge bg-dark border border-secondary text-white p-2 d-flex align-items-center justify-content-between" style="font-size: 0.85rem; font-weight: 800;">
<span>⚡ BenchmarkDotNet</span>
<span class="text-warning">+1 CPU / 2T</span>
</div>
<div class="badge bg-dark border border-secondary text-white p-2 d-flex align-items-center justify-content-between" style="font-size: 0.85rem; font-weight: 800;">
<span>📊 Serilog</span>
<span class="text-success">+2 Shield on Attack</span>
</div>
</div>
</div>
<div class="card p-3" style="background-color: rgba(33, 37, 43, 0.92); border: 2px solid #383e4a; border-radius: 12px; backdrop-filter: blur(6px);">
<div class="text-white fw-bolder mb-2" style="font-size: 0.88rem;">MECHANICS HIGHLIGHTS</div>
<div class="d-flex justify-content-between text-white" style="font-size: 0.85rem; font-weight: 800;">
<span>Branch Prediction Crit</span>
<span class="text-warning fw-bolder">25% chance for 2x DMG</span>
</div>
<div class="d-flex justify-content-between text-white mt-1" style="font-size: 0.85rem; font-weight: 800;">
<span>Git Procedural Map</span>
<span class="text-info fw-bolder">7-Floor Branching Paths</span>
</div>
<div class="d-flex justify-content-between text-white mt-1" style="font-size: 0.85rem; font-weight: 800;">
<span>Post-Battle PR Review</span>
<span class="text-success fw-bolder">Merge 1 of 3 Cards</span>
</div>
</div>
</div>
</div>
</div>
break;
case RunScreen.Map:
<MapView Run="@_run" />
break;
case RunScreen.Combat:
@if (_run.ActiveCombat != null)
{
<CombatArena Engine="@_run.ActiveCombat"
OnVictoryCompleted="HandleCombatVictory"
OnDefeatCompleted="HandleCombatDefeat"
OnRestartRequested="HandleCombatDefeat" />
}
break;
case RunScreen.Event:
<EventView Run="@_run" />
break;
case RunScreen.RestSite:
<RestSiteView Run="@_run" />
break;
case RunScreen.Shop:
<ShopView Run="@_run" />
break;
case RunScreen.RunVictory:
<!-- Act 1 Victory Screen -->
<div class="d-flex flex-column justify-content-center align-items-center h-100 p-4 font-mono"
style="background: linear-gradient(rgba(24, 26, 31, 0.80), rgba(24, 26, 31, 0.92)), url('images/title_screen_bg.jpg') no-repeat center center; background-size: cover;">
<div class="card p-5 text-center shadow-lg" style="max-width: 750px; background-color: rgba(33, 37, 43, 0.95); border: 3px solid #98c379; border-radius: 16px; backdrop-filter: blur(10px);">
<div style="font-size: 4rem;">🏆</div>
<h1 class="text-success fw-bolder mb-1" style="font-size: 2.5rem;">ACT 1 DEPLOYMENT SUCCESSFUL!</h1>
<h4 class="text-white fw-bold mb-3">The Legacy Monolith Boss Has Been Decoupled & Microserviced.</h4>
<div class="d-flex justify-content-center gap-4 my-4 p-3 rounded border border-secondary" style="background-color: #181a1f;">
<div>
<div class="text-muted" style="font-size: 0.8rem; font-weight: 800;">FINAL SLA HEALTH</div>
<div class="text-success fs-4 fw-bolder">@_run.Player.CurrentHp / @_run.Player.MaxHp HP</div>
</div>
<div class="border-start border-secondary ps-4">
<div class="text-muted" style="font-size: 0.8rem; font-weight: 800;">DEV CREDITS EARNED</div>
<div class="text-warning fs-4 fw-bolder">@_run.DevCredits Credits</div>
</div>
<div class="border-start border-secondary ps-4">
<div class="text-muted" style="font-size: 0.8rem; font-weight: 800;">FINAL DECK SIZE</div>
<div class="text-info fs-4 fw-bolder">@_run.Player.MasterDeck.Count Cards</div>
</div>
</div>
<div class="d-flex justify-content-center gap-3">
<button class="btn btn-success btn-lg font-mono fw-bold px-5 py-3 shadow" @onclick="TriggerCampaignLaunchAsync">
🚀 Start New Sprint / Run
</button>
<button class="btn btn-outline-secondary btn-lg font-mono fw-bold px-4 py-3" @onclick="() => _run.CurrentScreen = RunScreen.Title">
Main Dashboard
</button>
</div>
</div>
</div>
break;
case RunScreen.RunDefeat:
<!-- Run Defeat Screen -->
<div class="d-flex flex-column justify-content-center align-items-center h-100 p-4 font-mono"
style="background: linear-gradient(rgba(24, 26, 31, 0.85), rgba(24, 26, 31, 0.95)), url('images/combat_arena_bg.jpg') no-repeat center center; background-size: cover;">
<div class="card p-5 text-center shadow-lg" style="max-width: 650px; background-color: rgba(33, 37, 43, 0.95); border: 3px solid #e06c75; border-radius: 16px; backdrop-filter: blur(10px);">
<div style="font-size: 4.5rem;">💥</div>
<h1 class="text-danger fw-bolder mb-1" style="font-size: 2.5rem;">PRODUCTION OUTAGE</h1>
<p class="text-white fs-5 mb-4">SLA dropped to 0%. The deployment was rolled back by SRE.</p>
<div class="d-flex justify-content-center gap-3">
<button class="btn btn-danger btn-lg font-mono fw-bold px-5 py-3 shadow" @onclick="TriggerCampaignLaunchAsync">
🔄 Retrying Deployment (New Run)
</button>
<button class="btn btn-outline-secondary btn-lg font-mono fw-bold px-4 py-3" @onclick="() => _run.CurrentScreen = RunScreen.Title">
Main Dashboard
</button>
</div>
</div>
</div>
break;
}
}
<style>
/* Intro Boot Screen & Terminal Styles */
.intro-boot-screen {
position: fixed;
inset: 0;
background: #0d1117;
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
overflow: hidden;
animation: intro-fade-in 0.4s ease-out;
}
@@keyframes intro-fade-in {
from { opacity: 0; transform: scale(0.98); }
to { opacity: 1; transform: scale(1); }
}
.intro-glitch-out {
animation: intro-warp-out 0.45s forwards cubic-bezier(0.7, 0, 0.3, 1);
}
@@keyframes intro-warp-out {
0% { opacity: 1; transform: scale(1); filter: brightness(1); }
50% { opacity: 0.8; transform: scale(1.05) skewX(2deg); filter: brightness(2); }
100% { opacity: 0; transform: scale(1.15); filter: brightness(3); }
}
.intro-crt-lines {
position: absolute;
inset: 0;
background: repeating-linear-gradient(
rgba(0, 0, 0, 0.15) 0px,
rgba(0, 0, 0, 0.15) 1px,
transparent 1px,
transparent 2px
);
pointer-events: none;
opacity: 0.7;
}
.intro-content-box {
position: relative;
max-width: 820px;
width: 90%;
background: rgba(22, 27, 34, 0.95);
border: 2px solid #383e4a;
border-radius: 14px;
box-shadow: 0 0 50px rgba(0, 122, 204, 0.4), inset 0 0 20px rgba(0, 0, 0, 0.8);
backdrop-filter: blur(10px);
}
.intro-term-body {
background: #0a0c10;
border: 1px solid #21262d;
border-radius: 8px;
padding: 14px 18px;
font-size: 0.82rem;
display: flex;
flex-direction: column;
gap: 6px;
box-shadow: inset 0 2px 10px rgba(0,0,0,0.8);
}
.intro-start-btn {
animation: start-btn-glow 1.4s infinite alternate ease-in-out;
letter-spacing: 1px;
}
@@keyframes start-btn-glow {
0% { transform: scale(1); box-shadow: 0 0 15px rgba(46, 160, 67, 0.6); }
100% { transform: scale(1.04); box-shadow: 0 0 35px rgba(46, 160, 67, 1); }
}
.csharp-logo-glow {
width: 65px;
height: 65px;
border-radius: 12px;
background: #181a1f;
border: 2px solid #61afef;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 20px rgba(97, 175, 239, 0.6);
animation: logo-pulse 2s infinite alternate ease-in-out;
}
@@keyframes logo-pulse {
0% { transform: scale(1); box-shadow: 0 0 15px rgba(97, 175, 239, 0.4); }
100% { transform: scale(1.06); box-shadow: 0 0 30px rgba(97, 175, 239, 0.9); }
}
.neon-glow-text {
text-shadow: 0 0 12px rgba(97, 175, 239, 0.6);
}
.hero-campaign-card {
transition: all 0.25s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.hero-campaign-card:hover {
transform: translateY(-5px);
border-color: #61afef !important;
box-shadow: 0 12px 30px rgba(97, 175, 239, 0.3) !important;
}
.sandbox-card {
transition: all 0.2s ease;
}
.sandbox-card:hover {
transform: translateX(6px);
border-color: #61afef !important;
}
.deploying-overlay {
position: absolute;
inset: 0;
background: rgba(15, 17, 21, 0.92);
backdrop-filter: blur(12px);
z-index: 999;
animation: fade-in 0.3s ease-out;
}
@@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
.pulse-icon {
animation: pulse-icon-anim 1.5s infinite alternate ease-in-out;
}
@@keyframes pulse-icon-anim {
0% { transform: scale(1); }
100% { transform: scale(1.15); }
}
</style>
@code {
private readonly GameRun _run = new();
private bool _showIntroBoot = true;
private bool _isGlitchTransitioning = false;
private bool _isSandboxMode = false;
private bool _isDeploying = false;
protected override void OnInitialized()
{
_run.OnRunStateChanged += StateHasChanged;
}
private async Task EnterTitleScreenAsync()
{
if (_isGlitchTransitioning) return;
_isGlitchTransitioning = true;
StateHasChanged();
_ = JS.InvokeVoidAsync("slayAudio.playIntroChime");
await Task.Delay(400);
_showIntroBoot = false;
_isGlitchTransitioning = false;
StateHasChanged();
}
private async Task TriggerCampaignLaunchAsync()
{
_isDeploying = true;
StateHasChanged();
_ = JS.InvokeVoidAsync("slayAudio.playBootSound");
await Task.Delay(900);
_isDeploying = false;
_isSandboxMode = false;
_run.StartNewRun();
StateHasChanged();
}
private void StartSandboxEncounter(int type)
{
_isSandboxMode = true;
_run.Player = new Player("Senior Dev", 70, maxEnergy: 3);
_run.Player.MasterDeck.AddRange(StarterDecks.CreateCsharpArchitectDeck());
_run.Player.Relics.Add(new NewtonsoftJsonRelic());
_run.Player.Relics.Add(new BenchmarkDotNetRelic());
_run.Player.Relics.Add(new SerilogRelic());
List<Enemy> enemies = type switch
{
1 => new List<Enemy> { new NullReferenceExceptionEnemy(), new OffByOneBugEnemy() },
2 => new List<Enemy> { new MemoryLeakSpectreEnemy(), new OffByOneBugEnemy() },
3 => new List<Enemy> { new LegacyMonolithBossEnemy() },
_ => new List<Enemy> { new NullReferenceExceptionEnemy() }
};
_run.ActiveCombat = new CombatEngine();
_run.ActiveCombat.StartCombat(_run.Player, enemies);
_run.CurrentScreen = RunScreen.Combat;
_ = JS.InvokeVoidAsync("slayAudio.playCardSound");
}
private void HandleCombatVictory()
{
if (_isSandboxMode)
{
_run.CurrentScreen = RunScreen.Title;
StateHasChanged();
}
else
{
_run.CompleteCombatAndClaimRewards(25);
}
}
private void HandleCombatDefeat()
{
if (_isSandboxMode)
{
_run.CurrentScreen = RunScreen.Title;
StateHasChanged();
}
else
{
_run.ReturnToMap();
}
}
}