2021-02-19 17:33:02 +01:00
|
|
|
using System;
|
2021-02-22 16:29:17 +01:00
|
|
|
using System.Collections.Generic;
|
2021-03-04 19:53:12 +01:00
|
|
|
using System.Linq;
|
2021-03-05 16:34:52 +01:00
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
using FMOD;
|
2021-02-23 14:05:42 +01:00
|
|
|
using FMOD.Studio;
|
2021-03-05 16:34:52 +01:00
|
|
|
using FMODUnity;
|
2021-03-11 21:25:09 +01:00
|
|
|
using Ktyl.Util;
|
2021-02-19 17:33:02 +01:00
|
|
|
using UnityEngine;
|
2021-03-05 16:34:52 +01:00
|
|
|
using UnityEngine.Animations.Rigging;
|
2021-03-11 21:25:09 +01:00
|
|
|
using UnityEngine.InputSystem;
|
|
|
|
using UnityEngine.InputSystem.Users;
|
2021-03-05 16:34:52 +01:00
|
|
|
using UnityEngine.Networking;
|
|
|
|
using Debug = UnityEngine.Debug;
|
2021-03-05 19:22:18 +01:00
|
|
|
using STOP_MODE = FMOD.Studio.STOP_MODE;
|
2021-02-19 17:33:02 +01:00
|
|
|
#if UNITY_EDITOR
|
|
|
|
using UnityEditor;
|
2021-02-22 16:29:17 +01:00
|
|
|
|
2021-02-19 17:33:02 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
[CreateAssetMenu(menuName = "KernelPanic/Dialogue/Dialogue System")]
|
|
|
|
public partial class DialogueSystem : ScriptableObject
|
|
|
|
{
|
|
|
|
[SerializeField] private DialogueSettings _settings;
|
2021-02-22 16:29:17 +01:00
|
|
|
|
2021-02-19 17:33:02 +01:00
|
|
|
// https://stackoverflow.com/questions/2282476/actiont-vs-delegate-event
|
|
|
|
public event EventHandler<DialogueLine> onDialogueLine;
|
|
|
|
|
2021-03-02 14:24:18 +01:00
|
|
|
// a list of dialogue keys that have already been spoken
|
2021-03-05 16:34:52 +01:00
|
|
|
private readonly List<string> _usedClips = new List<string>();
|
|
|
|
|
|
|
|
private EVENT_CALLBACK _dialogueCallback;
|
2021-05-14 20:08:27 +02:00
|
|
|
private EventInstance _radioInstance;
|
|
|
|
private EventInstance _bazInstance;
|
|
|
|
|
2021-03-11 21:25:09 +01:00
|
|
|
private string _currentControlScheme;
|
2021-02-22 16:29:17 +01:00
|
|
|
|
|
|
|
private void OnEnable()
|
|
|
|
{
|
2021-03-05 16:34:52 +01:00
|
|
|
_usedClips.Clear();
|
|
|
|
_dialogueCallback = DialogueEventCallback;
|
|
|
|
}
|
|
|
|
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(EVENT_CALLBACK))]
|
|
|
|
private static RESULT DialogueEventCallback(EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
|
|
|
|
{
|
|
|
|
var instance = new EventInstance(instancePtr);
|
|
|
|
|
|
|
|
// retrieve user data
|
|
|
|
instance.getUserData(out IntPtr stringPtr);
|
|
|
|
|
|
|
|
// get string obejct
|
|
|
|
var stringHandle = GCHandle.FromIntPtr(stringPtr);
|
|
|
|
var key = stringHandle.Target as string;
|
|
|
|
|
|
|
|
switch (type)
|
2021-02-22 16:29:17 +01:00
|
|
|
{
|
2021-03-05 16:34:52 +01:00
|
|
|
case EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
|
|
|
|
{
|
|
|
|
MODE soundMode = MODE.LOOP_NORMAL | MODE.CREATECOMPRESSEDSAMPLE | MODE.NONBLOCKING;
|
|
|
|
var parameter =
|
|
|
|
(PROGRAMMER_SOUND_PROPERTIES) Marshal.PtrToStructure(parameterPtr,
|
|
|
|
typeof(PROGRAMMER_SOUND_PROPERTIES));
|
|
|
|
|
|
|
|
if (key.Contains("."))
|
|
|
|
{
|
|
|
|
Sound dialogueSound;
|
|
|
|
var soundResult = RuntimeManager.CoreSystem.createSound(Application.streamingAssetsPath + "/" + key,
|
|
|
|
soundMode, out dialogueSound);
|
|
|
|
if (soundResult == RESULT.OK)
|
|
|
|
{
|
|
|
|
parameter.sound = dialogueSound.handle;
|
|
|
|
parameter.subsoundIndex = -1;
|
|
|
|
Marshal.StructureToPtr(parameter, parameterPtr, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SOUND_INFO dialogueSoundInfo;
|
|
|
|
var keyResult = RuntimeManager.StudioSystem.getSoundInfo(key, out dialogueSoundInfo);
|
|
|
|
if (keyResult != RESULT.OK)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Sound dialogueSound;
|
|
|
|
var soundResult = RuntimeManager.CoreSystem.createSound(dialogueSoundInfo.name_or_data,
|
|
|
|
soundMode | dialogueSoundInfo.mode, ref dialogueSoundInfo.exinfo, out dialogueSound);
|
|
|
|
if (soundResult == RESULT.OK)
|
|
|
|
{
|
|
|
|
parameter.sound = dialogueSound.handle;
|
|
|
|
parameter.subsoundIndex = dialogueSoundInfo.subsoundindex;
|
|
|
|
Marshal.StructureToPtr(parameter, parameterPtr, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
|
|
|
|
{
|
|
|
|
var parameter =
|
|
|
|
(PROGRAMMER_SOUND_PROPERTIES) Marshal.PtrToStructure(parameterPtr,
|
|
|
|
typeof(PROGRAMMER_SOUND_PROPERTIES));
|
|
|
|
var sound = new Sound(parameter.sound);
|
|
|
|
sound.release();
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EVENT_CALLBACK_TYPE.DESTROYED:
|
|
|
|
{
|
|
|
|
stringHandle.Free();
|
|
|
|
break;
|
|
|
|
}
|
2021-02-22 16:29:17 +01:00
|
|
|
}
|
2021-03-05 16:34:52 +01:00
|
|
|
|
|
|
|
return RESULT.OK;
|
2021-02-22 16:29:17 +01:00
|
|
|
}
|
|
|
|
|
2021-05-14 20:08:27 +02:00
|
|
|
public float GetLineDuration(string key) => _settings.GetDialogueClip(key).length;
|
|
|
|
|
2021-05-17 15:31:13 +02:00
|
|
|
public void PlayLineBaz(string key, Transform bazObject) => PlayLine(ref _bazInstance, _settings.BazDialogueKey, key, speakerObject: bazObject);
|
2021-05-14 20:08:27 +02:00
|
|
|
public void PlayLineRadio(string key) => PlayLine(ref _radioInstance, _settings.RadioDialogueKey, key);
|
|
|
|
|
2021-03-02 14:24:18 +01:00
|
|
|
// noRepeat locks this key off from further use. further attempts to use the key will be discarded
|
2021-05-17 15:31:13 +02:00
|
|
|
private void PlayLine(ref EventInstance eventInstance, string speakerKey, string key, bool noRepeat = true, Transform speakerObject = null )
|
2021-02-19 17:33:02 +01:00
|
|
|
{
|
2021-03-02 14:24:18 +01:00
|
|
|
if (noRepeat)
|
|
|
|
{
|
2021-03-05 16:34:52 +01:00
|
|
|
if (_usedClips.Contains(key)) return;
|
2021-03-02 14:24:18 +01:00
|
|
|
|
2021-03-05 16:34:52 +01:00
|
|
|
_usedClips.Add(key);
|
2021-03-02 14:24:18 +01:00
|
|
|
}
|
2021-03-05 19:22:18 +01:00
|
|
|
|
2021-05-14 20:08:27 +02:00
|
|
|
if ( eventInstance.isValid() )
|
2021-03-05 19:22:18 +01:00
|
|
|
{
|
2021-05-14 20:08:27 +02:00
|
|
|
eventInstance.stop( STOP_MODE.IMMEDIATE );
|
|
|
|
eventInstance.release();
|
2021-03-05 19:22:18 +01:00
|
|
|
}
|
2021-03-02 14:24:18 +01:00
|
|
|
|
2021-05-14 20:08:27 +02:00
|
|
|
eventInstance = RuntimeManager.CreateInstance(speakerKey);
|
2021-02-23 14:05:42 +01:00
|
|
|
|
2021-03-05 16:34:52 +01:00
|
|
|
GCHandle stringHandle = GCHandle.Alloc(key, GCHandleType.Pinned);
|
2021-05-14 20:08:27 +02:00
|
|
|
eventInstance.setUserData(GCHandle.ToIntPtr(stringHandle));
|
2021-02-22 16:29:17 +01:00
|
|
|
|
2021-02-19 17:33:02 +01:00
|
|
|
DialogueLine dl;
|
2021-03-11 21:25:09 +01:00
|
|
|
var line = DialogueDatabase.ReadDialogue(key);
|
|
|
|
// process templates in string
|
|
|
|
line = ProcessTemplates(line);
|
|
|
|
dl.text = line;
|
2021-03-05 16:34:52 +01:00
|
|
|
|
|
|
|
var clip = _settings.GetDialogueClip(key);
|
|
|
|
dl.duration = clip.length;
|
2021-02-22 16:29:17 +01:00
|
|
|
|
2021-05-14 20:08:27 +02:00
|
|
|
eventInstance.setCallback(_dialogueCallback);
|
2021-05-17 15:31:13 +02:00
|
|
|
if ( speakerObject )
|
|
|
|
{
|
|
|
|
RuntimeManager.AttachInstanceToGameObject( eventInstance, speakerObject, (Rigidbody) null );
|
|
|
|
}
|
2021-05-14 20:08:27 +02:00
|
|
|
eventInstance.start();
|
2021-03-05 16:34:52 +01:00
|
|
|
|
2021-02-19 17:33:02 +01:00
|
|
|
onDialogueLine?.Invoke(this, dl);
|
|
|
|
}
|
2021-03-11 21:25:09 +01:00
|
|
|
|
|
|
|
private string ProcessTemplates(string text)
|
|
|
|
{
|
2021-03-12 19:29:36 +01:00
|
|
|
var spriteIndices = _currentControlScheme == InputSchemes.GAMEPAD
|
2021-03-11 21:25:09 +01:00
|
|
|
? _settings.GamepadInputPrompts
|
|
|
|
: _settings.KeyboardInputPrompts;
|
|
|
|
|
|
|
|
text = text.Replace("[BLINK]", $"<sprite index={spriteIndices.blink}>");
|
|
|
|
text = text.Replace("[BOOST]", $"<sprite index={spriteIndices.boost}>");
|
|
|
|
text = text.Replace("[TIME_FREEZE]", $"<sprite index={spriteIndices.timeFreeze}>");
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void UpdateControlPrompts(PlayerInput playerInput)
|
|
|
|
{
|
|
|
|
var controlScheme = playerInput.currentControlScheme;
|
2021-03-12 19:29:36 +01:00
|
|
|
if (controlScheme != InputSchemes.PCMR && controlScheme != InputSchemes.GAMEPAD)
|
2021-03-11 21:25:09 +01:00
|
|
|
{
|
|
|
|
Debug.LogError($"could not set unknown control scheme {controlScheme}");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_currentControlScheme = playerInput.currentControlScheme;
|
|
|
|
}
|
2021-02-19 17:33:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public struct DialogueLine
|
|
|
|
{
|
|
|
|
public string text;
|
|
|
|
public float duration;
|
2021-05-14 20:08:27 +02:00
|
|
|
}
|