48 lines
1.6 KiB
Dart
48 lines
1.6 KiB
Dart
/// Pure-Dart interstitial gate. No plugin imports so the monetization rules
|
|
/// are unit-tested headlessly. The service layer feeds it lifecycle events
|
|
/// and asks [canShowInterstitial] before every stage-end interstitial.
|
|
class AdFrequencyPolicy {
|
|
AdFrequencyPolicy({
|
|
this.minStagesBetween = 3,
|
|
this.minInterval = const Duration(seconds: 90),
|
|
this.firstStagesProtected = 5,
|
|
});
|
|
|
|
final int minStagesBetween;
|
|
final Duration minInterval;
|
|
final int firstStagesProtected;
|
|
|
|
int _totalStagesCompleted = 0;
|
|
int _stagesSinceLastInterstitial = 0;
|
|
bool _rewardedShownThisRound = false;
|
|
DateTime? _lastInterstitialAt;
|
|
|
|
/// A new stage attempt began (fresh round). Clears the per-round rewarded
|
|
/// flag so last round's rewarded does not block this round's interstitial.
|
|
void onRoundStart() => _rewardedShownThisRound = false;
|
|
|
|
/// A stage finished (won or lost). Drives both counters.
|
|
void onStageCompleted() {
|
|
_totalStagesCompleted++;
|
|
_stagesSinceLastInterstitial++;
|
|
}
|
|
|
|
/// The player watched a rewarded ad in the current round.
|
|
void onRewardedShown() => _rewardedShownThisRound = true;
|
|
|
|
/// An interstitial was actually shown. Resets the spacing counters.
|
|
void onInterstitialShown(DateTime now) {
|
|
_stagesSinceLastInterstitial = 0;
|
|
_lastInterstitialAt = now;
|
|
}
|
|
|
|
bool canShowInterstitial(DateTime now) {
|
|
if (_totalStagesCompleted <= firstStagesProtected) return false;
|
|
if (_rewardedShownThisRound) return false;
|
|
if (_stagesSinceLastInterstitial < minStagesBetween) return false;
|
|
final last = _lastInterstitialAt;
|
|
if (last != null && now.difference(last) < minInterval) return false;
|
|
return true;
|
|
}
|
|
}
|