From 45d8324cb3f1b0d45f195997463108a661e3310b Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 1 May 2022 18:02:30 -0400 Subject: [PATCH] Option to have character's face the player (#83) Implements #83 --- NewHorizons/Builder/Props/DialogueBuilder.cs | 83 +++++++++++++++++++ NewHorizons/Builder/Props/PropBuildManager.cs | 1 + NewHorizons/External/PropModule.cs | 2 + NewHorizons/schema.json | 9 ++ 4 files changed, 95 insertions(+) diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index 4c9e6bd8..ce80363f 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -16,10 +16,16 @@ namespace NewHorizons.Builder.Props { public static void Make(GameObject go, Sector sector, PropModule.DialogueInfo info, IModBehaviour mod) { + // In stock I think they disable dialogue stuff with conditions + // Here we just don't make it at all if (info.blockAfterPersistentCondition != null && PlayerData._currentGameSave.GetPersistentCondition(info.blockAfterPersistentCondition)) return; var dialogue = MakeConversationZone(go, sector, info, mod.ModHelper); if (info.remoteTriggerPosition != null) MakeRemoteDialogueTrigger(go, sector, info, dialogue); + + // Make the character look at the player + // Useful for dialogue replacement + if (!string.IsNullOrEmpty(info.pathToAnimController)) MakePlayerTrackingZone(go, dialogue, info); } public static void MakeRemoteDialogueTrigger(GameObject go, Sector sector, PropModule.DialogueInfo info, CharacterDialogueTree dialogue) @@ -81,6 +87,83 @@ namespace NewHorizons.Builder.Props return dialogueTree; } + public static void MakePlayerTrackingZone(GameObject go, CharacterDialogueTree dialogue, PropModule.DialogueInfo info) + { + var character = go.transform.Find(info.pathToAnimController); + + // At most one of these should ever not be null + var nomaiController = character.GetComponent(); + var controller = character.GetComponent(); + + var lookOnlyWhenTalking = info.lookAtRadius <= 0; + + // To have them look when you start talking + if (controller != null) + { + controller._dialogueTree = dialogue; + controller.lookOnlyWhenTalking = lookOnlyWhenTalking; + } + else if(nomaiController != null) + { + if (lookOnlyWhenTalking) + { + dialogue.OnStartConversation += nomaiController.StartWatchingPlayer; + dialogue.OnEndConversation += nomaiController.StopWatchingPlayer; + } + } + else + { + // TODO: make a custom controller for basic characters to just turn them to face you + } + + if (info.lookAtRadius > 0) + { + GameObject playerTrackingZone = new GameObject("PlayerTrackingZone"); + playerTrackingZone.SetActive(false); + + playerTrackingZone.layer = LayerMask.NameToLayer("BasicEffectVolume"); + playerTrackingZone.SetActive(false); + + var sphereCollider = playerTrackingZone.AddComponent(); + sphereCollider.radius = info.lookAtRadius; + sphereCollider.isTrigger = true; + + playerTrackingZone.AddComponent(); + + var triggerVolume = playerTrackingZone.AddComponent(); + + if (controller) + { + // Since the Awake method is CharacterAnimController was already called + if (controller.playerTrackingZone) + { + controller.playerTrackingZone.OnEntry -= controller.OnZoneEntry; + controller.playerTrackingZone.OnExit -= controller.OnZoneExit; + } + // Set it to use the new zone + controller.playerTrackingZone = triggerVolume; + triggerVolume.OnEntry += controller.OnZoneEntry; + triggerVolume.OnExit += controller.OnZoneExit; + } + // Simpler for the Nomai + else if(nomaiController) + { + triggerVolume.OnEntry += (_) => nomaiController.StartWatchingPlayer(); + triggerVolume.OnExit += (_) => nomaiController.StopWatchingPlayer(); + } + // No controller + else + { + // TODO + } + + playerTrackingZone.transform.parent = dialogue.gameObject.transform; + playerTrackingZone.transform.localPosition = Vector3.zero; + + playerTrackingZone.SetActive(true); + } + } + private static void AddTranslation(string xml) { XmlDocument xmlDocument = new XmlDocument(); diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index 3678956c..8b1c696d 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -56,6 +56,7 @@ namespace NewHorizons.Builder.Props VolcanoBuilder.Make(go, sector, volcanoInfo); } } + // Reminder that dialogue has to be built after props if they're going to be using CharacterAnimController stuff if (config.Props.Dialogue != null) { foreach(var dialogueInfo in config.Props.Dialogue) diff --git a/NewHorizons/External/PropModule.cs b/NewHorizons/External/PropModule.cs index a92827ac..cc60bdf9 100644 --- a/NewHorizons/External/PropModule.cs +++ b/NewHorizons/External/PropModule.cs @@ -76,6 +76,8 @@ namespace NewHorizons.External public string xmlFile; public MVector3 remoteTriggerPosition; public string blockAfterPersistentCondition; + public string pathToAnimController; + public float lookAtRadius; } public class RevealInfo diff --git a/NewHorizons/schema.json b/NewHorizons/schema.json index 7241cc67..e6c30389 100644 --- a/NewHorizons/schema.json +++ b/NewHorizons/schema.json @@ -667,6 +667,15 @@ "blockAfterPersistentCondition": { "type": "string", "description": "Prevents the dialogue from being created after a specific persistent condition is set. Useful for remote dialogue triggers that you want to have happen only once." + }, + "pathToAnimController": { + "type": "string", + "description": "If this dialogue is meant for a character, this is the relative path from the planet to that character's CharacterAnimController or SolanumAnimController." + }, + "lookAtRadius": { + "type": "number", + "default": 0, + "description": "If a pathToAnimController is supplied, if you are within this distance the character will look at you. If it is set to 0, they will only look at you when spoken to." } } }