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 SMExecutionTracker(GenCtxProperties <StateMachine> props, SMHandoff smh, out bool isClipped) { looper = new LoopControl <StateMachine>(props, smh.ch, out isClipped); this.smh = smh; this.smh.CanPrepend = true; waitChild = props.waitChild; sequential = props.sequential; checkIsChildDone = null; }
private static void SetUniqueBossUI(IUIManager?ui, bool first, SMHandoff smh, BossConfig b) { if (first) { ui?.AddProfile(b.profile); } else { ui?.SwitchProfile(b.profile); } ui?.SetBossColor(b.colors.uiColor, b.colors.uiHPColor); }
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); } }
private static (List <Enemy>, List <BehaviorEntity>) ConfigureAllBosses(IUIManager?ui, SMHandoff smh, BossConfig main, BossConfig[]?all) { all ??= new[] { main }; //BossConfig may summon an Enemy or just a BehaviorEntity. //Enemies are tracked in the main boss for HP sharing. //BehaviorEntities are tracked var subbosses = new List <Enemy>(); var subsummons = new List <BehaviorEntity>(); foreach (var(i, b) in all.Enumerate()) { var target = smh.Exec; if (i > 0) { target = Object.Instantiate(b.boss).GetComponent <BehaviorEntity>(); var mov = new Movement(new Vector2(-50f, 0f), 0f); target.Initialize(null, mov, new ParametricInfo(in mov), SMRunner.Null); subsummons.Add(target); } if (target.TryAsEnemy(out var e)) { if (i > 0) { e.distorter = null; // i dont really like this but it overlaps weirdly subbosses.Add(e); } e.ConfigureBoss(b); e.SetVulnerable(Vulnerability.NO_DAMAGE); } string trackerName = b.BottomTrackerName; if (trackerName.Length > 0) { ui?.TrackBEH(target, trackerName, smh.cT); } } smh.Exec.Enemy.Subbosses = subbosses; return(subbosses, subsummons); }
public override Task Start(SMHandoff smh) => func(smh);
public override Task Start(SMHandoff smh) { Dialoguer.ShowAndResetDialogue(); return(base.Start(smh).ContinueWithSync(Dialoguer.HideDialogue)); }
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); } } }
public override Task Start(SMHandoff smh) => Start(smh, null, new Enemy[0]);
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 Task Start(SMHandoff smh) { throw new NotImplementedException("Do not call Start on FinishSM"); }
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(); } } }
public override Task Start(SMHandoff smh) { values.Add(f(smh.GCX)); return(Task.CompletedTask); }