Skip to content

Commit 08c8d36

Browse files
committed
feat(compatibility): update API version handling and introduce new lifecycle events
- Enhanced compatibility definitions to support API versions 0.103.2, 0.104.0, and 0.105.0. - Updated GetCompatBranchLabel method to return appropriate labels based on the API version. - Introduced new lifecycle events: BeforeFlushEvent and CardsFlushedEvent, replacing the legacy CardRetainedEvent for API 0.105.0 and newer. - Refactored various classes to utilize the new IsInfiniteHpDisplayed method for checking infinite HP display status. - Improved documentation to reflect changes in lifecycle events and compatibility handling.
1 parent 4f31a47 commit 08c8d36

23 files changed

Lines changed: 281 additions & 55 deletions

Audio/Sts2SfxAlignedFmod.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,21 @@ public static void PlayOneShot(string eventPath, IReadOnlyDictionary<string, flo
4040
}
4141

4242
/// <summary>
43-
/// Starts a guarded loop via <see cref="SfxCmd.PlayLoop" />.
43+
/// Starts a guarded loop via <see>
44+
/// <cref>SfxCmd.PlayLoop</cref>
45+
/// </see>
46+
/// .
4447
/// </summary>
4548
public static void PlayLoop(string eventPath, bool usesLoopParam = true)
4649
{
4750
SfxCmd.PlayLoop(eventPath, usesLoopParam);
4851
}
4952

5053
/// <summary>
51-
/// Stops a loop via <see cref="SfxCmd.StopLoop" />.
54+
/// Stops a loop via <see>
55+
/// <cref>SfxCmd.StopLoop</cref>
56+
/// </see>
57+
/// .
5258
/// </summary>
5359
public static void StopLoop(string eventPath)
5460
{

Cards/FreePlay/FreePlayBindingRegistry.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#if STS2_V_0_103_2
1+
#if !STS2_AT_LEAST_0_104_0
22
using CombatStateLike = MegaCrit.Sts2.Core.Combat.CombatState;
33
#else
44
using CombatStateLike = MegaCrit.Sts2.Core.Combat.ICombatState;

Combat/CardTargeting/CardPlayUiFocus.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#if STS2_V_0_103_2
1+
#if !STS2_AT_LEAST_0_104_0
22
using MegaCrit.Sts2.Core.Nodes.GodotExtensions;
33
using MegaCrit.Sts2.Core.Nodes.Rooms;
44
#else
@@ -11,10 +11,9 @@ internal static class CardPlayUiFocus
1111
{
1212
internal static void AfterCardPlayFinished()
1313
{
14-
#if STS2_V_0_103_2
14+
#if !STS2_AT_LEAST_0_104_0
1515
NCombatRoom.Instance?.Ui.Hand.DefaultFocusedControl.TryGrabFocus();
1616
#else
17-
// 0.104.0+: NCardPlay now restores focus via screen context instead of directly focusing the hand.
1817
ActiveScreenContext.Instance.FocusOnDefaultControl();
1918
#endif
2019
}

Combat/HandSize/MaxHandSizePatchInstaller.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ internal static class MaxHandSizePatchInstaller
3838
?? throw new MissingMethodException(typeof(MaxHandSizeCalculator).FullName,
3939
nameof(MaxHandSizeCalculator.CalculateFromCardOwner));
4040

41-
#if !STS2_V_0_103_2
41+
#if STS2_AT_LEAST_0_104_0
4242
private static readonly MethodInfo MaxCardsInHandGetter =
4343
AccessTools.PropertyGetter(typeof(CardPile), nameof(CardPile.MaxCardsInHand))
4444
?? throw new MissingMethodException(typeof(CardPile).FullName, nameof(CardPile.MaxCardsInHand));
@@ -117,11 +117,14 @@ internal static void EnsurePatched()
117117
}
118118

119119
_patched = true;
120-
#if STS2_V_0_103_2
120+
#if !STS2_AT_LEAST_0_104_0
121121
RitsuLibFramework.Logger.Info("[MaxHandSize] RitsuLib hand-size patch set installed (compat 0.103.2 profile).");
122+
#elif !STS2_AT_LEAST_0_105_0
123+
RitsuLibFramework.Logger.Info(
124+
"[MaxHandSize] RitsuLib hand-size patch set installed (compat 0.104.0 profile).");
122125
#else
123126
RitsuLibFramework.Logger.Info(
124-
"[MaxHandSize] RitsuLib hand-size patch set installed (0.104.0 profile).");
127+
"[MaxHandSize] RitsuLib hand-size patch set installed (0.105.0 profile).");
125128
#endif
126129
}
127130
}
@@ -192,14 +195,10 @@ private static void TryAddAsyncMoveNextPatch(
192195

193196
private static bool IsMaxHandSizeToken(CodeInstruction ins)
194197
{
195-
#if STS2_V_0_103_2
196-
// 0.103.2 compatibility signatures keep most hand-limit checks as hard-coded constants.
197-
// Match literal 10 only.
198+
#if !STS2_AT_LEAST_0_104_0
198199
return (ins.opcode == OpCodes.Ldc_I4_S && ins.operand is sbyte and DefaultMaxHandSize)
199200
|| (ins.opcode == OpCodes.Ldc_I4 && ins.operand is DefaultMaxHandSize);
200201
#else
201-
// 0.104.0 switched many call sites to CardPile.MaxCardsInHand.
202-
// Match both legacy literal 10 and property getter invocations.
203202
var isDefaultConst =
204203
(ins.opcode == OpCodes.Ldc_I4_S && ins.operand is sbyte and DefaultMaxHandSize)
205204
|| (ins.opcode == OpCodes.Ldc_I4 && ins.operand is DefaultMaxHandSize);

Combat/HealthBars/IHealthBarForecastSource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#if STS2_V_0_103_2
1+
#if !STS2_AT_LEAST_0_104_0
22
using CombatStateLike = MegaCrit.Sts2.Core.Combat.CombatState;
33
#else
44
using CombatStateLike = MegaCrit.Sts2.Core.Combat.ICombatState;

Combat/HealthBars/Patches/NHealthBarForecastPatches.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static void RefreshForegroundOverlay(NHealthBar healthBar)
3232
return;
3333

3434
var creature = healthBar._creature;
35-
if (creature.CurrentHp <= 0 || creature.ShowsInfiniteHp)
35+
if (creature.CurrentHp <= 0 || creature.IsInfiniteHpDisplayed())
3636
{
3737
HideAllCustomSegments(healthBar);
3838
return;
@@ -193,7 +193,7 @@ public static void RefreshMiddlegroundOverlay(NHealthBar healthBar)
193193
return;
194194

195195
var creature = healthBar._creature;
196-
if (creature.CurrentHp <= 0 || creature.ShowsInfiniteHp)
196+
if (creature.CurrentHp <= 0 || creature.IsInfiniteHpDisplayed())
197197
return;
198198

199199
var hpMiddleground = healthBar._hpMiddleground;
@@ -220,7 +220,7 @@ public static void RefreshTextOverlay(NHealthBar healthBar)
220220
return;
221221

222222
var creature = healthBar._creature;
223-
if (creature.CurrentHp <= 0 || creature.ShowsInfiniteHp)
223+
if (creature.CurrentHp <= 0 || creature.IsInfiniteHpDisplayed())
224224
return;
225225

226226
var lethalColor = state.LastRender.LethalRightColor ?? state.LastRender.LethalLeftColor;

Combat/HealthBars/Patches/NHealthBarVisualGraftPatch.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public static void RefreshGraftOverlay(NHealthBar healthBar)
1919
return;
2020

2121
var creature = healthBar._creature;
22-
if (creature.CurrentHp <= 0 || creature.ShowsInfiniteHp)
22+
if (creature.CurrentHp <= 0 || creature.IsInfiniteHpDisplayed())
2323
{
2424
ResetGraft(healthBar);
2525
return;
@@ -60,7 +60,7 @@ public static void AfterForecastTouchup(NHealthBar healthBar)
6060
return;
6161

6262
var creature = healthBar._creature;
63-
if (creature.CurrentHp <= 0 || creature.ShowsInfiniteHp)
63+
if (creature.CurrentHp <= 0 || creature.IsInfiniteHpDisplayed())
6464
return;
6565

6666
var metrics = HealthBarVisualGraftRegistry.Aggregate(creature);

Combat/Powers/ModTemporaryPowerTemplate.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ public abstract class ModTemporaryPowerTemplate : ModPowerTemplate, ITemporaryPo
6969
public override bool AllowNegative => true;
7070

7171
/// <inheritdoc />
72+
#if !STS2_AT_LEAST_0_105_0
7273
public override bool IsInstanced => LastForXExtraTurns != 0;
74+
#else
75+
public override PowerInstanceType InstanceType =>
76+
LastForXExtraTurns != 0 ? PowerInstanceType.Instanced : PowerInstanceType.None;
77+
#endif
7378

7479
/// <summary>
7580
/// Remaining extra expiry cycles before removal.
@@ -136,7 +141,7 @@ await ApplyInternalPower(new ThrowingPlayerChoiceContext(), target, SignedAmount
136141
}
137142

138143
/// <inheritdoc />
139-
#if STS2_V_0_103_2
144+
#if !STS2_AT_LEAST_0_104_0
140145
public override async Task AfterPowerAmountChanged(PowerModel power, decimal amount, Creature? applier,
141146
CardModel? cardSource)
142147
#else
@@ -154,7 +159,7 @@ public override async Task AfterPowerAmountChanged(PlayerChoiceContext choiceCon
154159
return;
155160
}
156161

157-
#if STS2_V_0_103_2
162+
#if !STS2_AT_LEAST_0_104_0
158163
await ApplyInternalPower(new ThrowingPlayerChoiceContext(), Owner, SignedAmount(amount), applier,
159164
cardSource, true);
160165
#else
@@ -252,7 +257,7 @@ private static Task ApplyInternalPowerGeneric<TPower>(PlayerChoiceContext choice
252257
decimal amount,
253258
Creature? applier, CardModel? cardSource, bool silent) where TPower : PowerModel
254259
{
255-
#if STS2_V_0_103_2
260+
#if !STS2_AT_LEAST_0_104_0
256261
return PowerCmd.Apply<TPower>(target, amount, applier, cardSource, silent);
257262
#else
258263
return PowerCmd.Apply<TPower>(choiceContext, target, amount, applier, cardSource, silent);

Combat/Ui/ExtraCornerAmountLabels/ExtraCornerAmountLabelsRuntime.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,7 @@ private static IReadOnlyList<ExtraIconAmountLabelCorner> GetReservedCorners(Extr
224224
{
225225
return hostKind switch
226226
{
227-
ExtraCornerHostKind.Power => [ExtraIconAmountLabelCorner.BottomRight],
228-
ExtraCornerHostKind.Relic => [ExtraIconAmountLabelCorner.BottomRight],
227+
ExtraCornerHostKind.Power or ExtraCornerHostKind.Relic => [ExtraIconAmountLabelCorner.BottomRight],
229228
ExtraCornerHostKind.Intent => [ExtraIconAmountLabelCorner.BottomLeft],
230229
_ => [],
231230
};

Lifecycle/CombatLifecycleContracts.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
#if STS2_V_0_103_2
1+
#if !STS2_AT_LEAST_0_104_0
22
using CombatStateCompat = MegaCrit.Sts2.Core.Combat.CombatState;
33
#else
44
using CombatStateCompat = MegaCrit.Sts2.Core.Combat.ICombatState;
55
#endif
66
using MegaCrit.Sts2.Core.Combat;
77
using MegaCrit.Sts2.Core.Entities.Cards;
88
using MegaCrit.Sts2.Core.Entities.Creatures;
9+
using MegaCrit.Sts2.Core.Entities.Players;
910
using MegaCrit.Sts2.Core.Models;
1011
using MegaCrit.Sts2.Core.Rooms;
1112
using MegaCrit.Sts2.Core.Runs;
@@ -161,15 +162,54 @@ DateTimeOffset OccurredAtUtc
161162
/// <summary>
162163
/// A card was retained for the next turn.
163164
/// </summary>
165+
/// <remarks>
166+
/// On host API 0.105.0 and newer the underlying <c>Hook.AfterCardRetained</c> callback no longer exists; this event
167+
/// is replayed per retained card from <c>Hook.AfterFlush</c> for backward compatibility. Subscribe to
168+
/// <see cref="CardsFlushedEvent" /> instead to also observe the matching flushed cards and the player.
169+
/// </remarks>
164170
/// <param name="CombatState">Active combat state.</param>
165171
/// <param name="Card">Retained card.</param>
166172
/// <param name="OccurredAtUtc">When the event was raised.</param>
173+
[Obsolete(
174+
"Use CardsFlushedEvent. CardRetainedEvent is replayed from Hook.AfterFlush on host API 0.105.0 and newer.")]
167175
public readonly record struct CardRetainedEvent(
168176
CombatStateCompat CombatState,
169177
CardModel Card,
170178
DateTimeOffset OccurredAtUtc
171179
) : IFrameworkLifecycleEvent;
172180

181+
/// <summary>
182+
/// A flush sequence is about to run for the given player. Mirrors <c>Hook.BeforeFlush</c>.
183+
/// </summary>
184+
/// <param name="CombatState">Active combat state.</param>
185+
/// <param name="Player">Player whose hand is about to be flushed.</param>
186+
/// <param name="OccurredAtUtc">When the event was raised.</param>
187+
public readonly record struct BeforeFlushEvent(
188+
CombatStateCompat CombatState,
189+
Player Player,
190+
DateTimeOffset OccurredAtUtc
191+
) : IFrameworkLifecycleEvent;
192+
193+
/// <summary>
194+
/// Hand flush completed for the given player.
195+
/// </summary>
196+
/// <remarks>
197+
/// Fired from <c>Hook.AfterFlush</c> on host API 0.105.0 and newer. On older host APIs <c>Hook.AfterFlush</c> does
198+
/// not exist and this event is not raised; use the legacy <see cref="CardRetainedEvent" /> there.
199+
/// </remarks>
200+
/// <param name="CombatState">Active combat state.</param>
201+
/// <param name="Player">Player whose hand was flushed.</param>
202+
/// <param name="FlushedCards">Cards that left the hand during flush (non-retained).</param>
203+
/// <param name="RetainedCards">Cards that stayed in the hand (retain semantics).</param>
204+
/// <param name="OccurredAtUtc">When the event was raised.</param>
205+
public readonly record struct CardsFlushedEvent(
206+
CombatStateCompat CombatState,
207+
Player Player,
208+
IReadOnlyCollection<CardModel> FlushedCards,
209+
IReadOnlyCollection<CardModel> RetainedCards,
210+
DateTimeOffset OccurredAtUtc
211+
) : IFrameworkLifecycleEvent;
212+
173213
/// <summary>
174214
/// A creature is dying (HP reached zero or equivalent).
175215
/// </summary>

0 commit comments

Comments
 (0)