// input event requires application of rules to determine next state void SetNextState(InputEvent input, int?intparam) { if (_modelstate == ModelState.Level && !IsValidInGame(input)) { return; } // model state determines what to do with input switch (_modelstate) { case ModelState.Againing: case ModelState.Level: ApplyRules(input, intparam); if (_states.Last().IsAgaining) { _modelstate = ModelState.Againing; if (GameDef.GetSetting(OptionSetting.pause_on_again, false)) { break; } while (_states.Last().IsAgaining) { if (Logger.Level >= 3) { ShowLevel("againing"); } ApplyRules(InputEvent.Tick, null); } } CheckNextState(); break; // message level display ended, now find what's next case ModelState.Message: CheckNextState(); break; case ModelState.EndLevel: LoadLevel(_levelindex + 1); // sets state directly break; case ModelState.Failed: break; default: throw Error.Assert("bad model state {0}", _modelstate); } }
void CheckNextState() { if (CurrentLevelKind == LevelKind.Message) { LoadLevel(_levelindex + 1); } else if (!_states.Last().IsFinished) { _modelstate = ModelState.Level; } else if (GameDef.GetSetting(OptionSetting.pause_at_end_level, false)) { _modelstate = ModelState.EndLevel; } else { LoadLevel(_levelindex + 1); } }
// apply the result to decide what to do with the new state // sounds and messages not preserved void ApplyResult(GameState newstate) { var currentstate = _states.Last(); // use the result to decide what to do with the new state switch (newstate.Result) { case StateResult.Cancel: VerboseLog("Cancel, keep current state"); CheckTrigger(SoundTrigger.Cancel); if (currentstate.IsAgaining) { _states.Remove(currentstate); } break; // as if nothing happened case StateResult.Restart: VerboseLog("Restart from checkpoint"); CheckTrigger(SoundTrigger.Restart); Restart(newstate); break; case StateResult.Undo: if (_states.Count > 1) { VerboseLog("Undo, discard current state"); CheckTrigger(SoundTrigger.Undo); _states.Remove(currentstate); } else { VerboseLog("Undo ignored"); } break; case StateResult.Reset: VerboseLog("Reset, reload level"); LoadLevel(_levelindex); break; case StateResult.Againing: if (currentstate.IsAgaining) { _states.Remove(currentstate); } newstate.AgainCount = currentstate.AgainCount + 1; if (newstate.AgainCount >= 100) { throw Error.Fatal("too many agains"); } VerboseLog("Run rules again on current state"); CheckTrigger(SoundTrigger.Again); Logger.WriteLine(2, "Again count {0}", newstate.AgainCount); _states.Add(newstate); break; case StateResult.Finished: case StateResult.Success: if (currentstate.IsAgaining || _initialinput) { _states.Remove(currentstate); } // treat this combo as a cancel if (_playermovement && GameDef.GetSetting(OptionSetting.require_player_movement, false) && newstate.PlayerIndexes.SequenceEqual(currentstate.PlayerIndexes)) { VerboseLog("Player did not move, discard current state"); CheckTrigger(SoundTrigger.Cancel); } else { VerboseLog("Move complete"); _states.Add(newstate); if (newstate.Result == StateResult.Finished) { VerboseLog("Level complete"); CheckTrigger(SoundTrigger.Win); } } break; default: throw Error.Assert("bad result {0}", newstate.Result); } }
// get a puzzle object id given name public int GetObjectId(string name) { return(Enumerable.Range(1, GameDef.ObjectCount) .FirstOrDefault(x => GameDef.GetObject(x).Name .Equals(name, StringComparison.InvariantCultureIgnoreCase))); // 0 if not found }
// get a puzzle object, which may have been updated public PuzzleObject GetObject(int objid) { var obj = _states.Last().PuzzleObjects.SafeLookup(objid); return(obj ?? GameDef.GetObject(objid)); }