// Return the modification's value e.g. <speed=0.1> return 0.1. Not the tags content private object parseModValue(Modifications modType, string commandText) { // We're already a string if (modType == Modifications.SEND_MESSAGE || modType == Modifications.REMOVE_VARAIBLE || modType == Modifications.CHANGE_SPRITE || modType == Modifications.ACTION || modType == Modifications.ACTION_WITH_MESSAGE || modType == Modifications.ACTION_WITH_TARGET || modType == Modifications.LOG || modType == Modifications.LOG_WARNING || modType == Modifications.LOG_ERROR || modType == Modifications.CHANGE_THEME || modType == Modifications.BG_CONVERSATION) { return(commandText); } // Parse float else if (modType == Modifications.SPEED || modType == Modifications.WAIT) { var floatVal = 0f; if (float.TryParse(commandText, out floatVal)) { return(floatVal); } } DialogueLogger.LogError($"Couldn't parse parameter value for {modType} text modification"); return(null); }
// Progress in the conversation protected void progress() { if (_currentSentence >= _currentDialogue.Sentences.Count - 1) { // Progress to the next dialogue if (_currentDialogue.NextId != -1) { var nextDialogue = _conversation.Dialogues.Find(d => d.Id == _currentDialogue.NextId); if (nextDialogue == null) { DialogueLogger.LogError($"Trying to navigate to a dialogue with the Id {_currentDialogue.NextId}, but there's isn't one present in the current conversation"); return; } _currentDialogue = nextDialogue; startDialogue(); } else { finishedConversation(); } } else { // There's more to show _currentSentence++; StartCoroutine(showSentence(parseSentenceForCustomTags(_currentDialogue.Sentences[_currentSentence]))); } }
// Pre cast the variables for quicker retrieval public void Cast() { if (!FromRepo) { switch (Type.ToLower()) { case "short": _castValue = short.Parse(Value); break; case "int": _castValue = int.Parse(Value); break; case "long": _castValue = long.Parse(Value); break; case "float": _castValue = float.Parse(Value); break; case "bool": _castValue = bool.Parse(Value); break; case "string": _castValue = Value; break; default: DialogueLogger.LogError($"Unsupported type {Type} using in variable"); break; } } }
// Retrieve the sprite from the repo and apply it if it exists private void findAndChangeSprite(string name) { var imageSprite = getSprite(name, _elementsName); var buttonSprite = getSprite(name, _buttonElementName); if (imageSprite == null) { DialogueLogger.LogError($"GameObject with the name {gameObject.name} can't change sprite with theme, {name} doesn't contain a sprite for {_elementsName}. Skipping"); } else { _thisImage.sprite = imageSprite; } if (buttonSprite == null) { DialogueLogger.LogError($"GameObject with the name {gameObject.name} can't change sprite with theme, {name} doesn't contain a sprite for {_buttonElementName}. Skipping"); } else { var tempState = new SpriteState(); switch (_buttonStateToChange) { case buttonStates.HIGHLIGHT: tempState.highlightedSprite = buttonSprite; break; case buttonStates.PRESSED: tempState.pressedSprite = buttonSprite; break; case buttonStates.SELECTED: tempState.selectedSprite = buttonSprite; break; case buttonStates.DISABLED: tempState.disabledSprite = buttonSprite; break; } _thisButton.spriteState = tempState; } }
// Initialize private void Start() { _thisButton = GetComponent <Button>(); _thisImage = _thisButton?.image; // Check we've got a name before registering for the action if (string.IsNullOrEmpty(_elementsName) || string.IsNullOrEmpty(_buttonElementName)) { DialogueLogger.LogError($"GameObject with the name {gameObject.name} needs an Elements Name and Button Element Name value to be able to change theme"); return; } // Check we've got the components if (_thisImage == null || _thisButton == null) { DialogueLogger.LogError($"GameObject with the name {gameObject.name} needs an Image and Button component to be able to change button theme"); return; } // Check the button is using sprites if (_thisButton.transition != Selectable.Transition.SpriteSwap) { DialogueLogger.LogError($"GameObject with the name {gameObject.name}'s Button component needs to be in SpriteSwap transition mode to be able to change button theme"); return; } DialogueController.Instance.ThemeChanged += findAndChangeSprite; }
// Start the conversation if possible public void StartConversation(Conversation conversation) { if (_inConversation) { DialogueLogger.LogWarning($"Trying to start a conversation while already in a conversation"); return; } if (_uiController == null) { DialogueLogger.LogError("Trying to start a conversation, but there's not DialogueUIController assigned"); return; } // Find starting point _currentConversation = null; _currentDialogue = null; _currentSentence = 0; foreach (var diag in conversation.Dialogues) { if ((_currentDialogue == null || diag.Id > _currentDialogue.Id) && diag.CanBeUsedAsStartingPoint) { if (diag.StartConditions.Count > 0) { if (diag.EvaluateStartingConditions()) { _currentDialogue = diag; } } else { _currentDialogue = diag; } } } if (_currentDialogue == null) { DialogueLogger.LogError($"Couldn't find starting point for conversation"); return; } // Start the conversation _inConversation = true; _currentConversation = conversation; if (needToChangeTheme()) { ChangeTheme(_currentDialogue.Theme); } _uiController.ShowSentence(_currentDialogue.SpeakersName, parseSentenceForCustomTags(_currentDialogue.Sentences[_currentSentence]), SpriteRepo.Instance.RetrieveSprite(_currentDialogue.CharacterSpritesName, string.IsNullOrEmpty(_currentDialogue.StartingSprite) ? "Default" : _currentDialogue.StartingSprite), _currentDialogue.AutoProceed); ConversationStarted?.Invoke(); }
public T GetContent <T>() { if (ModificationContent is T) { return((T)ModificationContent); } DialogueLogger.LogError($"Trying to cast variable of type {ModificationContent.GetType()} to type {typeof(T)}. Returning default"); return(default(T)); }
// Used to return the name=value pair if possible private string[] getTagNameValuePair(string text, char separator) { var tempSplit = text.Split(separator); if (tempSplit.Length != 2) { DialogueLogger.LogError($"Cannot parse command {text}"); return(null); } return(tempSplit); }
// Get the variable if it exists and we don't care about the type public object Retrieve(string name) { if (!_variables.ContainsKey(name)) { DialogueLogger.LogWarning($"Trying to retrieve the variable {name} but it hasn't been registered"); return(null); } else { return(_variables[name].Value); } }
// Add a theme to the repo if it's valid public void RegisterThemeSprites(Theme_SO sprites) { if (_themeSprites.ContainsKey(sprites.Name)) { DialogueLogger.LogWarning($"Theme sprites for {sprites.Name} already exist, overwritting"); } if (validateSprites(sprites)) { _themeSprites[sprites.Name] = sprites; } }
// Add the sprites to the repo ready for retrieval public void RegisterCharacterSprites(DialogueSprites_SO sprites) { if (_characterSprites.ContainsKey(sprites.CharactersName)) { DialogueLogger.LogWarning($"Character sprites for {sprites.CharactersName} already exist, overwritting"); } if (validateSprites(sprites)) { _characterSprites[sprites.CharactersName] = sprites; } }
// Remove the variable if it exists public void Remove(string name) { if (!_variables.ContainsKey(name)) { DialogueLogger.LogError($"Trying to remove a variable {name} but it hasn't been registered"); return; } else { _variables.Remove(name); VariableRemoved(name); } }
// Return the variable if it exists public T Retrieve <T>(string name) { // I don't like returning a default like this, but it's better than an exception if (!_variables.ContainsKey(name)) { DialogueLogger.LogWarning($"Trying to retrieve the variable {name} but it hasn't been registered. Returning default"); return(default(T)); } else { return(_variables[name].GetValue <T>()); } }
// Get the conversation from the repo if it exists protected Conversation findConversation(string convoName) { // Check if repo exists and the conversation has been loaded var tempoConvo = ConversationRepo.Instance?.RetrieveConversation(convoName); if (tempoConvo == null) { DialogueLogger.LogError($"Tried to start conversation {convoName} but it doesn't exist is the ConversationRepo"); return(null); } return(tempoConvo); }
// Find the action in the conversation then pass it on for validating and execution public static void PerformAction(Conversation conversation, string actionName) { var action = getAction(conversation, actionName); if (action != null) { performAction(action); } else { DialogueLogger.LogError("Cannot find the selectedAction for the option selected. Skipping action"); } }
// Validate the action private static bool validateAction(DialogueAction action) { // Check we've got all the pieces we neewd switch (action.ActionType) { case DialogueAction.Types.LOG: case DialogueAction.Types.LOG_WARNING: case DialogueAction.Types.LOG_ERROR: // All we need's a message if (string.IsNullOrEmpty(action.Message) || string.IsNullOrWhiteSpace(action.Message)) { DialogueLogger.LogError($"An action with the name {action.Name} is trying to log with an empty message value"); return(false); } break; case DialogueAction.Types.SEND_MESSAGE: // We need a target and a message if (string.IsNullOrEmpty(action.Message) || string.IsNullOrWhiteSpace(action.Message)) { DialogueLogger.LogError($"An action with the name {action.Name} is trying to send a message with an empty message value"); return(false); } if (string.IsNullOrEmpty(action.Target) || string.IsNullOrWhiteSpace(action.Target)) { DialogueLogger.LogError($"An action with the name {action.Name} is trying to send a message with an empty target value"); return(false); } break; case DialogueAction.Types.CHANGE_THEME: // All we need's a message if (string.IsNullOrEmpty(action.Message) || string.IsNullOrWhiteSpace(action.Message)) { DialogueLogger.LogError($"An action with the name {action.Name} is trying to change the theme with an empty message value"); return(false); } break; case DialogueAction.Types.START_BG_CONVERSATION: // Just need message, the name of the conversation to start if (string.IsNullOrEmpty(action.Message) || string.IsNullOrWhiteSpace(action.Message)) { DialogueLogger.LogError($"An action with the name {action.Name} is trying to start a background conversation with an empty message value"); return(false); } break; } return(true); }
// Do we need to know the type when retrieving for evaluation? public object GetValue() { if (FromRepo) { return(VariableRepo.Instance.Retrieve(Name)); } if (_castValue == null) { DialogueLogger.LogError("Trying to retrieve a variable value for comparison that is null"); } return(_castValue); }
// Navigates to a dialogue inside the current conversation private void goToDialogue(int index) { var nextDialogue = _currentConversation.Dialogues.Find(d => d.Id == index); if (nextDialogue == null) { DialogueLogger.LogError($"Trying to navigate to a dialogue with the Id {index}, but there's isn't one present in the current conversation"); return; } _lastSpeaker = _currentDialogue.SpeakersName; _currentDialogue = nextDialogue; _currentSentence = -1; // Incremented in Next() Next(); }
// Initialize public virtual void Initialize(Conversation conversation) { _conversation = conversation; findStartingPoint(conversation); if (_currentDialogue == null) { DialogueLogger.LogError($"Couldn't find starting point for background conversation"); finishedConversation(); return; } BackgroundDialogueController.Instance.CloseConversation += finishedConversation; startDialogue(); }
// Retrieve a sprite from a registered theme public Sprite RetrieveSprite(string themeName, string spriteName) { if (string.IsNullOrEmpty(themeName)) { DialogueLogger.Log("Trying to retrieve sprite from repo, but the themeName parameter is empty"); return(null); } if (!_themeSprites.ContainsKey(themeName)) { DialogueLogger.LogError($"Trying to retrieve sprite {themeName} for the theme {themeName}, but it is not registered in the repo."); return(null); } // Seems to be consistently faster than Linq return(_themeSprites[themeName].ThemeSprites.Find(s => s.Name == spriteName)?.Sprite); }
// Return the sprite for the character if found public Sprite RetrieveSprite(string character, string name = "Default") { if (string.IsNullOrEmpty(character)) { DialogueLogger.Log("Trying to retrieve sprite from repo, but the character parameter is empty"); return(null); } if (!_characterSprites.ContainsKey(character)) { DialogueLogger.LogError($"trying to retrieve sprite {name} for the character {character}, but it is not registered in the repo."); return(null); } // Seems to be consistently faster than Linq return(_characterSprites[character].CharacterSprites.Find(s => s.Name == name)?.Sprite); }
// Validate then execute private static void performAction(DialogueAction action) { if (!validateAction(action)) { return; } switch (action.ActionType) { case DialogueAction.Types.LOG: DialogueLogger.Log(action.Message); break; case DialogueAction.Types.LOG_WARNING: DialogueLogger.LogWarning(action.Message); break; case DialogueAction.Types.LOG_ERROR: DialogueLogger.LogError(action.Message); break; case DialogueAction.Types.CLOSE_CONVERSATION: DialogueController.Instance?.StopCurrentConversation(); break; case DialogueAction.Types.SEND_MESSAGE: var targetObject = GameObject.Find(action.Target); if (targetObject == null) { DialogueLogger.LogError($"Trying to execute a send message action, but GameObject {action.Target} was not found. Skipping action"); return; } targetObject.SendMessage(action.Message, SendMessageOptions.DontRequireReceiver); break; case DialogueAction.Types.CHANGE_THEME: DialogueController.Instance?.ChangeTheme(action.Message); break; case DialogueAction.Types.CLOSE_BG_CONVERSATIONS: BackgroundDialogueController.Instance?.CloseConversations(); break; case DialogueAction.Types.START_BG_CONVERSATION: BackgroundDialogueController.Instance?.StartConversation(action.Message); break; default: DialogueLogger.LogError($"Action with the name {action.Name} has na unrecognised action type {action.ActionType}. The action type loaded from the conversation JSON is {action.Type}. Skipping action"); break; } }
// Sends the conversation to the repo public void LoadConversation() { // Can't load if there's no name if (string.IsNullOrEmpty(_name)) { DialogueLogger.LogError($"Object {gameObject.name} cannot register a conversation without a name"); return; } // Can't load if there's no conversation if (_file == null) { DialogueLogger.LogError($"Object {gameObject.name} has an empty conversation file"); return; } ConversationRepo.Instance.RegisterConversation(_name, _file); }
// Already found, just start public void StartConversation(Conversation conversation) { if (_uiControllerPrefab == null) { DialogueLogger.LogError("Trying to start a background conversation, but there's not BaseBackgroundDialogueUIController assigned"); return; } var tempObject = Instantiate(_uiControllerPrefab).GetComponent <BaseBackgroundDialogueUIController>(); if (tempObject == null) { DialogueLogger.LogError($"Background conversation spawned does't have a component of type BaseBackgroundDialogueUIController"); return; } tempObject.Initialize(conversation); }
// Registers a complex modification private void registerComplexModification(Modifications modType, string value, string content, int startingIndex) { // Would've already check the tag is custom to get this far, the only thing that's really required is the value, should definitely be able to handle empty content if (string.IsNullOrWhiteSpace(value) || string.IsNullOrEmpty(value)) { DialogueLogger.LogError($"Trying to parse custom tag {modType}, but it has an empty value"); return; } var modValue = parseModValue(modType, value); if (modValue != null) { _modifications.Add(new ComplexModification { Index = startingIndex, ModType = modType, ModificationValue = modValue, ModificationContent = content }); } }
// Parse type. Done this was instead of using JSONConverter to spit out an error that's a bit more helpful public bool GetActionType() { // Figure type switch (Type.ToLower()) { case "log": ActionType = Types.LOG; break; case "log_warning": case "log warning": case "logwarning": ActionType = Types.LOG_WARNING; break; case "log_error": case "log error": case "logerror": ActionType = Types.LOG_ERROR; break; case "close_conversation": case "close conversation": case "closeconversation": ActionType = Types.CLOSE_CONVERSATION; break; case "send_message": case "send message": case "sendmessage": ActionType = Types.SEND_MESSAGE; break; case "change_theme": case "change theme": case "changetheme": ActionType = Types.CHANGE_THEME; break; case "close_bg_conversations": case "close bg conversations": case "closebgconversations": ActionType = Types.CLOSE_BG_CONVERSATIONS; break; case "start_bg_conversation": case "start bg conversation": case "startbgconversation": ActionType = Types.START_BG_CONVERSATION; break; default: DialogueLogger.LogError($"Unsupported action type {Type} found in action with the name {Name}."); break; } return(true); }
// Get the right implementation of IComparison from the Comparison variable private void getComparer() { switch (Comparison) { case ">": _comparer = new GreaterThan(); break; case "<": _comparer = new LessThan(); break; case ">=": _comparer = new GreateOrEqualTo(); break; case "<=": _comparer = new LessToEqualTo(); break; case "==": _comparer = new EqualTo(); break; case "!=": _comparer = new NotEqualTo(); break; default: DialogueLogger.LogError($"Unsupported comparison operator {Comparison} used"); break; } }
// Registration through the dialogue system will always be a string, preparse as variableType and register public void Register(string name, string variable, TypeCode variableType) { // We're already of type string if (variableType == TypeCode.String) { Register(name, variable); return; } // I don't like this, there's got to be a better way of doing it try { object castVariable = null; switch (variableType) { case TypeCode.Int16: castVariable = short.Parse(variable); break; case TypeCode.Int32: castVariable = int.Parse(variable); break; case TypeCode.Int64: castVariable = long.Parse(variable); break; case TypeCode.Single: castVariable = float.Parse(variable); break; case TypeCode.Boolean: castVariable = bool.Parse(variable); break; } Register(name, castVariable); } catch (Exception e) { DialogueLogger.LogError($"Error registering variable {variable} to the type {variableType}. Error message: {e.Message}"); } }
// Find and start the conversation public void StartConversation(string convoName) { var tempConvo = findConversation(convoName); if (tempConvo == null) { return; } // Check if it's a background conversation if (tempConvo.ConversationType == Conversation.Types.BACKGROUND) { DialogueLogger.Log($"Trying to start background conversation {convoName} as a default conversation, redirecting to BackgroundConversationController."); BackgroundDialogueController.Instance?.StartConversation(tempConvo); return; } StartConversation(tempConvo); }
// Find in the repo and start public void StartConversation(string convoName) { var tempConvo = findConversation(convoName); if (tempConvo == null) { return; } // Check if it's a normal conversation and redirect if (tempConvo.ConversationType == Conversation.Types.DEFAULT) { DialogueLogger.Log($"Trying to start conversation {convoName} as a background conversation, redirecting to the default DialogueController"); DialogueController.Instance?.StartConversation(tempConvo); return; } StartConversation(tempConvo); }