make small changes to design. Implement crits.

This commit is contained in:
2026-07-08 19:32:12 +02:00
parent 9617f8aaf0
commit 61030d84b7
7 changed files with 521 additions and 137 deletions
+29 -4
View File
@@ -14,7 +14,7 @@ namespace KernelPanic.Combat
private readonly bool _canCrit;
private readonly bool _allEnemies;
public DealDamageEffect(int minAmount, int maxAmount, Language language, bool trueDamage = false, bool canCrit = false, bool allEnemies = false)
public DealDamageEffect(int minAmount, int maxAmount, Language language, bool trueDamage = false, bool canCrit = true, bool allEnemies = false)
{
_minAmount = minAmount;
_maxAmount = maxAmount;
@@ -78,6 +78,12 @@ namespace KernelPanic.Combat
public void Execute(CombatContext context)
{
int amount = UpgradeMath.ScaleShield(UpgradeMath.ApplySourceFlatBonus(_amount, context), context.Card);
if (!context.CombatManager.CanPlayerReceiveCardShield(context.Card))
{
context.CombatManager.ReportEffectResult($"shield blocked: {amount}");
return;
}
context.Source.Shield += amount;
context.CombatManager.ReportEffectResult($"gained {amount} shield");
}
@@ -98,6 +104,12 @@ namespace KernelPanic.Combat
{
int amount = _baseAmount + (context.Card != null && context.Card.WasFirstCardThisTurn ? _firstCardBonus : 0);
amount = UpgradeMath.ScaleShield(UpgradeMath.ApplySourceFlatBonus(amount, context), context.Card);
if (!context.CombatManager.CanPlayerReceiveCardShield(context.Card))
{
context.CombatManager.ReportEffectResult($"shield blocked: {amount}");
return;
}
context.Source.Shield += amount;
context.CombatManager.ReportEffectResult($"gained {amount} shield");
}
@@ -129,7 +141,7 @@ namespace KernelPanic.Combat
int roll = RandomRoll.RollRange(1, 100, new RollContext(context.Source));
int damageAmount = UpgradeMath.ScaleAmount(UpgradeMath.ApplySourceFlatBonus(_damageAmount, context), context.Card);
int amount = roll <= _successPercent ? damageAmount : 0;
context.DamagePipeline.DealDamage(new DamageRequest(context.Source, target, amount, _language, false, false));
context.DamagePipeline.DealDamage(new DamageRequest(context.Source, target, amount, _language, false, true));
context.CombatManager.ReportEffectResult(amount == 0 ? "typeof -> NaN (0)" : $"typeof -> {amount}");
}
}
@@ -176,6 +188,13 @@ namespace KernelPanic.Combat
{
if (_targetSelf)
{
if (StatusEffectController.GetDescriptor(_statusType).IsBeneficial
&& !context.CombatManager.CanPlayerReceiveCardBuff(context.Card))
{
context.CombatManager.ReportEffectResult($"{_statusType} blocked");
return;
}
context.StatusEffects.Apply(context.Source, _statusType, UpgradeMath.ScaleAmount(UpgradeMath.ApplySourceFlatBonus(_stacks, context), context.Card), _duration, context.Source, _skipNextTick);
return;
}
@@ -473,10 +492,16 @@ namespace KernelPanic.Combat
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);
DamageResult result = context.DamagePipeline.DealDamage(new DamageRequest(context.Source, target, amount, _language, false, true));
int overkill = UnityEngine.Mathf.Max(0, result.IncomingAmount - survivable);
if (overkill > 0)
{
if (!context.CombatManager.CanPlayerReceiveCardShield(context.Card))
{
context.CombatManager.ReportEffectResult($"overkill shield blocked: {overkill}");
continue;
}
context.Source.Shield += overkill;
context.CombatManager.ReportEffectResult($"overkill -> {overkill} shield");
}
+43 -1
View File
@@ -895,6 +895,48 @@ namespace KernelPanic.Combat
return string.Equals(runConfig?.Distro?.Id, id, StringComparison.OrdinalIgnoreCase);
}
public bool CanPlayerReceiveCardShield(CardInstance card)
{
return CanPlayerReceiveCardBuff(card);
}
public bool CanPlayerReceiveCardHeal(CardInstance card)
{
return CanPlayerReceiveCardBuff(card);
}
public bool CanPlayerReceiveCardBuff(CardInstance card)
{
return !IsDistro("mint") || IsCurrentDistroExclusiveCard(card);
}
private bool IsCurrentDistroExclusiveCard(CardInstance card)
{
CardDefinition definition = card?.Definition;
IReadOnlyList<CardDefinition> exclusiveCards = runConfig?.Distro?.ExclusiveCards;
if (definition == null || exclusiveCards == null)
{
return false;
}
for (int i = 0; i < exclusiveCards.Count; i++)
{
CardDefinition exclusive = exclusiveCards[i];
if (exclusive == null)
{
continue;
}
if (ReferenceEquals(exclusive, definition)
|| string.Equals(exclusive.Id, definition.Id, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private CombatContext BuildContext(CardInstance card, IReadOnlyList<CombatantState> targets)
{
return new CombatContext(card, playerState, targets, this, damagePipeline, statusEffects, deckController, handController, enemies);
@@ -1151,7 +1193,7 @@ namespace KernelPanic.Combat
List<CombatantState> targets = LivingEnemyStates();
for (int i = 0; i < targets.Count; i++)
{
damagePipeline.DealDamage(new DamageRequest(playerState, targets[i], amount, language, false, false));
damagePipeline.DealDamage(new DamageRequest(playerState, targets[i], amount, language, false, true));
}
Log($"{label}: dealt {amount} to all enemies");
+24 -12
View File
@@ -31,14 +31,24 @@ namespace KernelPanic.Combat
public readonly struct DamageResult
{
public DamageResult(int finalAmount, bool targetDefeated)
public DamageResult(int finalAmount, bool targetDefeated, bool wasCritical = false, int absorbedAmount = 0, int shieldDamage = 0, int uptimeDamage = 0, int incomingAmount = 0)
{
FinalAmount = finalAmount;
TargetDefeated = targetDefeated;
WasCritical = wasCritical;
AbsorbedAmount = absorbedAmount;
ShieldDamage = shieldDamage;
UptimeDamage = uptimeDamage;
IncomingAmount = incomingAmount;
}
public int FinalAmount { get; }
public bool TargetDefeated { get; }
public bool WasCritical { get; }
public int AbsorbedAmount { get; }
public int ShieldDamage { get; }
public int UptimeDamage { get; }
public int IncomingAmount { get; }
}
/// <summary>
@@ -55,11 +65,11 @@ namespace KernelPanic.Combat
int amount = Mathf.Max(0, request.Amount);
amount = ApplyMultipliers(amount, request);
amount = ApplyCrit(amount, request);
amount = ApplyCrit(amount, request, out bool wasCritical);
amount = ApplyResistancesAndWeaknesses(amount, request);
int finalAmount = ApplyShieldAndUptime(amount, request);
GameEvents.RaiseDamageDealt(new DamageDealtEvent(request.Source, request.Target, finalAmount, request.Language));
int finalAmount = ApplyShieldAndUptime(amount, request, out int absorbedAmount, out int shieldDamage, out int uptimeDamage);
GameEvents.RaiseDamageDealt(new DamageDealtEvent(request.Source, request.Target, finalAmount, request.Language, amount, absorbedAmount, wasCritical, shieldDamage, uptimeDamage));
bool defeated = false;
if (request.Target.CurrentUptime <= 0 && !request.Target.IsDefeated)
@@ -69,7 +79,7 @@ namespace KernelPanic.Combat
GameEvents.RaiseCombatantDefeated(new CombatantDefeatedEvent(request.Target));
}
return new DamageResult(finalAmount, defeated);
return new DamageResult(finalAmount, defeated, wasCritical, absorbedAmount, shieldDamage, uptimeDamage, amount);
}
private static int ApplyMultipliers(int amount, DamageRequest request)
@@ -85,15 +95,16 @@ namespace KernelPanic.Combat
return Mathf.RoundToInt(amount * (multiplier / 100f));
}
private static int ApplyCrit(int amount, DamageRequest request)
private static int ApplyCrit(int amount, DamageRequest request, out bool wasCritical)
{
if (request.Source != null && request.Source.forceMaxRolls)
wasCritical = false;
if (!request.CanCrit || amount <= 0 || request.Source != null && request.Source.forceMaxRolls)
{
return amount;
}
// TODO: Add crit calculation and crit modifiers.
return amount;
wasCritical = RandomRoll.RollRange(1, 100, new RollContext(request.Source)) <= 25;
return wasCritical ? Mathf.CeilToInt(amount * 1.5f) : amount;
}
private static int ApplyResistancesAndWeaknesses(int amount, DamageRequest request)
@@ -104,11 +115,11 @@ namespace KernelPanic.Combat
return amount;
}
private static int ApplyShieldAndUptime(int amount, DamageRequest request)
private static int ApplyShieldAndUptime(int amount, DamageRequest request, out int absorbedAmount, out int shieldDamage, out int uptimeDamage)
{
CombatantState target = request.Target;
int remaining = amount;
int shieldDamage = 0;
shieldDamage = 0;
if (!request.TrueDamage && target.Shield > 0)
{
@@ -117,8 +128,9 @@ namespace KernelPanic.Combat
remaining -= shieldDamage;
}
int uptimeDamage = Mathf.Min(target.CurrentUptime, remaining);
uptimeDamage = Mathf.Min(target.CurrentUptime, remaining);
target.CurrentUptime = Mathf.Max(0, target.CurrentUptime - uptimeDamage);
absorbedAmount = Mathf.Max(0, amount - shieldDamage - uptimeDamage);
return shieldDamage + uptimeDamage;
}
}