Implement basic structure for combat system and refactor code

This commit is contained in:
2026-07-03 13:14:36 +02:00
parent 754f81eb4c
commit d98d34ec3f
78 changed files with 1212 additions and 13 deletions
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 59b9870361cbdec4b9419c57a3ff71df
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+20
View File
@@ -0,0 +1,20 @@
using KernelPanic.Data;
namespace KernelPanic.Combat
{
/// <summary>
/// Represents one runtime card and its per-combat mutable state.
/// </summary>
public sealed class CardInstance
{
public CardInstance(CardDefinition definition)
{
Definition = definition;
}
public CardDefinition Definition { get; }
public int TemporaryCostDelta { get; set; }
public bool IsBroken { get; set; }
public bool IsLocked { get; set; }
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c05e61a09404ee046a6771f8ac75803c
+21
View File
@@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace KernelPanic.Combat
{
/// <summary>
/// Carries runtime combat references needed by card effects during resolution.
/// </summary>
public sealed class CombatContext
{
public CombatContext(CombatantState source, IReadOnlyList<CombatantState> targets, CombatManager combatManager)
{
Source = source;
Targets = targets;
CombatManager = combatManager;
}
public CombatantState Source { get; }
public IReadOnlyList<CombatantState> Targets { get; }
public CombatManager CombatManager { get; }
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2eed81ad20b42a447858691a49666804
+56
View File
@@ -0,0 +1,56 @@
using KernelPanic.Core;
using UnityEngine;
namespace KernelPanic.Combat
{
/// <summary>
/// Owns the combat turn phase state machine and coordinates combat lifecycle transitions.
/// </summary>
public sealed class CombatManager : MonoBehaviour
{
[SerializeField] private TurnPhase currentPhase = TurnPhase.Boot;
public TurnPhase CurrentPhase => currentPhase;
public void StartCombat()
{
SetPhase(TurnPhase.Boot);
}
public void AdvancePhase()
{
TurnPhase nextPhase = GetNextPhase(currentPhase);
SetPhase(nextPhase);
}
public void EndPlayerTurn()
{
SetPhase(TurnPhase.Interpret);
}
private void SetPhase(TurnPhase nextPhase)
{
TurnPhase previousPhase = currentPhase;
currentPhase = nextPhase;
GameEvents.RaisePhaseChanged(new PhaseChangedEvent(previousPhase, nextPhase));
EnterPhase(nextPhase);
}
private static TurnPhase GetNextPhase(TurnPhase phase)
{
return phase switch
{
TurnPhase.Boot => TurnPhase.Allocate,
TurnPhase.Allocate => TurnPhase.Execute,
TurnPhase.Execute => TurnPhase.Interpret,
TurnPhase.Interpret => TurnPhase.EnemyProcess,
TurnPhase.EnemyProcess => TurnPhase.GarbageCollection,
_ => TurnPhase.Allocate
};
}
private void EnterPhase(TurnPhase phase)
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9f87432412de1ee46bf7d4c9c530d4c6
+28
View File
@@ -0,0 +1,28 @@
using System.Collections.Generic;
using KernelPanic.Core;
namespace KernelPanic.Combat
{
/// <summary>
/// Stores mutable combat stats and statuses for a player or enemy.
/// </summary>
public sealed class CombatantState
{
private readonly Dictionary<StatusType, int> activeStatuses = new();
public CombatantState(int maxUptime, int ram, int cycles)
{
MaxUptime = maxUptime;
CurrentUptime = maxUptime;
Ram = ram;
Cycles = cycles;
}
public int CurrentUptime { get; set; }
public int MaxUptime { get; set; }
public int Shield { get; set; }
public int Ram { get; set; }
public int Cycles { get; set; }
public IReadOnlyDictionary<StatusType, int> ActiveStatuses => activeStatuses;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 93b1080022652b542903e204d36a64f8
+49
View File
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
namespace KernelPanic.Combat
{
/// <summary>
/// Manages draw, discard, and exhaust piles for a combat deck.
/// </summary>
public sealed class DeckController
{
private readonly List<CardInstance> drawPile = new();
private readonly List<CardInstance> discardPile = new();
private readonly List<CardInstance> exhaustPile = new();
public IReadOnlyList<CardInstance> DrawPile => drawPile;
public IReadOnlyList<CardInstance> DiscardPile => discardPile;
public IReadOnlyList<CardInstance> ExhaustPile => exhaustPile;
public void Initialize(IEnumerable<CardInstance> cards)
{
throw new NotImplementedException();
}
public IReadOnlyList<CardInstance> Draw(int count)
{
throw new NotImplementedException();
}
public void Discard(CardInstance card)
{
throw new NotImplementedException();
}
public void Exhaust(CardInstance card)
{
throw new NotImplementedException();
}
public void ShuffleDrawPile()
{
throw new NotImplementedException();
}
public void ReshuffleDiscardIntoDraw()
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6683933e853af334ba19e658b13c878d
+41
View File
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
namespace KernelPanic.Combat
{
/// <summary>
/// Manages cards currently held in hand with a RAM-based capacity.
/// </summary>
public sealed class HandController
{
private readonly List<CardInstance> cards = new();
public HandController(int ramCapacity)
{
RamCapacity = ramCapacity;
}
public int RamCapacity { get; private set; }
public IReadOnlyList<CardInstance> Cards => cards;
public bool CanAdd(CardInstance card)
{
throw new NotImplementedException();
}
public void Add(CardInstance card)
{
throw new NotImplementedException();
}
public bool Remove(CardInstance card)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: bece1c6fddc2c63489bf129bf6295a31
+10
View File
@@ -0,0 +1,10 @@
namespace KernelPanic.Combat
{
/// <summary>
/// Represents a runtime card effect that can apply itself to a combat context.
/// </summary>
public interface ICardEffect
{
void Execute(CombatContext context);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 886be8d59396e5e488eea3ea58445fb4
+11
View File
@@ -0,0 +1,11 @@
namespace KernelPanic.Combat
{
/// <summary>
/// Provides a common contract for card resolution tracks.
/// </summary>
public interface IResolutionTrack
{
void Enqueue(CardInstance card);
void Resolve(CombatContext context);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5d296d1160df6d747adaeec9ef68c710
+30
View File
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
namespace KernelPanic.Combat
{
/// <summary>
/// Stores played cards in FIFO order for resolution during the Interpret phase.
/// </summary>
public sealed class InterpreterQueue : IResolutionTrack
{
private readonly Queue<CardInstance> queuedCards = new();
public int Count => queuedCards.Count;
public void Enqueue(CardInstance card)
{
throw new NotImplementedException();
}
public void Resolve(CombatContext context)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 13c99cf765e7e8b48a58afbacc8a5440
+30
View File
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
namespace KernelPanic.Combat
{
/// <summary>
/// Stores played cards in LIFO order until explicitly force-evaluated.
/// </summary>
public sealed class LazyStack : IResolutionTrack
{
private readonly Stack<CardInstance> stackedCards = new();
public int Count => stackedCards.Count;
public void Enqueue(CardInstance card)
{
throw new NotImplementedException();
}
public void Resolve(CombatContext context)
{
throw new NotImplementedException();
}
public void ForceEvaluate(CombatContext context)
{
throw new NotImplementedException();
}
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 556859e6fac10a24bb4a60165fd82157
+20
View File
@@ -0,0 +1,20 @@
using System;
namespace KernelPanic.Combat
{
/// <summary>
/// Resolves cards immediately when they are added to the native track.
/// </summary>
public sealed class NativeTrack : IResolutionTrack
{
public void Enqueue(CardInstance card)
{
throw new NotImplementedException();
}
public void Resolve(CombatContext context)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b662e4e1a38b1554084a063fd3d415e2
@@ -0,0 +1,26 @@
using System;
using KernelPanic.Core;
namespace KernelPanic.Combat
{
/// <summary>
/// Applies, ticks, and expires status effects on a combatant.
/// </summary>
public sealed class StatusEffectController
{
public void Apply(CombatantState target, StatusType statusType, int stacks)
{
throw new NotImplementedException();
}
public void Tick(CombatantState target)
{
throw new NotImplementedException();
}
public void Expire(CombatantState target, StatusType statusType)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5f91b8d9171889f4996bb3106d97f1f6
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d51c41fa71b1068438855f64f946cc57
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+140
View File
@@ -0,0 +1,140 @@
using System;
using KernelPanic.Combat;
namespace KernelPanic.Core
{
/// <summary>
/// Provides runtime events for gameplay systems that need to observe combat and run milestones.
/// </summary>
public static class GameEvents
{
public static event Action<CardPlayedEvent> CardPlayed;
public static event Action<CardResolvedEvent> CardResolved;
public static event Action<PhaseChangedEvent> PhaseChanged;
public static event Action<DamageDealtEvent> DamageDealt;
public static event Action<WaveClearedEvent> WaveCleared;
public static event Action<RunEndedEvent> RunEnded;
public static void RaiseCardPlayed(CardPlayedEvent payload)
{
CardPlayed?.Invoke(payload);
}
public static void RaiseCardResolved(CardResolvedEvent payload)
{
CardResolved?.Invoke(payload);
}
public static void RaisePhaseChanged(PhaseChangedEvent payload)
{
PhaseChanged?.Invoke(payload);
}
public static void RaiseDamageDealt(DamageDealtEvent payload)
{
DamageDealt?.Invoke(payload);
}
public static void RaiseWaveCleared(WaveClearedEvent payload)
{
WaveCleared?.Invoke(payload);
}
public static void RaiseRunEnded(RunEndedEvent payload)
{
RunEnded?.Invoke(payload);
}
}
/// <summary>
/// Describes a card that entered a resolution track.
/// </summary>
public readonly struct CardPlayedEvent
{
public CardPlayedEvent(CardInstance card, ResolutionTrack track)
{
Card = card;
Track = track;
}
public CardInstance Card { get; }
public ResolutionTrack Track { get; }
}
/// <summary>
/// Describes a card whose effects finished resolving.
/// </summary>
public readonly struct CardResolvedEvent
{
public CardResolvedEvent(CardInstance card, ResolutionTrack track)
{
Card = card;
Track = track;
}
public CardInstance Card { get; }
public ResolutionTrack Track { get; }
}
/// <summary>
/// Describes a transition between combat phases.
/// </summary>
public readonly struct PhaseChangedEvent
{
public PhaseChangedEvent(TurnPhase previousPhase, TurnPhase nextPhase)
{
PreviousPhase = previousPhase;
NextPhase = nextPhase;
}
public TurnPhase PreviousPhase { get; }
public TurnPhase NextPhase { get; }
}
/// <summary>
/// Describes damage applied from one combatant to another.
/// </summary>
public readonly struct DamageDealtEvent
{
public DamageDealtEvent(CombatantState source, CombatantState target, int amount, Language language)
{
Source = source;
Target = target;
Amount = amount;
Language = language;
}
public CombatantState Source { get; }
public CombatantState Target { get; }
public int Amount { get; }
public Language Language { get; }
}
/// <summary>
/// Describes a completed enemy wave.
/// </summary>
public readonly struct WaveClearedEvent
{
public WaveClearedEvent(int waveNumber)
{
WaveNumber = waveNumber;
}
public int WaveNumber { get; }
}
/// <summary>
/// Describes the end of a run and its earned meta currency.
/// </summary>
public readonly struct RunEndedEvent
{
public RunEndedEvent(bool playerDied, int entropyEarned)
{
PlayerDied = playerDied;
EntropyEarned = entropyEarned;
}
public bool PlayerDied { get; }
public int EntropyEarned { get; }
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cf446c6fd7627294c9e2aa23c9b576e7
+21
View File
@@ -0,0 +1,21 @@
namespace KernelPanic.Core
{
/// <summary>
/// Identifies a programming language used as a card damage type.
/// </summary>
public enum Language
{
C,
CPlusPlus,
Rust,
Python,
JavaScript,
TypeScript,
Haskell,
Assembly,
Java,
Go,
Ruby,
PHP
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c06078e18f184c84cab3579c0e562dbe
+13
View File
@@ -0,0 +1,13 @@
namespace KernelPanic.Core
{
/// <summary>
/// Describes the rarity tier of cards and rewards.
/// </summary>
public enum Rarity
{
Common,
Rare,
Epic,
Legendary
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 542a3f97e8074ed428fd7019177f221b
+12
View File
@@ -0,0 +1,12 @@
namespace KernelPanic.Core
{
/// <summary>
/// Identifies the combat resolution track a card uses when played.
/// </summary>
public enum ResolutionTrack
{
Native,
InterpreterQueue,
LazyStack
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3b76dcac6b8cae447bb9079460692238
+15
View File
@@ -0,0 +1,15 @@
namespace KernelPanic.Core
{
/// <summary>
/// Identifies a combat status effect category.
/// </summary>
public enum StatusType
{
Segfault,
MemoryLeak,
RaceCondition,
Deprecated,
DependencyError,
Deadlock
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cbdd46dffec613346ae6191f18a94225
+15
View File
@@ -0,0 +1,15 @@
namespace KernelPanic.Core
{
/// <summary>
/// Describes the high-level phases of one combat turn.
/// </summary>
public enum TurnPhase
{
Boot,
Allocate,
Execute,
Interpret,
EnemyProcess,
GarbageCollection
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e1974726f936d0640943801549b4574e
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e0a39b97df9ee0641b2bbadf5bca417c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+34
View File
@@ -0,0 +1,34 @@
using System.Collections.Generic;
using KernelPanic.Core;
using UnityEngine;
namespace KernelPanic.Data
{
/// <summary>
/// Defines immutable card data used to create runtime card instances.
/// </summary>
public sealed class CardDefinition : ScriptableObject
{
[SerializeField] private string id;
[SerializeField] private string displayName;
[SerializeField] private string description;
[SerializeField] private Language language;
[SerializeField] private Rarity rarity;
[SerializeField] private int cycleCost;
[SerializeField] private ResolutionTrack resolutionTrack;
[SerializeField] private bool distroExclusive;
[SerializeField] private CardDefinition upgradeTarget;
[SerializeField] private List<CardEffectDefinition> effects = new();
public string Id => id;
public string DisplayName => displayName;
public string Description => description;
public Language Language => language;
public Rarity Rarity => rarity;
public int CycleCost => cycleCost;
public ResolutionTrack ResolutionTrack => resolutionTrack;
public bool DistroExclusive => distroExclusive;
public CardDefinition UpgradeTarget => upgradeTarget;
public IReadOnlyList<CardEffectDefinition> Effects => effects;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5178747fac4dbfc448ff15d8a687e03c
@@ -0,0 +1,13 @@
using KernelPanic.Combat;
using UnityEngine;
namespace KernelPanic.Data
{
/// <summary>
/// Defines serialized card effect data that can create a runtime effect.
/// </summary>
public abstract class CardEffectDefinition : ScriptableObject
{
public abstract ICardEffect CreateRuntimeEffect();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fdb3d58d94b76834598e408448e19d2a
+30
View File
@@ -0,0 +1,30 @@
using System.Collections.Generic;
using KernelPanic.Core;
using UnityEngine;
namespace KernelPanic.Data
{
/// <summary>
/// Defines a playable distro character and its starting combat constraints.
/// </summary>
public sealed class DistroDefinition : ScriptableObject
{
[SerializeField] private string id;
[SerializeField] private string displayName;
[SerializeField] private Language primaryLanguage;
[SerializeField] private Language secondaryLanguage;
[SerializeField] private int baseUptime;
[SerializeField] private int baseRam;
[SerializeField] private int baseCyclesPerTurn;
[SerializeField] private List<CardDefinition> exclusiveCards = new();
public string Id => id;
public string DisplayName => displayName;
public Language PrimaryLanguage => primaryLanguage;
public Language SecondaryLanguage => secondaryLanguage;
public int BaseUptime => baseUptime;
public int BaseRam => baseRam;
public int BaseCyclesPerTurn => baseCyclesPerTurn;
public IReadOnlyList<CardDefinition> ExclusiveCards => exclusiveCards;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: bd550368fd0c75d43a53aa4fb3b6541e
+26
View File
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using KernelPanic.Core;
using UnityEngine;
namespace KernelPanic.Data
{
/// <summary>
/// Defines enemy stats and language affinities for runtime combat setup.
/// </summary>
public sealed class EnemyDefinition : ScriptableObject
{
[SerializeField] private string id;
[SerializeField] private string displayName;
[SerializeField] private int baseUptime;
[SerializeField] private List<Language> resistedLanguages = new();
[SerializeField] private List<Language> weakLanguages = new();
public string Id => id;
public string DisplayName => displayName;
public int BaseUptime => baseUptime;
public IReadOnlyList<Language> ResistedLanguages => resistedLanguages;
public IReadOnlyList<Language> WeakLanguages => weakLanguages;
// TODO: Model enemy intents once combat actions are defined.
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8b5b43b43c697c142b33fbfc5160cf90
@@ -0,0 +1,17 @@
using KernelPanic.Core;
using UnityEngine;
namespace KernelPanic.Data
{
/// <summary>
/// Defines serialized status effect metadata and future stacking behavior.
/// </summary>
public sealed class StatusEffectDefinition : ScriptableObject
{
[SerializeField] private StatusType statusType;
[SerializeField] private string stackingBehavior;
public StatusType StatusType => statusType;
public string StackingBehavior => stackingBehavior;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1470a223647d6514f9439de7ded702c9
+14
View File
@@ -0,0 +1,14 @@
{
"name": "KernelPanic.Runtime",
"rootNamespace": "KernelPanic",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6aad857173769044f861a0c6b1318032
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ae19044bbccbb074da1d5e38b61e5657
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+26
View File
@@ -0,0 +1,26 @@
namespace KernelPanic.Meta
{
/// <summary>
/// Tracks the player's persistent entropy currency balance.
/// </summary>
public sealed class EntropyWallet
{
public int Balance { get; private set; }
public void Add(int amount)
{
Balance += amount;
}
public bool Spend(int amount)
{
if (Balance < amount)
{
return false;
}
Balance -= amount;
return true;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8b77d0de67282ca4181a7c53feec4343
+30
View File
@@ -0,0 +1,30 @@
using System;
namespace KernelPanic.Meta
{
/// <summary>
/// Provides the contract for spending entropy on persistent unlock pulls.
/// </summary>
public sealed class GachaService
{
public GachaPullResult PerformPull(EntropyWallet wallet, int cost)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Represents the result of a persistent unlock pull.
/// </summary>
public readonly struct GachaPullResult
{
public GachaPullResult(bool success, string unlockId)
{
Success = success;
UnlockId = unlockId;
}
public bool Success { get; }
public string UnlockId { get; }
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f62e697e985a8a44ca26ccb8e853ee66
+28
View File
@@ -0,0 +1,28 @@
using System;
namespace KernelPanic.Meta
{
/// <summary>
/// Provides save and load contracts for persistent meta progression.
/// </summary>
public sealed class SaveService
{
public void Save(SaveData data)
{
throw new NotImplementedException();
}
public SaveData Load()
{
throw new NotImplementedException();
}
}
/// <summary>
/// Stores persistent progression data for serialization.
/// </summary>
public sealed class SaveData
{
public int EntropyBalance { get; set; }
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 752a83f338afd024b8e728a1921e38ee
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9ae21840b594d4147a8f5497c992b4fa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+42
View File
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using KernelPanic.Data;
namespace KernelPanic.Run
{
/// <summary>
/// Provides post-wave reward choice generation contracts.
/// </summary>
public sealed class RewardService
{
public IReadOnlyList<RewardChoice> GeneratePostWaveChoices(RunConfig runConfig, int waveNumber)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Represents one selectable reward option after clearing a wave.
/// </summary>
public readonly struct RewardChoice
{
public RewardChoice(RewardChoiceType type, CardDefinition card)
{
Type = type;
Card = card;
}
public RewardChoiceType Type { get; }
public CardDefinition Card { get; }
}
/// <summary>
/// Identifies the type of reward choice offered after a wave.
/// </summary>
public enum RewardChoiceType
{
AddCard,
UpgradeCard,
Skip
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a06eada6fbafd6e44ae1d21238a64750
+25
View File
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using KernelPanic.Core;
using KernelPanic.Data;
namespace KernelPanic.Run
{
/// <summary>
/// Describes the player choices and starting deck used to begin a run.
/// </summary>
public sealed class RunConfig
{
public RunConfig(DistroDefinition distro, Language primaryLanguage, Language secondaryLanguage, IReadOnlyList<CardDefinition> startingDeck)
{
Distro = distro;
PrimaryLanguage = primaryLanguage;
SecondaryLanguage = secondaryLanguage;
StartingDeck = startingDeck;
}
public DistroDefinition Distro { get; }
public Language PrimaryLanguage { get; }
public Language SecondaryLanguage { get; }
public IReadOnlyList<CardDefinition> StartingDeck { get; }
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0c3d34a2ce0e53b4b8f9a77feb60fb95
+56
View File
@@ -0,0 +1,56 @@
using KernelPanic.Combat;
using KernelPanic.Core;
using UnityEngine;
namespace KernelPanic.Run
{
/// <summary>
/// Coordinates run lifecycle, wave progression, and run-level event handling.
/// </summary>
public sealed class RunManager : MonoBehaviour
{
[SerializeField] private CombatManager combatManager;
[SerializeField] private int currentWaveNumber;
[SerializeField] private bool isRunActive;
[SerializeField] private bool isPlayerDead;
public int CurrentWaveNumber => currentWaveNumber;
public bool IsRunActive => isRunActive;
public bool IsPlayerDead => isPlayerDead;
private void OnEnable()
{
GameEvents.WaveCleared += HandleWaveCleared;
GameEvents.RunEnded += HandleRunEnded;
}
private void OnDisable()
{
GameEvents.WaveCleared -= HandleWaveCleared;
GameEvents.RunEnded -= HandleRunEnded;
}
public void StartRun(RunConfig config)
{
isRunActive = true;
isPlayerDead = false;
currentWaveNumber = 1;
StartCombat();
}
public void StartCombat()
{
combatManager?.StartCombat();
}
private void HandleWaveCleared(WaveClearedEvent payload)
{
}
private void HandleRunEnded(RunEndedEvent payload)
{
isRunActive = false;
isPlayerDead = payload.PlayerDied;
}
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 234cc6c5ca577b64ea44020f5514cebe