public DisambigCommandHandler( Actor Actor, CommandParser.MatchedCommand MatchedCommand, ParserCommandHandler ParentHandler) { this.ParentHandler = ParentHandler; this.MatchedCommand = MatchedCommand; // Find an object parameter such that // a) each match has a parameter with that name // b) at least one match has a value different from the others var foundAmbiguousArgument = false; // Note that we iterate only over the arguments in the first match. If the first match doesn't have the // argument, that argument can't satisfy condition a above. foreach (var argument in MatchedCommand.Matches[0]) { // It's only possible to disambiguate on MudObjects. If any match - including the first one - has // a value for that argument that is not a MudObject, disambiguation will fail. if (argument.Value is MudObject) { var uniqueMatchables = new List <MudObject>(); var rejected = false; foreach (var match in MatchedCommand.Matches) { if (match.ContainsKey(argument.Key) && match[argument.Key] is MudObject) { var matchableObject = match[argument.Key] as MudObject; if (!uniqueMatchables.Contains(matchableObject)) { uniqueMatchables.Add(matchableObject); } } else { rejected = true; break; } } if (!rejected && uniqueMatchables.Count > 1) { // Disambiguate on this object. DisambigArgument = argument.Key; DisambigObjects = uniqueMatchables; foundAmbiguousArgument = true; break; } } } if (foundAmbiguousArgument) { var response = new StringBuilder(); response.Append("Which did you mean?\r\n"); for (var i = 0; i < DisambigObjects.Count; ++i) { response.Append(String.Format("{0}: {1}\r\n", i, Core.GlobalRules.ConsiderValueRule <String>("printed name", Actor, DisambigObjects[i], "the"))); } MudObject.SendMessage(Actor, response.ToString()); } else { MudObject.SendMessage(Actor, "I couldn't figure out how to disambiguate that command."); } }
public MatchedCommand ParseCommand(PendingCommand Command) { var tokens = new LinkedList <String>(Command.RawCommand.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries)); var rootMatch = new PossibleMatch(tokens.First); rootMatch.Upsert("ACTOR", Command.Actor); rootMatch.Upsert("LOCATION", Command.Actor == null ? null : Command.Actor.Location); // A pending command can have some properties set when it is queued. For example, the 'go' action // generates a 'look' command after processing. It will give the 'look' command a property named // 'auto' to indicate that the look command was generated. Rules can then use this property to // implement behavior. From the same example, 'look' may emit a brief description if it is generated // by the 'go' action, but emit a more detailed description if the player enters the command 'look'. // See StandardActions.Go if (Command.PreSettings != null) { foreach (var setting in Command.PreSettings) { rootMatch.Upsert(setting.Key.ToUpper(), setting.Value); } } var matchContext = new MatchContext { ExecutingActor = Command.Actor }; // Try every single command defined, until one matches. foreach (var command in Commands) { IEnumerable <PossibleMatch> matches; try { matches = command.Matcher.Match(rootMatch, matchContext); } catch (MatchAborted ma) { // The match didn't fail; it generated an error. These means the match progressed to a point // where the author of the command felt that the input could not logically match any other // command, however, the input was still malformed in some way. Abort matching, and dummy up // a command entry to display the error message to the player. return(new MatchedCommand( new CommandEntry().ProceduralRule((match, actor) => { MudObject.SendMessage(actor, ma.Message); return PerformResult.Continue; }), // We need a fake match just so it can be passed to the procedural rule. new PossibleMatch[] { new PossibleMatch(null) })); } // Only accept matches that consumed all of the input. matches = matches.Where(m => m.Next == null); // If we did consume all of the input, we will assume this match is successful. Note it is // possible for a command to generate multiple matches, but not possible to match multiple commands. if (matches.Count() > 0) { return(new MatchedCommand(command, matches)); } } return(null); }