diff --git a/src/DamagePerTurnSim.cs b/src/DamagePerTurnSim.cs --- a/src/DamagePerTurnSim.cs +++ b/src/DamagePerTurnSim.cs @@ -60,16 +60,21 @@ var handSnapshot = hand.Cards.Select(c => CardLabel(c)).ToList(); var hpBefore = harness.Dummy.CurrentHp; - int energyLeft = Energy; + var pcs = harness.Player.PlayerCombatState!; + // Reset PCS.Energy = Energy at turn start. PCS.Energy is the source + // of truth — cards like Offering / Bloodletting modify it via + // PlayerCmd.GainEnergy/LoseEnergy, and we read it back to give + // the policy an accurate budget. + SetEnergy(pcs, Energy); var played = new List(); - while (energyLeft > 0) + while (pcs.Energy > 0) { - var card = Policy.ChooseCard(harness, energyLeft, policyRng); + var card = Policy.ChooseCard(harness, pcs.Energy, policyRng); if (card == null) break; var cost = card.EnergyCost.GetResolved(); - if (cost > energyLeft) break; // policy lied; bail + if (cost > pcs.Energy) break; // policy lied; bail var resources = new ResourceInfo { @@ -81,8 +86,12 @@ var target = card.TargetType == MegaCrit.Sts2.Core.Entities.Cards.TargetType.Self ? harness.Player.Creature : harness.Dummy; + // OnPlayWrapper does NOT auto-debit PCS.Energy — that's done + // by SpendResources upstream of the play action. So we deduct + // the cost ourselves; cards that gain energy mid-OnPlay + // (Offering, Bloodletting) will be visible in the next iter. + pcs.LoseEnergy(cost); await card.OnPlayWrapper(harness.Ctx, target, isAutoPlay: true, resources, skipCardPileVisuals: true); - energyLeft -= cost; played.Add(CardLabel(card)); } @@ -132,6 +141,13 @@ draw.RemoveInternal(top); hand.AddInternal(top); } + } + + private static void SetEnergy(MegaCrit.Sts2.Core.Entities.Players.PlayerCombatState pcs, int amount) + { + var prop = typeof(MegaCrit.Sts2.Core.Entities.Players.PlayerCombatState) + .GetProperty("Energy", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + prop!.SetValue(pcs, amount); } /// "CARD.STRIKE_IRONCLAD" + upgrade level → "Strike Ironclad+" diff --git a/src/SmokeTests.cs b/src/SmokeTests.cs --- a/src/SmokeTests.cs +++ b/src/SmokeTests.cs @@ -38,6 +38,7 @@ Test_Inflame_BoostsLaterStrike, Test_BashPlus_Deals10, Test_StrikePlus_Deals9, + Test_Offering_GivesEnergyAndDraw, }; var results = new List(); @@ -122,6 +123,40 @@ return $"expected Vulnerable on dummy, got [{string.Join(",", h.Dummy.Powers.Select(p => p.Id.Entry))}]"; return null; }); + + private static async Task Test_Offering_GivesEnergyAndDraw() + { + // Offering: lose 6 HP, gain 2 energy, draw 3, exhaust. + var h = Harness.BeginCombat(deckOverride: new List + { + typeof(Offering), + typeof(StrikeIronclad), typeof(StrikeIronclad), typeof(StrikeIronclad), typeof(StrikeIronclad), + }); + try + { + var pcs = h.Player.PlayerCombatState!; + SetEnergy(pcs, 0); // start with zero so we can verify Offering gave +2 + var offering = pcs.DrawPile.Cards.OfType().First(); + pcs.DrawPile.RemoveInternal(offering); + pcs.Hand.AddInternal(offering); + + var hpBefore = h.Player.Creature.CurrentHp; + var handBefore = pcs.Hand.Cards.Count; + + await offering.OnPlayWrapper(h.Ctx, h.Player.Creature, isAutoPlay: true, + new ResourceInfo { EnergySpent = 0, EnergyValue = 0, StarsSpent = 0, StarValue = 0 }, + skipCardPileVisuals: true); + + // Check: HP -6, energy = 2 (was 0 + gain 2), 3 cards drawn (-1 played offering) + var hpDelta = hpBefore - h.Player.Creature.CurrentHp; + if (hpDelta != 6) return new TestResult("Offering gives +2 energy and draws 3", false, $"HP delta expected 6, got {hpDelta}"); + if (pcs.Energy != 2) return new TestResult("Offering gives +2 energy and draws 3", false, $"Energy expected 2, got {pcs.Energy}"); + // hand was {Offering}; played -> Play pile or Exhaust; +3 from Draw → total 3 + if (pcs.Hand.Cards.Count != 3) return new TestResult("Offering gives +2 energy and draws 3", false, $"Hand expected 3, got {pcs.Hand.Cards.Count}"); + return new TestResult("Offering gives +2 energy and draws 3", true, null); + } + finally { Harness.EndCombat(); } + } private static async Task Test_BashPlus_Deals10() {