changed most TODOs to TODONEs
This commit is contained in:
@@ -14,7 +14,7 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
id: shop_py_async_def
|
||||
displayName: async def
|
||||
description: 'queue: draw 2. TODO: gain 1 Cycle next turn.'
|
||||
description: 'queue: draw 2. gain 1 Cycle next turn.'
|
||||
flavorText: returns eventually. emotionally, at least.
|
||||
language: 3
|
||||
rarity: 1
|
||||
@@ -23,7 +23,7 @@ MonoBehaviour:
|
||||
distroExclusive: 0
|
||||
isToken: 0
|
||||
isRunOnly: 1
|
||||
designNotes: Run-only Repository card. Draw 2 works now; next-turn cycle grant waits for deferred-cycle-grant.
|
||||
designNotes: Run-only Repository card. Draw 2 and grants +1 Cycle on the next turn.
|
||||
requiresSystem:
|
||||
- deferred-cycle-grant
|
||||
upgradeTarget: {fileID: 0}
|
||||
|
||||
@@ -14,7 +14,7 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
id: shop_py_list_append
|
||||
displayName: list.append()
|
||||
description: 'queue: deal 4. TODO: grows when re-queued.'
|
||||
description: 'queue: deal 4. gains +1 damage each time it is re-queued.'
|
||||
flavorText: mutable state, but make it cute.
|
||||
language: 3
|
||||
rarity: 1
|
||||
@@ -23,7 +23,7 @@ MonoBehaviour:
|
||||
distroExclusive: 0
|
||||
isToken: 0
|
||||
isRunOnly: 1
|
||||
designNotes: Run-only Repository card. Base deal 4 works now; permanent per-combat requeue growth waits for requeue-tracking.
|
||||
designNotes: Run-only Repository card. Base deal 4 with per-combat requeue growth.
|
||||
requiresSystem:
|
||||
- requeue-tracking
|
||||
upgradeTarget: {fileID: 0}
|
||||
|
||||
@@ -14,7 +14,7 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
id: shop_js_spread
|
||||
displayName: spread ...
|
||||
description: 'TODO: copy the lowest-cost card in hand.'
|
||||
description: 'copy the lowest-cost card in hand.'
|
||||
flavorText: cloning by punctuation, naturally.
|
||||
language: 4
|
||||
rarity: 1
|
||||
@@ -23,7 +23,7 @@ MonoBehaviour:
|
||||
distroExclusive: 0
|
||||
isToken: 0
|
||||
isRunOnly: 1
|
||||
designNotes: Run-only Repository card. Requires card-copy.
|
||||
designNotes: Run-only Repository card. Copies the lowest-cost card currently in hand.
|
||||
requiresSystem:
|
||||
- card-copy
|
||||
upgradeTarget: {fileID: 0}
|
||||
|
||||
@@ -14,7 +14,7 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
id: shop_py_zip
|
||||
displayName: zip()
|
||||
description: 'TODO: next 2 queued cards resolve twice.'
|
||||
description: 'next 2 queued cards resolve twice.'
|
||||
flavorText: pairs nicely until lengths disagree.
|
||||
language: 3
|
||||
rarity: 1
|
||||
@@ -23,7 +23,7 @@ MonoBehaviour:
|
||||
distroExclusive: 0
|
||||
isToken: 0
|
||||
isRunOnly: 1
|
||||
designNotes: Run-only Repository card. Requires queue-effect-multiply.
|
||||
designNotes: Run-only Repository card. Adds two charges that make queued cards resolve twice.
|
||||
requiresSystem:
|
||||
- queue-effect-multiply
|
||||
upgradeTarget: {fileID: 0}
|
||||
|
||||
@@ -83,6 +83,26 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FirstCardShieldEffect : ICardEffect
|
||||
{
|
||||
private readonly int _baseAmount;
|
||||
private readonly int _firstCardBonus;
|
||||
|
||||
public FirstCardShieldEffect(int baseAmount, int firstCardBonus)
|
||||
{
|
||||
_baseAmount = baseAmount;
|
||||
_firstCardBonus = firstCardBonus;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
int amount = _baseAmount + (context.Card != null && context.Card.WasFirstCardThisTurn ? _firstCardBonus : 0);
|
||||
amount = UpgradeMath.ScaleShield(UpgradeMath.ApplySourceFlatBonus(amount, context), context.Card);
|
||||
context.Source.Shield += amount;
|
||||
context.CombatManager.ReportEffectResult($"gained {amount} shield");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ChanceDamageEffect : ICardEffect
|
||||
{
|
||||
private readonly int _damageAmount;
|
||||
@@ -115,6 +135,26 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class QueuedGrowthDamageEffect : ICardEffect
|
||||
{
|
||||
private readonly int _baseAmount;
|
||||
private readonly Language _language;
|
||||
|
||||
public QueuedGrowthDamageEffect(int baseAmount, Language language)
|
||||
{
|
||||
_baseAmount = baseAmount;
|
||||
_language = language;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
int queueGrowth = UnityEngine.Mathf.Max(0, (context.Card?.QueuePlayCount ?? 1) - 1);
|
||||
int amount = _baseAmount + queueGrowth;
|
||||
new DealDamageEffect(amount, amount, _language).Execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public sealed class ApplyStatusEffect : ICardEffect
|
||||
{
|
||||
private readonly StatusType _statusType;
|
||||
@@ -209,6 +249,81 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NextTurnCycleEffect : ICardEffect
|
||||
{
|
||||
private readonly int _amount;
|
||||
|
||||
public NextTurnCycleEffect(int amount)
|
||||
{
|
||||
_amount = amount;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
int amount = UpgradeMath.ScaleAmount(_amount, context.Card);
|
||||
context.CombatManager.AddNextTurnCycleBonus(amount);
|
||||
context.CombatManager.ReportEffectResult($"next turn +{amount} cycle");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class QueueRepeatEffect : ICardEffect
|
||||
{
|
||||
private readonly int _count;
|
||||
|
||||
public QueueRepeatEffect(int count)
|
||||
{
|
||||
_count = count;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
context.CombatManager.AddQueuedRepeatCharges(UpgradeMath.ScaleAmount(_count, context.Card));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CopyLowestCostHandCardEffect : ICardEffect
|
||||
{
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
if (context.HandController.Cards.Count >= context.HandController.RamCapacity)
|
||||
{
|
||||
context.CombatManager.ReportEffectResult("hand full: copied 0");
|
||||
return;
|
||||
}
|
||||
|
||||
CardInstance selected = null;
|
||||
int selectedCost = int.MaxValue;
|
||||
for (int i = 0; i < context.HandController.Cards.Count; i++)
|
||||
{
|
||||
CardInstance candidate = context.HandController.Cards[i];
|
||||
if (candidate == null || candidate.Definition == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int cost = context.CombatManager.GetEffectiveCardCost(candidate);
|
||||
if (cost < selectedCost)
|
||||
{
|
||||
selected = candidate;
|
||||
selectedCost = cost;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected == null)
|
||||
{
|
||||
context.CombatManager.ReportEffectResult("no card to copy");
|
||||
return;
|
||||
}
|
||||
|
||||
CardInstance copy = selected.CopyForCombat();
|
||||
if (context.HandController.Add(copy))
|
||||
{
|
||||
string name = string.IsNullOrWhiteSpace(copy.Definition.DisplayName) ? copy.Definition.Id : copy.Definition.DisplayName;
|
||||
context.CombatManager.ReportEffectResult($"copied {name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class UpgradeHandCardsEffect : ICardEffect
|
||||
{
|
||||
private readonly int _minCount;
|
||||
@@ -247,6 +362,34 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ShuffleTokenIntoDrawPileEffect : ICardEffect
|
||||
{
|
||||
private readonly string _tokenId;
|
||||
|
||||
public ShuffleTokenIntoDrawPileEffect(string tokenId)
|
||||
{
|
||||
_tokenId = tokenId;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
if (context.CombatManager.TryCreateGeneratedCardById(_tokenId, out CardInstance token))
|
||||
{
|
||||
context.DeckController.AddToDrawPile(token, shuffle: true);
|
||||
context.CombatManager.ReportEffectResult($"shuffled {GetCardName(token)} into draw pile");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CombatManager.ReportEffectResult($"token {_tokenId} unavailable");
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetCardName(CardInstance card)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(card?.Definition?.DisplayName) ? card?.Definition?.Id ?? "--" : card.Definition.DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class GenerateCardEffect : ICardEffect
|
||||
{
|
||||
private readonly Language _language;
|
||||
@@ -293,18 +436,123 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NoOpCardEffect : ICardEffect
|
||||
public sealed class OverkillToShieldDamageEffect : ICardEffect
|
||||
{
|
||||
private readonly string _todo;
|
||||
private readonly int _amount;
|
||||
private readonly Language _language;
|
||||
|
||||
public NoOpCardEffect(string todo)
|
||||
public OverkillToShieldDamageEffect(int amount, Language language)
|
||||
{
|
||||
_todo = todo;
|
||||
_amount = amount;
|
||||
_language = language;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
context.CombatManager.LogEffectTodo(context.Card, _todo);
|
||||
for (int i = 0; i < context.Targets.Count; i++)
|
||||
{
|
||||
CombatantState target = context.Targets[i];
|
||||
if (target == null || target.IsDefeated)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int amount = UpgradeMath.ScaleAmount(UpgradeMath.ApplySourceFlatBonus(_amount, context), context.Card);
|
||||
int survivable = target.CurrentUptime + target.Shield;
|
||||
context.DamagePipeline.DealDamage(new DamageRequest(context.Source, target, amount, _language, false, false));
|
||||
int overkill = UnityEngine.Mathf.Max(0, amount - survivable);
|
||||
if (overkill > 0)
|
||||
{
|
||||
context.Source.Shield += overkill;
|
||||
context.CombatManager.ReportEffectResult($"overkill -> {overkill} shield");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ConditionalShieldRepeatDamageEffect : ICardEffect
|
||||
{
|
||||
private readonly int _amount;
|
||||
private readonly Language _language;
|
||||
|
||||
public ConditionalShieldRepeatDamageEffect(int amount, Language language)
|
||||
{
|
||||
_amount = amount;
|
||||
_language = language;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
int repeats = context.Source.Shield > 0 ? 2 : 1;
|
||||
for (int repeat = 0; repeat < repeats; repeat++)
|
||||
{
|
||||
new DealDamageEffect(_amount, _amount, _language).Execute(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class UpdateManagerEffect : ICardEffect
|
||||
{
|
||||
private readonly int _amount;
|
||||
|
||||
public UpdateManagerEffect(int amount)
|
||||
{
|
||||
_amount = amount;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
int amount = UpgradeMath.ScaleAmount(UpgradeMath.ApplySourceFlatBonus(_amount, context), context.Card);
|
||||
new DealDamageEffect(amount, amount, Language.Python, allEnemies: true).Execute(context);
|
||||
context.CombatManager.ScheduleUpdateManagerRepeat(amount);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TimeshiftSnapshotEffect : ICardEffect
|
||||
{
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
context.CombatManager.ScheduleTimeshiftRestore(context.Source.CurrentUptime);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RawhideEffect : ICardEffect
|
||||
{
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
new DrawEffect(2).Execute(context);
|
||||
context.CombatManager.GrantRawhideBonus(1);
|
||||
context.CombatManager.ReportEffectResult("rawhide armed next card");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class IncomingAttackHalveEffect : ICardEffect
|
||||
{
|
||||
private readonly int _charges;
|
||||
|
||||
public IncomingAttackHalveEffect(int charges)
|
||||
{
|
||||
_charges = charges;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
context.CombatManager.AddIncomingAttackHalfCharges(UpgradeMath.ScaleAmount(_charges, context.Card));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NoOpCardEffect : ICardEffect
|
||||
{
|
||||
private readonly string _message;
|
||||
|
||||
public NoOpCardEffect(string message)
|
||||
{
|
||||
_message = message;
|
||||
}
|
||||
|
||||
public void Execute(CombatContext context)
|
||||
{
|
||||
context.CombatManager.LogEffectTodo(context.Card, _message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,9 +585,13 @@ namespace KernelPanic.Combat
|
||||
new CleanseEffect(harmfulOnly: true)
|
||||
},
|
||||
"lang_js_typeof" => One(new ChanceDamageEffect(8, 75, Language.JavaScript)),
|
||||
"shop_py_list_append" => One(new DealDamageEffect(4, 4, Language.Python)),
|
||||
"shop_py_async_def" => One(new DrawEffect(2)),
|
||||
"shop_py_zip" => Todo("TODO: zip() needs queue-effect-multiply support."),
|
||||
"shop_py_list_append" => One(new QueuedGrowthDamageEffect(4, Language.Python)),
|
||||
"shop_py_async_def" => new ICardEffect[]
|
||||
{
|
||||
new DrawEffect(2),
|
||||
new NextTurnCycleEffect(1)
|
||||
},
|
||||
"shop_py_zip" => One(new QueueRepeatEffect(2)),
|
||||
"shop_js_math_random" => One(new DealDamageEffect(2, 20, Language.JavaScript)),
|
||||
"shop_js_promise_all" => new ICardEffect[]
|
||||
{
|
||||
@@ -347,28 +599,37 @@ namespace KernelPanic.Combat
|
||||
new DealDamageEffect(4, 10, Language.JavaScript),
|
||||
new DealDamageEffect(4, 10, Language.JavaScript)
|
||||
},
|
||||
"shop_js_spread" => Todo("TODO: spread ... needs card-copy support."),
|
||||
"ubuntu_snap_install" => Todo("TODO: snap install needs token/junk insertion system."),
|
||||
"shop_js_spread" => One(new CopyLowestCostHandCardEffect()),
|
||||
"ubuntu_snap_install" => new ICardEffect[]
|
||||
{
|
||||
new DealDamageEffect(6, 14, Language.JavaScript),
|
||||
new ShuffleTokenIntoDrawPileEffect("ubuntu_pro_trial")
|
||||
},
|
||||
"ubuntu_do_release_upgrade" => One(new UpgradeHandCardsEffect(1, 3)),
|
||||
"ubuntu_apt_install" => One(new GenerateCardEffect(Language.Python, Rarity.Common)),
|
||||
"ubuntu_pro_trial" => Todo("TODO: ubuntu pro trial needs unplayable token handling."),
|
||||
"mint_update_manager" => Todo("TODO: update manager needs delayed repeat system."),
|
||||
"mint_cinnamon" => Todo("TODO: cinnamon needs shield effect support."),
|
||||
"mint_nemo" => Todo("TODO: nemo needs conditional repeat based on shield."),
|
||||
"mint_timeshift" => Todo("TODO: timeshift snapshot V3 needs delayed restore system before threshold/restore can be upgraded."),
|
||||
"fedora_borrow_checker" => Todo("TODO: borrow checker needs shield and first-card tracking."),
|
||||
"fedora_cargo_build" => Todo("TODO: cargo build --release needs overkill-to-shield handling."),
|
||||
"fedora_dnf_update" => Todo("TODO: dnf update needs Java played count and cost mutation."),
|
||||
"fedora_rawhide" => Todo("TODO: rawhide V3 needs permanent damage-growth doubling after growth effects exist."),
|
||||
"fedora_selinux" => Todo("TODO: SELinux enforcing needs enemy attack mitigation statuses."),
|
||||
_ => Todo("TODO: card effect is not implemented yet.")
|
||||
"ubuntu_pro_trial" => One(new NoOpCardEffect("ubuntu pro trial is unplayable")),
|
||||
"mint_update_manager" => One(new UpdateManagerEffect(5)),
|
||||
"mint_cinnamon" => new ICardEffect[]
|
||||
{
|
||||
new ShieldEffect(3),
|
||||
new DrawEffect(1)
|
||||
},
|
||||
"mint_nemo" => One(new ConditionalShieldRepeatDamageEffect(6, Language.JavaScript)),
|
||||
"mint_timeshift" => One(new TimeshiftSnapshotEffect()),
|
||||
"fedora_borrow_checker" => One(new FirstCardShieldEffect(8, 8)),
|
||||
"fedora_cargo_build" => One(new OverkillToShieldDamageEffect(10, Language.Rust)),
|
||||
"fedora_dnf_update" => One(new DealDamageEffect(9, 9, Language.Java)),
|
||||
"fedora_rawhide" => One(new RawhideEffect()),
|
||||
"fedora_selinux" => One(new IncomingAttackHalveEffect(2)),
|
||||
_ => Todo("card effect is not authored")
|
||||
};
|
||||
}
|
||||
|
||||
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 "shop_py_list_append" or "shop_js_math_random" or "shop_js_promise_all";
|
||||
or "shop_py_list_append" or "shop_js_math_random" or "shop_js_promise_all" or "ubuntu_snap_install"
|
||||
or "mint_nemo" or "fedora_cargo_build" or "fedora_dnf_update";
|
||||
}
|
||||
|
||||
public static bool TargetsAllEnemies(CardDefinition definition)
|
||||
@@ -423,18 +684,28 @@ namespace KernelPanic.Combat
|
||||
"lang_js_console_log" => $"deal {UpgradeMath.ScaleAmount(3, card)}-{UpgradeMath.ScaleAmount(9, card)}.{marker}",
|
||||
"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}",
|
||||
"shop_py_list_append" => $"queue: deal {UpgradeMath.ScaleAmount(4, card)}. TODO: grows when re-queued.{marker}",
|
||||
"shop_py_async_def" => $"queue: draw {UpgradeMath.ScaleAmount(2, card)}. TODO: gain 1 cycle next turn.{marker}",
|
||||
"shop_py_zip" => $"TODO: next 2 queued cards resolve twice.{marker}",
|
||||
"shop_py_list_append" => $"queue: deal {UpgradeMath.ScaleAmount(4 + UnityEngine.Mathf.Max(0, card.QueuePlayCount - 1), card)}; +1 each time re-queued.{marker}",
|
||||
"shop_py_async_def" => $"queue: draw {UpgradeMath.ScaleAmount(2, card)}. gain {UpgradeMath.ScaleAmount(1, card)} cycle next turn.{marker}",
|
||||
"shop_py_zip" => $"next {UpgradeMath.ScaleAmount(2, card)} queued cards resolve twice.{marker}",
|
||||
"shop_js_math_random" => $"deal {UpgradeMath.ScaleAmount(2, card)}-{UpgradeMath.ScaleAmount(20, card)}.{marker}",
|
||||
"shop_js_promise_all" => $"deal {UpgradeMath.ScaleAmount(4, card)}-{UpgradeMath.ScaleAmount(10, card)} three times.{marker}",
|
||||
"shop_js_spread" => $"TODO: copy the lowest-cost card in hand.{marker}",
|
||||
"shop_js_spread" => $"copy the lowest-cost card in hand.{marker}",
|
||||
"ubuntu_ask_ubuntu" => $"queue: draw {UpgradeMath.ScaleAmount(2, card)}.{marker}",
|
||||
"ubuntu_unattended_upgrades" => $"queue: gain {UpgradeMath.ScaleShield(4, card)} shield now and next 2 turns.{marker}",
|
||||
"ubuntu_snap_install" => $"deal {UpgradeMath.ScaleAmount(6, card)}-{UpgradeMath.ScaleAmount(14, card)}. shuffle an ubuntu pro trial into your draw pile.{marker}",
|
||||
"ubuntu_do_release_upgrade" => $"upgrade {UpgradeMath.ScaleAmount(1, card)}-{UpgradeMath.ScaleAmount(3, card)} random hand cards.{marker}",
|
||||
"ubuntu_apt_install" => $"queue: generate a random common Python card at 0c.{marker}",
|
||||
"mint_fix_broken" => $"deal {UpgradeMath.ScaleAmount(8, card)}. cleanse harmful statuses.{marker}",
|
||||
_ => string.IsNullOrWhiteSpace(card.Definition.Description) ? "TODO: effect not implemented." : $"{card.Definition.Description}{marker}"
|
||||
"mint_update_manager" => $"queue: deal {UpgradeMath.ScaleAmount(5, card)} to all enemies. repeats at the end of your next turn.{marker}",
|
||||
"mint_cinnamon" => $"queue: gain {UpgradeMath.ScaleShield(3, card)} shield. draw {UpgradeMath.ScaleAmount(1, card)}.{marker}",
|
||||
"mint_nemo" => $"deal {UpgradeMath.ScaleAmount(6, card)}. if you have shield, deal it again.{marker}",
|
||||
"mint_timeshift" => $"queue: record uptime now. at end of your next turn, restore to it if lower.{marker}",
|
||||
"fedora_borrow_checker" => $"gain {UpgradeMath.ScaleShield(8, card)} shield. if first card this turn, gain {UpgradeMath.ScaleShield(8, card)} more.{marker}",
|
||||
"fedora_cargo_build" => $"deal {UpgradeMath.ScaleAmount(10, card)}. overkill becomes shield.{marker}",
|
||||
"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}",
|
||||
_ => string.IsNullOrWhiteSpace(card.Definition.Description) ? "effect not authored." : $"{card.Definition.Description}{marker}"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace KernelPanic.Combat
|
||||
public int MagnitudeBonus { get; private set; }
|
||||
public int DrawRider { get; private set; }
|
||||
public int UpgradeLevel { get; private set; }
|
||||
public int QueuePlayCount { get; private set; }
|
||||
public bool WasFirstCardThisTurn { get; private set; }
|
||||
public bool IsBroken { get; set; }
|
||||
public bool IsLocked { get; set; }
|
||||
public bool CanUpgrade => UpgradeLevel == 0;
|
||||
@@ -67,6 +69,17 @@ namespace KernelPanic.Combat
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ApplyCombatMagnitudeBonus(int amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MagnitudeBonus += amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ApplyDrawRider(int amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
@@ -79,6 +92,35 @@ namespace KernelPanic.Combat
|
||||
return true;
|
||||
}
|
||||
|
||||
public void MarkQueued()
|
||||
{
|
||||
QueuePlayCount++;
|
||||
}
|
||||
|
||||
public void MarkPlayedThisTurn(bool wasFirst)
|
||||
{
|
||||
WasFirstCardThisTurn = wasFirst;
|
||||
}
|
||||
|
||||
public CardInstance CopyForCombat()
|
||||
{
|
||||
CardInstance copy = new(Definition)
|
||||
{
|
||||
TemporaryCostDelta = TemporaryCostDelta,
|
||||
IsBroken = IsBroken,
|
||||
IsLocked = IsLocked
|
||||
};
|
||||
|
||||
copy.PermanentCostDelta = PermanentCostDelta;
|
||||
copy.MagnitudeBonus = MagnitudeBonus;
|
||||
copy.DrawRider = DrawRider;
|
||||
copy.UpgradeLevel = UpgradeLevel;
|
||||
copy.QueuePlayCount = QueuePlayCount;
|
||||
copy.WasFirstCardThisTurn = WasFirstCardThisTurn;
|
||||
copy.SetTargetSnapshot(TargetSnapshot);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void SetTargetSnapshot(IEnumerable<CombatantState> targets)
|
||||
{
|
||||
_targetSnapshot.Clear();
|
||||
@@ -98,6 +140,8 @@ namespace KernelPanic.Combat
|
||||
public void ResetCombatState()
|
||||
{
|
||||
TemporaryCostDelta = 0;
|
||||
QueuePlayCount = 0;
|
||||
WasFirstCardThisTurn = false;
|
||||
IsBroken = false;
|
||||
IsLocked = false;
|
||||
ClearTargetSnapshot();
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace KernelPanic.Combat
|
||||
private readonly List<EnemyInstance> enemies = new();
|
||||
private readonly HashSet<string> loggedEffectTodos = new();
|
||||
private readonly List<CardDefinition> generatedCardPool = new();
|
||||
private readonly List<DelayedCombatEffect> delayedEffects = new();
|
||||
private RunManager runManager;
|
||||
private RunConfig runConfig;
|
||||
private HandController handController;
|
||||
@@ -37,6 +38,11 @@ namespace KernelPanic.Combat
|
||||
private bool ubuntuEmptyHandRefillUsed;
|
||||
private int fedoraCardsDiscountedThisTurn;
|
||||
private int fedoraCrashChance = 10;
|
||||
private int cardsPlayedThisTurn;
|
||||
private int javaCardsPlayedThisCombat;
|
||||
private int rawhideBonusCharges;
|
||||
private int queuedRepeatCharges;
|
||||
private int nextTurnCycleBonus;
|
||||
|
||||
public TurnPhase CurrentPhase => currentPhase;
|
||||
public RunConfig RunConfig => runConfig;
|
||||
@@ -145,8 +151,14 @@ namespace KernelPanic.Combat
|
||||
return false;
|
||||
}
|
||||
|
||||
if (card.Definition.IsToken)
|
||||
{
|
||||
Log($"{GetCardName(card)} is unplayable");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fedoraBonus = CanApplyFedoraBonus();
|
||||
int cost = GetCardCost(card);
|
||||
int cost = GetEffectiveCardCost(card);
|
||||
if (fedoraBonus)
|
||||
{
|
||||
cost = Mathf.Max(0, cost - 1);
|
||||
@@ -173,6 +185,13 @@ namespace KernelPanic.Combat
|
||||
|
||||
playerState.Cycles -= cost;
|
||||
ResolutionTrack track = card.Definition.ResolutionTrack;
|
||||
card.MarkPlayedThisTurn(cardsPlayedThisTurn == 0);
|
||||
cardsPlayedThisTurn++;
|
||||
if (card.Definition.Language == Language.Java)
|
||||
{
|
||||
javaCardsPlayedThisCombat++;
|
||||
}
|
||||
|
||||
GameEvents.RaiseCardPlayed(new CardPlayedEvent(card, track));
|
||||
LogPlayIntent(card);
|
||||
card.SetTargetSnapshot(CaptureTargets(card));
|
||||
@@ -180,7 +199,13 @@ namespace KernelPanic.Combat
|
||||
if (fedoraBonus)
|
||||
{
|
||||
fedoraCardsDiscountedThisTurn++;
|
||||
if (RandomRoll.RollRange(1, 100, new RollContext(playerState)) <= fedoraCrashChance)
|
||||
bool rawhideChargeUsed = rawhideBonusCharges > 0;
|
||||
if (rawhideChargeUsed)
|
||||
{
|
||||
rawhideBonusCharges--;
|
||||
}
|
||||
|
||||
if (!IsCrashImmune(card) && RandomRoll.RollRange(1, 100, new RollContext(playerState)) <= fedoraCrashChance)
|
||||
{
|
||||
fedoraCrashChance = 10;
|
||||
if (IsDistro("fedora") && runConfig.DistroVersion >= 4)
|
||||
@@ -198,11 +223,13 @@ namespace KernelPanic.Combat
|
||||
|
||||
fedoraCrashChance = Mathf.Min(90, fedoraCrashChance + 10);
|
||||
playerState.DamageMultiplierPercent = runConfig.DistroVersion >= 2 ? 175 : 150;
|
||||
ApplyFedoraGrowth(card, rawhideChargeUsed);
|
||||
}
|
||||
|
||||
switch (track)
|
||||
{
|
||||
case ResolutionTrack.InterpreterQueue:
|
||||
card.MarkQueued();
|
||||
interpreterQueue.Enqueue(card);
|
||||
break;
|
||||
case ResolutionTrack.LazyStack:
|
||||
@@ -235,6 +262,17 @@ namespace KernelPanic.Combat
|
||||
return Mathf.Max(0, card.Definition.CycleCost + card.PermanentCostDelta + card.TemporaryCostDelta);
|
||||
}
|
||||
|
||||
public int GetEffectiveCardCost(CardInstance card)
|
||||
{
|
||||
int cost = GetCardCost(card);
|
||||
if (card?.Definition != null && string.Equals(card.Definition.Id, "fedora_dnf_update", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
cost -= javaCardsPlayedThisCombat;
|
||||
}
|
||||
|
||||
return Mathf.Max(0, cost);
|
||||
}
|
||||
|
||||
public bool RequiresTarget(CardInstance card)
|
||||
{
|
||||
return card?.Definition != null && CardEffectFactory.RequiresSingleTarget(card.Definition) && LivingEnemyStates().Count > 0;
|
||||
@@ -258,6 +296,86 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
}
|
||||
|
||||
public void AddNextTurnCycleBonus(int amount)
|
||||
{
|
||||
nextTurnCycleBonus += Mathf.Max(0, amount);
|
||||
}
|
||||
|
||||
public void AddQueuedRepeatCharges(int amount)
|
||||
{
|
||||
queuedRepeatCharges += Mathf.Max(0, amount);
|
||||
if (amount > 0)
|
||||
{
|
||||
ReportEffectResult($"next {amount} queued card(s) resolve twice");
|
||||
}
|
||||
}
|
||||
|
||||
public int ConsumeQueuedResolutionCount(CardInstance card)
|
||||
{
|
||||
if (queuedRepeatCharges <= 0 || card == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
queuedRepeatCharges--;
|
||||
return 2;
|
||||
}
|
||||
|
||||
public void GrantRawhideBonus(int charges)
|
||||
{
|
||||
rawhideBonusCharges += Mathf.Max(0, charges);
|
||||
}
|
||||
|
||||
public void AddIncomingAttackHalfCharges(int charges)
|
||||
{
|
||||
if (playerState == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerState.IncomingAttackHalfCharges += Mathf.Max(0, charges);
|
||||
ReportEffectResult($"next {charges} enemy attack(s) halved");
|
||||
}
|
||||
|
||||
public void ScheduleUpdateManagerRepeat(int damageAmount)
|
||||
{
|
||||
delayedEffects.Add(DelayedCombatEffect.UpdateManagerRepeat(damageAmount, 2));
|
||||
ReportEffectResult("update manager scheduled repeat");
|
||||
}
|
||||
|
||||
public void ScheduleTimeshiftRestore(int uptime)
|
||||
{
|
||||
delayedEffects.Add(DelayedCombatEffect.TimeshiftRestore(uptime, 2));
|
||||
ReportEffectResult($"timeshift snapshot recorded {uptime} uptime");
|
||||
}
|
||||
|
||||
public bool TryCreateGeneratedCardById(string id, out CardInstance generatedCard)
|
||||
{
|
||||
generatedCard = null;
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IReadOnlyList<CardDefinition> source = generatedCardPool.Count > 0 ? generatedCardPool : runConfig?.StartingDeck;
|
||||
if (source == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
CardDefinition definition = source[i];
|
||||
if (definition != null && string.Equals(definition.Id, id, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
generatedCard = new CardInstance(definition);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetGeneratedCardPool(IEnumerable<CardDefinition> cardPool)
|
||||
{
|
||||
generatedCardPool.Clear();
|
||||
@@ -268,7 +386,7 @@ namespace KernelPanic.Combat
|
||||
|
||||
foreach (CardDefinition card in cardPool)
|
||||
{
|
||||
if (card != null && !card.IsToken && !card.IsRunOnly)
|
||||
if (card != null)
|
||||
{
|
||||
generatedCardPool.Add(card);
|
||||
}
|
||||
@@ -394,6 +512,12 @@ namespace KernelPanic.Combat
|
||||
ubuntuEmptyHandRefillUsed = false;
|
||||
fedoraCrashChance = 10;
|
||||
fedoraCardsDiscountedThisTurn = 0;
|
||||
cardsPlayedThisTurn = 0;
|
||||
javaCardsPlayedThisCombat = 0;
|
||||
rawhideBonusCharges = 0;
|
||||
queuedRepeatCharges = 0;
|
||||
nextTurnCycleBonus = 0;
|
||||
delayedEffects.Clear();
|
||||
RandomRoll.Seed(runConfig.RunSeed);
|
||||
playerState = new CombatantState(runManager.EffectiveMaxUptime(), runManager.EffectiveRam(), runManager.EffectiveMaxCycles());
|
||||
ApplyVersionState(playerState);
|
||||
@@ -418,6 +542,7 @@ namespace KernelPanic.Combat
|
||||
playerState.MaxCycles = runManager.EffectiveMaxCycles();
|
||||
playerState.Cycles = 0;
|
||||
playerState.Shield = 0;
|
||||
playerState.IncomingAttackHalfCharges = 0;
|
||||
playerState.IsDefeated = false;
|
||||
playerState.MutableStatuses.Clear();
|
||||
ApplyVersionState(playerState);
|
||||
@@ -456,6 +581,13 @@ namespace KernelPanic.Combat
|
||||
|
||||
playerState.Cycles = playerState.MaxCycles;
|
||||
fedoraCardsDiscountedThisTurn = 0;
|
||||
cardsPlayedThisTurn = 0;
|
||||
if (nextTurnCycleBonus > 0)
|
||||
{
|
||||
playerState.Cycles += nextTurnCycleBonus;
|
||||
Log($"deferred cycle grant: +{nextTurnCycleBonus}");
|
||||
nextTurnCycleBonus = 0;
|
||||
}
|
||||
statusEffects.Tick(playerState, StatusTickTiming.StartOfTurn, playerState, damagePipeline);
|
||||
if (CheckLoss())
|
||||
{
|
||||
@@ -481,7 +613,16 @@ namespace KernelPanic.Combat
|
||||
while (interpreterQueue.TryDequeue(out CardInstance card))
|
||||
{
|
||||
LogPlayIntent(card);
|
||||
int resolutionCount = ConsumeQueuedResolutionCount(card);
|
||||
for (int repeatIndex = 0; repeatIndex < resolutionCount; repeatIndex++)
|
||||
{
|
||||
ExecuteCardEffects(card, ResolveQueuedTargets(card));
|
||||
if (CheckWinOrLoss())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ResolveCardRiders(card);
|
||||
deckController.Discard(card);
|
||||
GameEvents.RaiseCardResolved(new CardResolvedEvent(card, ResolutionTrack.InterpreterQueue));
|
||||
@@ -541,6 +682,12 @@ namespace KernelPanic.Combat
|
||||
return;
|
||||
}
|
||||
|
||||
ResolveDelayedEndOfTurnEffects();
|
||||
if (CheckWinOrLoss())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveDefeatedEnemies();
|
||||
CheckWinOrLoss();
|
||||
StateChanged?.Invoke();
|
||||
@@ -638,6 +785,11 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
|
||||
private bool CanApplyFedoraBonus()
|
||||
{
|
||||
return CanApplyFedoraPassiveBonus() || rawhideBonusCharges > 0;
|
||||
}
|
||||
|
||||
private bool CanApplyFedoraPassiveBonus()
|
||||
{
|
||||
if (!IsDistro("fedora") || playerState == null)
|
||||
{
|
||||
@@ -648,6 +800,27 @@ namespace KernelPanic.Combat
|
||||
return fedoraCardsDiscountedThisTurn < limit;
|
||||
}
|
||||
|
||||
private void ApplyFedoraGrowth(CardInstance card, bool rawhideChargeUsed)
|
||||
{
|
||||
if (!IsDistro("fedora") || runConfig.DistroVersion < 3 || card == null || !card.WasFirstCardThisTurn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int growth = rawhideChargeUsed ? 2 : 1;
|
||||
if (card.ApplyCombatMagnitudeBonus(growth))
|
||||
{
|
||||
Log($"rawhide growth: {GetCardName(card)} +{growth} effect");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsCrashImmune(CardInstance card)
|
||||
{
|
||||
string id = card?.Definition?.Id;
|
||||
return string.Equals(id, "fedora_cargo_build", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(id, "fedora_selinux", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private void ApplyVersionState(CombatantState state)
|
||||
{
|
||||
if (state == null)
|
||||
@@ -856,6 +1029,7 @@ namespace KernelPanic.Combat
|
||||
switch (intent.Kind)
|
||||
{
|
||||
case EnemyIntentKind.Attack:
|
||||
value = ApplyIncomingAttackMitigation(value);
|
||||
DamageResult attackResult = damagePipeline.DealDamage(new DamageRequest(enemy.State, playerState, value, intent.DamageType, intent.TrueDamage, false));
|
||||
GameEvents.RaisePlayerDamaged(new PlayerDamagedEvent(enemy, attackResult.FinalAmount));
|
||||
break;
|
||||
@@ -879,6 +1053,79 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
}
|
||||
|
||||
private int ApplyIncomingAttackMitigation(int value)
|
||||
{
|
||||
if (playerState == null || playerState.IncomingAttackHalfCharges <= 0)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
playerState.IncomingAttackHalfCharges--;
|
||||
int mitigated = Mathf.CeilToInt(Mathf.Max(0, value) * 0.5f);
|
||||
Log($"SELinux enforcing halved attack {value}->{mitigated}");
|
||||
return mitigated;
|
||||
}
|
||||
|
||||
private void ResolveDelayedEndOfTurnEffects()
|
||||
{
|
||||
for (int i = delayedEffects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
DelayedCombatEffect effect = delayedEffects[i];
|
||||
effect.GarbageCollectionsRemaining--;
|
||||
if (effect.GarbageCollectionsRemaining > 0)
|
||||
{
|
||||
delayedEffects[i] = effect;
|
||||
continue;
|
||||
}
|
||||
|
||||
delayedEffects.RemoveAt(i);
|
||||
switch (effect.Kind)
|
||||
{
|
||||
case DelayedCombatEffectKind.UpdateManagerRepeat:
|
||||
DealToAllEnemies(effect.Amount, Language.Python, "update manager repeat");
|
||||
break;
|
||||
case DelayedCombatEffectKind.TimeshiftRestore:
|
||||
RestoreTimeshift(effect.Amount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DealToAllEnemies(int amount, Language language, string label)
|
||||
{
|
||||
List<CombatantState> targets = LivingEnemyStates();
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
damagePipeline.DealDamage(new DamageRequest(playerState, targets[i], amount, language, false, false));
|
||||
}
|
||||
|
||||
Log($"{label}: dealt {amount} to all enemies");
|
||||
RemoveDefeatedEnemies();
|
||||
}
|
||||
|
||||
private void RestoreTimeshift(int snapshot)
|
||||
{
|
||||
if (playerState == null || playerState.IsDefeated || playerState.CurrentUptime >= snapshot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int target = snapshot;
|
||||
if (IsDistro("mint") && runConfig.DistroVersion >= 3)
|
||||
{
|
||||
target = Mathf.Min(playerState.MaxUptime, snapshot + 2);
|
||||
}
|
||||
|
||||
if (IsDistro("mint") && runConfig.DistroVersion >= 5)
|
||||
{
|
||||
target = playerState.MaxUptime;
|
||||
}
|
||||
|
||||
int restored = target - playerState.CurrentUptime;
|
||||
playerState.CurrentUptime = target;
|
||||
Log($"timeshift restored {restored} uptime");
|
||||
}
|
||||
|
||||
private bool CheckWinOrLoss()
|
||||
{
|
||||
return CheckLoss() || CheckWin();
|
||||
@@ -944,5 +1191,35 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
|
||||
private bool IsCombatPaused => awaitingWaveContinue || runLost;
|
||||
|
||||
private enum DelayedCombatEffectKind
|
||||
{
|
||||
UpdateManagerRepeat,
|
||||
TimeshiftRestore
|
||||
}
|
||||
|
||||
private struct DelayedCombatEffect
|
||||
{
|
||||
private DelayedCombatEffect(DelayedCombatEffectKind kind, int amount, int garbageCollectionsRemaining)
|
||||
{
|
||||
Kind = kind;
|
||||
Amount = amount;
|
||||
GarbageCollectionsRemaining = garbageCollectionsRemaining;
|
||||
}
|
||||
|
||||
public DelayedCombatEffectKind Kind { get; }
|
||||
public int Amount { get; }
|
||||
public int GarbageCollectionsRemaining { get; set; }
|
||||
|
||||
public static DelayedCombatEffect UpdateManagerRepeat(int amount, int garbageCollectionsRemaining)
|
||||
{
|
||||
return new DelayedCombatEffect(DelayedCombatEffectKind.UpdateManagerRepeat, amount, garbageCollectionsRemaining);
|
||||
}
|
||||
|
||||
public static DelayedCombatEffect TimeshiftRestore(int uptime, int garbageCollectionsRemaining)
|
||||
{
|
||||
return new DelayedCombatEffect(DelayedCombatEffectKind.TimeshiftRestore, uptime, garbageCollectionsRemaining);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace KernelPanic.Combat
|
||||
public bool AllowFlatDamageBuffs { get; set; }
|
||||
public int FlatEffectBonus { get; set; }
|
||||
public int DamageMultiplierPercent { get; set; } = 100;
|
||||
public int IncomingAttackHalfCharges { get; set; }
|
||||
public IReadOnlyList<StatusInstance> Statuses => _statuses;
|
||||
public IReadOnlyDictionary<StatusType, int> ActiveStatuses => BuildStatusSnapshot();
|
||||
|
||||
|
||||
@@ -100,6 +100,20 @@ namespace KernelPanic.Combat
|
||||
}
|
||||
}
|
||||
|
||||
public void AddToDrawPile(CardInstance card, bool shuffle)
|
||||
{
|
||||
if (card == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_drawPile.Add(card);
|
||||
if (shuffle)
|
||||
{
|
||||
ShuffleDrawPile();
|
||||
}
|
||||
}
|
||||
|
||||
public void Exhaust(CardInstance card)
|
||||
{
|
||||
if (card != null)
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace KernelPanic.Meta
|
||||
{
|
||||
1 => "first card each turn costs 1 less and deals +50%, with crash risk",
|
||||
2 => "first-card damage bonus rises to +75%",
|
||||
3 => "rawhide TODO: double permanent damage growth on non-crash first-card turn",
|
||||
3 => "non-crashing first-card plays gain +1 effect this combat; rawhide doubles that growth",
|
||||
4 => "crashes grant +1 Cycle that turn",
|
||||
5 => "first two cards each turn get the discount, bonus, and independent crash rolls",
|
||||
_ => "--"
|
||||
@@ -50,12 +50,12 @@ namespace KernelPanic.Meta
|
||||
{
|
||||
1 => "random values roll max; no crits; ignores multiplicative damage buffs",
|
||||
2 => "fixed effect values are treated as +2",
|
||||
3 => "timeshift snapshot TODO: stronger restore when delayed restore exists",
|
||||
3 => "timeshift snapshot restores 2 uptime beyond the snapshot, up to max uptime",
|
||||
4 => "flat +damage can apply; multiplicative crit/mult remains ignored",
|
||||
5 => "TODO: re-resolve reduced effect when enemy reduction/coercion exists",
|
||||
5 => "timeshift snapshot restores to full uptime if lower",
|
||||
_ => "--"
|
||||
},
|
||||
_ => safeVersion == 1 ? "base release" : $"TODO: V{safeVersion} release effect"
|
||||
_ => safeVersion == 1 ? "base release" : $"V{safeVersion} release effect"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,12 @@ namespace KernelPanic.UI
|
||||
private IReadOnlyList<CardDefinition> BuildGeneratedCardPool()
|
||||
{
|
||||
List<CardDefinition> cards = new();
|
||||
if (cardDatabase != null)
|
||||
{
|
||||
cards.AddRange(cardDatabase.AllCards);
|
||||
return cards;
|
||||
}
|
||||
|
||||
if (languageDeckDatabase == null)
|
||||
{
|
||||
return cards;
|
||||
@@ -795,7 +801,7 @@ namespace KernelPanic.UI
|
||||
button.AddToClassList("hand-card-selected");
|
||||
}
|
||||
|
||||
if (CombatManager.GetCardCost(card) > combatManager.PlayerState.Cycles)
|
||||
if (GetDisplayCardCost(card) > combatManager.PlayerState.Cycles)
|
||||
{
|
||||
button.AddToClassList("hand-card-unaffordable");
|
||||
}
|
||||
@@ -804,7 +810,7 @@ namespace KernelPanic.UI
|
||||
return button;
|
||||
}
|
||||
|
||||
private static VisualElement CardFaceView(CardInstance card)
|
||||
private VisualElement CardFaceView(CardInstance card)
|
||||
{
|
||||
VisualElement face = new();
|
||||
face.AddToClassList("hand-card");
|
||||
@@ -812,7 +818,7 @@ namespace KernelPanic.UI
|
||||
return face;
|
||||
}
|
||||
|
||||
private static void PopulateCardFace(VisualElement target, CardInstance card)
|
||||
private void PopulateCardFace(VisualElement target, CardInstance card)
|
||||
{
|
||||
string cardRarityClass = CardRarityClass(card.Definition.Rarity);
|
||||
if (!string.IsNullOrEmpty(cardRarityClass))
|
||||
@@ -822,7 +828,7 @@ namespace KernelPanic.UI
|
||||
|
||||
VisualElement top = new();
|
||||
top.AddToClassList("card-top-row");
|
||||
Label cost = new(CombatManager.GetCardCost(card).ToString());
|
||||
Label cost = new(GetDisplayCardCost(card).ToString());
|
||||
cost.AddToClassList("card-cost");
|
||||
Label name = new($"{DisplayName(card.Definition)}{(card.IsUpgraded ? " +" : string.Empty)}");
|
||||
name.AddToClassList("card-name");
|
||||
@@ -1337,7 +1343,7 @@ namespace KernelPanic.UI
|
||||
chip.AddToClassList(LanguageClass(card.Definition.Language));
|
||||
Label name = new(DisplayName(card.Definition));
|
||||
name.AddToClassList("card-chip-name");
|
||||
Label meta = new($"{CombatManager.GetCardCost(card)}c / {TrackText(card.Definition.ResolutionTrack)}");
|
||||
Label meta = new($"{GetDisplayCardCost(card)}c / {TrackText(card.Definition.ResolutionTrack)}");
|
||||
meta.AddToClassList("card-chip-meta");
|
||||
chip.Add(name);
|
||||
chip.Add(meta);
|
||||
@@ -1351,6 +1357,11 @@ namespace KernelPanic.UI
|
||||
return label;
|
||||
}
|
||||
|
||||
private int GetDisplayCardCost(CardInstance card)
|
||||
{
|
||||
return combatManager == null ? CombatManager.GetCardCost(card) : combatManager.GetEffectiveCardCost(card);
|
||||
}
|
||||
|
||||
private static VisualElement CreatePanel(string title, string subtitle, float width)
|
||||
{
|
||||
VisualElement panel = new();
|
||||
|
||||
Reference in New Issue
Block a user