private void PrepareTimeout(IUIManager?ui, IReadOnlyList <Enemy> subbosses, SMHandoff smh, Cancellable toCancel) { smh.Exec.PhaseShifter = toCancel; var timeout = Timeout; //Note that the <!> HP(hp) sets invulnTime=0. if (props.invulnTime != null && props.phaseType != PhaseType.TIMEOUT) { WaitingUtils.WaitThenCB(smh.Exec, smh.cT, props.invulnTime.Value, false, () => smh.Exec.Enemy.SetVulnerable(Vulnerability.VULNERABLE)); } WaitingUtils.WaitThenCancel(smh.Exec, smh.cT, timeout, true, toCancel); if (props.phaseType?.IsSpell() ?? false) { smh.Exec.Enemy.RequestSpellCircle(timeout, smh.cT); foreach (var subboss in subbosses) { subboss.RequestSpellCircle(timeout, smh.cT); } } //else smh.exec.Enemy.DestroySpellCircle(); if (!props.HideTimeout && smh.Exec.TriggersUITimeout) { ui?.DoTimeout(props.phaseType?.IsCard() ?? false, timeout, smh.cT); } }
public void DoAIteration(AsyncPattern[] target) { //To prevent secondary sequential children from trying to copy this object's GCX // which will have already changed when the next loop starts. GenCtx base_gcx = looper.GCX.Copy(); bool done = false; Action loop_done = () => { base_gcx.Dispose(); done = true; }; if (waitChild) { checkIsChildDone = () => done; //On the frame that the child finishes, the waitstep will increment elapsedFrames //even though it should not. However, it is difficult to tell the waitstep whether //the child was finished or not finished before that frame. This is the easiest solution. --elapsedFrames; } else { checkIsChildDone = null; } if (looper.props.childSelect == null) { if (sequential) { void DoNext(int ii) { if (ii >= target.Length || looper.Handoff.cT.Cancelled) { loop_done(); } else { DoAIteration(target[ii], () => DoNext(ii + 1), base_gcx); } } DoNext(0); } else { var loop_fragment_done = WaitingUtils.GetManyCallback(target.Length, loop_done); for (int ii = 0; ii < target.Length; ++ii) { DoAIteration(target[ii], loop_fragment_done, base_gcx); } } } else { DoAIteration(target[(int)looper.props.childSelect(looper.GCX) % target.Length], loop_done, base_gcx); } }
public void OnFail(ChallengeManager.TrackingContext ctx) { Log.Unity($"FAILED challenge {cr.Description}"); UIManager.MessageChallengeEnd(false, out float t); if (ctx.exec != null) { ctx.exec.ShiftPhase(); } WaitingUtils.WaitThenCB(ctx.cm, Cancellable.Null, t, false, () => { BulletManager.PlayerTarget.Player.Hit(999, true); }); }
public void DoAIteration(ref float elapsedFrames, IReadOnlyList <StateMachine> target) { //See IPExecution tracker for comments on this code GenCtx base_gcx = looper.GCX.Copy(); bool done = false; Action loop_done = () => { base_gcx.Dispose(); done = true; }; if (waitChild) { checkIsChildDone = () => done; --elapsedFrames; } else { checkIsChildDone = null; } if (looper.props.childSelect == null) { if (sequential) { void DoNext(int ii) { if (ii >= target.Count || looper.Handoff.cT.Cancelled) { loop_done(); } else { DoAIteration(target[ii], () => DoNext(ii + 1), base_gcx); } } DoNext(0); } else { var loop_fragment_done = WaitingUtils.GetManyCallback(target.Count, loop_done); for (int ii = 0; ii < target.Count; ++ii) { DoAIteration(target[ii], loop_fragment_done, base_gcx); } } } else { DoAIteration(target[(int)looper.props.childSelect(looper.GCX) % target.Count], loop_done, base_gcx); } }
private async Task OnFinish(SMHandoff smh, ICancellee prepared, CampaignSnapshot start_campaign, IBackgroundOrchestrator?bgo, IAyaPhotoBoard?photoBoard) { if (props.BgTransitionOut != null) { bgo?.QueueTransition(props.BgTransitionOut); } if (props.BossPhotoHP.Try(out _)) { photoBoard?.TearDown(); } //The shift-phase token is cancelled by timeout or by HP. var completedBy = prepared.Cancelled ? (smh.Exec.isEnemy ? (smh.Exec.Enemy.PhotoHP <= 0 && (props.photoHP ?? 0) > 0) ? PhaseClearMethod.PHOTO : (smh.Exec.Enemy.HP <= 0 && (props.hp ?? 0) > 0) ? PhaseClearMethod.HP : (PhaseClearMethod?)null : null) ?? PhaseClearMethod.TIMEOUT : PhaseClearMethod.CANCELLED; var pc = new PhaseCompletion(props, completedBy, smh.Exec, start_campaign, Timeout); if (pc.StandardCardFinish) { if (props.Boss != null) { BulletManager.RequestPowerAura("powerup1", 0, 0, new RealizedPowerAuraOptions( new PowerAuraOptions(new[] { PowerAuraOption.Color(_ => ColorHelpers.CV4(props.Boss.colors.powerAuraColor)), PowerAuraOption.Time(_ => 1f), PowerAuraOption.Iterations(_ => - 1f), PowerAuraOption.Scale(_ => 3.5f), PowerAuraOption.Static(), PowerAuraOption.High(), }), GenCtx.Empty, smh.Exec.GlobalPosition(), smh.cT, null !)); } smh.Exec.DropItems(pc.DropItems, 1.4f, 0.6f, 1f, 0.2f, 2f); DependencyInjection.MaybeFind <IRaiko>() ?.Shake(defaultShakeTime, defaultShakeMult, defaultShakeMag, smh.cT, null); } GameManagement.Instance.PhaseEnd(pc); if (pc.StandardCardFinish && !smh.Cancelled && props.Boss != null && pc.CaptureStars.HasValue) { Object.Instantiate(GameManagement.References.prefabReferences.phasePerformance) .GetComponent <PhasePerformance>().Initialize($"{props.Boss.CasualName} / Boss Card", pc); await WaitingUtils.WaitForUnchecked(smh.Exec, smh.cT, EndOfCardDelayTime, false); } }
public void DoLastAIteration(IReadOnlyList <StateMachine> target) { //Unlike GIR, which hoists its cleanup code into a callback, GTR awaits its last child // and calls its cleanup code in Start. //Therefore, this code follows the wait-child pattern. GenCtx base_gcx = looper.GCX.Copy(); bool done = false; checkIsChildDone = () => done; Action loop_done = () => { base_gcx.Dispose(); done = true; //AllDone called by Start code }; if (looper.props.childSelect == null) { if (sequential) { void DoNext(int ii) { if (ii >= target.Count || looper.Handoff.cT.Cancelled) { loop_done(); } else { DoAIteration(target[ii], () => DoNext(ii + 1), base_gcx); } } DoNext(0); } else { var loop_fragment_done = WaitingUtils.GetManyCallback(target.Count, loop_done); for (int ii = 0; ii < target.Count; ++ii) { DoAIteration(target[ii], loop_fragment_done, base_gcx); } } } else { DoAIteration(target[(int)looper.props.childSelect(looper.GCX) % target.Count], loop_done, base_gcx); } }
public void DoLastAIteration(AsyncPattern[] target) { GenCtx base_gcx = looper.GCX.Copy(); Action loop_done = () => { base_gcx.Dispose(); AllADone(); }; if (looper.props.childSelect == null) { if (sequential) { void DoNext(int ii) { if (ii >= target.Length || looper.Handoff.cT.Cancelled) { loop_done(); } else { DoAIteration(target[ii], () => DoNext(ii + 1), base_gcx); } } DoNext(0); } else { var loop_fragment_done = WaitingUtils.GetManyCallback(target.Length, loop_done); for (int ii = 0; ii < target.Length; ++ii) { DoAIteration(target[ii], loop_fragment_done, base_gcx); } } } else { DoAIteration(target[(int)looper.props.childSelect(looper.GCX) % target.Length], loop_done, base_gcx); } }
public static Event BossExplode() => smh => { UnityEngine.Object.Instantiate(ResourceManager.GetSummonable("bossexplode")).GetComponent <ExplodeEffect>().Initialize(BossExplodeWait, smh.Exec.rBPI.loc); DependencyInjection.MaybeFind <IRaiko>()?.Shake(BossExplodeShake, ShakeMag, 2, smh.cT, null); SFXService.BossExplode(); return(WaitingUtils.WaitForUnchecked(smh.Exec, smh.cT, BossExplodeWait, false)); };
public async Task Start(SMHandoff smh, IUIManager?ui, IReadOnlyList <Enemy> subbosses) { var bgo = DependencyInjection.MaybeFind <IBackgroundOrchestrator>(); var photoBoard = DependencyInjection.MaybeFind <IAyaPhotoBoard>(); PreparePhase(ui, smh, out Task cutins, bgo, photoBoard); var lenienceToken = props.Lenient ? GameManagement.Instance.Lenience.CreateToken1(MultiOp.Priority.CLEAR_PHASE) : null; if (props.rootMove != null) { await props.rootMove.Start(smh); } await cutins; smh.ThrowIfCancelled(); if (props.phaseType?.IsPattern() == true) { ETime.Timer.PhaseTimer.Restart(); } var joint_smh = smh.CreateJointCancellee(out var pcTS); PrepareTimeout(ui, subbosses, joint_smh, pcTS); //The start snapshot is taken after the root movement, // so meter can be used during the 1+2 seconds between cards var start_campaign = new CampaignSnapshot(GameManagement.Instance); if (props.phaseType != null) { DependencyInjection.MaybeFind <IChallengeManager>()?.SetupBossPhase(joint_smh); } try { await base.Start(joint_smh); await WaitingUtils.WaitForUnchecked(joint_smh.Exec, joint_smh.cT, 0f, true); //Wait for synchronization before returning to parent joint_smh.ThrowIfCancelled(); } catch (OperationCanceledException) { if (smh.Exec.PhaseShifter == pcTS) { smh.Exec.PhaseShifter = null; } //This is critical to avoid boss destruction during the two-frame phase buffer if (smh.Exec.isEnemy) { smh.Exec.Enemy.SetVulnerable(Vulnerability.NO_DAMAGE); } lenienceToken?.TryRevoke(); if (props.Cleanup) { GameManagement.ClearPhaseAutocull(props.SoftcullProps(smh.Exec)); } if (smh.Exec.AllowFinishCalls) { //TODO why does this use parentCT? finishPhase?.Trigger(smh.Exec, smh.GCX, smh.parentCT); await OnFinish(smh, pcTS, start_campaign, bgo, photoBoard); } if (smh.Cancelled) { throw; } if (props.phaseType != null) { Log.Unity($"Cleared {props.phaseType.Value} phase: {props.cardTitle?.ValueOrEn ?? ""}"); } if (endPhase != null) { await endPhase.Start(smh); } } }
private void PreparePhase(IUIManager?ui, SMHandoff smh, out Task cutins, IBackgroundOrchestrator?bgo, IAyaPhotoBoard?photoBoard) { cutins = Task.CompletedTask; ui?.ShowPhaseType(props.phaseType); if (props.cardTitle != null || props.phaseType != null) { var rate = (props.Boss != null) ? GameManagement.Instance.LookForSpellHistory(props.Boss.key, props.Index) : null; ui?.SetSpellname(props.cardTitle?.ToString(), rate); } if (!props.HideTimeout && smh.Exec.TriggersUITimeout) { ui?.ShowStaticTimeout(Timeout); } if (props.livesOverride.HasValue) { ui?.ShowBossLives(props.livesOverride.Value); } if (smh.Exec.isEnemy) { if (props.photoHP.Try(out var photoHP)) { smh.Exec.Enemy.SetPhotoHP(photoHP, photoHP); } else if ((props.hp ?? props.phaseType?.DefaultHP()).Try(out var hp)) { if (props.Boss != null) { hp = (int)(hp * GameManagement.Difficulty.bossHPMod); } smh.Exec.Enemy.SetHP(hp, hp); } if ((props.hpbar ?? props.phaseType?.HPBarLength()).Try(out var hpbar)) { smh.Exec.Enemy.SetHPBar(hpbar, props.phaseType ?? PhaseType.NONSPELL); } smh.Exec.Enemy.SetVulnerable(props.phaseType?.DefaultVulnerability() ?? (props.Boss == null ? Vulnerability.VULNERABLE : Vulnerability.NO_DAMAGE)); } if (props.BossPhotoHP.Try(out var pins)) { photoBoard?.SetupPins(pins); } bool forcedBG = false; if (GameManagement.Instance.mode != InstanceMode.CARD_PRACTICE && !SaveData.Settings.TeleportAtPhaseStart) { if (props.bossCutin && props.Boss != null && props.Background != null) { GameManagement.Instance.ExternalLenience(props.Boss.bossCutinTime); SFXService.BossCutin(); //Service not required since no callback DependencyInjection.MaybeFind <IRaiko>()?.Shake(props.Boss.bossCutinTime / 2f, null, 1f, smh.cT, null); Object.Instantiate(props.Boss.bossCutin); bgo?.QueueTransition(props.Boss.bossCutinTrIn); bgo?.ConstructTarget(props.Boss.bossCutinBg, true); WaitingUtils.WaitFor(smh, props.Boss.bossCutinBgTime, false).ContinueWithSync(() => { if (!smh.Cancelled) { bgo?.QueueTransition(props.Boss.bossCutinTrOut); bgo?.ConstructTarget(props.Background, true); } }); cutins = WaitingUtils.WaitForUnchecked(smh.Exec, smh.cT, props.Boss.bossCutinTime, false); forcedBG = true; } else if (props.GetSpellCutin(out var sc)) { SFXService.BossSpellCutin(); DependencyInjection.MaybeFind <IRaiko>()?.Shake(1.5f, null, 1f, smh.cT, null); Object.Instantiate(sc); } } if (!forcedBG && props.Background != null) { if (props.BgTransitionIn != null) { bgo?.QueueTransition(props.BgTransitionIn); } bgo?.ConstructTarget(props.Background, true); } }
public override async Task Start(SMHandoff smh) { var jsmh = smh.CreateJointCancellee(out var cts); var subbosses = new List <Enemy>(); var subsummons = new List <BehaviorEntity>(); var ui = DependencyInjection.MaybeFind <IUIManager>(); if (props.boss != null) { GameManagement.Instance.SetCurrentBoss(props.boss, jsmh.Exec, jsmh.cT); ui?.SetBossHPLoader(jsmh.Exec.Enemy); (subbosses, subsummons) = ConfigureAllBosses(ui, jsmh, props.boss, props.bosses); } bool firstBoss = true; for (var next = jsmh.Exec.phaseController.WhatIsNextPhase(); next > -1 && next < phases.Length; next = jsmh.Exec.phaseController.WhatIsNextPhase(next + 1)) { if (phases[next].props.skip) { continue; } if (PHASE_BUFFER) { await WaitingUtils.WaitForUnchecked(jsmh.Exec, jsmh.cT, ETime.FRAME_TIME * 2f, false); } jsmh.ThrowIfCancelled(); if (props.bgms != null) { AudioTrackService.InvokeBGM(props.bgms.GetBounded(next, null)); } if (props.boss != null && next >= props.setUIFrom) { SetUniqueBossUI(ui, firstBoss, jsmh, props.bosses == null ? props.boss : props.bosses[props.bossUI?.GetBounded(next, 0) ?? 0]); firstBoss = false; } //don't show lives on setup phase if (next > 0 && props.boss != null) { ui?.ShowBossLives(RemainingLives(next)); } try { await phases[next].Start(jsmh, ui, subbosses); } catch (OperationCanceledException) { //Runs the cleanup code if we were cancelled break; } } cts.Cancel(); if (props.boss != null && !SceneIntermediary.LOADING) { ui?.CloseBoss(); if (!firstBoss) { ui?.CloseProfile(); } foreach (var subsummon in subsummons) { subsummon.InvokeCull(); } } }
private void DoTransition(BackgroundTransition bgt) { if (FromBG == null) { return; } if (ToBG == null) { throw new Exception("Cannot do transition when target BG is null"); } var pb = new MaterialPropertyBlock(); var mat = Instantiate(baseMixerMaterial); float timeout = bgt.TimeToFinish(); var cts = new Cancellable(); transitionCTS.Add(cts); Func <bool>?condition = null; if (bgt.type == BackgroundTransition.EffectType.WipeTex) { bgt.WipeTex.Apply(mat); CombinerKeywords.Apply(mat, CombinerKeywords.WIPE_TEX); } else if (bgt.type == BackgroundTransition.EffectType.Wipe1) { bgt.Wipe1.Apply(mat); CombinerKeywords.Apply(mat, CombinerKeywords.WIPE1); } else if (bgt.type == BackgroundTransition.EffectType.WipeFromCenter) { bgt.WipeFromCenter.Apply(mat); CombinerKeywords.Apply(mat, CombinerKeywords.WIPEFROMCENTER); } else if (bgt.type == BackgroundTransition.EffectType.Shatter4) { Action cb = WaitingUtils.GetCondition(out condition); FromBG.Shatter4(bgt.Shatter4, false, cb); CombinerKeywords.Apply(mat, CombinerKeywords.TO_ONLY); } else if (bgt.type == BackgroundTransition.EffectType.WipeY) { bgt.WipeY.Apply(mat); CombinerKeywords.Apply(mat, CombinerKeywords.WIPEY); //TODO apply these two generics from within the BGT scope. } BackgroundCombiner.SetMaterial(mat, pb); void Finish() { if (!cts.Cancelled) { FinishTransition(); } transitionCTS.Remove(cts); } if (condition == null) { if (timeout > 0) { WaitingUtils.WaitThenCBEvenIfCancelled(this, cts, timeout, false, Finish); } else { throw new Exception("Cannot wait for transition without a timeout or condition"); } } else { WaitingUtils.WaitThenCBEvenIfCancelled(this, cts, timeout, condition, Finish); } }