//Start timer on music confirmation void gameMemory_OnMusicConfirm(object sender, EventArgs e) { if (this.Settings.AutoStart) { _timer.Start(); } }
public override void Update(IInvalidator invalidator, LiveSplitState state, float width, float height, LayoutMode mode) { if (!gameMemory.ProcessHook()) { return; } if (logic.ShouldStart()) { logic.ResetLogic(); if (liveSplitState.CurrentPhase == TimerPhase.Running) { timerModel.Reset(); } timerModel.Start(); } if (liveSplitState.CurrentPhase == TimerPhase.Running) { if (logic.ShouldSplit(liveSplitState.CurrentSplitIndex, liveSplitState.Run.Count)) { timerModel.Split(); } } }
void gameMemory_OnStart(object sender, EventArgs e) { if (_state.CurrentPhase == TimerPhase.NotRunning && Settings.AutoStart) { _timer.Start(); } }
void gameMemory_OnPlayerGainedControl(object sender, EventArgs e) { if (this.Settings.AutoStart) { _timer.Start(); } }
void gameMemory_OnFirstLevelLoaded(object sender, EventArgs e) { if (this.Settings.StartOnFirstLevelLoad) { _timer.Start(); } }
public void HandleLoadEnd(long timestamp, string zoneName) { if (settings.LoadRemovalEnabled) { loadTimes += timestamp - startTimestamp.GetValueOrDefault(timestamp); state.IsGameTimePaused = false; state.LoadingTimes = TimeSpan.FromMilliseconds(loadTimes); } IZone zone = Zone.Parse(zoneName, encounteredZones); if (settings.LabSpeedrunningEnabled) { if (state.CurrentPhase == TimerPhase.NotRunning && LAB_ENTRANCE.Equals(previousZone)) { // Start the timer on the first zone of the lab. timer.Start(); } else { // And split on subsequent zones. timer.Split(); } } else if (settings.AutoSplitEnabled) { if (!encounteredZones.Contains(zone) && settings.SplitZones.Contains(zone)) { timer.Split(); } // Keep track of all encountered zones for part prediction. encounteredZones.Add(zone); } previousZone = zone; }
public async Task UpdateState() { if (!IsConfigReady()) { Disconnect(); _update_timer.Interval = 1000; } else if (_mystate != MyState.READY) { _update_timer.Interval = 1000; Connect(); } else { _update_timer.Interval = 33; List <Split> splits = GenerateSplitList(); if (splits == null) { return; } if (await CheckSplits(splits, _state.CurrentSplitIndex)) { if (splits.Contains(_autostart)) { _model.Start(); } else { _model.Split(); } } } }
public override void Update(IInvalidator invalidator, LiveSplitState state, float width, float height, LayoutMode mode) { state.IsGameTimePaused = true; bool start = false; lock (eventsLock) { foreach (var ev in events) { if (ev is GameEndEvent) { if (state.CurrentPhase == TimerPhase.Running && settings.ShouldSplitOnGameEnd()) { state.SetGameTime(ev.Time); model.Split(); } } else if (ev is MapChangeEvent) { var e = (MapChangeEvent)ev; if (visitedMaps.Add(e.Map) && settings.ShouldSplitOn(e.Map)) { state.SetGameTime(e.Time); model.Split(); } } else if (ev is TimerResetEvent) { if (settings.IsAutoResetEnabled()) { state.SetGameTime(ev.Time); model.Reset(); } } else if (ev is TimerStartEvent) { if (settings.IsAutoStartEnabled()) { state.SetGameTime(ev.Time); start = true; } } } events.Clear(); } if (start) { model.Start(); } TimeSpan curTime; lock (currentTimeLock) { curTime = currentTime; } state.SetGameTime(curTime); }
/// <summary> /// Secondary update function used to do most of the actual autosplitting work. Also useful for testing. /// </summary> public void Autosplit() { if (!memory.HookProcess()) { processHooked = false; return; } processHooked = true; bool matchFileTime = settings.MatchFileTime; if (!runStarted) { // The autostart condition changes depending on whether file time is matched. When matching file time, // the timer doesn't start until the first proper IGT frame (when the player gains control). bool shouldAutostart; if (matchFileTime) { shouldAutostart = memory.GetIGTFrames() > 0; } else { shouldAutostart = memory.CheckFileSelect(); } if (shouldAutostart) { startingFrames = memory.GetRawFrames(); // Checking for null allows this function to be used in testing (where no timer exists). timer?.Start(); runStarted = true; fileSelected = true; } else { return; } } if (fileSelected) { fileSelected = memory.CheckFileSelect(); if (!fileSelected) { runStarted = false; } } // File time (i.e. the time shown on the file select screen) excludes cutscenes and menus. long frames = matchFileTime ? memory.GetIGTFrames() : memory.GetRawFrames() - startingFrames; timer?.CurrentState.SetGameTime(TimeSpan.FromSeconds(frames / 60f)); splitCollection.Update(); }
private void CheckStart() { if (_mem.GetMenuLevel(0) == MenuLevel.VentureForth && _mem.GetMenuTransitionMode(0) == TransitionMode.AllOut) { _model.Start(); } }
// This is executed repeatedly as long as the game is connected and initialized. private void DoUpdate(LiveSplitState state) { OldState = State.RefreshValues(_game); if (!(RunMethod(_methods.update, state) ?? true)) { // If Update explicitly returns false, don't run anything else return; } // Call the start segment if the splits are not running, but ensure that the segment is called at least once. if (state.CurrentPhase == TimerPhase.NotRunning || !startMethodCalledFromUpdate) { if (RunMethod(_methods.start, state) ?? false) { if (_settings.GetBasicSettingValue("start") && state.CurrentPhase == TimerPhase.NotRunning) { _timer.Start(); } } startMethodCalledFromUpdate = true; } if (state.CurrentPhase == TimerPhase.Running || state.CurrentPhase == TimerPhase.Paused) { if (_uses_game_time && !state.IsGameTimeInitialized) { _timer.InitializeGameTime(); } var is_paused = RunMethod(_methods.isLoading, state); if (is_paused != null) { state.IsGameTimePaused = is_paused; } var game_time = RunMethod(_methods.gameTime, state); if (game_time != null) { state.SetGameTime(game_time); } if (RunMethod(_methods.reset, state) ?? false) { if (_settings.GetBasicSettingValue("reset")) { _timer.Reset(); } } else if (RunMethod(_methods.split, state) ?? false) { if (_settings.GetBasicSettingValue("split")) { _timer.Split(); } } } }
void StartTimer(TimeSpan time) { TimeSpan originalOffset = _state.Run.Offset; _state.Run.Offset = time; _timer.Start(); _state.Run.Offset = originalOffset; Trace.WriteLine($"[NoLoads] Started timer at {time}"); }
// This is executed repeatedly as long as the game is connected and initialized. private void DoUpdate(LiveSplit.UI.Components.LiveSplitState state) { OldState = State.RefreshValues(_game); if (!(RunMethod(_methods.update, state) ?? true)) { // If Update explicitly returns false, don't run anything else return; } if (state.CurrentPhase == TimerPhase.Running || state.CurrentPhase == TimerPhase.Paused) { if (_uses_game_time && !state.IsGameTimeInitialized) { _timer.InitializeGameTime(); } var is_paused = RunMethod(_methods.isLoading, state); if (is_paused != null) { state.IsGameTimePaused = is_paused; } var game_time = RunMethod(_methods.gameTime, state); if (game_time != null) { state.SetGameTime(game_time); } if (RunMethod(_methods.reset, state) ?? false) { if (_settings.GetBasicSettingValue("reset")) { _timer.Reset(); } } else if (RunMethod(_methods.split, state) ?? false) { if (_settings.GetBasicSettingValue("split")) { _timer.Split(); } } } if (state.CurrentPhase == TimerPhase.NotRunning) { if (RunMethod(_methods.start, state) ?? false) { if (_settings.GetBasicSettingValue("start")) { _timer.Start(); } } } }
void gameMemory_OnPlayerGainedControl(object sender, PlayerControlChangedEventArgs e) { if (!this.Settings.AutoStartEndResetEnabled) { return; } _timer.Reset(); // make sure to reset for games that start from a quicksave (Aperture Tag) _timer.Start(); _sessionTicksOffset += e.TicksOffset; }
void gameMemory_OnTimerStart(object sender, uint ticks) { if (_state.CurrentPhase == TimerPhase.NotRunning && this.Settings.AutoStart) { var timeToAdd = TimeSpan.FromSeconds((float)ticks / (float)60); TimeSpan originalOffset = _state.Run.Offset; _state.Run.Offset = timeToAdd; _timer.Start(); _state.Run.Offset = originalOffset; TimerTicks = ticks; Trace.WriteLine(String.Format("[NoLoads] Start at GT: {1} ({2} ticks) RT: {3} - {0}", _gameMemory.frameCounter, _state.CurrentTime.GameTime, ticks, _state.CurrentTime.RealTime)); } }
public void On_Start() { if (_state.CurrentPhase != TimerPhase.NotRunning && _settings.AutoReset) { _timer.Reset(); } if (_state.CurrentPhase == TimerPhase.NotRunning && _settings.AutoStart) { _timer.Start(); } _state.IsGameTimePaused = true; _starting = true; }
private void OnFadeStart() { // There are only two instances where fading in is valuable to the autosplitter. The first is starting a new game (at // which point the fade begins immediately) and the second is a quick fade following the opening cutscene for each game. bool onTitle = memory.Title.Read() == 2; bool timerRunning = timer.CurrentState.CurrentPhase != TimerPhase.NotRunning; // With the addition of practice mode, I considered making the timer intentionally NOT start from the title // screen. I decided to keep things as usual so that players can keep their regular splits open with // practice mode enabled and still start real runs if they'd like. if (onTitle && !timerRunning && memory.EnteringGame.Read()) { timer.Start(); return; } // This all accounts for restarting IGT following the opening cutscene. It's a special case because there's no fade from // the load screen into each opening cutscene. if (firstLoad) { firstLoad = false; if (startedFromTitle) { timer.CurrentState.IsGameTimePaused = false; } return; } quit = memory.Paused.Read(); if (inHub && quit) { exitingToTitle = true; } }
private void _gameMemory_OnFirstLevelLoaded(object sender, EventArgs e) { if (_state.CurrentPhase == TimerPhase.NotRunning) { if (this.Settings.AutoReset) { Debug.WriteLine($"Restting"); _timer.Reset(); } } if (this.Settings.AutoStart) { Debug.WriteLine($"strating timer"); _timer.Start(); } }
private void MaybeStart(string fullPath) { var timestamp = ReadTimestampFromSessionLock(fullPath); if (!timestamp.HasValue) { return; } var curr = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); var diffInMs = curr - timestamp.Value; //MessageBox.Show($"diffInMs: {diffInMs}, timestamp: {timestamp}, curr: {curr}"); if (diffInMs > 1000 * 10) { return; } timer.CurrentState.Run.Offset = TimeSpan.FromMilliseconds(diffInMs); timer.Start(); //MessageBox.Show($"diffInMs: {diffInMs}\n\ncurrentTime: {timer.CurrentState.CurrentTime.RealTime.Value.TotalMilliseconds}"); if (File.Exists(Path.Combine(latestSavePath, "level.dat"))) { var startingLatestSavePath = latestSavePath; int i; for (i = 0; i < NUM_RETRIES && latestSavePath == startingLatestSavePath; i++) { try { var levelDat = new NbtFile(Path.Combine(latestSavePath, "level.dat")); while (NewWool(levelDat)) { ; } } catch (Exception) { Thread.Sleep(RETRY_MS); } } if (i == 5) { MessageBox.Show("Couldn't read level.dat"); } } }
/// <summary> /// Reset and start the timer /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnNewGame(object sender, StateEventArgs e) { if (gameData.IsRandomized) { e.State.Run.Clear(); // LiveSplit pretends its in Timer-only mode if there is only one segment (Triforce), which means new segments won't be added e.State.Run.AddSegment(START); e.State.Run.AddSegment(TRIFORCE, icon: icons[TRIFORCE]); } timer.Reset(); timer.Start(); e.State.IsGameTimePaused = true; }
private void UpdateAutosplitter() { var previousLatestSavePath = latestSavePath; latestSavePath = FindLatestSavePath(); var levelDat = new NbtFile(Path.Combine(latestSavePath, "level.dat")); if (levelDat.RootTag.First()["DataVersion"].IntValue < 1122) { timer.Reset(); Properties.Settings.Default.AutosplitterEnabled = false; Properties.Settings.Default.Save(); MessageBox.Show("Autosplitting is not supported for versions under 1.12 and has been disabled", ComponentName, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } var previousWorldTime = worldTime; worldTime = levelDat.RootTag.First()["Time"].LongValue; // We don't have a previous time to compare with yet, skip if (previousWorldTime == -1) { return; } if (timer.CurrentState.CurrentPhase != TimerPhase.NotRunning && worldTime == 0) { timer.Reset(); } if (timer.CurrentState.CurrentPhase == TimerPhase.NotRunning && worldTime > 0 && previousWorldTime != worldTime && latestSavePath == previousLatestSavePath) { timer.Start(); } if (timer.CurrentState.CurrentPhase == TimerPhase.Running && previousWorldTime != worldTime && latestSavePath == previousLatestSavePath && levelDat.RootTag.First()["Player"]["seenCredits"].ByteValue == 1) { timer.Split(); } }
public TimerViewModel(TimerModel model) { model.StateObservable().Subscribe((TimerState state) => { if (state == TimerState.FINISHED) { Messenger.Raise(new WindowActionMessage(WindowAction.Close, "Close")); } }); model.TimeObservable().Subscribe((int time) => { int h = time / (60 * 60); time -= h * (60 * 60); int m = time / 60; time -= m * 60; int s = time; Text = String.Format("{0:D2}:{1:D2}:{2:D2}", h, m, s); }); model.Start(); }
public async Task UpdateSplits() { if (_config_state != ConfigState.READY || _state.CurrentPhase == TimerPhase.NotRunning) { CheckConfig(); } if (_state.CurrentPhase == TimerPhase.NotRunning || _proto_state != ProtocolState.ATTACHED) { CheckConnection(); } if (_config_state != ConfigState.READY || _proto_state != ProtocolState.ATTACHED) { _update_timer.Interval = 1000; } else { _update_timer.Interval = 33; if (_state.CurrentPhase == TimerPhase.NotRunning) { if (_settings.Config.autostart != null && _settings.Config.autostart.active == "1") { if (await doCheckSplitWithNext(_settings.Config.autostart.GetSplit())) { _model.Start(); } } } else if (_state.CurrentPhase == TimerPhase.Running) { var splitName = _splits[_state.CurrentSplitIndex]; var split = _settings.Config.definitions.Where(x => x.name == splitName).First(); if (await doCheckSplitWithNext(split)) { await DoSplit(); } } } }
public void Update(IInvalidator invalidator, LiveSplitState state, float width, float height, LayoutMode mode) { int millis = Pointer.GetIgt(); //Native.GetGameTimeMilliseconds(addr, p.Id.GetHandle(), 8); if (millis > 100) { _oldMillis = millis; _latch = false; } if (millis == 0 && !_latch) { _oldMillis -= 594; _latch = true; } if (_oldMillis <= 0) { _oldMillis = 0; } state.SetGameTime(new TimeSpan(0, 0, 0, 0, _oldMillis <= 1 ? 1 : _oldMillis)); //autostart timer. Might be worth changing this to something based on some memory flag if (_control.cb_autoStartTimer.Checked && millis > 0 && millis < 500) { if (state.CurrentPhase == TimerPhase.NotRunning) { TimerModel timer = new TimerModel(); timer.CurrentState = state; timer.Start(); } } //autosplit if (_control.cb_autoSplit.Checked) { _splitter.AttemptSplit(); } }
void gameMemory_OnPlayerGainedControl(object sender, PlayerControlChangedEventArgs e) { if (Settings.ResetMapTransitions.Value) { _splitOperations.Clear(); _splitCount = 0; } if (!this.Settings.AutoStartEnabled.Value) { return; } if (_timer.CurrentState.CurrentPhase == TimerPhase.Running) { if (Settings.SplitInstead.Value) { gameMemory_ManualSplit(sender, e); return; } else if (!Settings.AutoResetEnabled.Value) { return; } } _timer.Reset(); // make sure to reset for games that start from a quicksave (Aperture Tag) _timer.Start(); if (Settings.RTAStartOffset.Value) { _timer.CurrentState.AdjustedStartTime -= TimeSpan.FromSeconds(Math.Abs(e.TicksOffset) * _intervalPerTick); } _sessionTicksOffset += e.TicksOffset; _tickOffset = e.TicksOffset; }
public void Update() { _oldMemoryState = _currentMemoryState; _currentMemoryState = Memory.GetState(); switch (_state.CurrentPhase) { case TimerPhase.NotRunning: if (ShouldStart()) { _startingMemoryState = _currentMemoryState; // Temporarily adjust the start offset for real-time accuracy. var userOffset = _state.Run.Offset; _state.Run.Offset = DateTime.Now - _firstLoadStartTime + TimeSpan.FromSeconds(StartOffset[AutosplitterSettings.StartCondition]); _model.Start(); _state.Run.Offset = userOffset; _state.SetGameTime(TimeSpan.FromSeconds(StartOffset[AutosplitterSettings.StartCondition])); } break; case TimerPhase.Running: // Pause timer when loading _state.IsGameTimePaused = _currentMemoryState.IsLoading; if (DateTime.Now > _timeUntilNextSplit && ShouldSplit()) { _model.Split(); } else if (ShouldReset()) { _model.Reset(); } break; } }
public void UpdateSplits() { if (_inTimer == true) { return; } _inTimer = true; if (_state.CurrentPhase == TimerPhase.NotRunning) { if (_error == false && _settings.Device != null && _game == null && (!_usb2snes.Connected())) { if (!_usb2snes.Connected()) { if (!this.connect()) { _error = true; _inTimer = false; return; } } if (_game == null) { if (!this.readConfig()) { _error = true; _inTimer = false; return; } } } if (_game != null && _game.autostart.active == "1") { if (_usb2snes.Connected()) { var data = new byte[64]; data = _usb2snes.GetAddress((0xF50000 + _game.autostart.addressint), (uint)64); uint value = (uint)data[0]; uint word = (uint)(data[0] + (data[1] << 8)); switch (_game.autostart.type) { case "bit": if ((value & _game.autostart.valueint) != 0) { _model.Start(); } break; case "eq": if (value == _game.autostart.valueint) { _model.Start(); } break; case "gt": if (value > _game.autostart.valueint) { _model.Start(); } break; case "lt": if (value < _game.autostart.valueint) { _model.Start(); } break; case "gte": if (value >= _game.autostart.valueint) { _model.Start(); } break; case "lte": if (value <= _game.autostart.valueint) { _model.Start(); } break; case "wbit": if ((word & _game.autostart.valueint) != 0) { _model.Start(); } break; case "weq": if (word == _game.autostart.valueint) { _model.Start(); } break; case "wgt": if (word > _game.autostart.valueint) { _model.Start(); } break; case "wlt": if (word < _game.autostart.valueint) { _model.Start(); } break; case "wgte": if (word >= _game.autostart.valueint) { _model.Start(); } break; case "wlte": if (word <= _game.autostart.valueint) { _model.Start(); } break; } } } } else if (_state.CurrentPhase == TimerPhase.Running) { if (_splits != null) { if (_usb2snes.Connected()) { var splitName = _splits[_state.CurrentSplitIndex]; var split = _game.definitions.Where(x => x.name == splitName).First(); var data = new byte[64]; data = _usb2snes.GetAddress((0xF50000 + split.addressint), (uint)64); uint value = (uint)data[0]; uint word = (uint)(data[0] + (data[1] << 8)); bool ok = checkSplit(split, value, word); if (split.more != null) { foreach (var moreSplit in split.more) { data = _usb2snes.GetAddress((0xF50000 + moreSplit.addressint), (uint)64); value = (uint)data[0]; word = (uint)(data[0] + (data[1] << 8)); ok = ok && checkSplit(moreSplit, value, word); } } if (ok) { DoSplit(); } } } } _inTimer = false; }
void do_start(object sender, EventArgs e) { _tm.Start(); }
// Making the phase nullable makes testing easier. public void Refresh(TimerPhase?nullablePhase = null) { // In theory, the first IGT frame of a run should have a time value of about 32 milliseconds (i.e. one // frame at 30 fps). In practice, though, the first non-zero time value tends to be a bit bigger than that. // This threshold is arbitrary, but is meant to be big enough to cover that variation in start time, but // small enough that later loads into a file don't autostart the timer. const int TimerAutostartThreshold = 150; if (!Hook()) { return; } // This will only be true while testing (through a console program). if (nullablePhase == null) { return; } if (waitingOnFirstLoad) { if (!memory.IsPlayerLoaded()) { return; } InitializeItems(); UpdateRunState(); waitingOnFirstLoad = false; } TimerPhase phase = nullablePhase.Value; // Someone might want to disable timer autostart if using real time and starting their timer from the main // menu (or some other reason). if (phase == TimerPhase.NotRunning && masterControl.StartTimerAutomatically) { int time = memory.GetGameTimeInMilliseconds(); // The timer should autostart on a new game, but not every time you load into a file. Note that // if someone resets their timer manually immediately as the run begins, the timer may // autostart again if game time is still less than the threshold value. This is fixable, but // likely not worth the effort. if (run.GameTime == 0 && time > 0 && time < TimerAutostartThreshold) { timer.Start(); } } // Quitout splits (below) are detected using game time (specifically, when IGT is reset back to zero). As // such, the current IGT value needs to be stored here before it's reset while updating game time. int previousGameTime = run.GameTime; int newGameTime = memory.GetGameTimeInMilliseconds(); // The timer is intentionally updated before an autosplit occurs (to ensure the split time is as accurate // as possible). if (masterControl.UseGameTime) { UpdateGameTime(newGameTime); } else { // Game time must be tracked even if "Use game time" is disabled (in order to accomodate quitout // splits). run.GameTime = newGameTime; } // This is called each tick regardless of whether the current split is an item split (to ensure that the // inventory state is accurate by the time an item split crops up). if (itemsEnabled) { memory.RefreshItems(); } // This check is intentionally done relatively far down in the function. The reason is that an ended timer // can be undone, and if that happens, I'd like all splits to continue working properly. Specifically, that // means that items and IGT (if applicable) are tracked even when the timer has ended. if (phase == TimerPhase.Ended) { return; } Split split = splitCollection.CurrentSplit; // It's possible for the current split to be null if no splits were configured at all. if (split == null || split.Type == SplitTypes.Manual || !split.IsFinished) { return; } // This condition covers all split types with warping as an option. if (preparedForWarp) { if (isBonfireWarpSplitActive) { int[] leaveValues = { (int)AnimationFlags.BonfireLeave1, (int)AnimationFlags.BonfireLeave2, (int)AnimationFlags.BonfireLeave3 }; int animation = memory.GetForcedAnimation(); // Without this check, the player could rest at a target bonfire (without warping), then warp from // another bonfire and have the tool incorrectly split. if (leaveValues.Contains(animation)) { preparedForWarp = false; return; } } else if (isItemWarpSplitActive && !IsTargetItemSatisfied()) { preparedForWarp = false; return; } if (CheckWarp()) { timer.Split(); } return; } // Similar to warping, this covers all splits with quitouts as a timing option (and Quitout splits // themselves, of course). if (waitingOnQuitout) { // Game time is reset to zero on the title screen, but not on regular loading screens. if (newGameTime == 0 && previousGameTime > 0) { if (splitCollection.CurrentSplit.Type == SplitTypes.Quitout) { run.Data++; // Quitout splits track quitout count. If that target hasn't yet been satisfied, the function // can just return immediately (which leaves the waitingOnQuitout variable intact). if (run.Data < run.Target) { return; } } // On quitout splits, the actual split occurs on the loading screen following a reload // (specifically when the player is loaded). This is done to account for any potential IGT drift // between quitout and reload. In theory, this drift shouldn't exist, but it seems to happen anyway // in practice. waitingOnUnload = true; waitingOnQuitout = false; } return; } if (waitingOnUnload) { // When the load screen appears following a quitout, the player isn't unloaded instantly. if (!memory.IsPlayerLoaded()) { waitingOnUnload = false; waitingOnReload = true; } return; } if (waitingOnReload) { // By the time this point in the code is reached, the player must already be unloaded (due to quitting // to the title screen). if (memory.IsPlayerLoaded()) { waitingOnReload = false; timer.Split(); } return; } if (splitFunctions[split.Type](split.Data)) { timer.Split(); } }
public SegmentStatus Update(LiveSplitState state) { AssertHooksActive(); if (MaybeJournalTracker != null) { if (!MaybeJournalTracker.Visible) { MaybeJournalTracker.Show(); } MaybeJournalTracker.Update(Hooks); } if (MaybeCharactersTracker != null) { if (!MaybeCharactersTracker.Visible) { MaybeCharactersTracker.Show(); } MaybeCharactersTracker.Update(Hooks); } if (state.Run.Count != Segments.Length - 1) // Validate user splits { return(new SegmentStatus() { Type = SegmentStatusType.ERROR, Message = "Expected " + (Segments.Length - 1) + " segments, got " + state.Run.Count + " (correct this before continuing)." }); } else if (state.CurrentSplitIndex + 1 >= Segments.Length) // Check if the run is done { return(new SegmentStatus() { Type = SegmentStatusType.INFO, Message = "Run completed!" }); } else { var autoLoadResult = UpdateAutoLoad(state); if (autoLoadResult != null) { return(autoLoadResult); } SplitAction splitAction; if (state.CurrentSplitIndex == -1) { splitAction = delegate() { Timer.Start(); }; } else { splitAction = delegate() { Timer.Split(); }; } var segment = Segments[state.CurrentSplitIndex + 1]; var status = segment.CheckStatus(Hooks); if (segment.Cycle(Hooks)) { splitAction(); } return(status); } }