//Room MainLocation { get; } public bool CanRecognize(Item item, StringWords input, int start, int end) { //TODO eventually: Features like sight flags and ignore color codes and so on. For now... if (TextParsing.CheckAutoCompleteText(input, item.Name, start, end) || TextParsing.CheckAutoCompleteText(input, item.Description, start, end)) { return(true); } return(false); }
public override void Execute(MOB mob, QueuedCommand command) { StringWords input = command.parsedCommand; int distance = -1; MovementUnit unit; if (input.Segments.Length > 1) { TextParsing.ParseAsDistance(input, out distance, out unit, 1); } //TODO: finish this }
/// <summary> /// Attempt to parse input as a distance. Text inputs must be a number, followed by an optional unit, to be parsed as /// a distance. /// </summary> /// <param name="text">Input to parse</param> /// <param name="mob"></param> /// <param name="distance">Distance to move, in millimeters. If -1, then parsing failed.</param> /// <param name="startIndex">Where the distance is expected to be.</param> /// <returns>Number of words successfully parsed. 0 if failed, up to 2 if succeeded. </returns> public static int ParseAsDistance(StringWords text, out int distance, out MovementUnit unit, int startIndex = 0, int endIndex = 0) { distance = -1; unit = MovementUnit.NoDistance; if (startIndex >= text.Segments.Length) { return(0); } string firstWord, secondWord = null; firstWord = text.Segments[startIndex]; if (startIndex < text.Segments.Length - 1) { secondWord = text.Segments[startIndex + 1]; } int magnitude, distanceType; if (!int.TryParse(firstWord, out magnitude)) { if (secondWord == null || !int.TryParse(secondWord, out magnitude)) { return(0); } //firstWord MUST be a valid distance enum. if (FindAPreposition(text, typeof(Distances), out distanceType, startIndex, startIndex + 1) == -1) { return(0); } } else { if (secondWord == null || FindAPreposition(text, typeof(Distances), out distanceType, startIndex + 1, startIndex + 2) == -1) { distanceType = (int)Distances.Default; secondWord = null; } } if (distanceType >= (int)Distances.Default) { distance = magnitude; unit = distanceType == (int)Distances.Default ? MovementUnit.Unspecified : distanceType == (int)Distances.Step ? MovementUnit.Step : MovementUnit.NoDistance; } else { distance = (int)((magnitude * (long)distanceConversions[distanceType]) / 10); unit = MovementUnit.Absolute; } return(secondWord == null ? 2 : 1); }
public static int FindMatchingWord(StringWords text, string[] options) { foreach (string option in options) { for (int i = 0; i < text.Segments.Length; i++) { if (text.Segments[i].Equals(option, StringComparison.CurrentCultureIgnoreCase)) { return(i); } } } return(-1); }
public static List <Item> FindKnownItems(StringWords input, MOB mob, int start, int end) { input.ValidateEndIndex(ref end); Item[] items = GeneralUtilities.VisibleItems(mob, mob.Body); List <Item> matchingItems = new List <Item>(); foreach (Item item in items) { if (mob.CanRecognize(item, input, start, end)) { matchingItems.Add(item); } } return(matchingItems); }
/// <summary> /// Find the first word in an input that matches a preposition from a list of prepositions /// </summary> /// <param name="text">Input to search</param> /// <param name="typeOfPreposition">Type/List of prepositions</param> /// <param name="prepositionValue">Which preposition was found (can be cast to that preposition). -1 if no preposition found.</param> /// <param name="startIndex">Which word in input to start searching on (inclusive). Defaults to 0 (start of input)</param> /// <param name="endIndex">Which word in input to stop searching on (exclusive). Defaults to -1 (end of input)</param> /// <returns>Index of the first word of the preposition</returns> public static int FindAPreposition(StringWords text, Type typeOfPreposition, out int prepositionValue, int startIndex = 0, int endIndex = -1) //TODO: Replace typeOfPreposition for an easier-to-use type of variable. An enum of enums? { text.ValidateEndIndex(ref endIndex); //Array values = Enum.GetValues(typeOfPreposition); string[][] options = OptionsForPreposition(typeOfPreposition); for (int i = startIndex; i < endIndex; i++) { int j = i; prepositionValue = MatchString(options, text.Segments, ref j); //NOTE: This assumes enums always have default-assigned values (0, 1, 2, etc.) if (prepositionValue != -1) { return(i); } } prepositionValue = -1; return(-1); }
/// <summary> /// Find an item that the specified mob can sense and identify from the given text input. /// </summary> /// <param name="input"></param> /// <param name="mob"></param> /// <param name="start"></param> /// <param name="end"></param> /// <param name="onPrompt"></param> /// <param name="addToPrompt"></param> /// <returns></returns> public static Item FindKnownItem(StringWords input, MOB mob, int start, int end, SelectAction onPrompt = null, StandardHeldPrompt addToPrompt = null) //, out RoomLink viaExit { //viaExit = null; //TODO later: When finding items in other rooms, report what direction the item is in. List <Item> options = FindKnownItems(input, mob, start, end); if (options.Count == 1) { return(options[0]); } Client client = mob.Client; if (client == null) { //TODO: Have an NPC say they don't know what item they were told to find? return(null); } if (options.Count == 0) { client.sendMessage("You don't notice a '" + input.StringRange(start, end) + "'"); return(null); } if (onPrompt == null) { client.sendMessage("There are too many things that fit that description."); return(null); } //TODO: Consider an alternative function to move prompt stuff to and add an out bool for prompting instead, not //sure if there's a need for that bool right now though; leave this one without prompt stuff //TODO: Some way to check if the prompt is still valid would be nice too, but not sure how to do that well without // just requiring another function passed into here. Might do that anyways. SelectFromList <Item> itemSelectPrompt = new SelectFromList <Item>(onPrompt.Invoke); if (addToPrompt == null || !addToPrompt.TransitionTo(itemSelectPrompt, true)) { client.Prompt(itemSelectPrompt); } return(null); }
/// <summary> /// Check if 'input' (or a specific part of it) can be found inside of 'text'. /// </summary> /// <param name="input">Text to search for</param> /// <param name="text">Text to look inside for matches</param> /// <param name="startIndex">First word of 'input' to start searching for</param> /// <param name="endIndex">After last word of 'input' to search for (exclusive end)</param> /// <returns>True if text contains input in the same order.</returns> public static bool CheckAutoCompleteText(StringWords input, string text, int startIndex = 0, int endIndex = -1) { input.ValidateEndIndex(ref endIndex); if (startIndex >= endIndex) { throw new ArgumentException("startIndex must be before endIndex"); } int textIndex = 0; for (int inputIndex = 0; textIndex < text.Length; textIndex++) { string wordToFind = input.Segments[inputIndex]; textIndex = text.IndexOf(wordToFind, textIndex); if (textIndex == -1) { return(false); //Input word was not found. } if (textIndex != 0) { //Check to make sure this could reasonably be the start of a word. If not, continue at the next character. char prevChar = text[textIndex - 1]; //TODO: Finetune below checks. //if (!Char.IsWhiteSpace(prevChar)) continue; if (Char.IsLetterOrDigit(prevChar)) { continue; } } //Found a match. Continue with the next word. inputIndex++; if (inputIndex == endIndex) { return(true); //No next word, found all the words. } textIndex += wordToFind.Length; //Skip the matched text (plus 1 character from loop counter) } //Ran out of letters before matching all the words. return(false); }
public override void Execute(MOB mob, QueuedCommand command) { /* * Form is always (optional direction/distance) (optional preposition) (optional target) * direction/distance can work alone (implies 'from here') * if direction/distance is missing, if preposition is not specific, 'in reach' is implied * preposition requires a target * target can work alone (depends on target. If target defaults to enterable, implies 'in'. Else 'near') * Search for prepositions, get a list of them. * Validate prepositions. Need to have nothing or a valid direction/distance before preposition. * How many can actually be possible? I think only zero or one preposition is really possible? * TODO: Special user input to specify a preposition instead of parser guessing. Mostly goes in StringWords but * matching prepositions/item names needs to respect that too (might work automatically). * Some system similar to quotes probably. Quotes may be useful though, like book titles or something. * If no prepositions, attempt to parse full string as direction/distance. If successful, do that * //Search whole string for item/target matches and associated strings. * In general, 'visible to MOB' items. * Must match to end of string? * Nevermind, this string will be identified but targets will not be searched for until a TryGoEvent is run. * //Find associated strings that line up with a preposition * Instead of above two major points: * Identify all valid strings (start after preposition, go to end of string) * TODO: Special user input to specify a string instead of parser guessing? * This is basically implied by a preposition. However it maybe makes more sense to specify the target instead of the preposition. * //Search all 'visible to MOB' items that exactly match any of the valid strings. * How to pick best match? * Maybe just not go if more than one match found, request more info. prompt? */ StringWords goToInput = command.parsedCommand; //See if there's a preposition int start = 1; //Skip command ("go") int prepValue; MovementPreposition prepEnum; MovementDirection direction = null; TryGoEvent moveEvent = new TryGoEvent(); int index = TextParsing.FindAPreposition(goToInput, typeof(MovementPreposition), out prepValue, start); if (index != -1) { //Is this a valid preposition? Check if it starts correctly. prepEnum = (MovementPreposition)prepValue; if (!prepEnum.AllowDirection() && !prepEnum.AllowDistance()) { if (index != start + 1) { //Invalid preposition for this location, needs to be at start of command. goto noPrepositionFound; } } else if ((prepEnum.RequireDirection() || prepEnum.RequireDistance()) && index == start + 1) { //Invalid preposition for this location, needs to come after a direction/distance. goto noPrepositionFound; } else { direction = TextParsing.ParseAsDirection(goToInput, start, ref index, prepEnum.RequireDirection(), prepEnum.RequireDistance(), prepEnum.AllowDirection(), prepEnum.AllowDistance()); if (direction == null) //Not a valid preposition. Ignore it. { goto noPrepositionFound; } else //Maybe valid preposition. { //Check if preposition matches direction more closely? I think the previous checks have finished that successfully at this point. //Valid preposition. Parse the REST of the sentence as an object. start = index; goto startEvent; } } } else { //Is the whole thing a direction? direction = TextParsing.ParseAsDirection(goToInput, start, ref index); if (direction != null) { if (direction.direction != Directions.NoDirection && index == goToInput.Segments.Length) { //Mark it as ONLY a direction, no destination (goToInput has been completely parsed/consumed) goToInput = null; //goto startEvent; } else { //Can't go only a distance with NoDirection. Cancel this parsing. direction = null; } } //Else try to parse the whole thing as an object to go to } //Text parsing has decided what words go to what (i.e. direction vs. target). //Text parsing has NOT identified objects yet. noPrepositionFound: moveEvent.targetDescription = goToInput; start = 1; prepEnum = MovementPreposition.To; // null; ? startEvent: //foundPreposition moveEvent.targetDescriptionStart = start; moveEvent.direction = direction; moveEvent.relation = prepEnum; moveEvent.SetMoveSource(mob, null); //Raw command parsing done, target parsing has not been done. //Run the event to try to find a path to the target. moveEvent.FullRunEvent(); }
/// <summary> /// Attempt to parse user's input text as a direction / distance. /// </summary> /// <param name="text">Full input to parse</param> /// <param name="startIndex">Index of first word to try to parse (inclusive). </param> /// <param name="endIndex">Index of last word to try to parse (exclusive). If the text is found, this will be updated /// to after the last word found (first index not belonging to the MovementDirection)</param> /// <param name="requireDirection">If true, parsing will return failure if no direction component is found.</param> /// <param name="requireDistance">If true, parsing will return failure if no distance component is found.</param> /// <param name="getDistance">If true, distance may be parsed. This is ignored (assumed true) if requireDistance is true.</param> /// <returns>The direction and distance parsed from the input text.</returns> public static MovementDirection ParseAsDirection(StringWords text, int startIndex, ref int endIndex, bool requireDirection = false, bool requireDistance = false, bool getDirection = true, bool getDistance = true) { text.ValidateEndIndex(ref endIndex); getDistance |= requireDistance; Directions foundDirection = Directions.NoDirection; //List<Directions> directions = new List<Directions>(); KeyValuePair <string[][], Directions[]> directionOptions = DirectionCommand.GetDirectionOptions(); string[][] directionStrings = directionOptions.Key; Directions[] directionValues = directionOptions.Value; int foundDistance = -1; MovementUnit foundUnit = MovementUnit.NoDistance; int i = startIndex; for (; i < endIndex; i++) { string word = text.Segments[i]; if (foundDirection == Directions.NoDirection) { int found = MatchString(directionStrings, text.Segments, ref i); if (found != -1) { foundDirection = directionValues[found]; //Loop will increment but i is already the correct value after MatchString, i--; //decrement here to 'skip' the increment. continue; } } if (getDistance && foundUnit == MovementUnit.NoDistance) { int distanceWords = ParseAsDistance(text, out foundDistance, out foundUnit, i, Math.Min(i + 1, endIndex - 1)); if (distanceWords != 0) { if (distanceWords == 2) { i++; } continue; } } break; } if (foundDirection == Directions.NoDirection && requireDirection) { return(null); } if (foundUnit == MovementUnit.NoDistance && requireDistance) { return(null); } if (foundDirection == Directions.NoDirection && foundUnit == MovementUnit.NoDistance) { return(null); } endIndex = i; return(new MovementDirection() { direction = foundDirection, distanceCount = foundDistance, distanceUnit = foundUnit }); }