Implement Arch and some additional changes
This commit is contained in:
@@ -47,3 +47,14 @@ MonoBehaviour:
|
||||
- {fileID: 11400000, guid: a7d60000000000000000000000000004, type: 2}
|
||||
- {fileID: 11400000, guid: a7d60000000000000000000000000005, type: 2}
|
||||
- {fileID: 11400000, guid: a7d60000000000000000000000000006, type: 2}
|
||||
- {fileID: 11400000, guid: aa110000000000000000000000000001, type: 2}
|
||||
- {fileID: 11400000, guid: aa110000000000000000000000000002, type: 2}
|
||||
- {fileID: 11400000, guid: aa110000000000000000000000000003, type: 2}
|
||||
- {fileID: 11400000, guid: aa110000000000000000000000000004, type: 2}
|
||||
- {fileID: 11400000, guid: aa110000000000000000000000000005, type: 2}
|
||||
- {fileID: 11400000, guid: aa120000000000000000000000000001, type: 2}
|
||||
- {fileID: 11400000, guid: aa120000000000000000000000000002, type: 2}
|
||||
- {fileID: 11400000, guid: aa120000000000000000000000000003, type: 2}
|
||||
- {fileID: 11400000, guid: aa120000000000000000000000000004, type: 2}
|
||||
- {fileID: 11400000, guid: aa120000000000000000000000000005, type: 2}
|
||||
- {fileID: 11400000, guid: aa120000000000000000000000000006, type: 2}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5178747fac4dbfc448ff15d8a687e03c, type: 3}
|
||||
m_Name: Malloc
|
||||
m_EditorClassIdentifier:
|
||||
id: lang_c_malloc
|
||||
displayName: malloc()
|
||||
description: deal 8. 20% segfault: take 3.
|
||||
flavorText: memory acquired. ownership emotionally unresolved.
|
||||
language: 0
|
||||
rarity: 0
|
||||
cycleCost: 1
|
||||
resolutionTrack: 0
|
||||
distroExclusive: 0
|
||||
isToken: 0
|
||||
isRunOnly: 0
|
||||
designNotes: C starter. Segfault rolls through RandomRoll; damage and recoil route through DamagePipeline.
|
||||
requiresSystem: []
|
||||
upgradeTarget: {fileID: 0}
|
||||
effects: []
|
||||
@@ -0,0 +1,29 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5178747fac4dbfc448ff15d8a687e03c, type: 3}
|
||||
m_Name: Pointer
|
||||
m_EditorClassIdentifier:
|
||||
id: lang_c_pointer
|
||||
displayName: pointer
|
||||
description: deal 6. if the previous card was C, deal 10 instead.
|
||||
flavorText: points at the bug with suspicious confidence.
|
||||
language: 0
|
||||
rarity: 0
|
||||
cycleCost: 1
|
||||
resolutionTrack: 0
|
||||
distroExclusive: 0
|
||||
isToken: 0
|
||||
isRunOnly: 0
|
||||
designNotes: C starter. Uses existing last-played-language tracking on CombatantState; damage routes through DamagePipeline.
|
||||
requiresSystem: []
|
||||
upgradeTarget: {fileID: 0}
|
||||
effects: []
|
||||
@@ -0,0 +1,29 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5178747fac4dbfc448ff15d8a687e03c, type: 3}
|
||||
m_Name: Printf
|
||||
m_EditorClassIdentifier:
|
||||
id: lang_c_printf
|
||||
displayName: printf()
|
||||
description: deal 4.
|
||||
flavorText: debugging by yelling into the void.
|
||||
language: 0
|
||||
rarity: 0
|
||||
cycleCost: 0
|
||||
resolutionTrack: 0
|
||||
distroExclusive: 0
|
||||
isToken: 0
|
||||
isRunOnly: 0
|
||||
designNotes: C starter. Clean low-cost damage routed through DamagePipeline.
|
||||
requiresSystem: []
|
||||
upgradeTarget: {fileID: 0}
|
||||
effects: []
|
||||
@@ -0,0 +1,22 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a7d10000000000000000000000000001, type: 3}
|
||||
m_Name: CDeck
|
||||
m_EditorClassIdentifier:
|
||||
language: 0
|
||||
entries:
|
||||
- card: {fileID: 11400000, guid: aa120000000000000000000000000001, type: 2}
|
||||
count: 2
|
||||
- card: {fileID: 11400000, guid: aa120000000000000000000000000002, type: 2}
|
||||
count: 1
|
||||
- card: {fileID: 11400000, guid: aa120000000000000000000000000003, type: 2}
|
||||
count: 1
|
||||
@@ -17,3 +17,4 @@ MonoBehaviour:
|
||||
- {fileID: 11400000, guid: a7d50000000000000000000000000002, type: 2}
|
||||
- {fileID: 11400000, guid: a7d70000000000000000000000000001, type: 2}
|
||||
- {fileID: 11400000, guid: a7d70000000000000000000000000002, type: 2}
|
||||
- {fileID: 11400000, guid: aa130000000000000000000000000001, type: 2}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5178747fac4dbfc448ff15d8a687e03c, type: 3}
|
||||
m_Name: Free
|
||||
m_EditorClassIdentifier:
|
||||
id: shop_c_free
|
||||
displayName: free()
|
||||
description: deal 12. if segfault recoil happens, draw 2.
|
||||
flavorText: double-free your schedule, not your heap.
|
||||
language: 0
|
||||
rarity: 0
|
||||
cycleCost: 2
|
||||
resolutionTrack: 0
|
||||
distroExclusive: 0
|
||||
isToken: 0
|
||||
isRunOnly: 1
|
||||
designNotes: C run-only shop card. Uses segfault recoil hook through RandomRoll and DamagePipeline; draw uses existing draw pipeline.
|
||||
requiresSystem: []
|
||||
upgradeTarget: {fileID: 0}
|
||||
effects: []
|
||||
@@ -0,0 +1,29 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5178747fac4dbfc448ff15d8a687e03c, type: 3}
|
||||
m_Name: KernelExploit
|
||||
m_EditorClassIdentifier:
|
||||
id: shop_c_kernel_exploit
|
||||
displayName: kernel_exploit
|
||||
description: deal 25 ignoring Shield. 50% segfault: take 8.
|
||||
flavorText: privilege escalated. heartbeat also escalated.
|
||||
language: 0
|
||||
rarity: 1
|
||||
cycleCost: 3
|
||||
resolutionTrack: 0
|
||||
distroExclusive: 0
|
||||
isToken: 0
|
||||
isRunOnly: 1
|
||||
designNotes: C run-only shop card. Ignore-Shield damage uses the true-damage pipeline flag; segfault recoil uses RandomRoll and DamagePipeline.
|
||||
requiresSystem: []
|
||||
upgradeTarget: {fileID: 0}
|
||||
effects: []
|
||||
@@ -0,0 +1,29 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5178747fac4dbfc448ff15d8a687e03c, type: 3}
|
||||
m_Name: Sizeof
|
||||
m_EditorClassIdentifier:
|
||||
id: shop_c_sizeof
|
||||
displayName: sizeof()
|
||||
description: deal 3 x C cards in hand.
|
||||
flavorText: compile-time confidence, runtime consequences.
|
||||
language: 0
|
||||
rarity: 1
|
||||
cycleCost: 1
|
||||
resolutionTrack: 0
|
||||
distroExclusive: 0
|
||||
isToken: 0
|
||||
isRunOnly: 1
|
||||
designNotes: C run-only shop card. Counts current C cards in hand and deals through DamagePipeline.
|
||||
requiresSystem: []
|
||||
upgradeTarget: {fileID: 0}
|
||||
effects: []
|
||||
@@ -0,0 +1,37 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bd550368fd0c75d43a53aa4fb3b6541e, type: 3}
|
||||
m_Name: Arch
|
||||
m_EditorClassIdentifier:
|
||||
id: arch
|
||||
displayName: Arch
|
||||
primaryLanguage: 0
|
||||
secondaryLanguage: 2
|
||||
passive: {fileID: 11400000, guid: aa100000000000000000000000000001, type: 2}
|
||||
description: The DIY power-user. Maximum control, maximum fragility. She can do things no one else can - and break herself doing it.
|
||||
playstyleSummary: volatile C/Rust tempo. every played card adds btw, which makes C and Rust hits bigger until the turn ends.
|
||||
languageBlurbs:
|
||||
- lang: 0
|
||||
blurb: sharp native damage with a higher crit rate and occasional segfault recoil.
|
||||
- lang: 2
|
||||
blurb: reliable native damage; overkill converts into shield.
|
||||
asciiArt: {fileID: 4900000, guid: 004c732ec64a45fd82c5a47ae7ffed43, type: 3}
|
||||
accentColor: {r: 0.09019608, g: 0.5764706, b: 0.81960785, a: 1}
|
||||
baseUptime: 50
|
||||
baseRam: 6
|
||||
baseCyclesPerTurn: 4
|
||||
exclusiveCards:
|
||||
- {fileID: 11400000, guid: aa110000000000000000000000000001, type: 2}
|
||||
- {fileID: 11400000, guid: aa110000000000000000000000000002, type: 2}
|
||||
- {fileID: 11400000, guid: aa110000000000000000000000000003, type: 2}
|
||||
- {fileID: 11400000, guid: aa110000000000000000000000000004, type: 2}
|
||||
- {fileID: 11400000, guid: aa110000000000000000000000000005, type: 2}
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
++
|
||||
++++
|
||||
+++++=
|
||||
+++++++=
|
||||
+ ++++++=
|
||||
====+++++++
|
||||
==========+++
|
||||
==++++++++++++
|
||||
+++++++ +++++++
|
||||
++++++ ++++++
|
||||
+++++++ +++++++
|
||||
++++++++ +++++++
|
||||
+++++++ +++++++
|
||||
+++ +++
|
||||
+ +
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 004c732ec64a45fd82c5a47ae7ffed43
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -16,3 +16,4 @@ MonoBehaviour:
|
||||
- {fileID: 11400000, guid: 1b0892a0d4c34b03a54c3d511ae59356, type: 2}
|
||||
- {fileID: 11400000, guid: 245dfb73afe047d5a3a3ef54c30d5135, type: 2}
|
||||
- {fileID: 11400000, guid: 366e71361387449abcfaa1a50c9af87b, type: 2}
|
||||
- {fileID: 11400000, guid: aa101000000000000000000000000001, type: 2}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
|
||||
m_Name: Aur
|
||||
m_EditorClassIdentifier:
|
||||
id: pkg_aur
|
||||
displayName: AUR
|
||||
slot: 1
|
||||
rarity: 4
|
||||
description: start each TURN with a generated C/Rust card. on Arch, any rarity and costs 1 less this turn; off-distro, common only with no discount.
|
||||
flavourText: probably maintained by one person and a shell script named pls-work.sh.
|
||||
designNotes: Arch 4-star Runtime package. Scope TURN. On Arch: generate random C/Rust card up to Legendary rarity and apply -1 temporary cost. Off-distro: generate Common C/Rust only with no discount. Uses existing generated-card pool; requiresSystem card-generation if no eligible pool is available. Runtime slot intentionally does not conflict with systemd Daemon.
|
||||
intendedDistroId: arch
|
||||
requiresSystem:
|
||||
- card-generation
|
||||
offDistroEffect: {kind: 26, amount: 0, threshold: 0, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0, persistArchBtw: 0}
|
||||
onDistroEffect: {kind: 26, amount: 1, threshold: 3, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0, persistArchBtw: 0}
|
||||
@@ -44,3 +44,5 @@ MonoBehaviour:
|
||||
- {fileID: 11400000, guid: 159fca0abb7249e1be485aaac5f83fbe, type: 2}
|
||||
- {fileID: 11400000, guid: ac481beaf7e6453d82a439d54c2fb2f3, type: 2}
|
||||
- {fileID: 11400000, guid: a8e4cfd86f554fa19198551a7d5ed8ab, type: 2}
|
||||
- {fileID: 11400000, guid: aa140000000000000000000000000001, type: 2}
|
||||
- {fileID: 11400000, guid: aa140000000000000000000000000002, type: 2}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 48447d3db18343398488d3064324147d, type: 3}
|
||||
m_Name: Systemd
|
||||
m_EditorClassIdentifier:
|
||||
id: pkg_systemd
|
||||
displayName: systemd
|
||||
slot: 2
|
||||
rarity: 5
|
||||
description: first C card each TURN deals 50% more damage. on Arch, btw stacks persist for the whole WAVE.
|
||||
flavourText: one daemon to start them all, stop them all, and explain none of it.
|
||||
designNotes: Arch signature Daemon. Off-distro grants only the first-C-card DamagePipeline multiplier. On Arch also sets persistArchBtw, so the Arch passive skips end-of-TURN btw reset and clears only on WAVE start or rolling-release recovery.
|
||||
intendedDistroId: arch
|
||||
requiresSystem: []
|
||||
offDistroEffect: {kind: 25, amount: 150, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0, persistArchBtw: 0}
|
||||
onDistroEffect: {kind: 25, amount: 150, threshold: 1, refundCycle: 0, cleanseDebuffs: 0, enableFedoraSecondCardPassive: 0, persistArchBtw: 1}
|
||||
@@ -0,0 +1,19 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 9b1f51fb7c354203a2f93ad22fbeb101, type: 3}
|
||||
m_Name: IUseArchBtw
|
||||
m_EditorClassIdentifier:
|
||||
id: arch_i_use_arch_btw
|
||||
displayName: I use Arch, btw
|
||||
rulesText: Each card played adds 1 btw until end of turn. C and Rust damage gains +1 per btw. Once per wave, lethal uptime sets Arch to 1 instead and clears btw.
|
||||
flavorText: Compiled from source, emotionally and otherwise.
|
||||
designNotes: V1 base passive. Increment btw before resolving each played card. End of TURN resets btw unless Arch has systemd equipped on-distro. First lethal damage each WAVE consumes rolling-release save, sets uptime to 1, clears btw, and logs a recovery beat.
|
||||
@@ -49,10 +49,35 @@ namespace KernelPanic.Combat
|
||||
? minAmount
|
||||
: RandomRoll.RollRange(minAmount, maxAmount, new RollContext(context.Source));
|
||||
|
||||
context.DamagePipeline.DealDamage(new DamageRequest(context.Source, target, amount, _language, _trueDamage, _canCrit));
|
||||
bool rustOverkillToShield = _language == Language.Rust || context.Card?.Definition?.Language == Language.Rust;
|
||||
int survivable = rustOverkillToShield
|
||||
? _trueDamage ? target.CurrentUptime : target.CurrentUptime + target.Shield
|
||||
: 0;
|
||||
DamageResult result = context.DamagePipeline.DealDamage(new DamageRequest(context.Source, target, amount, _language, _trueDamage, _canCrit));
|
||||
if (rustOverkillToShield)
|
||||
{
|
||||
GrantRustOverkillShield(context, result, survivable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GrantRustOverkillShield(CombatContext context, DamageResult result, int survivable)
|
||||
{
|
||||
int overkill = UnityEngine.Mathf.Max(0, result.IncomingAmount - survivable);
|
||||
if (overkill <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.CombatManager.CanPlayerReceiveCardShield(context.Card))
|
||||
{
|
||||
context.CombatManager.ReportEffectResult($"overkill shield blocked: {overkill}");
|
||||
return;
|
||||
}
|
||||
|
||||
context.CombatManager.GrantPlayerShield(overkill, "rust overkill");
|
||||
}
|
||||
|
||||
private static IReadOnlyList<CombatantState> ShuffleTargets(IReadOnlyList<CombatantState> targets, CombatantState source)
|
||||
{
|
||||
List<CombatantState> shuffled = targets.ToList();
|
||||
@@ -164,6 +189,177 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SegfaultDamageEffect : ICardEffect
|
||||
{
|
||||
private readonly int _amount;
|
||||
private readonly int _selfDamage;
|
||||
private readonly int _segfaultPercent;
|
||||
private readonly bool _trueDamage;
|
||||
private readonly bool _drawOnSegfault;
|
||||
|
||||
public SegfaultDamageEffect(int amount, int selfDamage, int segfaultPercent, bool trueDamage = false, bool drawOnSegfault = false)
|
||||
{
|
||||
_amount = amount;
|
||||
_selfDamage = selfDamage;
|
||||
_segfaultPercent = segfaultPercent;
|
||||
_trueDamage = trueDamage;
|
||||
_drawOnSegfault = drawOnSegfault;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
new DealDamageEffect(_amount, _amount, Language.C, _trueDamage).Execute(context);
|
||||
bool segfault = RandomRoll.RollRange(1, 100, new RollContext(context.Source)) <= _segfaultPercent;
|
||||
if (!segfault)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.DamagePipeline.DealDamage(new DamageRequest(context.Source, context.Source, _selfDamage, Language.C, false, false));
|
||||
context.CombatManager.ReportEffectResult($"segfault: took {_selfDamage}");
|
||||
if (_drawOnSegfault)
|
||||
{
|
||||
context.CombatManager.DrawCards(2, "free() recoil");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PreviousLanguageDamageEffect : ICardEffect
|
||||
{
|
||||
private readonly int _baseAmount;
|
||||
private readonly int _matchingAmount;
|
||||
private readonly Language _language;
|
||||
|
||||
public PreviousLanguageDamageEffect(int baseAmount, int matchingAmount, Language language)
|
||||
{
|
||||
_baseAmount = baseAmount;
|
||||
_matchingAmount = matchingAmount;
|
||||
_language = language;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
bool matches = context.Source != null
|
||||
&& context.Source.HasPreviousPlayedCardLanguage
|
||||
&& context.Source.PreviousPlayedCardLanguage == _language;
|
||||
int amount = matches ? _matchingAmount : _baseAmount;
|
||||
new DealDamageEffect(amount, amount, _language).Execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BtwScaledDamageEffect : ICardEffect
|
||||
{
|
||||
private readonly int _baseAmount;
|
||||
private readonly int _perBtw;
|
||||
private readonly Language _language;
|
||||
|
||||
public BtwScaledDamageEffect(int baseAmount, int perBtw, Language language)
|
||||
{
|
||||
_baseAmount = baseAmount;
|
||||
_perBtw = perBtw;
|
||||
_language = language;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
int btw = UnityEngine.Mathf.Max(0, context.Source?.ArchBtwStacks ?? 0);
|
||||
int perBtw = UnityEngine.Mathf.Max(_perBtw, context.Source?.ArchMakepkgBtwMultiplier ?? _perBtw);
|
||||
int amount = _baseAmount + (btw * perBtw);
|
||||
new DealDamageEffect(amount, amount, _language).Execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RandomCommonLanguageCardEffect : ICardEffect
|
||||
{
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
Language language = RandomRoll.RollRange(0, 1, new RollContext(context.Source)) == 0 ? Language.C : Language.Rust;
|
||||
if (!context.CombatManager.TryCreateGeneratedCard(language, Rarity.Common, out CardInstance generated))
|
||||
{
|
||||
context.CombatManager.ReportEffectResult("TODO card-generation: no C/Rust common available");
|
||||
return;
|
||||
}
|
||||
|
||||
generated.TemporaryCostDelta -= CombatManager.GetCardCost(generated);
|
||||
if (context.HandController.Add(generated))
|
||||
{
|
||||
context.CombatManager.ReportEffectResult($"AUR helper: generated {GetCardName(generated)} at 0c");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CombatManager.ReportEffectResult("AUR helper: hand full");
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetCardName(CardInstance card)
|
||||
{
|
||||
return card?.Definition == null || string.IsNullOrWhiteSpace(card.Definition.DisplayName) ? card?.Definition?.Id ?? "--" : card.Definition.DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MinimalInstallEffect : ICardEffect
|
||||
{
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
int exhausted = 0;
|
||||
List<CardInstance> candidates = context.HandController.Cards.Where(card => card != null).Take(2).ToList();
|
||||
for (int i = 0; i < candidates.Count; i++)
|
||||
{
|
||||
CardInstance card = candidates[i];
|
||||
if (!context.HandController.Remove(card))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
context.DeckController.Exhaust(card);
|
||||
context.CombatManager.HandleCardExhausted(card);
|
||||
exhausted++;
|
||||
}
|
||||
|
||||
if (exhausted <= 0)
|
||||
{
|
||||
context.CombatManager.ReportEffectResult("minimal install: no hand cards to exhaust");
|
||||
return;
|
||||
}
|
||||
|
||||
int shield = exhausted * 3;
|
||||
if (context.CombatManager.CanPlayerReceiveCardShield(context.Card))
|
||||
{
|
||||
context.CombatManager.GrantPlayerShield(shield, "minimal install");
|
||||
}
|
||||
|
||||
context.Source.Cycles += exhausted;
|
||||
context.CombatManager.ReportEffectResult($"minimal install: exhausted {exhausted}, +{exhausted} Cycle");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SizeofDamageEffect : ICardEffect
|
||||
{
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
int cCards = context.HandController.Cards.Count(card => card?.Definition != null && card.Definition.Language == Language.C);
|
||||
int amount = 3 * cCards;
|
||||
new DealDamageEffect(amount, amount, Language.C).Execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ConsultWikiEffect : ICardEffect
|
||||
{
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
CardInstance broken = context.HandController.Cards.FirstOrDefault(card => card != null && card.IsBroken);
|
||||
if (broken != null)
|
||||
{
|
||||
broken.IsBroken = false;
|
||||
context.CombatManager.DrawCards(1, "consult the wiki");
|
||||
context.CombatManager.ReportEffectResult("fixed a broken card");
|
||||
return;
|
||||
}
|
||||
|
||||
new DealDamageEffect(6, 6, Language.Rust).Execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public sealed class ApplyStatusEffect : ICardEffect
|
||||
{
|
||||
@@ -696,13 +892,16 @@ namespace KernelPanic.Combat
|
||||
"lang_py_for_loop" => One(new DealDamageEffect(2, 2, Language.Python, allEnemies: true)),
|
||||
"lang_js_console_log" => One(new DealDamageEffect(3, 9, Language.JavaScript)),
|
||||
"lang_js_fetch" => One(new DealDamageEffect(5, 13, Language.JavaScript)),
|
||||
"lang_rs_fn_main" => One(new OverkillToShieldDamageEffect(6, Language.Rust)),
|
||||
"lang_rs_fn_main" => One(new DealDamageEffect(6, 6, Language.Rust)),
|
||||
"lang_rs_unwrap" => new ICardEffect[]
|
||||
{
|
||||
new DealDamageEffect(9, 9, Language.Rust),
|
||||
new DrawEffect(1)
|
||||
},
|
||||
"lang_rs_borrow" => One(new ShieldEffect(5)),
|
||||
"lang_c_malloc" => One(new SegfaultDamageEffect(8, 3, 20)),
|
||||
"lang_c_printf" => One(new DealDamageEffect(4, 4, Language.C)),
|
||||
"lang_c_pointer" => One(new PreviousLanguageDamageEffect(6, 10, Language.C)),
|
||||
"lang_java_public_static_main" => One(new DealDamageEffect(7, 7, Language.Java)),
|
||||
"lang_java_system_out_println" => One(new DealDamageEffect(5, 5, Language.Java)),
|
||||
"lang_java_new_object" => One(new DealDamageEffect(3, 3, Language.Java)),
|
||||
@@ -754,6 +953,14 @@ namespace KernelPanic.Combat
|
||||
"fedora_dnf_update" => One(new DealDamageEffect(9, 9, Language.Java)),
|
||||
"fedora_rawhide" => One(new RawhideEffect()),
|
||||
"fedora_selinux" => One(new IncomingAttackHalveEffect(2)),
|
||||
"arch_pacman_syu" => One(new DealDamageEffect(8, 8, Language.C)),
|
||||
"arch_consult_wiki" => One(new ConsultWikiEffect()),
|
||||
"arch_makepkg" => One(new BtwScaledDamageEffect(4, 2, Language.C)),
|
||||
"arch_aur_helper" => One(new RandomCommonLanguageCardEffect()),
|
||||
"arch_minimal_install" => One(new MinimalInstallEffect()),
|
||||
"shop_c_free" => One(new SegfaultDamageEffect(12, 3, 20, drawOnSegfault: true)),
|
||||
"shop_c_sizeof" => One(new SizeofDamageEffect()),
|
||||
"shop_c_kernel_exploit" => One(new SegfaultDamageEffect(25, 8, 50, true)),
|
||||
_ => Todo("card effect is not authored")
|
||||
};
|
||||
}
|
||||
@@ -761,10 +968,11 @@ namespace KernelPanic.Combat
|
||||
public static bool RequiresSingleTarget(CardDefinition definition)
|
||||
{
|
||||
return definition?.Id is "lang_js_console_log" or "lang_js_fetch" or "lang_js_typeof" or "mint_fix_broken"
|
||||
or "lang_rs_fn_main" or "lang_rs_unwrap" or "lang_java_public_static_main"
|
||||
or "lang_rs_fn_main" or "lang_rs_unwrap" or "lang_c_malloc" or "lang_c_printf" or "lang_c_pointer" or "lang_java_public_static_main"
|
||||
or "lang_java_system_out_println" or "lang_java_new_object"
|
||||
or "shop_py_list_append" or "shop_js_math_random" or "shop_js_promise_all" or "ubuntu_snap_install"
|
||||
or "mint_nemo" or "fedora_dnf_autoremove" or "fedora_first_to_package" or "fedora_dnf_update";
|
||||
or "mint_nemo" or "fedora_dnf_autoremove" or "fedora_first_to_package" or "fedora_dnf_update"
|
||||
or "arch_pacman_syu" or "arch_consult_wiki" or "arch_makepkg" or "shop_c_free" or "shop_c_sizeof" or "shop_c_kernel_exploit";
|
||||
}
|
||||
|
||||
public static bool TargetsAllEnemies(CardDefinition definition)
|
||||
@@ -820,8 +1028,11 @@ namespace KernelPanic.Combat
|
||||
"lang_js_fetch" => $"deal {UpgradeMath.ScaleAmount(5, card)}-{UpgradeMath.ScaleAmount(13, card)}.{marker}",
|
||||
"lang_js_typeof" => $"75%: deal {UpgradeMath.ScaleAmount(8, card)}. 25%: deal 0.{marker}",
|
||||
"lang_rs_fn_main" => $"deal {UpgradeMath.ScaleAmount(6, card)}. overkill becomes shield.{marker}",
|
||||
"lang_rs_unwrap" => $"deal {UpgradeMath.ScaleAmount(9, card)}. draw {UpgradeMath.ScaleAmount(1, card)}.{marker}",
|
||||
"lang_rs_unwrap" => $"deal {UpgradeMath.ScaleAmount(9, card)}. overkill becomes shield. draw {UpgradeMath.ScaleAmount(1, card)}.{marker}",
|
||||
"lang_rs_borrow" => $"gain {UpgradeMath.ScaleShield(5, card)} shield.{marker}",
|
||||
"lang_c_malloc" => $"deal {UpgradeMath.ScaleAmount(8, card)}. 20%: segfault, take 3.{marker}",
|
||||
"lang_c_printf" => $"deal {UpgradeMath.ScaleAmount(4, card)}.{marker}",
|
||||
"lang_c_pointer" => $"deal {UpgradeMath.ScaleAmount(6, card)}; {UpgradeMath.ScaleAmount(10, card)} if the previous card was C.{marker}",
|
||||
"lang_java_public_static_main" => $"uses 2 RAM. deal {UpgradeMath.ScaleAmount(7, card)}. JIT: Java cards cost 1 less this combat each time you play a Java card; discount decays by 1 after each wave.{marker}",
|
||||
"lang_java_system_out_println" => $"uses 2 RAM. deal {UpgradeMath.ScaleAmount(5, card)}. JIT applies; discount decays by 1 after each wave.{marker}",
|
||||
"lang_java_new_object" => $"uses 2 RAM. deal {UpgradeMath.ScaleAmount(3, card)}. JIT applies; discount decays by 1 after each wave.{marker}",
|
||||
@@ -846,6 +1057,14 @@ namespace KernelPanic.Combat
|
||||
"fedora_dnf_update" => $"deal {UpgradeMath.ScaleAmount(9, card)}. costs 1 less per Java card played this combat.{marker}",
|
||||
"fedora_rawhide" => $"draw {UpgradeMath.ScaleAmount(2, card)}. next card this turn gains bleeding edge.{marker}",
|
||||
"fedora_selinux" => $"next {UpgradeMath.ScaleAmount(2, card)} enemy attacks deal half damage.{marker}",
|
||||
"arch_pacman_syu" => $"deal {UpgradeMath.ScaleAmount(8, card)}. TODO: 20% break a random hand card.{marker}",
|
||||
"arch_consult_wiki" => $"fix a broken card and draw {UpgradeMath.ScaleAmount(1, card)}; if none, deal {UpgradeMath.ScaleAmount(6, card)}.{marker}",
|
||||
"arch_makepkg" => $"deal {UpgradeMath.ScaleAmount(4, card)} + btw x2; x3 at Arch v3+.{marker}",
|
||||
"arch_aur_helper" => $"add a random C/Rust common card to hand; it costs 0 this turn.{marker}",
|
||||
"arch_minimal_install" => $"exhaust up to 2 hand cards. per card: gain 3 Shield and 1 Cycle.{marker}",
|
||||
"shop_c_free" => $"deal {UpgradeMath.ScaleAmount(12, card)}. segfault recoil draws 2.{marker}",
|
||||
"shop_c_sizeof" => $"deal 3 x C cards in hand.{marker}",
|
||||
"shop_c_kernel_exploit" => $"deal {UpgradeMath.ScaleAmount(25, card)} ignoring Shield. 50%: take 8.{marker}",
|
||||
_ => string.IsNullOrWhiteSpace(card.Definition.Description) ? "effect not authored." : $"{card.Definition.Description}{marker}"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace KernelPanic.Combat
|
||||
private int fedoraCrashChance = 10;
|
||||
private int cardsPlayedThisTurn;
|
||||
private int cardsPlayedThisWave;
|
||||
private int cCardsPlayedThisTurn;
|
||||
private int turnNumberThisWave;
|
||||
private bool packageShieldBonusTriggeredThisTurn;
|
||||
private bool packageTimeshiftTriggeredThisWave;
|
||||
@@ -214,6 +215,19 @@ namespace KernelPanic.Combat
|
||||
card.MarkPlayedThisTurn(cardsPlayedThisTurn == 0);
|
||||
cardsPlayedThisTurn++;
|
||||
cardsPlayedThisWave++;
|
||||
if (IsDistro("arch"))
|
||||
{
|
||||
playerState.ArchBtwStacks++;
|
||||
}
|
||||
|
||||
playerState.HasPreviousPlayedCardLanguage = playerState.HasLastPlayedCardLanguage;
|
||||
if (playerState.HasLastPlayedCardLanguage)
|
||||
{
|
||||
playerState.PreviousPlayedCardLanguage = playerState.LastPlayedCardLanguage;
|
||||
}
|
||||
|
||||
playerState.LastPlayedCardLanguage = card.Definition.Language;
|
||||
playerState.HasLastPlayedCardLanguage = true;
|
||||
if (card.Definition.Language == Language.Java)
|
||||
{
|
||||
javaCardsPlayedThisCombat++;
|
||||
@@ -254,6 +268,11 @@ namespace KernelPanic.Combat
|
||||
ApplyFedoraGrowth(card, rawhideChargeUsed);
|
||||
}
|
||||
|
||||
if (card.Definition.Language == Language.C && cCardsPlayedThisTurn == 0 && HasPackageEffect(PackageEffectKind.FirstCThisTurnDamageMultiplier, out PackageEffectData cPackageEffect))
|
||||
{
|
||||
playerState.CurrentCardDamageMultiplierPercent = Mathf.Max(100, cPackageEffect.Amount);
|
||||
}
|
||||
|
||||
switch (track)
|
||||
{
|
||||
case ResolutionTrack.InterpreterQueue:
|
||||
@@ -273,6 +292,12 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
|
||||
playerState.DamageMultiplierPercent = 100;
|
||||
playerState.CurrentCardDamageMultiplierPercent = 100;
|
||||
if (card.Definition.Language == Language.C)
|
||||
{
|
||||
cCardsPlayedThisTurn++;
|
||||
}
|
||||
|
||||
ApplyPackageCardPlayedEffects(card);
|
||||
|
||||
pendingTargetCard = null;
|
||||
@@ -485,6 +510,16 @@ namespace KernelPanic.Combat
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DrawCards(int count, string label)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine(DrawCardsToHandSequenced(count, label));
|
||||
}
|
||||
|
||||
private void SetPhase(TurnPhase nextPhase)
|
||||
{
|
||||
if (IsCombatPaused && nextPhase != TurnPhase.Boot)
|
||||
@@ -566,6 +601,7 @@ namespace KernelPanic.Combat
|
||||
fedoraCardsDiscountedThisTurn = 0;
|
||||
cardsPlayedThisTurn = 0;
|
||||
cardsPlayedThisWave = 0;
|
||||
cCardsPlayedThisTurn = 0;
|
||||
turnNumberThisWave = 0;
|
||||
packageShieldBonusTriggeredThisTurn = false;
|
||||
packageTimeshiftTriggeredThisWave = false;
|
||||
@@ -617,6 +653,7 @@ namespace KernelPanic.Combat
|
||||
turnNumberThisWave = 0;
|
||||
packageShieldBonusTriggeredThisTurn = false;
|
||||
packageTimeshiftTriggeredThisWave = false;
|
||||
ResetArchWaveState();
|
||||
|
||||
List<CardInstance> startingCards = new();
|
||||
for (int i = 0; i < runManager.RunDeck.Count; i++)
|
||||
@@ -650,6 +687,7 @@ namespace KernelPanic.Combat
|
||||
playerState.Cycles = playerState.MaxCycles;
|
||||
fedoraCardsDiscountedThisTurn = 0;
|
||||
cardsPlayedThisTurn = 0;
|
||||
cCardsPlayedThisTurn = 0;
|
||||
turnNumberThisWave++;
|
||||
packageShieldBonusTriggeredThisTurn = false;
|
||||
javaCardsDiscountThisTurn = 0;
|
||||
@@ -770,6 +808,7 @@ namespace KernelPanic.Combat
|
||||
return;
|
||||
}
|
||||
|
||||
ResetArchBtwAtEndOfTurn();
|
||||
RemoveDefeatedEnemies();
|
||||
CheckWinOrLoss();
|
||||
StateChanged?.Invoke();
|
||||
@@ -1063,6 +1102,11 @@ namespace KernelPanic.Combat
|
||||
{
|
||||
GrantPlayerShield(Mathf.Max(0, effect.Amount), "auditd");
|
||||
}
|
||||
|
||||
if (HasPackageEffect(PackageEffectKind.TurnStartGenerateLanguageCard, out effect))
|
||||
{
|
||||
GenerateAurPackageCard(effect);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyPackageCardPlayedEffects(CardInstance card)
|
||||
@@ -1170,8 +1214,77 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateAurPackageCard(PackageEffectData effect)
|
||||
{
|
||||
if (handController == null || handController.RemainingRam <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int maxRarity = Mathf.Clamp(effect.Threshold, 0, (int)Rarity.Legendary);
|
||||
List<CardDefinition> pool = new();
|
||||
HashSet<string> seenIds = new(StringComparer.OrdinalIgnoreCase);
|
||||
IReadOnlyList<CardDefinition> source = generatedCardPool.Count > 0 ? generatedCardPool : runConfig?.StartingDeck;
|
||||
if (source == null)
|
||||
{
|
||||
Log("AUR: TODO card-generation pool unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
CardDefinition definition = source[i];
|
||||
if (definition == null
|
||||
|| definition.IsToken
|
||||
|| definition.IsRunOnly
|
||||
|| (int)definition.Rarity > maxRarity
|
||||
|| definition.Language != Language.C && definition.Language != Language.Rust)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seenIds.Add(definition.Id))
|
||||
{
|
||||
pool.Add(definition);
|
||||
}
|
||||
}
|
||||
|
||||
if (pool.Count == 0)
|
||||
{
|
||||
Log("AUR: TODO card-generation pool unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
int index = RandomRoll.RollRange(0, pool.Count - 1, new RollContext(playerState));
|
||||
CardDefinition selected = pool[index];
|
||||
CardInstance generated = new(selected)
|
||||
{
|
||||
TemporaryCostDelta = -Mathf.Clamp(effect.Amount, 0, selected.CycleCost)
|
||||
};
|
||||
|
||||
if (handController.Add(generated))
|
||||
{
|
||||
string discount = effect.Amount > 0 ? " at -1c" : string.Empty;
|
||||
Log($"AUR: generated {GetCardName(generated)}{discount}");
|
||||
StateChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePackageDamageDealt(DamageDealtEvent payload)
|
||||
{
|
||||
if (payload.Target == playerState && playerState != null && playerState.ArchRollingReleaseRecoveredThisHit)
|
||||
{
|
||||
playerState.ArchRollingReleaseRecoveredThisHit = false;
|
||||
string recovery = "rolling release: recovered at 1 Uptime";
|
||||
if (playerState.ArchRollingReleaseShieldOnSave > 0 || playerState.ArchRollingReleaseCyclesOnSave > 0)
|
||||
{
|
||||
recovery += $", +{playerState.ArchRollingReleaseShieldOnSave} Shield, +{playerState.ArchRollingReleaseCyclesOnSave} Cycle";
|
||||
}
|
||||
|
||||
Log(recovery);
|
||||
StateChanged?.Invoke();
|
||||
}
|
||||
|
||||
if (payload.Target != playerState
|
||||
|| payload.UptimeDamage <= 0
|
||||
|| packageTimeshiftTriggeredThisWave
|
||||
@@ -1198,6 +1311,39 @@ namespace KernelPanic.Combat
|
||||
StateChanged?.Invoke();
|
||||
}
|
||||
|
||||
private void ResetArchBtwAtEndOfTurn()
|
||||
{
|
||||
if (!IsDistro("arch") || playerState == null || playerState.ArchBtwStacks <= 0 || ArchBtwPersistsThisWave())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerState.ArchBtwStacks = 0;
|
||||
}
|
||||
|
||||
private void ResetArchWaveState()
|
||||
{
|
||||
if (playerState == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerState.ArchBtwStacks = 0;
|
||||
playerState.ArchRollingReleaseSavesRemaining = IsDistro("arch") ? runConfig.DistroVersion >= 5 ? 2 : 1 : 0;
|
||||
playerState.ArchRollingReleaseAvailableThisWave = playerState.ArchRollingReleaseSavesRemaining > 0;
|
||||
playerState.ArchRollingReleaseRecoveredThisHit = false;
|
||||
playerState.HasLastPlayedCardLanguage = false;
|
||||
playerState.HasPreviousPlayedCardLanguage = false;
|
||||
}
|
||||
|
||||
private bool ArchBtwPersistsThisWave()
|
||||
{
|
||||
return IsDistro("arch")
|
||||
&& (runConfig.DistroVersion >= 5
|
||||
|| HasOnDistroPackageEffect(PackageEffectKind.FirstCThisTurnDamageMultiplier, out PackageEffectData effect)
|
||||
&& effect.PersistArchBtw);
|
||||
}
|
||||
|
||||
private bool HasOnDistroPackageEffect(PackageEffectKind kind, out PackageEffectData effect)
|
||||
{
|
||||
effect = default;
|
||||
@@ -1288,6 +1434,11 @@ namespace KernelPanic.Combat
|
||||
state.AllowFlatDamageBuffs = IsDistro("mint") && runConfig.DistroVersion >= 4;
|
||||
state.FlatEffectBonus = IsDistro("mint") && runConfig.DistroVersion >= 2 ? 2 : 0;
|
||||
state.DamageMultiplierPercent = 100;
|
||||
state.CurrentCardDamageMultiplierPercent = 100;
|
||||
state.ArchBtwDamagePerStack = IsDistro("arch") && runConfig.DistroVersion >= 2 ? 2 : 1;
|
||||
state.ArchMakepkgBtwMultiplier = IsDistro("arch") && runConfig.DistroVersion >= 3 ? 3 : 2;
|
||||
state.ArchRollingReleaseShieldOnSave = IsDistro("arch") && runConfig.DistroVersion >= 4 ? 15 : 0;
|
||||
state.ArchRollingReleaseCyclesOnSave = IsDistro("arch") && runConfig.DistroVersion >= 4 ? 2 : 0;
|
||||
}
|
||||
|
||||
private bool IsDistro(string id)
|
||||
|
||||
@@ -32,7 +32,20 @@ namespace KernelPanic.Combat
|
||||
public int FlatEffectBonus { get; set; }
|
||||
public int JavaScriptFlatDamageBonus { get; set; }
|
||||
public int DamageMultiplierPercent { get; set; } = 100;
|
||||
public int CurrentCardDamageMultiplierPercent { get; set; } = 100;
|
||||
public int IncomingAttackHalfCharges { get; set; }
|
||||
public int ArchBtwStacks { get; set; }
|
||||
public int ArchBtwDamagePerStack { get; set; } = 1;
|
||||
public int ArchMakepkgBtwMultiplier { get; set; } = 2;
|
||||
public int ArchRollingReleaseSavesRemaining { get; set; }
|
||||
public int ArchRollingReleaseShieldOnSave { get; set; }
|
||||
public int ArchRollingReleaseCyclesOnSave { get; set; }
|
||||
public bool ArchRollingReleaseAvailableThisWave { get; set; }
|
||||
public bool ArchRollingReleaseRecoveredThisHit { get; set; }
|
||||
public bool HasLastPlayedCardLanguage { get; set; }
|
||||
public Language LastPlayedCardLanguage { get; set; }
|
||||
public bool HasPreviousPlayedCardLanguage { get; set; }
|
||||
public Language PreviousPlayedCardLanguage { get; set; }
|
||||
public IReadOnlyList<StatusInstance> Statuses => _statuses;
|
||||
public IReadOnlyDictionary<StatusType, int> ActiveStatuses => BuildStatusSnapshot();
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace KernelPanic.Combat
|
||||
amount = ApplyResistancesAndWeaknesses(amount, request);
|
||||
|
||||
int finalAmount = ApplyShieldAndUptime(amount, request, out int absorbedAmount, out int shieldDamage, out int uptimeDamage);
|
||||
ApplyArchRollingReleaseSave(request);
|
||||
GameEvents.RaiseDamageDealt(new DamageDealtEvent(request.Source, request.Target, finalAmount, request.Language, amount, absorbedAmount, wasCritical, shieldDamage, uptimeDamage));
|
||||
|
||||
bool defeated = false;
|
||||
@@ -93,7 +94,8 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
|
||||
int multiplier = request.Source == null ? 100 : Mathf.Max(0, request.Source.DamageMultiplierPercent);
|
||||
return Mathf.RoundToInt(amount * (multiplier / 100f));
|
||||
int currentCardMultiplier = request.Source == null ? 100 : Mathf.Max(0, request.Source.CurrentCardDamageMultiplierPercent);
|
||||
return Mathf.RoundToInt(amount * (multiplier / 100f) * (currentCardMultiplier / 100f));
|
||||
}
|
||||
|
||||
private static int ApplyFlatAdditions(int amount, DamageRequest request)
|
||||
@@ -103,6 +105,11 @@ namespace KernelPanic.Combat
|
||||
amount += Mathf.Max(0, request.Source.JavaScriptFlatDamageBonus);
|
||||
}
|
||||
|
||||
if (request.Source != null && (request.Language == Language.C || request.Language == Language.Rust))
|
||||
{
|
||||
amount += Mathf.Max(0, request.Source.ArchBtwStacks) * Mathf.Max(1, request.Source.ArchBtwDamagePerStack);
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
@@ -114,10 +121,29 @@ namespace KernelPanic.Combat
|
||||
return amount;
|
||||
}
|
||||
|
||||
wasCritical = RandomRoll.RollRange(1, 100, new RollContext(request.Source)) <= 25;
|
||||
int critChance = request.Language == Language.C ? 50 : 25;
|
||||
wasCritical = RandomRoll.RollRange(1, 100, new RollContext(request.Source)) <= critChance;
|
||||
return wasCritical ? Mathf.CeilToInt(amount * 1.5f) : amount;
|
||||
}
|
||||
|
||||
private static void ApplyArchRollingReleaseSave(DamageRequest request)
|
||||
{
|
||||
CombatantState target = request.Target;
|
||||
if (target == null || target.CurrentUptime > 0 || target.ArchRollingReleaseSavesRemaining <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
target.ArchRollingReleaseSavesRemaining--;
|
||||
target.ArchRollingReleaseAvailableThisWave = target.ArchRollingReleaseSavesRemaining > 0;
|
||||
target.ArchBtwStacks = 0;
|
||||
target.CurrentUptime = 1;
|
||||
target.Shield += Mathf.Max(0, target.ArchRollingReleaseShieldOnSave);
|
||||
target.Cycles += Mathf.Max(0, target.ArchRollingReleaseCyclesOnSave);
|
||||
target.IsDefeated = false;
|
||||
target.ArchRollingReleaseRecoveredThisHit = true;
|
||||
}
|
||||
|
||||
private static int ApplyResistancesAndWeaknesses(int amount, DamageRequest request)
|
||||
{
|
||||
// TODO: Enemy resistances/reductions are not authored yet. Mint V5 should re-resolve
|
||||
|
||||
@@ -37,7 +37,9 @@ namespace KernelPanic.Data
|
||||
FirstInterpreterQueueCardEachWaveShield = 21,
|
||||
EveryNthTurnDraw = 22,
|
||||
StartTurnNoDebuffShield = 23,
|
||||
EveryNthCardEachWaveCycle = 24
|
||||
EveryNthCardEachWaveCycle = 24,
|
||||
FirstCThisTurnDamageMultiplier = 25,
|
||||
TurnStartGenerateLanguageCard = 26
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -49,8 +51,9 @@ namespace KernelPanic.Data
|
||||
[SerializeField] private bool refundCycle;
|
||||
[SerializeField] private bool cleanseDebuffs;
|
||||
[SerializeField] private bool enableFedoraSecondCardPassive;
|
||||
[SerializeField] private bool persistArchBtw;
|
||||
|
||||
public PackageEffectData(PackageEffectKind kind, int amount, int threshold, bool refundCycle, bool cleanseDebuffs, bool enableFedoraSecondCardPassive)
|
||||
public PackageEffectData(PackageEffectKind kind, int amount, int threshold, bool refundCycle, bool cleanseDebuffs, bool enableFedoraSecondCardPassive, bool persistArchBtw = false)
|
||||
{
|
||||
this.kind = kind;
|
||||
this.amount = amount;
|
||||
@@ -58,6 +61,7 @@ namespace KernelPanic.Data
|
||||
this.refundCycle = refundCycle;
|
||||
this.cleanseDebuffs = cleanseDebuffs;
|
||||
this.enableFedoraSecondCardPassive = enableFedoraSecondCardPassive;
|
||||
this.persistArchBtw = persistArchBtw;
|
||||
}
|
||||
|
||||
public PackageEffectKind Kind => kind;
|
||||
@@ -66,6 +70,7 @@ namespace KernelPanic.Data
|
||||
public bool RefundCycle => refundCycle;
|
||||
public bool CleanseDebuffs => cleanseDebuffs;
|
||||
public bool EnableFedoraSecondCardPassive => enableFedoraSecondCardPassive;
|
||||
public bool PersistArchBtw => persistArchBtw;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -142,7 +147,7 @@ namespace KernelPanic.Data
|
||||
}
|
||||
|
||||
int amount = effect.Amount + GetAmountPerLevel(effect.Kind, effect.Amount) * level;
|
||||
return new PackageEffectData(effect.Kind, amount, effect.Threshold, effect.RefundCycle, effect.CleanseDebuffs, effect.EnableFedoraSecondCardPassive);
|
||||
return new PackageEffectData(effect.Kind, amount, effect.Threshold, effect.RefundCycle, effect.CleanseDebuffs, effect.EnableFedoraSecondCardPassive, effect.PersistArchBtw);
|
||||
}
|
||||
|
||||
private static int GetAmountPerLevel(PackageEffectKind kind, int baseAmount)
|
||||
@@ -159,6 +164,7 @@ namespace KernelPanic.Data
|
||||
PackageEffectKind.FirstCardEachWaveCostReduction => 1,
|
||||
PackageEffectKind.FirstNativeCardEachWaveFlatDamage => 1,
|
||||
PackageEffectKind.JavaScriptFlatDamage => 1,
|
||||
PackageEffectKind.TurnStartGenerateLanguageCard => 0,
|
||||
_ => 1
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ namespace KernelPanic.Meta
|
||||
{
|
||||
["ubuntu"] = new[] { "20.04", "21.04", "22.04", "23.04", "24.04" },
|
||||
["fedora"] = new[] { "38", "39", "40", "41", "42" },
|
||||
["mint"] = new[] { "21", "21.2", "21.3", "22", "22.1" }
|
||||
["mint"] = new[] { "21", "21.2", "21.3", "22", "22.1" },
|
||||
["arch"] = new[] { "2024.01", "2024.06", "2024.12", "2025.06", "2025.12" }
|
||||
};
|
||||
|
||||
public static string GetReleaseLabel(string unitId, int version)
|
||||
@@ -55,6 +56,15 @@ namespace KernelPanic.Meta
|
||||
5 => "timeshift snapshot restores to full uptime if lower",
|
||||
_ => "--"
|
||||
},
|
||||
"arch" => safeVersion switch
|
||||
{
|
||||
1 => "btw stacks add C/Rust damage; once per wave, lethal uptime recovers to 1",
|
||||
2 => "btw stacks add +2 C/Rust damage instead of +1",
|
||||
3 => "makepkg scales at btw x3 instead of x2",
|
||||
4 => "rolling-release recovery also grants 15 Shield and 2 Cycles",
|
||||
5 => "btw persists for the wave and rolling release can save twice per wave",
|
||||
_ => "--"
|
||||
},
|
||||
_ => safeVersion == 1 ? "base release" : $"V{safeVersion} release effect"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -309,6 +309,21 @@ namespace KernelPanic.Meta
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsBeginnerInstallMediaDistro(DistroDefinition distro)
|
||||
{
|
||||
return distro != null
|
||||
&& (string.Equals(distro.Id, "ubuntu", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(distro.Id, "fedora", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(distro.Id, "mint", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(distro.Id, "arch", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public static bool IsBeginnerStarterDistro(DistroDefinition distro)
|
||||
{
|
||||
return IsBeginnerInstallMediaDistro(distro)
|
||||
&& !string.Equals(distro.Id, "arch", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static double GetFiveStarChance(GachaBannerState state)
|
||||
{
|
||||
return GetFiveStarChance(state == null ? 0 : state.fiveStarPityCounter);
|
||||
@@ -353,7 +368,8 @@ namespace KernelPanic.Meta
|
||||
if (pullNumber == 50)
|
||||
{
|
||||
_beginnerState.pityCounter = 0;
|
||||
return GachaReward.FutureStandardFiveStar("future standard 5-star distro", "50-pull beginner capstone");
|
||||
_beginnerState.fiveStarPityCounter = 0;
|
||||
return RewardGuaranteedBeginnerFiveStar("50-pull beginner guarantee");
|
||||
}
|
||||
|
||||
if (TryRollFiveStar(_beginnerState, out bool fiveStarPityTriggered))
|
||||
@@ -372,8 +388,11 @@ namespace KernelPanic.Meta
|
||||
_beginnerState.pityCounter = 0;
|
||||
if (_bannerPool.Count > 0 && _random.NextDouble() < DistroChanceOnFeaturedTier)
|
||||
{
|
||||
DistroDefinition distro = _bannerPool[_random.Next(_bannerPool.Count)];
|
||||
return GachaReward.DistroReward(distro, 4, pityTriggered, false);
|
||||
DistroDefinition distro = FindRandomBannerPoolDistroByStars(4);
|
||||
if (distro != null)
|
||||
{
|
||||
return GachaReward.DistroReward(distro, 4, pityTriggered, false);
|
||||
}
|
||||
}
|
||||
|
||||
return GachaReward.Package(4, "4-star package", pityTriggered, false);
|
||||
@@ -413,8 +432,18 @@ namespace KernelPanic.Meta
|
||||
return GachaReward.Package(5, "5-star package", pityTriggered, false);
|
||||
}
|
||||
|
||||
DistroDefinition distro = _bannerPool.Count == 0 ? null : _bannerPool[_random.Next(_bannerPool.Count)];
|
||||
return GachaReward.DistroReward(distro, 5, pityTriggered, false);
|
||||
DistroDefinition distro = FindRandomBannerPoolDistroByStars(5);
|
||||
return distro == null
|
||||
? GachaReward.Package(5, "5-star package", pityTriggered, false)
|
||||
: GachaReward.DistroReward(distro, 5, pityTriggered, false);
|
||||
}
|
||||
|
||||
private GachaReward RewardGuaranteedBeginnerFiveStar(string reason)
|
||||
{
|
||||
DistroDefinition distro = FindRandomBannerPoolDistroByStars(5);
|
||||
return distro == null
|
||||
? GachaReward.Package(5, "5-star package", true, true)
|
||||
: GachaReward.DistroReward(distro, 5, true, true, reason);
|
||||
}
|
||||
|
||||
public bool ResolveFeaturedTierIsDistro()
|
||||
@@ -446,16 +475,16 @@ namespace KernelPanic.Meta
|
||||
DistroDefinition distro = null;
|
||||
if (guaranteeIndex >= 0 && guaranteeIndex < _beginnerState.guaranteedDistroIds.Count)
|
||||
{
|
||||
distro = FindBannerPoolDistro(_beginnerState.guaranteedDistroIds[guaranteeIndex]);
|
||||
distro = FindBannerPoolDistroByStars(_beginnerState.guaranteedDistroIds[guaranteeIndex], 4);
|
||||
}
|
||||
|
||||
distro ??= _bannerPool.Count == 0 ? null : _bannerPool[Math.Min(guaranteeIndex, _bannerPool.Count - 1)];
|
||||
distro ??= FindBannerPoolDistroByStars(guaranteeIndex, 4);
|
||||
return distro == null
|
||||
? GachaReward.Package(4, "4-star package", true, true)
|
||||
: GachaReward.DistroReward(distro, 4, true, true, reason);
|
||||
}
|
||||
|
||||
private DistroDefinition FindBannerPoolDistro(string id)
|
||||
private DistroDefinition FindBannerPoolDistroByStars(string id, int stars)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
@@ -465,7 +494,7 @@ namespace KernelPanic.Meta
|
||||
for (int i = 0; i < _bannerPool.Count; i++)
|
||||
{
|
||||
DistroDefinition distro = _bannerPool[i];
|
||||
if (distro != null && string.Equals(distro.Id, id, StringComparison.OrdinalIgnoreCase))
|
||||
if (distro != null && GetDistroStars(distro) == stars && string.Equals(distro.Id, id, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return distro;
|
||||
}
|
||||
@@ -474,6 +503,48 @@ namespace KernelPanic.Meta
|
||||
return null;
|
||||
}
|
||||
|
||||
private DistroDefinition FindBannerPoolDistroByStars(int index, int stars)
|
||||
{
|
||||
int seen = 0;
|
||||
for (int i = 0; i < _bannerPool.Count; i++)
|
||||
{
|
||||
DistroDefinition distro = _bannerPool[i];
|
||||
if (distro == null || GetDistroStars(distro) != stars)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seen == index)
|
||||
{
|
||||
return distro;
|
||||
}
|
||||
|
||||
seen++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private DistroDefinition FindRandomBannerPoolDistroByStars(int stars)
|
||||
{
|
||||
List<DistroDefinition> eligible = new();
|
||||
for (int i = 0; i < _bannerPool.Count; i++)
|
||||
{
|
||||
DistroDefinition distro = _bannerPool[i];
|
||||
if (distro != null && GetDistroStars(distro) == stars)
|
||||
{
|
||||
eligible.Add(distro);
|
||||
}
|
||||
}
|
||||
|
||||
return eligible.Count == 0 ? null : eligible[_random.Next(eligible.Count)];
|
||||
}
|
||||
|
||||
private static int GetDistroStars(DistroDefinition distro)
|
||||
{
|
||||
return distro != null && string.Equals(distro.Id, "arch", StringComparison.OrdinalIgnoreCase) ? 5 : 4;
|
||||
}
|
||||
|
||||
private void SetCurrencyBalanceSilently(GachaCurrencyType currencyType, int amount)
|
||||
{
|
||||
_currencyBalances[currencyType] = Math.Max(0, amount);
|
||||
|
||||
@@ -391,7 +391,7 @@ namespace KernelPanic.Run
|
||||
continue;
|
||||
}
|
||||
|
||||
if (card.IsRunOnly || card.Language == CurrentConfig.PrimaryLanguage || card.Language == CurrentConfig.SecondaryLanguage)
|
||||
if (card.Language == CurrentConfig.PrimaryLanguage || card.Language == CurrentConfig.SecondaryLanguage)
|
||||
{
|
||||
pool.Add(card);
|
||||
}
|
||||
|
||||
@@ -707,7 +707,7 @@ namespace KernelPanic.UI
|
||||
|
||||
private static int GetDistroStars(DistroDefinition unit)
|
||||
{
|
||||
return 4;
|
||||
return unit != null && string.Equals(unit.Id, "arch", StringComparison.OrdinalIgnoreCase) ? 5 : 4;
|
||||
}
|
||||
|
||||
private static Color GetLanguageColor(Language language)
|
||||
@@ -2024,6 +2024,8 @@ namespace KernelPanic.UI
|
||||
PackageEffectKind.JavaScriptFlatDamage => $"+{amount} JavaScript damage",
|
||||
PackageEffectKind.WaveThresholdRestore => $"restore {amount} Uptime after wave {effect.Threshold}",
|
||||
PackageEffectKind.DnfFedoraPassive => "Fedora passive can trigger twice",
|
||||
PackageEffectKind.FirstCThisTurnDamageMultiplier => $"first C card each turn deals {amount}% damage",
|
||||
PackageEffectKind.TurnStartGenerateLanguageCard => amount > 0 ? "turn start: generate C/Rust card at -1c" : "turn start: generate common C/Rust card",
|
||||
_ => amount > 0 ? $"+{amount} package effect" : "package effect"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -607,7 +607,7 @@ namespace KernelPanic.UI
|
||||
IReadOnlyList<string> ids = _gachaService.BeginnerState.guaranteedDistroIds;
|
||||
string first = ids.Count > 0 ? ids[0] : "--";
|
||||
string second = ids.Count > 1 ? ids[1] : "--";
|
||||
return $"20={first}, 40={second}, 50=future standard 5-star";
|
||||
return $"20={first}, 40={second}, 50=guaranteed 5-star";
|
||||
}
|
||||
|
||||
private static VisualElement BuildInfoLine(string key, string value)
|
||||
|
||||
@@ -517,12 +517,16 @@ namespace KernelPanic.UI
|
||||
}
|
||||
}
|
||||
|
||||
if (saveData.starterChosen && gacha.BannerPool.Count == 0)
|
||||
if (saveData.starterChosen)
|
||||
{
|
||||
IReadOnlyList<DistroDefinition> distros = distroDatabase.AllDistros;
|
||||
for (int i = 0; i < distros.Count; i++)
|
||||
{
|
||||
gacha.AddToBannerPool(distros[i]);
|
||||
DistroDefinition distro = distros[i];
|
||||
if (GachaService.IsBeginnerInstallMediaDistro(distro))
|
||||
{
|
||||
gacha.AddToBannerPool(distro);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -707,7 +707,7 @@ namespace KernelPanic.UI
|
||||
|
||||
if (_saveData.starterChosen)
|
||||
{
|
||||
AddAllFourStarDistrosToBeginnerBannerPool();
|
||||
AddBeginnerInstallMediaDistrosToBannerPool();
|
||||
}
|
||||
|
||||
if (_gachaService.BeginnerState.guaranteedDistroIds.Count == 0 && _saveData.bannerPoolIds.Count > 0)
|
||||
@@ -730,7 +730,7 @@ namespace KernelPanic.UI
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllFourStarDistrosToBeginnerBannerPool()
|
||||
private void AddBeginnerInstallMediaDistrosToBannerPool()
|
||||
{
|
||||
if (distroDatabase == null)
|
||||
{
|
||||
@@ -740,7 +740,11 @@ namespace KernelPanic.UI
|
||||
IReadOnlyList<DistroDefinition> distros = distroDatabase.AllDistros;
|
||||
for (int i = 0; i < distros.Count; i++)
|
||||
{
|
||||
_gachaService.AddToBannerPool(distros[i]);
|
||||
DistroDefinition distro = distros[i];
|
||||
if (GachaService.IsBeginnerInstallMediaDistro(distro))
|
||||
{
|
||||
_gachaService.AddToBannerPool(distro);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,7 +810,7 @@ namespace KernelPanic.UI
|
||||
private void HandleStarterConfirmed(DistroDefinition picked, IReadOnlyList<DistroDefinition> remaining)
|
||||
{
|
||||
_playerCollection.Add(picked, 1);
|
||||
AddAllFourStarDistrosToBeginnerBannerPool();
|
||||
AddBeginnerInstallMediaDistrosToBannerPool();
|
||||
_gachaService.SetBeginnerGuaranteedDistros(remaining);
|
||||
_saveData.starterChosen = true;
|
||||
SaveCurrentState();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using KernelPanic.Data;
|
||||
using KernelPanic.Meta;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
@@ -196,7 +197,24 @@ namespace KernelPanic.UI
|
||||
return false;
|
||||
}
|
||||
|
||||
starters = allDistros;
|
||||
List<DistroDefinition> starterDistros = new();
|
||||
for (int i = 0; i < allDistros.Count; i++)
|
||||
{
|
||||
DistroDefinition distro = allDistros[i];
|
||||
if (GachaService.IsBeginnerStarterDistro(distro))
|
||||
{
|
||||
starterDistros.Add(distro);
|
||||
}
|
||||
}
|
||||
|
||||
if (starterDistros.Count < StarterCount)
|
||||
{
|
||||
starters = Array.Empty<DistroDefinition>();
|
||||
error = $"DistroDatabase needs at least {StarterCount} starter distros (found {starterDistros.Count})";
|
||||
return false;
|
||||
}
|
||||
|
||||
starters = starterDistros;
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user