/// <summary> /// Completes implicit or explicit target selection with the specified target location. /// </summary> /// <param name="target"> /// The coordinates of the selected target <see cref="Site"/>.</param> /// <returns> /// <c>true</c> if a command was successfully executed; otherwise, <c>false</c>.</returns> /// <remarks><para> /// <b>Complete</b> calls <see cref="Cancel"/> and fails immediately if the specified /// <paramref name="target"/> is not in the <see cref="MapView.SelectedRegion"/> of the /// default <see cref="Session.MapView"/>. /// </para><para> /// Otherwise, if the current <see cref="Session.State"/> equals <see /// cref="SessionState.Human"/>, <b>Complete</b> issues either an <see /// cref="AttackCommand"/> or a <see cref="MoveCommand"/> with the specified <paramref /// name="target"/> site, depending on its contents. <b>Complete</b> then invokes <see /// cref="HumanAction.SelectUnit"/> to cycle to the next active unit. /// </para><para> /// Otherwise, if the current <see cref="Session.State"/> equals <see /// cref="SessionState.Selection"/>, <b>Complete</b> issues the pending command with the /// specified <paramref name="target"/> site. Depending on the pending command, /// <b>Complete</b> then calls <see cref="HumanAction.Build"/> or <see /// cref="HumanAction.ManageEntities"/>, allowing the local human player to continue /// building or placing entities, respectively. /// </para><para> /// The execution of any command implicitly resets the <see cref="Session.State"/> to <see /// cref="SessionState.Human"/> and calls <see cref="Clear"/> to clear all data managed by /// the <see cref="TargetSelection"/> class. Finally, <b>Complete</b> returns the return /// value of the <see cref="Session.Executor"/> method invoked for command execution. /// </para></remarks> public void Complete(PointI target) { // check for valid target if (!Session.MapView.InSelectedRegion(target)) { Cancel(); return; } // remember currently selected entity, if any string id = MainWindow.Instance.SelectedEntity; AsyncAction.BeginRun(delegate { Action action = null; SessionExecutor executor = Session.Instance.Executor; WorldState world = Session.Instance.WorldState; if (Session.State == SessionState.Human) { // issue implicit Attack or Move command, then cycle to next active unit if (this._attackTargets != null && this._attackTargets.Contains(target)) { executor.ExecuteAttack(world, this._attackUnits, target); action = () => HumanAction.SelectUnit(id, false, false); } else if (this._moveTargets != null && this._moveTargets.Contains(target)) { executor.ExecuteMove(world, this._moveUnits, target); action = () => HumanAction.SelectUnit(id, false, false); } } else if (Session.State == SessionState.Selection) { // issue Build or Place command, then let user build or place more entities if (this._commandType == typeof(BuildCommand)) { executor.ExecuteBuildPlace(world, this._entityClass.Id, 1, target); action = HumanAction.Build; } else if (this._commandType == typeof(PlaceCommand)) { executor.ExecutePlace(world, this._entities, target); action = () => HumanAction.ManageEntities(Dialog.ShowEntitiesMode.Unplaced); } } // execute automatic user action, if any if (action == null) { AsyncAction.EndRun(); } else { AsyncAction.BeginInvoke(delegate { action(); AsyncAction.EndRun(); }); } }); }
/// <summary> /// Replay the next history <see cref="Command"/>.</summary> /// <returns> /// <c>true</c> if interactive replay should continue; <c>false</c> if <see cref="Stop"/> /// should be called.</returns> /// <exception cref="InvalidCommandException"> /// The current <see cref="Command"/> contains invalid data.</exception> /// <remarks><para> /// <b>NextCommand</b> fetches the next history command, validates and executes it, and /// increments the command counter as necessary. <b>NextCommand</b> returns <c>false</c> if /// the command history has been exhausted. /// </para><para> /// The <see cref="Command.Source"/> and <see cref="Command.Target"/> sites of each history /// command are scrolled into view on the default <see cref="Session.MapView"/> if the <see /// cref="Options.ReplayOptions.Scroll"/> property of the current <see /// cref="ApplicationOptions"/> instance is <c>true</c>.</para></remarks> private bool NextCommand() { Debug.Assert(this._originalWorldState != null); Debug.Assert(this._commandIndex >= 0); Debug.Assert(!this._commandSkip); // retrieve original command history IList <Command> commands = this._originalWorldState.History.Commands; // stop replay if history exhausted if (this._commandIndex >= commands.Count) { return(false); } MapView mapView = Session.MapView; // retrieve next command in history if (this._command == null) { this._command = commands[this._commandIndex]; // validate & show command this._command.Validate(Session.Instance.WorldState); SessionExecutor.ShowCommand(this._command); // scroll sites into view if desired PointI source = this._command.Source.Location; if (ApplicationOptions.Instance.Game.Replay.Scroll) { PointI target = this._command.Target.Location; AsyncAction.Invoke(() => mapView.ScrollIntoView(source, target)); } // highlight valid source site if (Finder.MapGrid.Contains(source)) { AsyncAction.Invoke(() => mapView.SelectedSite = source); // select first entity if specified EntityReference[] entities = this._command.Entities; if (entities != null && entities.Length > 0) { AsyncAction.Invoke(() => MainWindow.Instance.SelectedEntity = entities[0].Id); } return(true); // show source for a while } } // attempt to execute command this._command.Execute(new ExecutionContext( Session.Instance.WorldState, null, SessionExecutor.ShowCommandEvent)); // check if command cleared by reentrant call if (this._command != null) { // add some delay after commands with message events this._commandSkip = this._command.Program.Exists(x => x is MessageInstruction); // highlight active faction for Begin/EndTurn, otherwise command target if (this._command is BeginTurnCommand || this._command is EndTurnCommand) { ShowFaction(this._command.Faction.Value); } else { ShowSite(this._command.Target.Location); } // ensure command effects are shown AsyncAction.Invoke(mapView.Redraw); } // prepare for next command this._command = null; ++this._commandIndex; return(true); }