//Start timer on music confirmation
 void gameMemory_OnMusicConfirm(object sender, EventArgs e)
 {
     if (this.Settings.AutoStart)
     {
         _timer.Start();
     }
 }
示例#2
0
        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();
                }
            }
        }
示例#3
0
 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();
                    }
                }
            }
        }
示例#8
0
        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);
        }
示例#9
0
        /// <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();
        }
示例#10
0
 private void CheckStart()
 {
     if (_mem.GetMenuLevel(0) == MenuLevel.VentureForth &&
         _mem.GetMenuTransitionMode(0) == TransitionMode.AllOut)
     {
         _model.Start();
     }
 }
示例#11
0
        // 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();
                    }
                }
            }
        }
示例#12
0
        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}");
        }
示例#13
0
        // 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();
                    }
                }
            }
        }
示例#14
0
        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;
        }
示例#15
0
 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));
     }
 }
示例#16
0
 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;
 }
示例#17
0
        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;
            }
        }
示例#18
0
 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();
     }
 }
示例#19
0
        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");
                }
            }
        }
示例#20
0
        /// <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();
 }
示例#23
0
        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();
                    }
                }
            }
        }
示例#24
0
        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;
        }
示例#28
0
 void do_start(object sender, EventArgs e)
 {
     _tm.Start();
 }
示例#29
0
        // 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();
            }
        }
示例#30
0
        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);
            }
        }