changes
@@ -1,61 +1,61 @@
|
|||||||
|
@inject IGameDataLoader DataLoader
|
||||||
@inject IBattleEngine BattleEngine
|
@inject IBattleEngine BattleEngine
|
||||||
@inject ILocalStorageService Storage
|
|
||||||
@inject NavigationManager Nav
|
|
||||||
|
|
||||||
<div class="battle-arena-wrapper" tabindex="0" @ref="arenaElement" @onkeydown="HandleKeyDown" autofocus>
|
<div class="battle-arena-wrapper" tabindex="0" @onkeydown="HandleKeyDown" @ref="arenaElement">
|
||||||
<!-- Top Header / Bar -->
|
<!-- Top HUD / Level Info -->
|
||||||
<div class="battle-header">
|
<div class="battle-header">
|
||||||
|
|
||||||
<button class="btn-back" @onclick="GoBackToMap">⬅ Mapa</button>
|
<button class="btn-back" @onclick="GoBackToMap">⬅ Mapa</button>
|
||||||
<div class="level-info">
|
<div class="level-info">
|
||||||
<span class="world-title">@CurrentLevel.Name</span>
|
<span class="world-title">@CurrentLevel.Name</span>
|
||||||
@if (CurrentLevel.IsBoss)
|
@if (CurrentLevel.IsBoss)
|
||||||
{
|
{
|
||||||
<span class="boss-badge">👑 BOSS</span>
|
<span class="boss-badge">👑 BOSS SÚBOJ</span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="combo-counter @(State.ComboStreak > 1 ? "active-streak" : "")">
|
<div class="combo-counter @(State.ComboStreak > 1 ? "active-streak" : "")">
|
||||||
🔥 Kombo: <strong>@State.ComboStreak</strong>
|
🔥 Séria: <strong>@State.ComboStreak</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Feedback / Toast Banner -->
|
<!-- Dynamic Feedback Banner -->
|
||||||
@if (!string.IsNullOrEmpty(State.LastFeedbackMessage))
|
@if (!string.IsNullOrEmpty(State.LastFeedbackMessage) && !State.IsBattleOver)
|
||||||
{
|
{
|
||||||
<div class="feedback-banner @GetFeedbackClass()">
|
<div class="feedback-banner @GetFeedbackClass() anim-fade-in">
|
||||||
@State.LastFeedbackMessage
|
@State.LastFeedbackMessage
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Main Combat Arena -->
|
<!-- Visual Combat Stage with Dynamic World Background -->
|
||||||
<div class="arena-stage" style="background: linear-gradient(rgba(0,0,0,0.15), rgba(0,0,0,0.35)), url('assets/images/backgrounds/world1_meadow.jpg') center/cover no-repeat;">
|
<div class="arena-stage" style="background: linear-gradient(rgba(0,0,0,0.35), rgba(0,0,0,0.45)), url('@BackgroundImage') center/cover no-repeat;">
|
||||||
<!-- Player Side -->
|
<!-- Hero Side -->
|
||||||
|
<div class="combatant hero-side @GetPlayerAnimationClass()">
|
||||||
<div class="combatant player-side @GetPlayerAnimationClass()">
|
|
||||||
<div class="combatant-info">
|
<div class="combatant-info">
|
||||||
<span class="combatant-name">@Hero.Name</span>
|
<span class="combatant-name">@Hero.Name</span>
|
||||||
<div class="hp-hearts">
|
<div class="hp-hearts">
|
||||||
@for (int i = 1; i <= State.PlayerMaxHp; i++)
|
@for (int i = 0; i < State.PlayerMaxHp; i++)
|
||||||
{
|
{
|
||||||
<span class="heart @(i <= State.PlayerCurrentHp ? "full" : "empty")">
|
if (i < State.PlayerCurrentHp)
|
||||||
@(i <= State.PlayerCurrentHp ? "❤️" : "🤍")
|
{
|
||||||
</span>
|
<span class="heart full">❤️</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="heart empty">🖤</span>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Player Visual (4 Phases & Combat Poses) -->
|
<!-- Hero Visual (4 Phases & Combat Poses) -->
|
||||||
<div class="character-sprite-box">
|
<div class="character-sprite-box">
|
||||||
<div class="sprite-phase-indicator">Fáza @State.PlayerHpPhase / 4</div>
|
|
||||||
<img src="@GetCharacterImage(Hero.AssetFolder, State.PlayerHpPhase, State.PlayerPose)"
|
<img src="@GetCharacterImage(Hero.AssetFolder, State.PlayerHpPhase, State.PlayerPose)"
|
||||||
alt="@Hero.Name"
|
alt="@Hero.Name"
|
||||||
class="character-image"
|
class="character-image hero-img" />
|
||||||
onerror="this.src='data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' width=\'180\' height=\'240\'><rect width=\'100%\' height=\'100%\' fill=\'%234a90e2\'/><text x=\'50%\' y=\'50%\' fill=\'white\' font-size=\'20\' text-anchor=\'middle\'>🧙♂️ @Hero.Name (Fáza @State.PlayerHpPhase)</text></svg>'"/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- VS Divider / Dynamic Timer -->
|
<!-- Center Round Timer Orb -->
|
||||||
<div class="arena-center">
|
<div class="arena-center-timer">
|
||||||
<div class="timer-orb @GetTimerColorClass()">
|
<div class="timer-orb @GetTimerColorClass()">
|
||||||
<div class="timer-seconds">@RemainingTimeSeconds.ToString("F1")s</div>
|
<div class="timer-seconds">@RemainingTimeSeconds.ToString("F1")s</div>
|
||||||
<div class="timer-label">@(State.IsDefensePhase ? "OBRANA!" : "ÚTOK")</div>
|
<div class="timer-label">@(State.IsDefensePhase ? "OBRANA!" : "ÚTOK")</div>
|
||||||
@@ -77,8 +77,7 @@
|
|||||||
<div class="sprite-phase-indicator">Fáza @State.EnemyHpPhase / 4</div>
|
<div class="sprite-phase-indicator">Fáza @State.EnemyHpPhase / 4</div>
|
||||||
<img src="@GetCharacterImage(State.Enemy.AssetFolder, State.EnemyHpPhase, State.EnemyPose)"
|
<img src="@GetCharacterImage(State.Enemy.AssetFolder, State.EnemyHpPhase, State.EnemyPose)"
|
||||||
alt="@State.Enemy.Name"
|
alt="@State.Enemy.Name"
|
||||||
class="character-image enemy-img"
|
class="character-image enemy-img" />
|
||||||
onerror="this.src='data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' width=\'180\' height=\'240\'><rect width=\'100%\' height=\'100%\' fill=\'%23e74c3c\'/><text x=\'50%\' y=\'50%\' fill=\'white\' font-size=\'20\' text-anchor=\'middle\'>👾 @State.Enemy.Name (Fáza @State.EnemyHpPhase)</text></svg>'"/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -105,39 +104,116 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
<!-- Victory / Defeat Modal -->
|
|
||||||
<div class="battle-end-card @(State.IsVictory ? "victory-card" : "defeat-card")">
|
|
||||||
<h2>@(State.IsVictory ? "🏆 VÍŤAZSTVO!" : "💀 PORÁŽKA!")</h2>
|
|
||||||
<p>@State.LastFeedbackMessage</p>
|
|
||||||
<div class="end-actions">
|
|
||||||
@if (State.IsVictory)
|
|
||||||
{
|
|
||||||
<button class="action-btn next-level-btn" @onclick="OnVictoryContinue">Pokračovať v ceste ➡️</button>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<button class="action-btn retry-btn" @onclick="RetryBattle">Skúsiť znova 🔄</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- FULLSCREEN VICTORY / DEFEAT CELEBRATION OVERLAY -->
|
||||||
|
@if (State.IsBattleOver)
|
||||||
|
{
|
||||||
|
<div class="battle-end-overlay anim-fade-in">
|
||||||
|
<div class="celebration-modal @(State.IsVictory ? "victory-theme" : "defeat-theme") anim-victory-modal-pop">
|
||||||
|
@if (State.IsVictory)
|
||||||
|
{
|
||||||
|
<div class="celebration-particles">
|
||||||
|
<span class="particle star-p1">✨</span>
|
||||||
|
<span class="particle star-p2">⭐</span>
|
||||||
|
<span class="particle star-p3">🌟</span>
|
||||||
|
<span class="particle star-p4">✨</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="celebration-header">
|
||||||
|
<span class="celebration-crown">👑</span>
|
||||||
|
<h1 class="celebration-title">LEVEL PREKONANÝ!</h1>
|
||||||
|
<p class="celebration-subtitle">Úspešne si vyriešil všetky úlohy a zvíťazil!</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="celebration-body">
|
||||||
|
<div class="celebration-hero-stage">
|
||||||
|
<img src="@GetCharacterImage(Hero.AssetFolder, 1, CombatPose.Attack)"
|
||||||
|
alt="@Hero.Name"
|
||||||
|
class="celebration-hero-img anim-hero-victory" />
|
||||||
|
<span class="victory-hero-name">@Hero.Name</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="celebration-rewards-card">
|
||||||
|
<div class="reward-row">
|
||||||
|
<span class="reward-icon">⭐</span>
|
||||||
|
<div class="reward-info">
|
||||||
|
<strong>@(CurrentLevel.IsBoss ? "+3 HVIEZDY" : "+1 HVIEZDA")</strong>
|
||||||
|
<span>Odmena za víťazstvo</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="reward-row">
|
||||||
|
<span class="reward-icon">💰</span>
|
||||||
|
<div class="reward-info">
|
||||||
|
<strong>@(CurrentLevel.IsBoss ? "+50 ZLATA" : "+20 ZLATA")</strong>
|
||||||
|
<span>Kráľovská odmena</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="celebration-actions">
|
||||||
|
@if (NextLevel != null)
|
||||||
|
{
|
||||||
|
<button class="btn-grand-celebration btn-next-quest" @onclick="HandleNextLevelClick">
|
||||||
|
🚀 POSTÚPIŤ DO LEVELU: @NextLevel.Name ➜
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<div class="secondary-actions-row">
|
||||||
|
<button class="btn-secondary-celebration" @onclick="OnVictoryContinue">
|
||||||
|
🗺️ Mapa sveta
|
||||||
|
</button>
|
||||||
|
<button class="btn-secondary-celebration" @onclick="RetryBattle">
|
||||||
|
🔁 Hrať znova
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="defeat-header">
|
||||||
|
<span class="defeat-icon">💀</span>
|
||||||
|
<h1 class="defeat-title">NEVZDÁVAJ SA!</h1>
|
||||||
|
<p class="defeat-subtitle">@State.LastFeedbackMessage</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="defeat-body">
|
||||||
|
<p class="defeat-encouragement">
|
||||||
|
„Každý udatný počtár sa učí na vlastných chybách. Skús to znova a ukáž svoju matematickú silu!“
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="celebration-actions">
|
||||||
|
<button class="btn-grand-celebration btn-retry-quest" @onclick="RetryBattle">
|
||||||
|
🔄 SKÚSIŤ ZNOVA ⚔️
|
||||||
|
</button>
|
||||||
|
<button class="btn-secondary-celebration" @onclick="GoBackToMap">
|
||||||
|
🗺️ Návrat na mapu
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public Hero Hero { get; set; } = new();
|
[Parameter] public Hero Hero { get; set; } = new();
|
||||||
[Parameter] public Level CurrentLevel { get; set; } = new();
|
[Parameter] public Level CurrentLevel { get; set; } = new();
|
||||||
|
[Parameter] public int PlayerGrade { get; set; } = 1;
|
||||||
|
[Parameter] public string BackgroundImage { get; set; } = "assets/images/backgrounds/world1_meadow.jpg";
|
||||||
[Parameter] public GameConfig Config { get; set; } = new();
|
[Parameter] public GameConfig Config { get; set; } = new();
|
||||||
[Parameter] public EventCallback OnReturnToMap { get; set; }
|
[Parameter] public EventCallback OnReturnToMap { get; set; }
|
||||||
[Parameter] public EventCallback<Level> OnLevelCompleted { get; set; }
|
[Parameter] public EventCallback<Level> OnLevelCompleted { get; set; }
|
||||||
|
[Parameter] public EventCallback<Level> OnNextLevelRequested { get; set; }
|
||||||
|
|
||||||
private BattleState State => BattleEngine.State;
|
private BattleState State => BattleEngine.State;
|
||||||
private string CurrentInput { get; set; } = string.Empty;
|
private string CurrentInput { get; set; } = string.Empty;
|
||||||
private System.Threading.Timer? _timer;
|
private System.Threading.Timer? _timer;
|
||||||
private double RemainingTimeSeconds { get; set; } = 6.0;
|
private double RemainingTimeSeconds { get; set; } = 6.0;
|
||||||
|
|
||||||
|
private Level? NextLevel => DataLoader.GetNextLevel(PlayerGrade, CurrentLevel.Id);
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
StartBattle();
|
StartBattle();
|
||||||
@@ -229,6 +305,18 @@
|
|||||||
await OnLevelCompleted.InvokeAsync(CurrentLevel);
|
await OnLevelCompleted.InvokeAsync(CurrentLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task HandleNextLevelClick()
|
||||||
|
{
|
||||||
|
if (NextLevel != null)
|
||||||
|
{
|
||||||
|
await OnNextLevelRequested.InvokeAsync(NextLevel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await OnVictoryContinue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task GoBackToMap()
|
private async Task GoBackToMap()
|
||||||
{
|
{
|
||||||
_timer?.Dispose();
|
_timer?.Dispose();
|
||||||
@@ -297,23 +385,28 @@
|
|||||||
if (e.Key == "Enter")
|
if (e.Key == "Enter")
|
||||||
{
|
{
|
||||||
if (State.IsVictory)
|
if (State.IsVictory)
|
||||||
await OnVictoryContinue();
|
{
|
||||||
|
if (NextLevel != null)
|
||||||
|
await HandleNextLevelClick();
|
||||||
|
else
|
||||||
|
await OnVictoryContinue();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
await RetryBattle();
|
await RetryBattle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (State.CurrentProblem.InputType == "MultipleChoice" || State.IsDefensePhase)
|
if (State.CurrentProblem.InputType == "MultipleChoice" || State.IsDefensePhase)
|
||||||
{
|
{
|
||||||
// Handle number keys 1, 2, 3 to pick choices
|
|
||||||
if (int.TryParse(e.Key, out int choiceIndex) && choiceIndex >= 1 && choiceIndex <= State.CurrentProblem.Choices.Count)
|
if (int.TryParse(e.Key, out int choiceIndex) && choiceIndex >= 1 && choiceIndex <= State.CurrentProblem.Choices.Count)
|
||||||
{
|
{
|
||||||
await HandleChoiceSelected(State.CurrentProblem.Choices[choiceIndex - 1]);
|
await HandleChoiceSelected(State.CurrentProblem.Choices[choiceIndex - 1]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle A/N shortcuts for ÁNO / NIE
|
|
||||||
if (e.Key.Equals("a", StringComparison.OrdinalIgnoreCase) || e.Key.Equals("y", StringComparison.OrdinalIgnoreCase))
|
if (e.Key.Equals("a", StringComparison.OrdinalIgnoreCase) || e.Key.Equals("y", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var match = State.CurrentProblem.Choices.FirstOrDefault(c => c.StartsWith("Á", StringComparison.OrdinalIgnoreCase) || c.StartsWith("A", StringComparison.OrdinalIgnoreCase));
|
var match = State.CurrentProblem.Choices.FirstOrDefault(c => c.StartsWith("Á", StringComparison.OrdinalIgnoreCase) || c.StartsWith("A", StringComparison.OrdinalIgnoreCase));
|
||||||
@@ -329,7 +422,6 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Direct numeric input (0-9, Backspace, Enter)
|
|
||||||
if (e.Key.Length == 1 && char.IsDigit(e.Key[0]))
|
if (e.Key.Length == 1 && char.IsDigit(e.Key[0]))
|
||||||
{
|
{
|
||||||
if (CurrentInput.Length < 6)
|
if (CurrentInput.Length < 6)
|
||||||
@@ -356,4 +448,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,106 +1,379 @@
|
|||||||
@inject IGameDataLoader DataLoader
|
@inject IGameDataLoader DataLoader
|
||||||
@inject ILocalStorageService Storage
|
@inject ILocalStorageService Storage
|
||||||
|
|
||||||
<div class="character-select-screen">
|
<div class="wizard-screen">
|
||||||
<div class="parchment-container select-card-compact">
|
@switch (CurrentStep)
|
||||||
<div class="select-header">
|
{
|
||||||
<h1 class="game-main-title">🏰 ČÍSELNÁ VÝPRAVA</h1>
|
case WizardStep.SelectGrade:
|
||||||
<p class="subtitle">Vytvor si svojho hrdinu a vyber ročník</p>
|
{
|
||||||
</div>
|
<div class="parchment-container wizard-container">
|
||||||
|
<div class="wizard-top-bar">
|
||||||
<div class="select-content-grid">
|
<button class="btn-wizard-back" @onclick="HandleBackToMenu">
|
||||||
<!-- LEFT: ROČNÍK & MENO -->
|
⬅ Späť do menu
|
||||||
<div class="select-col-left">
|
</button>
|
||||||
<div class="selection-section">
|
<div class="wizard-stepper">
|
||||||
<h2 class="step-title">1. Tvoj ročník</h2>
|
<span class="step-badge active">1. Ročník 🎒</span>
|
||||||
<div class="grade-toggle-group">
|
<span class="step-divider">➔</span>
|
||||||
<button class="grade-btn @(SelectedGrade == 1 ? "active" : "")" @onclick="() => SetGrade(1)">
|
<span class="step-badge">2. Hrdina 🛡️</span>
|
||||||
<span class="grade-badge">🎒 1. ROČNÍK</span>
|
<span class="step-divider">➔</span>
|
||||||
<span class="grade-desc">Do 10 / 20</span>
|
<span class="step-badge">3. Meno 👑</span>
|
||||||
</button>
|
|
||||||
<button class="grade-btn @(SelectedGrade == 2 ? "active" : "")" @onclick="() => SetGrade(2)">
|
|
||||||
<span class="grade-badge">🎒 2. ROČNÍK</span>
|
|
||||||
<span class="grade-desc">Do 20 / 100</span>
|
|
||||||
</button>
|
|
||||||
<button class="grade-btn @(SelectedGrade == 3 ? "active" : "")" @onclick="() => SetGrade(3)">
|
|
||||||
<span class="grade-badge">🎒 3. ROČNÍK</span>
|
|
||||||
<span class="grade-desc">Násobilka & delenie</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="selection-section">
|
<div class="wizard-header">
|
||||||
<h2 class="step-title">3. Meno hrdinu</h2>
|
<h1 class="wizard-title">🎒 VYBER SI ROČNÍK</h1>
|
||||||
<div class="name-field-row">
|
<p class="wizard-subtitle">Zvoľ si úroveň matematických výziev pre svoje dobrodružstvo</p>
|
||||||
<input type="text" class="hero-name-input" @bind="HeroName" placeholder="Tvoje meno..." maxlength="20" />
|
</div>
|
||||||
<button class="btn-start-quest" @onclick="StartQuest">
|
|
||||||
Vstúpiť do hry 🚀
|
<div class="grades-grid">
|
||||||
</button>
|
<!-- GRADE 1 -->
|
||||||
|
<div class="grade-card grade-1-card @(SelectedGrade == 1 ? "selected" : "")" @onclick="() => SelectGradeAndProceed(1)">
|
||||||
|
<div class="grade-banner-stage" style="background: url('assets/images/backgrounds/world1_meadow.jpg') center/cover no-repeat;">
|
||||||
|
<div class="grade-banner-overlay">
|
||||||
|
<span class="grade-card-icon">⭐</span>
|
||||||
|
<div class="grade-card-badge grade-1-badge">1. ROČNÍK</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grade-card-content">
|
||||||
|
<h3 class="grade-card-title">Prváčik</h3>
|
||||||
|
<p class="grade-card-desc">Základné sčítanie a odčítanie v rozprávkovej lúke.</p>
|
||||||
|
<ul class="grade-topics-list">
|
||||||
|
<li>➕ Sčítanie a odčítanie do 10 a 20</li>
|
||||||
|
<li>⚖️ Porovnávanie čísel (<, >, =)</li>
|
||||||
|
<li>🔟 Počítanie po desiatkach do 100</li>
|
||||||
|
</ul>
|
||||||
|
<button class="btn-grade-select btn-grade-1" @onclick:stopPropagation="true" @onclick="() => SelectGradeAndProceed(1)">
|
||||||
|
Zvoliť 1. ročník ➜
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- GRADE 2 -->
|
||||||
|
<div class="grade-card grade-2-card @(SelectedGrade == 2 ? "selected" : "")" @onclick="() => SelectGradeAndProceed(2)">
|
||||||
|
<div class="grade-banner-stage" style="background: url('assets/images/backgrounds/world2_forest.jpg') center/cover no-repeat;">
|
||||||
|
<div class="grade-banner-overlay">
|
||||||
|
<span class="grade-card-icon">🌟</span>
|
||||||
|
<div class="grade-card-badge grade-2-badge">2. ROČNÍK</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grade-card-content">
|
||||||
|
<h3 class="grade-card-title">Druhák</h3>
|
||||||
|
<p class="grade-card-desc">Počítanie do 100 s prechodom a úvod do násobilky.</p>
|
||||||
|
<ul class="grade-topics-list">
|
||||||
|
<li>➕ Sčítanie a odčítanie do 100 s prechodom</li>
|
||||||
|
<li>⚖️ Porovnávanie hodnôt do 100</li>
|
||||||
|
<li>✖️ Malá násobilka (2, 3, 5, 10)</li>
|
||||||
|
</ul>
|
||||||
|
<button class="btn-grade-select btn-grade-2" @onclick:stopPropagation="true" @onclick="() => SelectGradeAndProceed(2)">
|
||||||
|
Zvoliť 2. ročník ➜
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- GRADE 3 -->
|
||||||
|
<div class="grade-card grade-3-card @(SelectedGrade == 3 ? "selected" : "")" @onclick="() => SelectGradeAndProceed(3)">
|
||||||
|
<div class="grade-banner-stage" style="background: url('assets/images/backgrounds/world3_river.jpg') center/cover no-repeat;">
|
||||||
|
<div class="grade-banner-overlay">
|
||||||
|
<span class="grade-card-icon">👑</span>
|
||||||
|
<div class="grade-card-badge grade-3-badge">3. ROČNÍK</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grade-card-content">
|
||||||
|
<h3 class="grade-card-title">Tretiak</h3>
|
||||||
|
<p class="grade-card-desc">Kompletná násobilka, delenie a počítanie do tisícky!</p>
|
||||||
|
<ul class="grade-topics-list">
|
||||||
|
<li>✖️ Celá násobilka a delenie (2 – 10)</li>
|
||||||
|
<li>🧮 Počítanie do 1000 (stovky a desiatky)</li>
|
||||||
|
<li>🏆 Finálne bitky o kráľovskú citadelu</li>
|
||||||
|
</ul>
|
||||||
|
<button class="btn-grade-select btn-grade-3" @onclick:stopPropagation="true" @onclick="() => SelectGradeAndProceed(3)">
|
||||||
|
Zvoliť 3. ročník ➜
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
<!-- RIGHT: VÝBER HRDINU -->
|
case WizardStep.SelectHero:
|
||||||
<div class="select-col-right">
|
{
|
||||||
<div class="selection-section">
|
<div class="parchment-container wizard-container">
|
||||||
<h2 class="step-title">2. Vyber si hrdinu</h2>
|
<div class="wizard-top-bar">
|
||||||
<div class="heroes-grid">
|
<button class="btn-wizard-back" @onclick="() => CurrentStep = WizardStep.SelectGrade">
|
||||||
@foreach (var hero in DataLoader.Heroes)
|
⬅ Späť na ročník
|
||||||
|
</button>
|
||||||
|
<div class="wizard-stepper">
|
||||||
|
<span class="step-badge completed" @onclick="() => CurrentStep = WizardStep.SelectGrade">✓ @SelectedGrade. Ročník</span>
|
||||||
|
<span class="step-divider">➔</span>
|
||||||
|
<span class="step-badge active">2. Hrdina 🛡️</span>
|
||||||
|
<span class="step-divider">➔</span>
|
||||||
|
<span class="step-badge">3. Meno 👑</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wizard-header">
|
||||||
|
<h1 class="wizard-title">🛡️ ZVOĽ SI SVOJHO HRDINU</h1>
|
||||||
|
<p class="wizard-subtitle">Vyber si postavu, s ktorou zachrániš Číselné Kráľovstvo</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hero-showcase-grid">
|
||||||
|
@foreach (var hero in DataLoader.Heroes)
|
||||||
|
{
|
||||||
|
var isSelected = SelectedHeroId == hero.Id;
|
||||||
|
var heroImg = $"{hero.AssetFolder}_idle_p1.png";
|
||||||
|
var themeClass = hero.Id switch
|
||||||
{
|
{
|
||||||
<div class="hero-card @(SelectedHeroId == hero.Id ? "selected-card" : "")"
|
"knight" => "hero-theme-knight",
|
||||||
@onclick="() => SelectHero(hero.Id)">
|
"mage" => "hero-theme-mage",
|
||||||
<div class="hero-avatar">
|
"archer" => "hero-theme-archer",
|
||||||
@(hero.Id == "knight" ? "⚔️" : hero.Id == "mage" ? "🧙♂️" : "🏹")
|
_ => "hero-theme-knight"
|
||||||
|
};
|
||||||
|
var icon = hero.Id switch
|
||||||
|
{
|
||||||
|
"knight" => "⚔️",
|
||||||
|
"mage" => "🔮",
|
||||||
|
"archer" => "🏹",
|
||||||
|
_ => "🛡️"
|
||||||
|
};
|
||||||
|
|
||||||
|
<div class="hero-showcase-card @themeClass @(isSelected ? "selected" : "")"
|
||||||
|
@onclick="() => SelectHeroAndProceed(hero.Id)">
|
||||||
|
<div class="hero-card-banner">
|
||||||
|
<span class="hero-crest-icon">@icon</span>
|
||||||
|
<span class="hero-class-label">@hero.Title</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hero-portrait-stage">
|
||||||
|
<img src="@heroImg" alt="@hero.Name" class="hero-portrait-img anim-hero-idle" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hero-info-box">
|
||||||
|
<h3 class="hero-title-name">@hero.Name</h3>
|
||||||
|
<p class="hero-lore-desc">@hero.Description</p>
|
||||||
|
|
||||||
|
<div class="hero-skill-pill">
|
||||||
|
✨ <strong>@hero.SpecialAttackName</strong>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="hero-name">@hero.Name</h3>
|
|
||||||
<span class="hero-title">@hero.Title</span>
|
<div class="hero-stats-row">
|
||||||
<div class="hero-skill">
|
<span class="stat-tag">❤️ @hero.BaseHp HP</span>
|
||||||
✨ @hero.SpecialAttackName
|
<span class="stat-tag">⚡ @hero.AttackPower ÚTOK</span>
|
||||||
|
<span class="stat-tag">🛡️ ŠTÍT</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
|
<button class="btn-hero-select" @onclick:stopPropagation="true" @onclick="() => SelectHeroAndProceed(hero.Id)">
|
||||||
|
Zvoliť hrdinu ➜
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WizardStep.NameAndStart:
|
||||||
|
{
|
||||||
|
<div class="parchment-container wizard-container wizard-name-container">
|
||||||
|
<div class="wizard-top-bar">
|
||||||
|
<button class="btn-wizard-back" @onclick="() => CurrentStep = WizardStep.SelectHero">
|
||||||
|
⬅ Späť na výber hrdinu
|
||||||
|
</button>
|
||||||
|
<div class="wizard-stepper">
|
||||||
|
<span class="step-badge completed" @onclick="() => CurrentStep = WizardStep.SelectGrade">✓ @SelectedGrade. Ročník</span>
|
||||||
|
<span class="step-divider">➔</span>
|
||||||
|
<span class="step-badge completed" @onclick="() => CurrentStep = WizardStep.SelectHero">✓ @CurrentSelectedHero.Name</span>
|
||||||
|
<span class="step-divider">➔</span>
|
||||||
|
<span class="step-badge active">3. Meno & Štart 👑</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wizard-header">
|
||||||
|
<h1 class="wizard-title">👑 POJMENUJ SVOJHO HRDINU</h1>
|
||||||
|
<p class="wizard-subtitle">Posledný krok pred otvorením brány kráľovstva!</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="naming-content-stage">
|
||||||
|
<!-- LEFT: HERO AVATAR & BADGES -->
|
||||||
|
<div class="naming-hero-preview">
|
||||||
|
<div class="hero-stage-pedestal">
|
||||||
|
<img src="@CurrentSelectedHeroImg" alt="@CurrentSelectedHero.Name" class="naming-hero-img anim-hero-idle" />
|
||||||
|
</div>
|
||||||
|
<div class="naming-badges-row">
|
||||||
|
<span class="grade-pill-badge">🎒 @SelectedGrade. Ročník</span>
|
||||||
|
<span class="hero-pill-badge">🛡️ @CurrentSelectedHero.Title</span>
|
||||||
|
</div>
|
||||||
|
<div class="naming-stat-summary">
|
||||||
|
<span>❤️ 5 Životov</span>
|
||||||
|
<span>⚡ 10 Útok</span>
|
||||||
|
<span>⏱️ <3s Kritický zásah (150%)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- RIGHT: NAME INPUT & ACTION BUTTON -->
|
||||||
|
<div class="naming-form-box">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="heroNameInput">Ako sa volá tvoj hrdina?</label>
|
||||||
|
<div class="name-input-with-dice">
|
||||||
|
<input id="heroNameInput"
|
||||||
|
type="text"
|
||||||
|
class="hero-name-big-input"
|
||||||
|
@bind="HeroName"
|
||||||
|
placeholder="Zadaj meno hrdinu..."
|
||||||
|
maxlength="20" />
|
||||||
|
<button type="button"
|
||||||
|
class="btn-dice-randomizer @(IsRollingDice ? "rolling" : "")"
|
||||||
|
@onclick="RollRandomName"
|
||||||
|
title="Vyžrebovať náhodné meno!">
|
||||||
|
<span class="dice-icon">🎲</span>
|
||||||
|
<span class="dice-label">Náhodné meno</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="quick-names-section">
|
||||||
|
<span class="quick-names-title">Alebo si vyber obľúbené meno:</span>
|
||||||
|
<div class="quick-names-grid">
|
||||||
|
@foreach (var suggestion in QuickNameSuggestions)
|
||||||
|
{
|
||||||
|
<button type="button"
|
||||||
|
class="name-chip-btn @(HeroName == suggestion ? "active-chip" : "")"
|
||||||
|
@onclick="() => SetHeroName(suggestion)">
|
||||||
|
@suggestion
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="naming-action-footer">
|
||||||
|
<button class="btn-grand-start" @onclick="StartQuest">
|
||||||
|
⚔️ VSTÚPIŤ DO HRY A ZAČAŤ VÝPRAVU 🚀
|
||||||
|
</button>
|
||||||
|
<p class="autosave-notice">💾 Tvoj postup a hviezdičky sa automaticky ukladajú na tomto zariadení.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
break;
|
||||||
</div>
|
}
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public EventCallback<PlayerProfile> OnProfileReady { get; set; }
|
[Parameter] public EventCallback<PlayerProfile> OnProfileReady { get; set; }
|
||||||
|
[Parameter] public EventCallback OnCancel { get; set; }
|
||||||
|
|
||||||
|
private enum WizardStep
|
||||||
|
{
|
||||||
|
SelectGrade,
|
||||||
|
SelectHero,
|
||||||
|
NameAndStart
|
||||||
|
}
|
||||||
|
|
||||||
|
private WizardStep CurrentStep { get; set; } = WizardStep.SelectGrade;
|
||||||
private int SelectedGrade { get; set; } = 2;
|
private int SelectedGrade { get; set; } = 2;
|
||||||
private string SelectedHeroId { get; set; } = "knight";
|
private string SelectedHeroId { get; set; } = "knight";
|
||||||
private string HeroName { get; set; } = "Mladý Hrdina";
|
private string HeroName { get; set; } = "Udatný Rytier";
|
||||||
|
private bool IsRollingDice { get; set; } = false;
|
||||||
|
|
||||||
|
private Hero CurrentSelectedHero => DataLoader.GetHero(SelectedHeroId) ?? DataLoader.Heroes.FirstOrDefault() ?? new Hero();
|
||||||
|
private string CurrentSelectedHeroImg => $"{CurrentSelectedHero.AssetFolder}_idle_p1.png";
|
||||||
|
|
||||||
|
private readonly Random _random = new();
|
||||||
|
|
||||||
|
private readonly List<string> QuickNameSuggestions = new()
|
||||||
|
{
|
||||||
|
"Arthur", "Eliška", "Maxim", "Sofia", "Oliver",
|
||||||
|
"Viktória", "Lucas", "Nela", "Jakub", "Mia"
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly List<string> AllHeroNamesPool = new()
|
||||||
|
{
|
||||||
|
// Chlapčenské mená
|
||||||
|
"Arthur", "Maxim", "Oliver", "Lucas", "Leo", "Alex", "Matias", "Michal",
|
||||||
|
"Adam", "Jakub", "Filip", "Damián", "Teodor", "Bruno", "Hugo", "Šimon",
|
||||||
|
"Tobiáš", "Viktor", "Benjamín", "Richard", "Tomáš", "Patrik", "Matej",
|
||||||
|
// Dievčenské mená
|
||||||
|
"Eliška", "Sofia", "Viktória", "Nela", "Mia", "Nina", "Ema", "Tamara",
|
||||||
|
"Laura", "Dorotka", "Linda", "Sára", "Stella", "Lea", "Hana", "Kristína",
|
||||||
|
"Amélia", "Zoja", "Diana", "Barborka", "Zuzka", "Veronika", "Natália",
|
||||||
|
// Hrdinské rozprávkové tituly
|
||||||
|
"Udatný Arthur", "Bystrá Sofia", "Rýchly Alex", "Odvážna Eliška", "Múdry Oliver",
|
||||||
|
"Čarovná Mia", "Hrdina Maxim", "Jasná Stella", "Šampión Leo", "Zlatá Viktória",
|
||||||
|
"Rytier Pravítko", "Mág Číselník", "Lučostrelec Stovka", "Kráľovský Strážca"
|
||||||
|
};
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
if (DataLoader.Heroes.Any())
|
if (DataLoader.Heroes.Any())
|
||||||
{
|
{
|
||||||
SelectedHeroId = DataLoader.Heroes.First().Id;
|
SelectedHeroId = DataLoader.Heroes.First().Id;
|
||||||
|
UpdateDefaultName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetGrade(int grade)
|
private void SelectGradeAndProceed(int grade)
|
||||||
{
|
{
|
||||||
SelectedGrade = grade;
|
SelectedGrade = grade;
|
||||||
|
CurrentStep = WizardStep.SelectHero;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectHero(string heroId)
|
private void SelectHeroAndProceed(string heroId)
|
||||||
{
|
{
|
||||||
SelectedHeroId = heroId;
|
SelectedHeroId = heroId;
|
||||||
|
UpdateDefaultName();
|
||||||
|
CurrentStep = WizardStep.NameAndStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDefaultName()
|
||||||
|
{
|
||||||
|
HeroName = SelectedHeroId switch
|
||||||
|
{
|
||||||
|
"knight" => "Udatný Rytier",
|
||||||
|
"mage" => "Mág Číselník",
|
||||||
|
"archer" => "Bystrý Lučostrelec",
|
||||||
|
_ => "Mladý Hrdina"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RollRandomName()
|
||||||
|
{
|
||||||
|
IsRollingDice = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
var available = AllHeroNamesPool.Where(n => n != HeroName).ToList();
|
||||||
|
if (!available.Any()) available = AllHeroNamesPool;
|
||||||
|
|
||||||
|
HeroName = available[_random.Next(available.Count)];
|
||||||
|
|
||||||
|
await Task.Delay(300);
|
||||||
|
IsRollingDice = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetHeroName(string name)
|
||||||
|
{
|
||||||
|
HeroName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleBackToMenu()
|
||||||
|
{
|
||||||
|
await OnCancel.InvokeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartQuest()
|
private async Task StartQuest()
|
||||||
{
|
{
|
||||||
|
var finalName = string.IsNullOrWhiteSpace(HeroName) ? "Mladý Hrdina" : HeroName.Trim();
|
||||||
|
|
||||||
var profile = new PlayerProfile
|
var profile = new PlayerProfile
|
||||||
{
|
{
|
||||||
Name = string.IsNullOrWhiteSpace(HeroName) ? "Mladý Hrdina" : HeroName,
|
Name = finalName,
|
||||||
SelectedGrade = SelectedGrade,
|
SelectedGrade = SelectedGrade,
|
||||||
SelectedHeroId = SelectedHeroId,
|
SelectedHeroId = SelectedHeroId,
|
||||||
CurrentWorldId = 1,
|
CurrentWorldId = 1,
|
||||||
UnlockedLevels = new HashSet<string> { $"{SelectedGrade}-1-1" }
|
UnlockedLevels = new HashSet<string> { $"{SelectedGrade}-1-1" },
|
||||||
|
CompletedLevels = new HashSet<string>()
|
||||||
};
|
};
|
||||||
|
|
||||||
await Storage.SaveProfileAsync(profile);
|
await Storage.SaveProfileAsync(profile);
|
||||||
|
|||||||
@@ -1,61 +1,180 @@
|
|||||||
@inject IGameDataLoader DataLoader
|
@inject IGameDataLoader DataLoader
|
||||||
|
|
||||||
<div class="world-map-screen">
|
<div class="world-map-screen">
|
||||||
<!-- Top Player Status Bar -->
|
<!-- TOP STATUS BAR -->
|
||||||
<div class="map-top-bar">
|
<div class="map-top-bar">
|
||||||
<div class="player-pill">
|
<div class="player-pill">
|
||||||
<span class="avatar-icon">@(Profile.SelectedHeroId == "knight" ? "⚔️" : Profile.SelectedHeroId == "mage" ? "🧙♂️" : "🏹")</span>
|
<img src="@HeroAvatarUrl" alt="Hrdina" class="mini-hero-avatar" />
|
||||||
<span class="player-name">@Profile.Name</span>
|
<div class="player-info-text">
|
||||||
<span class="grade-tag">@(Profile.SelectedGrade). Ročník</span>
|
<span class="player-name">@Profile.Name</span>
|
||||||
|
<span class="grade-tag">🎒 @(Profile.SelectedGrade). Ročník</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-pill">
|
|
||||||
<span>⭐ Hviezdy: <strong>@Profile.TotalStars</strong></span>
|
<div class="stats-pill-group">
|
||||||
|
<div class="stat-badge stars-badge">
|
||||||
|
<span>⭐ <strong>@Profile.TotalStars</strong> Hviezd</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-badge gold-badge">
|
||||||
|
<span>💰 <strong>@Profile.Gold</strong> Zlata</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="map-top-actions">
|
||||||
|
<button class="btn-top-action" @onclick="OnChangeHero" title="Zmeniť ročník alebo hrdinu">
|
||||||
|
🎒 Zmeniť hrdinu
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-change-hero" @onclick="OnChangeHero">Zmeniť hrdinu 🔄</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Worlds Container -->
|
<!-- MAIN WORLD MAP CONTENT AREA -->
|
||||||
<div class="worlds-carousel">
|
<div class="world-map-content">
|
||||||
@foreach (var world in Worlds)
|
<!-- WORLD SELECTION TABS -->
|
||||||
{
|
<div class="world-tabs-nav">
|
||||||
<div class="world-card">
|
@for (int i = 0; i < Worlds.Count; i++)
|
||||||
<div class="world-header">
|
{
|
||||||
<h2>@world.Name</h2>
|
var w = Worlds[i];
|
||||||
<p>@world.Description</p>
|
var worldIndex = i + 1;
|
||||||
</div>
|
var isSelected = SelectedWorldIndex == worldIndex;
|
||||||
<div class="levels-path">
|
var isWorldUnlocked = w.Levels.Any(l => Profile.UnlockedLevels.Contains(l.Id));
|
||||||
@foreach (var level in world.Levels)
|
var completedInWorld = w.Levels.Count(l => Profile.CompletedLevels.Contains(l.Id));
|
||||||
{
|
var totalInWorld = w.Levels.Count;
|
||||||
bool isUnlocked = Profile.UnlockedLevels.Contains(level.Id);
|
var isWorldCompleted = totalInWorld > 0 && completedInWorld == totalInWorld;
|
||||||
bool isCompleted = Profile.CompletedLevels.Contains(level.Id);
|
|
||||||
|
|
||||||
<button class="level-node @(level.IsBoss ? "boss-node" : "") @(isUnlocked ? "unlocked" : "locked") @(isCompleted ? "completed" : "")"
|
<button class="world-tab-btn @(isSelected ? "active" : "") @(isWorldUnlocked ? "unlocked" : "locked") @(isWorldCompleted ? "world-completed" : "")"
|
||||||
disabled="@(!isUnlocked)"
|
disabled="@(!isWorldUnlocked)"
|
||||||
@onclick="() => SelectLevel(level)">
|
@onclick="() => SelectWorld(worldIndex)">
|
||||||
<div class="node-icon">
|
<span class="world-tab-icon">
|
||||||
|
@if (isWorldCompleted)
|
||||||
|
{
|
||||||
|
<span>🌟</span>
|
||||||
|
}
|
||||||
|
else if (!isWorldUnlocked)
|
||||||
|
{
|
||||||
|
<span>🔒</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>@(worldIndex == 1 ? "🌸" : worldIndex == 2 ? "🌲" : worldIndex == 3 ? "🌊" : worldIndex == 4 ? "⛰️" : "🏰")</span>
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
<span class="world-tab-label">Svet @worldIndex</span>
|
||||||
|
<span class="world-tab-progress">@completedInWorld/@totalInWorld</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CURRENT SELECTED WORLD SHOWCASE -->
|
||||||
|
@if (ActiveWorld != null)
|
||||||
|
{
|
||||||
|
var completedCount = ActiveWorld.Levels.Count(l => Profile.CompletedLevels.Contains(l.Id));
|
||||||
|
var totalCount = ActiveWorld.Levels.Count;
|
||||||
|
var progressPercent = totalCount > 0 ? (int)((double)completedCount / totalCount * 100) : 0;
|
||||||
|
|
||||||
|
<div class="active-world-card parchment-container">
|
||||||
|
<!-- WORLD BANNER HEADER -->
|
||||||
|
<div class="world-header-row">
|
||||||
|
<div class="world-title-col">
|
||||||
|
<h2 class="active-world-title">@ActiveWorld.Name</h2>
|
||||||
|
<p class="active-world-desc">@ActiveWorld.Description</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="world-progress-col">
|
||||||
|
<div class="progress-label-row">
|
||||||
|
<span>Postup vo svete:</span>
|
||||||
|
<strong>@completedCount z @totalCount splnených</strong>
|
||||||
|
</div>
|
||||||
|
<div class="world-progress-bar">
|
||||||
|
<div class="world-progress-fill" style="width: @(progressPercent)%"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- LEVELS RICH CARDS GRID -->
|
||||||
|
<div class="levels-rich-grid">
|
||||||
|
@foreach (var level in ActiveWorld.Levels)
|
||||||
|
{
|
||||||
|
var isUnlocked = Profile.UnlockedLevels.Contains(level.Id);
|
||||||
|
var isCompleted = Profile.CompletedLevels.Contains(level.Id);
|
||||||
|
var enemyImg = $"{level.Enemy.AssetFolder}_idle_p1.png";
|
||||||
|
var mathDesc = GetMathDescription(level.MathConfig);
|
||||||
|
|
||||||
|
<div class="level-rich-card @(level.IsBoss ? "boss-card" : "") @(isCompleted ? "status-completed" : isUnlocked ? "status-current" : "status-locked")">
|
||||||
|
|
||||||
|
<!-- CARD STATUS BADGE -->
|
||||||
|
<div class="card-status-banner">
|
||||||
@if (isCompleted)
|
@if (isCompleted)
|
||||||
{
|
{
|
||||||
<span>⭐</span>
|
<span class="badge-completed">🏆 PREKONANÝ</span>
|
||||||
|
<span class="stars-awarded">@(level.IsBoss ? "⭐⭐⭐" : "⭐")</span>
|
||||||
}
|
}
|
||||||
else if (level.IsBoss)
|
else if (level.IsBoss)
|
||||||
{
|
{
|
||||||
<span>👑</span>
|
<span class="badge-boss">👑 VEĽKÝ BOSS</span>
|
||||||
}
|
}
|
||||||
else if (isUnlocked)
|
else if (isUnlocked)
|
||||||
{
|
{
|
||||||
<span>⚔️</span>
|
<span class="badge-active">⚔️ NOVÁ VÝZVA</span>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span>🔒</span>
|
<span class="badge-locked">🔒 ZAMKNUTÉ</span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<span class="level-title">@level.Name</span>
|
|
||||||
@if (level.IsBoss)
|
<!-- ENEMY AVATAR STAGE -->
|
||||||
{
|
<div class="level-enemy-stage">
|
||||||
<span class="boss-tag">BOSS</span>
|
<div class="enemy-avatar-wrapper @(isCompleted ? "defeated-glow" : isUnlocked ? "active-glow" : "locked-silhouette")">
|
||||||
}
|
<img src="@enemyImg" alt="@level.Enemy.Name" class="level-enemy-img @(isUnlocked && !isCompleted ? "anim-hero-idle" : "")" />
|
||||||
</button>
|
@if (!isUnlocked)
|
||||||
|
{
|
||||||
|
<div class="lock-overlay">🔒</div>
|
||||||
|
}
|
||||||
|
@if (isCompleted)
|
||||||
|
{
|
||||||
|
<div class="check-overlay">✓</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- LEVEL DETAILS -->
|
||||||
|
<div class="level-info-body">
|
||||||
|
<div class="level-number-tag">Level @level.LevelNumber</div>
|
||||||
|
<h3 class="level-name-title">@level.Name</h3>
|
||||||
|
<div class="level-enemy-name">Protivník: <strong>@level.Enemy.Name</strong></div>
|
||||||
|
|
||||||
|
<div class="level-math-pill">
|
||||||
|
@mathDesc
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="level-stats-row">
|
||||||
|
<span class="stat-bubble">❤️ @level.Enemy.MaxHp HP</span>
|
||||||
|
<span class="stat-bubble">🎯 @level.RequiredCorrectAnswers správne</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ACTION BUTTON -->
|
||||||
|
<div class="level-card-footer">
|
||||||
|
@if (isCompleted)
|
||||||
|
{
|
||||||
|
<button class="btn-level-action btn-replay" @onclick="() => SelectLevel(level)">
|
||||||
|
🔁 Hrať znova (Tréning)
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
else if (isUnlocked)
|
||||||
|
{
|
||||||
|
<button class="btn-level-action btn-battle @(level.IsBoss ? "btn-boss-battle" : "")" @onclick="() => SelectLevel(level)">
|
||||||
|
⚔️ VYZVAŤ NA SÚBOJ! ➜
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<button class="btn-level-action btn-locked" disabled="true">
|
||||||
|
🔒 Splň predchádzajúci level
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,9 +188,81 @@
|
|||||||
[Parameter] public EventCallback OnChangeHero { get; set; }
|
[Parameter] public EventCallback OnChangeHero { get; set; }
|
||||||
|
|
||||||
private List<World> Worlds => DataLoader.GetWorldsForGrade(Profile.SelectedGrade);
|
private List<World> Worlds => DataLoader.GetWorldsForGrade(Profile.SelectedGrade);
|
||||||
|
private int SelectedWorldIndex { get; set; } = 1;
|
||||||
|
|
||||||
|
private World? ActiveWorld => Worlds.FirstOrDefault(w => w.Id == SelectedWorldIndex)
|
||||||
|
?? Worlds.FirstOrDefault();
|
||||||
|
|
||||||
|
private string HeroAvatarUrl
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var hero = DataLoader.GetHero(Profile.SelectedHeroId) ?? DataLoader.Heroes.FirstOrDefault();
|
||||||
|
return hero != null ? $"{hero.AssetFolder}_idle_p1.png" : "assets/images/heroes/knight_idle_p1.png";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
// Automatically select the world that has the highest unlocked level
|
||||||
|
if (Worlds.Any())
|
||||||
|
{
|
||||||
|
var highestWorldWithUnlocked = Worlds
|
||||||
|
.Where(w => w.Levels.Any(l => Profile.UnlockedLevels.Contains(l.Id)))
|
||||||
|
.OrderByDescending(w => w.Id)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (highestWorldWithUnlocked != null)
|
||||||
|
{
|
||||||
|
SelectedWorldIndex = highestWorldWithUnlocked.Id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SelectedWorldIndex = Worlds.First().Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectWorld(int worldIndex)
|
||||||
|
{
|
||||||
|
SelectedWorldIndex = worldIndex;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SelectLevel(Level level)
|
private async Task SelectLevel(Level level)
|
||||||
{
|
{
|
||||||
await OnLevelSelected.InvokeAsync(level);
|
await OnLevelSelected.InvokeAsync(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetMathDescription(MathConfig config)
|
||||||
|
{
|
||||||
|
if (config.Operations.Contains("<") || config.Operations.Contains(">") || config.Operations.Contains("="))
|
||||||
|
{
|
||||||
|
return "⚖️ Porovnávanie čísel (< > =)";
|
||||||
|
}
|
||||||
|
if (config.Operations.Contains("*") && config.Operations.Contains("/"))
|
||||||
|
{
|
||||||
|
return "✖️ ➗ Násobilka a delenie";
|
||||||
|
}
|
||||||
|
if (config.Operations.Contains("*"))
|
||||||
|
{
|
||||||
|
return "✖️ Malá násobilka";
|
||||||
|
}
|
||||||
|
if (config.Operations.Contains("/"))
|
||||||
|
{
|
||||||
|
return "➗ Delenie čísel";
|
||||||
|
}
|
||||||
|
if (config.Operations.Contains("+") && config.Operations.Contains("-"))
|
||||||
|
{
|
||||||
|
return $"➕ ➖ Sčítanie a odčítanie do {config.MaxSum}";
|
||||||
|
}
|
||||||
|
if (config.Operations.Contains("+"))
|
||||||
|
{
|
||||||
|
return $"➕ Sčítanie do {config.MaxSum}";
|
||||||
|
}
|
||||||
|
if (config.Operations.Contains("-"))
|
||||||
|
{
|
||||||
|
return $"➖ Odčítanie do {config.MaxA}";
|
||||||
|
}
|
||||||
|
return "➕ Matematická výzva";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
}
|
}
|
||||||
else if (CurrentScreen == ScreenType.CharacterSelect)
|
else if (CurrentScreen == ScreenType.CharacterSelect)
|
||||||
{
|
{
|
||||||
<CharacterSelect OnProfileReady="HandleProfileReady" />
|
<CharacterSelect OnProfileReady="HandleProfileReady"
|
||||||
|
OnCancel="() => CurrentScreen = ScreenType.MainMenu" />
|
||||||
}
|
}
|
||||||
else if (CurrentScreen == ScreenType.WorldMap)
|
else if (CurrentScreen == ScreenType.WorldMap)
|
||||||
{
|
{
|
||||||
@@ -34,11 +35,42 @@
|
|||||||
else if (CurrentScreen == ScreenType.Battle && SelectedLevel != null)
|
else if (CurrentScreen == ScreenType.Battle && SelectedLevel != null)
|
||||||
{
|
{
|
||||||
var hero = DataLoader.GetHero(Profile.SelectedHeroId) ?? DataLoader.Heroes.First();
|
var hero = DataLoader.GetHero(Profile.SelectedHeroId) ?? DataLoader.Heroes.First();
|
||||||
|
var worlds = DataLoader.GetWorldsForGrade(Profile.SelectedGrade);
|
||||||
|
var currentWorld = worlds.FirstOrDefault(w => w.Levels.Any(l => l.Id == SelectedLevel.Id));
|
||||||
|
var bg = currentWorld?.Background ?? "assets/images/backgrounds/world1_meadow.jpg";
|
||||||
|
|
||||||
<BattleArena Hero="hero"
|
<BattleArena Hero="hero"
|
||||||
CurrentLevel="SelectedLevel"
|
CurrentLevel="SelectedLevel"
|
||||||
|
PlayerGrade="Profile.SelectedGrade"
|
||||||
|
BackgroundImage="@bg"
|
||||||
Config="DataLoader.Config"
|
Config="DataLoader.Config"
|
||||||
OnReturnToMap="() => CurrentScreen = ScreenType.WorldMap"
|
OnReturnToMap="() => CurrentScreen = ScreenType.WorldMap"
|
||||||
OnLevelCompleted="HandleLevelCompleted" />
|
OnLevelCompleted="HandleLevelCompleted"
|
||||||
|
OnNextLevelRequested="HandleNextLevelRequested" />
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- FAIRYTALE LEVEL TRANSITION SCREEN -->
|
||||||
|
@if (IsTransitioning)
|
||||||
|
{
|
||||||
|
var hero = DataLoader.GetHero(Profile.SelectedHeroId) ?? DataLoader.Heroes.FirstOrDefault();
|
||||||
|
var heroAvatar = hero != null ? $"{hero.AssetFolder}_idle_p1.png" : "assets/images/heroes/knight_idle_p1.png";
|
||||||
|
|
||||||
|
<div class="transition-screen-overlay anim-fade-in">
|
||||||
|
<div class="transition-parchment-box anim-victory-modal-pop">
|
||||||
|
<div class="transition-hero-marching">
|
||||||
|
<img src="@heroAvatar"
|
||||||
|
class="marching-hero-img anim-hero-march"
|
||||||
|
alt="Hrdina na ceste" />
|
||||||
|
</div>
|
||||||
|
<h2 class="transition-title">🗺️ Vstupuješ do novej oblasti...</h2>
|
||||||
|
<p class="transition-target">📍 @TransitionTargetText</p>
|
||||||
|
<div class="transition-sparkles">
|
||||||
|
<span class="sparkle">✨</span>
|
||||||
|
<span class="sparkle">⭐</span>
|
||||||
|
<span class="sparkle">✨</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -57,6 +89,9 @@
|
|||||||
private PlayerProfile Profile { get; set; } = new();
|
private PlayerProfile Profile { get; set; } = new();
|
||||||
private Level? SelectedLevel { get; set; }
|
private Level? SelectedLevel { get; set; }
|
||||||
|
|
||||||
|
private bool IsTransitioning { get; set; } = false;
|
||||||
|
private string TransitionTargetText { get; set; } = string.Empty;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -85,19 +120,44 @@
|
|||||||
await Storage.SaveProfileAsync(Profile);
|
await Storage.SaveProfileAsync(Profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleLevelSelected(Level level)
|
private async Task HandleLevelSelected(Level level)
|
||||||
{
|
{
|
||||||
|
TransitionTargetText = $"{level.Name} (Protivník: {level.Enemy.Name})";
|
||||||
|
IsTransitioning = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
await Task.Delay(850);
|
||||||
|
|
||||||
SelectedLevel = level;
|
SelectedLevel = level;
|
||||||
CurrentScreen = ScreenType.Battle;
|
CurrentScreen = ScreenType.Battle;
|
||||||
|
IsTransitioning = false;
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleLevelCompleted(Level completedLevel)
|
private async Task HandleLevelCompleted(Level completedLevel)
|
||||||
|
{
|
||||||
|
UnlockNextLevel(completedLevel);
|
||||||
|
await Storage.SaveProfileAsync(Profile);
|
||||||
|
CurrentScreen = ScreenType.WorldMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleNextLevelRequested(Level nextLevel)
|
||||||
|
{
|
||||||
|
if (SelectedLevel != null)
|
||||||
|
{
|
||||||
|
UnlockNextLevel(SelectedLevel);
|
||||||
|
await Storage.SaveProfileAsync(Profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
await HandleLevelSelected(nextLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnlockNextLevel(Level completedLevel)
|
||||||
{
|
{
|
||||||
Profile.CompletedLevels.Add(completedLevel.Id);
|
Profile.CompletedLevels.Add(completedLevel.Id);
|
||||||
Profile.TotalStars += completedLevel.IsBoss ? 3 : 1;
|
Profile.TotalStars += completedLevel.IsBoss ? 3 : 1;
|
||||||
Profile.Gold += completedLevel.IsBoss ? 50 : 20;
|
Profile.Gold += completedLevel.IsBoss ? 50 : 20;
|
||||||
|
|
||||||
// Unlock next level in current world
|
|
||||||
var worlds = DataLoader.GetWorldsForGrade(Profile.SelectedGrade);
|
var worlds = DataLoader.GetWorldsForGrade(Profile.SelectedGrade);
|
||||||
var currentWorld = worlds.FirstOrDefault(w => w.Levels.Any(l => l.Id == completedLevel.Id));
|
var currentWorld = worlds.FirstOrDefault(w => w.Levels.Any(l => l.Id == completedLevel.Id));
|
||||||
if (currentWorld != null)
|
if (currentWorld != null)
|
||||||
@@ -109,7 +169,6 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Unlock first level of next world
|
|
||||||
var nextWorldIndex = worlds.IndexOf(currentWorld) + 1;
|
var nextWorldIndex = worlds.IndexOf(currentWorld) + 1;
|
||||||
if (nextWorldIndex < worlds.Count && worlds[nextWorldIndex].Levels.Any())
|
if (nextWorldIndex < worlds.Count && worlds[nextWorldIndex].Levels.Any())
|
||||||
{
|
{
|
||||||
@@ -117,8 +176,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Storage.SaveProfileAsync(Profile);
|
|
||||||
CurrentScreen = ScreenType.WorldMap;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public interface IGameDataLoader
|
|||||||
List<Hero> Heroes { get; }
|
List<Hero> Heroes { get; }
|
||||||
List<World> GetWorldsForGrade(int grade);
|
List<World> GetWorldsForGrade(int grade);
|
||||||
Hero? GetHero(string id);
|
Hero? GetHero(string id);
|
||||||
|
Level? GetNextLevel(int grade, string currentLevelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GameDataLoader : IGameDataLoader
|
public class GameDataLoader : IGameDataLoader
|
||||||
@@ -60,4 +61,29 @@ public class GameDataLoader : IGameDataLoader
|
|||||||
return _heroes.FirstOrDefault(h => h.Id.Equals(id, StringComparison.OrdinalIgnoreCase))
|
return _heroes.FirstOrDefault(h => h.Id.Equals(id, StringComparison.OrdinalIgnoreCase))
|
||||||
?? _heroes.FirstOrDefault();
|
?? _heroes.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Level? GetNextLevel(int grade, string currentLevelId)
|
||||||
|
{
|
||||||
|
var worlds = GetWorldsForGrade(grade);
|
||||||
|
for (int w = 0; w < worlds.Count; w++)
|
||||||
|
{
|
||||||
|
var world = worlds[w];
|
||||||
|
for (int l = 0; l < world.Levels.Count; l++)
|
||||||
|
{
|
||||||
|
if (world.Levels[l].Id == currentLevelId)
|
||||||
|
{
|
||||||
|
if (l + 1 < world.Levels.Count)
|
||||||
|
{
|
||||||
|
return world.Levels[l + 1];
|
||||||
|
}
|
||||||
|
else if (w + 1 < worlds.Count && worlds[w + 1].Levels.Any())
|
||||||
|
{
|
||||||
|
return worlds[w + 1].Levels.First();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
|
After Width: | Height: | Size: 966 KiB |
@@ -2,54 +2,24 @@
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"grade": 1,
|
"grade": 1,
|
||||||
"name": "Rozprávková lúka (Počítanie do 10)",
|
"name": "Svet 1: Rozprávková lúka (Do 10)",
|
||||||
"description": "Jednoduché sčítanie a odčítanie do 10 pre malých prváčikov.",
|
"description": "Základné sčítanie a odčítanie v obore do 10 pre prváčikov.",
|
||||||
"background": "assets/images/backgrounds/world1_meadow.jpg",
|
"background": "assets/images/backgrounds/world1_meadow.jpg",
|
||||||
"levels": [
|
"levels": [
|
||||||
{
|
{
|
||||||
"id": "1-1-1",
|
"id": "1-1-1",
|
||||||
"levelNumber": 1,
|
"levelNumber": 1,
|
||||||
"name": "Lúčny Motýlik",
|
"name": "Lúčny Motýlik",
|
||||||
"enemy": {
|
"enemy": { "id": "meadow_butterfly", "name": "Lúčny Motýlik", "maxHp": 20, "attackPower": 1, "assetFolder": "assets/images/enemies/goblin" },
|
||||||
"id": "meadow_butterfly",
|
"mathConfig": { "operations": ["+"], "minA": 1, "maxA": 5, "minB": 1, "maxB": 5, "maxSum": 10, "allowCarryOver": false, "inputType": "DirectInput" },
|
||||||
"name": "Lúčny Motýlik",
|
|
||||||
"maxHp": 20,
|
|
||||||
"attackPower": 1,
|
|
||||||
"assetFolder": "assets/images/enemies/goblin"
|
|
||||||
},
|
|
||||||
"mathConfig": {
|
|
||||||
"operations": ["+"],
|
|
||||||
"minA": 1,
|
|
||||||
"maxA": 5,
|
|
||||||
"minB": 1,
|
|
||||||
"maxB": 5,
|
|
||||||
"maxSum": 10,
|
|
||||||
"allowCarryOver": false,
|
|
||||||
"inputType": "DirectInput"
|
|
||||||
},
|
|
||||||
"requiredCorrectAnswers": 3
|
"requiredCorrectAnswers": 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "1-1-2",
|
"id": "1-1-2",
|
||||||
"levelNumber": 2,
|
"levelNumber": 2,
|
||||||
"name": "Zvedavý Zajačik",
|
"name": "Zvedavý Zajačik",
|
||||||
"enemy": {
|
"enemy": { "id": "curious_bunny", "name": "Zvedavý Zajačik", "maxHp": 25, "attackPower": 1, "assetFolder": "assets/images/enemies/slime" },
|
||||||
"id": "curious_bunny",
|
"mathConfig": { "operations": ["+", "-"], "minA": 2, "maxA": 10, "minB": 1, "maxB": 5, "maxSum": 10, "allowCarryOver": false, "inputType": "DirectInput" },
|
||||||
"name": "Zvedavý Zajačik",
|
|
||||||
"maxHp": 25,
|
|
||||||
"attackPower": 1,
|
|
||||||
"assetFolder": "assets/images/enemies/slime"
|
|
||||||
},
|
|
||||||
"mathConfig": {
|
|
||||||
"operations": ["+", "-"],
|
|
||||||
"minA": 2,
|
|
||||||
"maxA": 10,
|
|
||||||
"minB": 1,
|
|
||||||
"maxB": 5,
|
|
||||||
"maxSum": 10,
|
|
||||||
"allowCarryOver": false,
|
|
||||||
"inputType": "DirectInput"
|
|
||||||
},
|
|
||||||
"requiredCorrectAnswers": 3
|
"requiredCorrectAnswers": 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -57,23 +27,8 @@
|
|||||||
"levelNumber": 3,
|
"levelNumber": 3,
|
||||||
"name": "Lesný Medveď (Boss)",
|
"name": "Lesný Medveď (Boss)",
|
||||||
"isBoss": true,
|
"isBoss": true,
|
||||||
"enemy": {
|
"enemy": { "id": "gentle_bear_boss", "name": "Strážca Lúky", "maxHp": 40, "attackPower": 1, "assetFolder": "assets/images/enemies/golem" },
|
||||||
"id": "gentle_bear_boss",
|
"mathConfig": { "operations": ["+", "-"], "minA": 3, "maxA": 10, "minB": 1, "maxB": 5, "maxSum": 10, "allowCarryOver": false, "inputType": "DirectInput" },
|
||||||
"name": "Strážca Lúky",
|
|
||||||
"maxHp": 40,
|
|
||||||
"attackPower": 1,
|
|
||||||
"assetFolder": "assets/images/enemies/golem"
|
|
||||||
},
|
|
||||||
"mathConfig": {
|
|
||||||
"operations": ["+", "-"],
|
|
||||||
"minA": 3,
|
|
||||||
"maxA": 10,
|
|
||||||
"minB": 1,
|
|
||||||
"maxB": 5,
|
|
||||||
"maxSum": 10,
|
|
||||||
"allowCarryOver": false,
|
|
||||||
"inputType": "DirectInput"
|
|
||||||
},
|
|
||||||
"requiredCorrectAnswers": 4
|
"requiredCorrectAnswers": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -81,32 +36,86 @@
|
|||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"grade": 1,
|
"grade": 1,
|
||||||
"name": "Slnečná čistinka (Počítanie do 20 bez prechodu)",
|
"name": "Svet 2: Slnečná čistinka (Do 20 bez prechodu)",
|
||||||
"description": "Sčítanie a odčítanie v obore do 20 (napr. 10 + 4, 15 - 3).",
|
"description": "Sčítanie a odčítanie v obore do 20 bez prechodu cez desiatku.",
|
||||||
"background": "assets/images/backgrounds/world2_forest.jpg",
|
"background": "assets/images/backgrounds/world2_forest.jpg",
|
||||||
"levels": [
|
"levels": [
|
||||||
{
|
{
|
||||||
"id": "1-2-1",
|
"id": "1-2-1",
|
||||||
"levelNumber": 1,
|
"levelNumber": 1,
|
||||||
"name": "Lesný Škriatok",
|
"name": "Lesný Škriatok",
|
||||||
"enemy": {
|
"enemy": { "id": "forest_gnome", "name": "Lesný Škriatok", "maxHp": 30, "attackPower": 1, "assetFolder": "assets/images/enemies/goblin" },
|
||||||
"id": "forest_gnome",
|
"mathConfig": { "operations": ["+", "-"], "minA": 10, "maxA": 19, "minB": 1, "maxB": 5, "maxSum": 20, "allowCarryOver": false, "inputType": "DirectInput" },
|
||||||
"name": "Lesný Škriatok",
|
|
||||||
"maxHp": 30,
|
|
||||||
"attackPower": 1,
|
|
||||||
"assetFolder": "assets/images/enemies/goblin"
|
|
||||||
},
|
|
||||||
"mathConfig": {
|
|
||||||
"operations": ["+", "-"],
|
|
||||||
"minA": 10,
|
|
||||||
"maxA": 19,
|
|
||||||
"minB": 1,
|
|
||||||
"maxB": 5,
|
|
||||||
"maxSum": 20,
|
|
||||||
"allowCarryOver": false,
|
|
||||||
"inputType": "DirectInput"
|
|
||||||
},
|
|
||||||
"requiredCorrectAnswers": 4
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1-2-2",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"name": "Kráľovský Ježko",
|
||||||
|
"enemy": { "id": "royal_hedgehog", "name": "Kráľovský Ježko", "maxHp": 35, "attackPower": 1, "assetFolder": "assets/images/enemies/slime" },
|
||||||
|
"mathConfig": { "operations": ["+", "-"], "minA": 11, "maxA": 19, "minB": 1, "maxB": 8, "maxSum": 20, "allowCarryOver": false, "inputType": "DirectInput" },
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"grade": 1,
|
||||||
|
"name": "Svet 3: Zrkadlové jazierko (Porovnávanie < > =)",
|
||||||
|
"description": "Zisťovanie, ktoré číslo je väčšie, menšie alebo rovnaké.",
|
||||||
|
"background": "assets/images/backgrounds/world3_river.jpg",
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"id": "1-3-1",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"name": "Vodná Víla",
|
||||||
|
"enemy": { "id": "water_fairy", "name": "Vodná Víla", "maxHp": 30, "attackPower": 1, "assetFolder": "assets/images/enemies/goblin" },
|
||||||
|
"mathConfig": { "operations": ["<", ">", "="], "minA": 1, "maxA": 20, "minB": 1, "maxB": 20, "inputType": "MultipleChoice" },
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1-3-2",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"name": "Riečny Had (Boss)",
|
||||||
|
"isBoss": true,
|
||||||
|
"enemy": { "id": "river_serpent_boss", "name": "Riečny Had", "maxHp": 50, "attackPower": 1, "assetFolder": "assets/images/enemies/golem" },
|
||||||
|
"mathConfig": { "operations": ["<", ">", "="], "minA": 5, "maxA": 20, "minB": 5, "maxB": 20, "inputType": "MultipleChoice" },
|
||||||
|
"requiredCorrectAnswers": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"grade": 1,
|
||||||
|
"name": "Svet 4: Horská pevnosť (Desiatky)",
|
||||||
|
"description": "Počítanie po desiatkach do 100 (10, 20, 30...).",
|
||||||
|
"background": "assets/images/backgrounds/world1_meadow.jpg",
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"id": "1-4-1",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"name": "Kamenný Strážca",
|
||||||
|
"enemy": { "id": "stone_watcher", "name": "Kamenný Strážca", "maxHp": 35, "attackPower": 1, "assetFolder": "assets/images/enemies/golem" },
|
||||||
|
"mathConfig": { "operations": ["+", "-"], "minA": 10, "maxA": 50, "minB": 10, "maxB": 40, "maxSum": 100, "allowCarryOver": false, "inputType": "DirectInput" },
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"grade": 1,
|
||||||
|
"name": "Svet 5: Kráľovský Hrad (Finále 1. ročníka)",
|
||||||
|
"description": "Veľký záverečný test všetkých zručností prvého ročníka!",
|
||||||
|
"background": "assets/images/backgrounds/title_screen_bg.jpg",
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"id": "1-5-1",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"name": "Kráľovský Drak (Veľký Boss)",
|
||||||
|
"isBoss": true,
|
||||||
|
"enemy": { "id": "king_dragon_boss", "name": "Kráľovský Drak", "maxHp": 60, "attackPower": 1, "assetFolder": "assets/images/enemies/golem" },
|
||||||
|
"mathConfig": { "operations": ["+", "-"], "minA": 5, "maxA": 20, "minB": 1, "maxB": 10, "maxSum": 20, "allowCarryOver": false, "inputType": "DirectInput" },
|
||||||
|
"requiredCorrectAnswers": 6
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"grade": 2,
|
"grade": 2,
|
||||||
"name": "Lúka sčítania a odčítania",
|
"name": "Svet 1: Lúka sčítania a odčítania (Do 20 s prechodom)",
|
||||||
"description": "Základné sčítanie a odčítanie do 20 s prechodom cez desiatku.",
|
"description": "Základné sčítanie a odčítanie do 20 s prechodom cez desiatku.",
|
||||||
"background": "assets/images/backgrounds/world1_meadow.jpg",
|
"background": "assets/images/backgrounds/world1_meadow.jpg",
|
||||||
"levels": [
|
"levels": [
|
||||||
@@ -19,10 +19,10 @@
|
|||||||
},
|
},
|
||||||
"mathConfig": {
|
"mathConfig": {
|
||||||
"operations": ["+"],
|
"operations": ["+"],
|
||||||
"minA": 1,
|
"minA": 2,
|
||||||
"maxA": 10,
|
"maxA": 9,
|
||||||
"minB": 1,
|
"minB": 2,
|
||||||
"maxB": 10,
|
"maxB": 9,
|
||||||
"maxSum": 20,
|
"maxSum": 20,
|
||||||
"allowCarryOver": true,
|
"allowCarryOver": true,
|
||||||
"inputType": "DirectInput"
|
"inputType": "DirectInput"
|
||||||
@@ -43,9 +43,9 @@
|
|||||||
"mathConfig": {
|
"mathConfig": {
|
||||||
"operations": ["+", "-"],
|
"operations": ["+", "-"],
|
||||||
"minA": 5,
|
"minA": 5,
|
||||||
"maxA": 20,
|
"maxA": 18,
|
||||||
"minB": 1,
|
"minB": 2,
|
||||||
"maxB": 10,
|
"maxB": 9,
|
||||||
"maxSum": 20,
|
"maxSum": 20,
|
||||||
"allowCarryOver": true,
|
"allowCarryOver": true,
|
||||||
"inputType": "DirectInput"
|
"inputType": "DirectInput"
|
||||||
@@ -55,21 +55,21 @@
|
|||||||
{
|
{
|
||||||
"id": "2-1-3",
|
"id": "2-1-3",
|
||||||
"levelNumber": 3,
|
"levelNumber": 3,
|
||||||
"name": "Kamenný Golem (Boss)",
|
"name": "Golem Čísel (Boss)",
|
||||||
"isBoss": true,
|
"isBoss": true,
|
||||||
"enemy": {
|
"enemy": {
|
||||||
"id": "stone_golem_boss",
|
"id": "stone_golem_boss",
|
||||||
"name": "Golem Čísiel",
|
"name": "Golem Čísel",
|
||||||
"maxHp": 60,
|
"maxHp": 60,
|
||||||
"attackPower": 1,
|
"attackPower": 1,
|
||||||
"assetFolder": "assets/images/enemies/golem"
|
"assetFolder": "assets/images/enemies/golem"
|
||||||
},
|
},
|
||||||
"mathConfig": {
|
"mathConfig": {
|
||||||
"operations": ["+", "-"],
|
"operations": ["+", "-"],
|
||||||
"minA": 8,
|
"minA": 6,
|
||||||
"maxA": 20,
|
"maxA": 20,
|
||||||
"minB": 4,
|
"minB": 3,
|
||||||
"maxB": 10,
|
"maxB": 9,
|
||||||
"maxSum": 20,
|
"maxSum": 20,
|
||||||
"allowCarryOver": true,
|
"allowCarryOver": true,
|
||||||
"inputType": "DirectInput"
|
"inputType": "DirectInput"
|
||||||
@@ -81,20 +81,63 @@
|
|||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"grade": 2,
|
"grade": 2,
|
||||||
"name": "Les porovnávania a rovností",
|
"name": "Svet 2: Les porovnávania (Do 100)",
|
||||||
"description": "Porovnávanie čísel pomocou znakov <, >, = a dopĺňanie do desiatky.",
|
"description": "Porovnávanie čísel pomocou znakov <, >, = v obore do 100.",
|
||||||
"background": "assets/images/backgrounds/world2_forest.jpg",
|
"background": "assets/images/backgrounds/world2_forest.jpg",
|
||||||
"levels": [
|
"levels": [
|
||||||
{
|
{
|
||||||
"id": "2-2-1",
|
"id": "2-2-1",
|
||||||
"levelNumber": 1,
|
"levelNumber": 1,
|
||||||
"name": "Múdra Sova",
|
"name": "Lesný Goblin",
|
||||||
"enemy": {
|
"enemy": {
|
||||||
"id": "wise_owl",
|
"id": "forest_goblin_comp",
|
||||||
"name": "Múdra Sova",
|
"name": "Lesný Goblin",
|
||||||
"maxHp": 30,
|
"maxHp": 35,
|
||||||
"attackPower": 1,
|
"attackPower": 1,
|
||||||
"assetFolder": "assets/images/enemies/owl"
|
"assetFolder": "assets/images/enemies/goblin"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["<", ">", "="],
|
||||||
|
"minA": 1,
|
||||||
|
"maxA": 50,
|
||||||
|
"minB": 1,
|
||||||
|
"maxB": 50,
|
||||||
|
"inputType": "MultipleChoice"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2-2-2",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"name": "Slizký Strážca",
|
||||||
|
"enemy": {
|
||||||
|
"id": "slime_comp_watcher",
|
||||||
|
"name": "Slizký Strážca",
|
||||||
|
"maxHp": 40,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/slime"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["<", ">", "="],
|
||||||
|
"minA": 20,
|
||||||
|
"maxA": 100,
|
||||||
|
"minB": 20,
|
||||||
|
"maxB": 100,
|
||||||
|
"inputType": "MultipleChoice"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2-2-3",
|
||||||
|
"levelNumber": 3,
|
||||||
|
"name": "Strážca Hvozdu (Boss)",
|
||||||
|
"isBoss": true,
|
||||||
|
"enemy": {
|
||||||
|
"id": "forest_golem_comp_boss",
|
||||||
|
"name": "Strážca Hvozdu",
|
||||||
|
"maxHp": 55,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/golem"
|
||||||
},
|
},
|
||||||
"mathConfig": {
|
"mathConfig": {
|
||||||
"operations": ["<", ">", "="],
|
"operations": ["<", ">", "="],
|
||||||
@@ -104,7 +147,235 @@
|
|||||||
"maxB": 100,
|
"maxB": 100,
|
||||||
"inputType": "MultipleChoice"
|
"inputType": "MultipleChoice"
|
||||||
},
|
},
|
||||||
|
"requiredCorrectAnswers": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"grade": 2,
|
||||||
|
"name": "Svet 3: Riečna zátoka (Do 100 bez prechodu)",
|
||||||
|
"description": "Sčítanie a odčítanie desiatok a dvojciferných čísel bez prechodu.",
|
||||||
|
"background": "assets/images/backgrounds/world3_river.jpg",
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"id": "2-3-1",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"name": "Riečny Goblin",
|
||||||
|
"enemy": {
|
||||||
|
"id": "river_goblin_easy",
|
||||||
|
"name": "Riečny Goblin",
|
||||||
|
"maxHp": 35,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/goblin"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-"],
|
||||||
|
"minA": 10,
|
||||||
|
"maxA": 80,
|
||||||
|
"minB": 1,
|
||||||
|
"maxB": 9,
|
||||||
|
"maxSum": 100,
|
||||||
|
"allowCarryOver": false,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
"requiredCorrectAnswers": 4
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2-3-2",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"name": "Kvapkový Sliz",
|
||||||
|
"enemy": {
|
||||||
|
"id": "droplet_slime",
|
||||||
|
"name": "Kvapkový Sliz",
|
||||||
|
"maxHp": 45,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/slime"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-"],
|
||||||
|
"minA": 20,
|
||||||
|
"maxA": 70,
|
||||||
|
"minB": 10,
|
||||||
|
"maxB": 25,
|
||||||
|
"maxSum": 100,
|
||||||
|
"allowCarryOver": false,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2-3-3",
|
||||||
|
"levelNumber": 3,
|
||||||
|
"name": "Balvanový Netvor (Boss)",
|
||||||
|
"isBoss": true,
|
||||||
|
"enemy": {
|
||||||
|
"id": "river_golem_boss",
|
||||||
|
"name": "Balvanový Netvor",
|
||||||
|
"maxHp": 65,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/golem"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-"],
|
||||||
|
"minA": 20,
|
||||||
|
"maxA": 90,
|
||||||
|
"minB": 5,
|
||||||
|
"maxB": 30,
|
||||||
|
"maxSum": 100,
|
||||||
|
"allowCarryOver": false,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"grade": 2,
|
||||||
|
"name": "Svet 4: Horský priesmyk (Do 100 s prechodom)",
|
||||||
|
"description": "Sčítanie a odčítanie s prechodom cez desiatku v obore do 100.",
|
||||||
|
"background": "assets/images/backgrounds/world1_meadow.jpg",
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"id": "2-4-1",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"name": "Horský Goblin",
|
||||||
|
"enemy": {
|
||||||
|
"id": "mountain_goblin",
|
||||||
|
"name": "Horský Goblin",
|
||||||
|
"maxHp": 40,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/goblin"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-"],
|
||||||
|
"minA": 15,
|
||||||
|
"maxA": 75,
|
||||||
|
"minB": 4,
|
||||||
|
"maxB": 9,
|
||||||
|
"maxSum": 100,
|
||||||
|
"allowCarryOver": true,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2-4-2",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"name": "Magický Horský Sliz",
|
||||||
|
"enemy": {
|
||||||
|
"id": "mountain_magic_slime",
|
||||||
|
"name": "Magický Horský Sliz",
|
||||||
|
"maxHp": 50,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/slime"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-"],
|
||||||
|
"minA": 20,
|
||||||
|
"maxA": 85,
|
||||||
|
"minB": 6,
|
||||||
|
"maxB": 15,
|
||||||
|
"maxSum": 100,
|
||||||
|
"allowCarryOver": true,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2-4-3",
|
||||||
|
"levelNumber": 3,
|
||||||
|
"name": "Horský Obor (Boss)",
|
||||||
|
"isBoss": true,
|
||||||
|
"enemy": {
|
||||||
|
"id": "mountain_giant_boss",
|
||||||
|
"name": "Horský Obor",
|
||||||
|
"maxHp": 70,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/golem"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-"],
|
||||||
|
"minA": 25,
|
||||||
|
"maxA": 90,
|
||||||
|
"minB": 8,
|
||||||
|
"maxB": 25,
|
||||||
|
"maxSum": 100,
|
||||||
|
"allowCarryOver": true,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"grade": 2,
|
||||||
|
"name": "Svet 5: Hrad malej násobilky (Úvod do násobenia)",
|
||||||
|
"description": "Základy násobenia číslami 2, 3, 5 a 10.",
|
||||||
|
"background": "assets/images/backgrounds/title_screen_bg.jpg",
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"id": "2-5-1",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"name": "Hradný Strážca",
|
||||||
|
"enemy": {
|
||||||
|
"id": "castle_guard_goblin",
|
||||||
|
"name": "Hradný Strážca",
|
||||||
|
"maxHp": 40,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/goblin"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["*"],
|
||||||
|
"factors": [2, 5],
|
||||||
|
"minMultiplier": 1,
|
||||||
|
"maxMultiplier": 10,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2-5-2",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"name": "Kráľovský Sliz",
|
||||||
|
"enemy": {
|
||||||
|
"id": "royal_castle_slime",
|
||||||
|
"name": "Kráľovský Sliz",
|
||||||
|
"maxHp": 50,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/slime"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["*"],
|
||||||
|
"factors": [2, 3, 5, 10],
|
||||||
|
"minMultiplier": 1,
|
||||||
|
"maxMultiplier": 10,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2-5-3",
|
||||||
|
"levelNumber": 3,
|
||||||
|
"name": "Veľký Hradný Pán (Finále 2. ročníka)",
|
||||||
|
"isBoss": true,
|
||||||
|
"enemy": {
|
||||||
|
"id": "castle_lord_boss",
|
||||||
|
"name": "Veľký Hradný Pán",
|
||||||
|
"maxHp": 75,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/golem"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["*"],
|
||||||
|
"factors": [2, 3, 5, 10],
|
||||||
|
"minMultiplier": 1,
|
||||||
|
"maxMultiplier": 10,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 6
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"grade": 3,
|
"grade": 3,
|
||||||
"name": "Rieka malej násobilky (2, 3, 4, 5)",
|
"name": "Svet 1: Rieka malej násobilky (2, 3, 4, 5)",
|
||||||
"description": "Základy násobenia a delenia číslami 2, 3, 4, 5.",
|
"description": "Základy násobenia a delenia číslami 2, 3, 4 a 5.",
|
||||||
"background": "assets/images/backgrounds/world3_river.jpg",
|
"background": "assets/images/backgrounds/world3_river.jpg",
|
||||||
"levels": [
|
"levels": [
|
||||||
{
|
{
|
||||||
@@ -11,11 +11,11 @@
|
|||||||
"levelNumber": 1,
|
"levelNumber": 1,
|
||||||
"name": "Riečny Škriatok",
|
"name": "Riečny Škriatok",
|
||||||
"enemy": {
|
"enemy": {
|
||||||
"id": "river_sprite",
|
"id": "river_sprite_goblin",
|
||||||
"name": "Riečny Škriatok",
|
"name": "Riečny Škriatok",
|
||||||
"maxHp": 30,
|
"maxHp": 35,
|
||||||
"attackPower": 1,
|
"attackPower": 1,
|
||||||
"assetFolder": "assets/images/enemies/sprite"
|
"assetFolder": "assets/images/enemies/goblin"
|
||||||
},
|
},
|
||||||
"mathConfig": {
|
"mathConfig": {
|
||||||
"operations": ["*"],
|
"operations": ["*"],
|
||||||
@@ -29,13 +29,34 @@
|
|||||||
{
|
{
|
||||||
"id": "3-1-2",
|
"id": "3-1-2",
|
||||||
"levelNumber": 2,
|
"levelNumber": 2,
|
||||||
"name": "Vodník Násobka",
|
"name": "Vodný Slizík",
|
||||||
"enemy": {
|
"enemy": {
|
||||||
"id": "water_demon",
|
"id": "water_slime_div",
|
||||||
"name": "Vodník Násobka",
|
"name": "Vodný Slizík",
|
||||||
"maxHp": 40,
|
"maxHp": 45,
|
||||||
"attackPower": 1,
|
"attackPower": 1,
|
||||||
"assetFolder": "assets/images/enemies/water_demon"
|
"assetFolder": "assets/images/enemies/slime"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["/"],
|
||||||
|
"factors": [2, 3, 4, 5],
|
||||||
|
"minMultiplier": 1,
|
||||||
|
"maxMultiplier": 10,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3-1-3",
|
||||||
|
"levelNumber": 3,
|
||||||
|
"name": "Jazerný Strážca (Boss)",
|
||||||
|
"isBoss": true,
|
||||||
|
"enemy": {
|
||||||
|
"id": "lake_golem_boss",
|
||||||
|
"name": "Jazerný Strážca",
|
||||||
|
"maxHp": 65,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/golem"
|
||||||
},
|
},
|
||||||
"mathConfig": {
|
"mathConfig": {
|
||||||
"operations": ["*", "/"],
|
"operations": ["*", "/"],
|
||||||
@@ -45,22 +66,71 @@
|
|||||||
"inputType": "DirectInput"
|
"inputType": "DirectInput"
|
||||||
},
|
},
|
||||||
"requiredCorrectAnswers": 5
|
"requiredCorrectAnswers": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"grade": 3,
|
||||||
|
"name": "Svet 2: Les veľkej násobilky (6, 7, 8, 9, 10)",
|
||||||
|
"description": "Násobenie a delenie vyššími číslami 6 až 10.",
|
||||||
|
"background": "assets/images/backgrounds/world2_forest.jpg",
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"id": "3-2-1",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"name": "Lesný Piadimužík",
|
||||||
|
"enemy": {
|
||||||
|
"id": "forest_dwarf_goblin",
|
||||||
|
"name": "Lesný Piadimužík",
|
||||||
|
"maxHp": 40,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/goblin"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["*"],
|
||||||
|
"factors": [6, 7, 8, 9, 10],
|
||||||
|
"minMultiplier": 1,
|
||||||
|
"maxMultiplier": 10,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3-1-3",
|
"id": "3-2-2",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"name": "Živicový Sliz",
|
||||||
|
"enemy": {
|
||||||
|
"id": "resin_slime_div",
|
||||||
|
"name": "Živicový Sliz",
|
||||||
|
"maxHp": 50,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/slime"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["/"],
|
||||||
|
"factors": [6, 7, 8, 9, 10],
|
||||||
|
"minMultiplier": 1,
|
||||||
|
"maxMultiplier": 10,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3-2-3",
|
||||||
"levelNumber": 3,
|
"levelNumber": 3,
|
||||||
"name": "Jazerný Drak (Boss)",
|
"name": "Starý Lesný Golem (Boss)",
|
||||||
"isBoss": true,
|
"isBoss": true,
|
||||||
"enemy": {
|
"enemy": {
|
||||||
"id": "lake_dragon_boss",
|
"id": "ancient_forest_golem_boss",
|
||||||
"name": "Jazerný Drak",
|
"name": "Starý Lesný Golem",
|
||||||
"maxHp": 60,
|
"maxHp": 70,
|
||||||
"attackPower": 1,
|
"attackPower": 1,
|
||||||
"assetFolder": "assets/images/enemies/dragon"
|
"assetFolder": "assets/images/enemies/golem"
|
||||||
},
|
},
|
||||||
"mathConfig": {
|
"mathConfig": {
|
||||||
"operations": ["*", "/"],
|
"operations": ["*", "/"],
|
||||||
"factors": [2, 3, 4, 5],
|
"factors": [6, 7, 8, 9, 10],
|
||||||
"minMultiplier": 1,
|
"minMultiplier": 1,
|
||||||
"maxMultiplier": 10,
|
"maxMultiplier": 10,
|
||||||
"inputType": "DirectInput"
|
"inputType": "DirectInput"
|
||||||
@@ -68,5 +138,237 @@
|
|||||||
"requiredCorrectAnswers": 6
|
"requiredCorrectAnswers": 6
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"grade": 3,
|
||||||
|
"name": "Svet 3: Krištáľové údolie (Počítanie do 1000)",
|
||||||
|
"description": "Sčítanie a odčítanie v obore do 1000 po stovkách a desiatkach.",
|
||||||
|
"background": "assets/images/backgrounds/world1_meadow.jpg",
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"id": "3-3-1",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"name": "Jaskynný Goblin",
|
||||||
|
"enemy": {
|
||||||
|
"id": "cave_goblin_1000",
|
||||||
|
"name": "Jaskynný Goblin",
|
||||||
|
"maxHp": 45,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/goblin"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-"],
|
||||||
|
"minA": 100,
|
||||||
|
"maxA": 800,
|
||||||
|
"minB": 50,
|
||||||
|
"maxB": 200,
|
||||||
|
"maxSum": 1000,
|
||||||
|
"allowCarryOver": false,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3-3-2",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"name": "Trblietavý Sliz",
|
||||||
|
"enemy": {
|
||||||
|
"id": "glitter_slime_1000",
|
||||||
|
"name": "Trblietavý Sliz",
|
||||||
|
"maxHp": 55,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/slime"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-"],
|
||||||
|
"minA": 200,
|
||||||
|
"maxA": 900,
|
||||||
|
"minB": 100,
|
||||||
|
"maxB": 300,
|
||||||
|
"maxSum": 1000,
|
||||||
|
"allowCarryOver": true,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3-3-3",
|
||||||
|
"levelNumber": 3,
|
||||||
|
"name": "Kryštálový Golem (Boss)",
|
||||||
|
"isBoss": true,
|
||||||
|
"enemy": {
|
||||||
|
"id": "crystal_golem_boss",
|
||||||
|
"name": "Kryštálový Golem",
|
||||||
|
"maxHp": 75,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/golem"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-"],
|
||||||
|
"minA": 150,
|
||||||
|
"maxA": 850,
|
||||||
|
"minB": 50,
|
||||||
|
"maxB": 350,
|
||||||
|
"maxSum": 1000,
|
||||||
|
"allowCarryOver": true,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 6
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"grade": 3,
|
||||||
|
"name": "Svet 4: Veterné bralo (Porovnávanie do 1000)",
|
||||||
|
"description": "Porovnávanie veľkých trojciferných čísel pomocou znakov <, >, =.",
|
||||||
|
"background": "assets/images/backgrounds/world3_river.jpg",
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"id": "3-4-1",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"name": "Veterný Škriatok",
|
||||||
|
"enemy": {
|
||||||
|
"id": "wind_sprite_goblin",
|
||||||
|
"name": "Veterný Škriatok",
|
||||||
|
"maxHp": 40,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/goblin"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["<", ">", "="],
|
||||||
|
"minA": 100,
|
||||||
|
"maxA": 999,
|
||||||
|
"minB": 100,
|
||||||
|
"maxB": 999,
|
||||||
|
"inputType": "MultipleChoice"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3-4-2",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"name": "Búrkový Sliz",
|
||||||
|
"enemy": {
|
||||||
|
"id": "storm_slime_comp",
|
||||||
|
"name": "Búrkový Sliz",
|
||||||
|
"maxHp": 50,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/slime"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["<", ">", "="],
|
||||||
|
"minA": 300,
|
||||||
|
"maxA": 1000,
|
||||||
|
"minB": 300,
|
||||||
|
"maxB": 1000,
|
||||||
|
"inputType": "MultipleChoice"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3-4-3",
|
||||||
|
"levelNumber": 3,
|
||||||
|
"name": "Hromový Golem (Boss)",
|
||||||
|
"isBoss": true,
|
||||||
|
"enemy": {
|
||||||
|
"id": "thunder_golem_boss",
|
||||||
|
"name": "Hromový Golem",
|
||||||
|
"maxHp": 75,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/golem"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["<", ">", "="],
|
||||||
|
"minA": 100,
|
||||||
|
"maxA": 1000,
|
||||||
|
"minB": 100,
|
||||||
|
"maxB": 1000,
|
||||||
|
"inputType": "MultipleChoice"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 6
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"grade": 3,
|
||||||
|
"name": "Svet 5: Kráľovská citadela (Veľké finále)",
|
||||||
|
"description": "Záverečná výzva tretieho ročníka – mix násobilky, delenia a veľkých čísel!",
|
||||||
|
"background": "assets/images/backgrounds/title_screen_bg.jpg",
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"id": "3-5-1",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"name": "Elitný Goblin",
|
||||||
|
"enemy": {
|
||||||
|
"id": "elite_guard_goblin",
|
||||||
|
"name": "Elitný Goblin",
|
||||||
|
"maxHp": 50,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/goblin"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["*", "/"],
|
||||||
|
"factors": [2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||||
|
"minMultiplier": 1,
|
||||||
|
"maxMultiplier": 10,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3-5-2",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"name": "Magický Plazmový Sliz",
|
||||||
|
"enemy": {
|
||||||
|
"id": "plasma_slime_boss",
|
||||||
|
"name": "Magický Plazmový Sliz",
|
||||||
|
"maxHp": 65,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/slime"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-", "*", "/"],
|
||||||
|
"minA": 10,
|
||||||
|
"maxA": 100,
|
||||||
|
"minB": 5,
|
||||||
|
"maxB": 50,
|
||||||
|
"maxSum": 200,
|
||||||
|
"factors": [2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||||
|
"minMultiplier": 1,
|
||||||
|
"maxMultiplier": 10,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3-5-3",
|
||||||
|
"levelNumber": 3,
|
||||||
|
"name": "Arcimág Čísel (Záverečný Boss)",
|
||||||
|
"isBoss": true,
|
||||||
|
"enemy": {
|
||||||
|
"id": "archmage_golem_final_boss",
|
||||||
|
"name": "Arcimág Čísel",
|
||||||
|
"maxHp": 85,
|
||||||
|
"attackPower": 1,
|
||||||
|
"assetFolder": "assets/images/enemies/golem"
|
||||||
|
},
|
||||||
|
"mathConfig": {
|
||||||
|
"operations": ["+", "-", "*", "/"],
|
||||||
|
"minA": 10,
|
||||||
|
"maxA": 500,
|
||||||
|
"minB": 10,
|
||||||
|
"maxB": 200,
|
||||||
|
"maxSum": 1000,
|
||||||
|
"factors": [2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||||
|
"minMultiplier": 1,
|
||||||
|
"maxMultiplier": 10,
|
||||||
|
"inputType": "DirectInput"
|
||||||
|
},
|
||||||
|
"requiredCorrectAnswers": 8
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -13,8 +13,6 @@
|
|||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<link rel="preload" id="webassembly" />
|
<link rel="preload" id="webassembly" />
|
||||||
<link rel="stylesheet" href="css/app.css" />
|
<link rel="stylesheet" href="css/app.css" />
|
||||||
<link href="CiselnaVyprava.styles.css" rel="stylesheet" />
|
|
||||||
|
|
||||||
|
|
||||||
<script type="importmap"></script>
|
<script type="importmap"></script>
|
||||||
</head>
|
</head>
|
||||||
@@ -38,8 +36,9 @@
|
|||||||
<a href="." class="reload">Obnoviť hru</a>
|
<a href="." class="reload">Obnoviť hru</a>
|
||||||
<span class="dismiss">🗙</span>
|
<span class="dismiss">🗙</span>
|
||||||
</div>
|
</div>
|
||||||
<script src="_framework/blazor.webassembly.js"></script>
|
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -59,21 +59,15 @@
|
|||||||
<mimeMap fileExtension=".wav" mimeType="audio/wav" />
|
<mimeMap fileExtension=".wav" mimeType="audio/wav" />
|
||||||
</staticContent>
|
</staticContent>
|
||||||
|
|
||||||
<!-- URL Rewrite for Single Page Application (SPA routing) -->
|
<!-- SPA URL Rewrite -->
|
||||||
<rewrite>
|
<rewrite>
|
||||||
<rules>
|
<rules>
|
||||||
<rule name="Direct-File-Access" stopProcessing="true">
|
|
||||||
<match url=".*" />
|
|
||||||
<conditions logicalGrouping="MatchAll">
|
|
||||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" />
|
|
||||||
</conditions>
|
|
||||||
<action type="None" />
|
|
||||||
</rule>
|
|
||||||
<rule name="SPA-Routing-Fallback" stopProcessing="true">
|
<rule name="SPA-Routing-Fallback" stopProcessing="true">
|
||||||
<match url=".*" />
|
<match url=".*" />
|
||||||
<conditions logicalGrouping="MatchAll">
|
<conditions logicalGrouping="MatchAll">
|
||||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
||||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
||||||
|
<add input="{REQUEST_URI}" pattern="^/(_framework|assets|css|data)/" negate="true" />
|
||||||
</conditions>
|
</conditions>
|
||||||
<action type="Rewrite" url="/" />
|
<action type="Rewrite" url="/" />
|
||||||
</rule>
|
</rule>
|
||||||
|
|||||||