Forum rules - please read before posting.
Tech questions during the weekend? Drop in on the community Discord channel!

Show pending interaction whilst action/interaction is running during cutscene?

I'm trying to get the nine-verb to interact similarly to Scumm, specifically DOTT, and I believe in order to make it have a closer feel, I need to make sure cutscenes don't hide verbs/inventory. So, I'm planning on showing both menus except when paused and planning on making "black bar" the highest order menu, and just trigger it on/off depending on the actionlist. This is in part due to the fact that scumm seems to have two "cutscene" modes, a soft-cutscene and a hard-cutscene.

Here's what I'd call a soft-cutscene: https://gfycat.com/CavernousChubbyGalapagoshawk - notice that during the "reach" animation, the cursor disappears but the verbs and inventory are still intact? Also notice that the verbline holds the current interaction in the verb-line during the "soft-cutscene"?

Here's a longer example of a soft-cutscene, with the verb-line interaction still held: https://gfycat.com/HandmadeDaringAnnelida - again, the verb-line is held during.

Here, the "reach" animation part of the actionlist is soft-cutscene, before the dialog is "play in background" style, so as to return control to the user and clear the held verb-line: https://gfycat.com/SpitefulDimGrosbeak

Now, this is an example of a "hard-cutscene", which is identical in every way, except for the "black bar" menu covers everything: https://gfycat.com/PowerlessImprobableChick - for all I know, there could be held verbs underneath it, but it's covered by the "black bar", which is all that matters.

So, two questions regarding this:

  • would it be best to do as I propose and set the verb/inventory menu as "always on except when paused" and cover them with a manually triggered "black bar" menu that is the highest order of menus so as to cover them
  • is there a way of showing the current interaction sentence during the interaction? I might be able to use AC.KickStarter.actionListManager.IsGameplayBlocked ()inside my verb-holding script, but I'm not sure how to get a string for the current interaction that is running. I'd probably put it in this script I'm using: http://pasteall.org/1446173

So far, it almost works except for the verb-line (you can just about make out the cursor disappearing during the "paused gameplay" actionlist for opening the door, and the "play in background" dialog at the end of the actionlist restores control and the cursor): https://gfycat.com/ScratchyUniformChrysomelid

I'm also not certain that turning the black bar on manually is the best practice, but it may be the only option!

«1

Comments

  • The default Verbs menu is set to appear during gameplay - so if your Interaction's When running property is set to Run In Background, then it won't disappear. The Engine: Manage systems Action can be used to temporarily restrict the player's ability to move/interact while this occurs.

    Otherwise, placing a black bar over everything should do it.

    A script would be necessary to have the interaction sentence linger, but if you're relying on a "dummy" verb line that's set by AC - and you're in fact displaying you're own with full control over - then you can just read the last value of the dummy line before the interaction begins.

  • edited January 11

    Thanks, so I need to add into my code "if an interaction/actionlist is running" and use that. Does the text clear before the interaction starts? In which case I'll need to make sure the dummy text isn't cleared after walking to the hotspot until I've checked no interaction is running. However, judging by the video:https://gfycat.com/ScratchyUniformChrysomelid it doesn't seem to build the sentence if the player is already at the walk-to marker/standing at the object and using defaultInteraction (right-click to open the door), it seems to just run the interaction and not show anything on the sentence? So would I need to manually build the sentence in this case?

  • If you read:

    AC.KickStarter.playerMenus.GetHotspotLabel ();
    

    From within the OnInteractHotspot event, it'll display the full line.

    If you did want to rebuilt the sentence, you can use that event's Hotspot and Button parameters to extract both the name of the Hotspot, and the icon label that was used, i.e.:

    using UnityEngine;
    using System.Collections;
    using AC;
    
    public class HotspotEventTest : MonoBehaviour
    {
    
        private void OnEnable ()
        {
            EventManager.OnHotspotInteract += MyUse;
        }
    
    
        private void OnDisable ()
        {
            EventManager.OnHotspotInteract -= MyUse;
        }
    
    
        private void MyUse (Hotspot hotspot, AC.Button button)
        {
            Debug.Log ("Existing verb label: " + KickStarter.playerMenus.GetHotspotLabel ());
    
            // Build a new one from scratch
            if (button != null)
            {
                int language = Options.GetLanguage ();
                string newLabel = KickStarter.cursorManager.GetLabelFromID (button.iconID, language) + " " + hotspot.GetName (language);
                Debug.Log ("Rebuilt label: " + newLabel);
            }
        }
    
    }
    
  • Yikes, I think I got it working! This may not be best practice, but thanks to a lot of reading old forum messages and cutting and pasting, this so far works like DOTT does! :disappointed: `using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;
    using AC;

    public class Verb1txtVerb2 : MonoBehaviour
    {

    public Text textBox1;
    public Text textBox2;
    public GameObject bg;
    private bool textOnce;
    private bool textOnEvent;
    private string newLabel;
    
    private void Start()
    {
    
    }
    
    private void OnEnable()
    {
        EventManager.OnHotspotInteract += MyUse;
        EventManager.OnExitGameState += ExitGameState;
    
    }
    
    
    private void OnDisable()
    {
        EventManager.OnHotspotInteract -= MyUse;
        EventManager.OnExitGameState -= ExitGameState;
    }
    private void ExitGameState(GameState gamestate)
    {
        if (gamestate == GameState.Cutscene)
        {
            textOnEvent = false;
        }
    }
    
        private void MyUse(Hotspot hotspot, AC.Button button)
    {
        Debug.Log("Existing verb label: " + KickStarter.playerMenus.GetHotspotLabel());
        textOnEvent = true;
    
        // Build a new one from scratch
        if (button != null)
        {
            int language = Options.GetLanguage();
            newLabel = KickStarter.cursorManager.GetLabelFromID(button.iconID, language) + " " + hotspot.GetName(language);
        }
    }
    
    private void Update()
    {
    
        if (KickStarter.playerInteraction.GetHotspotMovingTo() != null 
            )
        {
            if (textOnce == false)
           {
                textBox2.text = textBox1.text;
                textBox2.enabled = true;
                bg.SetActive(true);
                textOnce = true;
            }
    
        }
        else {
            textBox2.text = textBox1.text;
            textBox2.enabled = false;
            bg.SetActive(false);
            textOnce = false;
        }
        if (textOnEvent == true)
            {
            textBox1.text = newLabel;
        }
    
    }
    

    }`
    I had to use a bool to toggle the text as I don't believe there's a way of doing something during an interaction, but rather the event hook only happens on the frame it happens. I also need it to return to normal during non-cutscene dialog, which would technically count as still during an interaction, which is why I used the cutscene hook. Hope it's of use to anyone! :D

  • Ah, slight problem, cicking on an auto-walk-to hotspot causes it to stick on, since I used "onexit state cutscene" to release the held text.

    Are auto-walk to's differently defined, and therefore could be ignored as part of this method? If not, maybe tagging them could be an option, and just skipping them entirely.

    Is there an ExitState for hotspot interactions? OnHotspotInteract is great for holding it, but is there a hook for when the interaction is finished?

    I was thinking of adding this to the end of the script:
    if (Input.GetMouseButtonDown(0) && KickStarter.playerInteraction.GetActiveHotspot() == null || KickStarter.playerInteraction.GetHotspotMovingTo() != null ) { textOnEvent = false; }

    But that breaks it, as the click just overrides the hold-during-interaction pending sentence.

  • Okay, so here's an update. This is the code I've got at the minute:
    `public class Verb1txtVerb2 : MonoBehaviour
    {

    public Text textBox1;
    public Text textBox2;
    public GameObject bg;
    private bool textOnce;
    private bool textOnEvent;
    private string newLabel;
    
    private void Start()
    {
    
    }
    
    private void OnEnable()
    {
        EventManager.OnHotspotInteract += MyUse;
        EventManager.OnExitGameState += ExitGameState;
    
    }
    
    
    private void OnDisable()
    {
        EventManager.OnHotspotInteract -= MyUse;
        EventManager.OnExitGameState -= ExitGameState;
    }
    private void ExitGameState(GameState gamestate)
    {
        if (gamestate == GameState.Cutscene)
        {
            textOnEvent = false;
        }
    }
    
    private void MyUse(Hotspot hotspot, AC.Button button)
    {
        textOnEvent = true;
    
        // Build a new one from scratch
        if (button != null)
        {
            int language = Options.GetLanguage();
            newLabel = KickStarter.cursorManager.GetLabelFromID(button.iconID, language) + " " + hotspot.GetName(language);
        }
    }
    
    private void Update()
    {
    
        if (KickStarter.playerInteraction.GetHotspotMovingTo() != null
            )
        {
            if (textOnce == false)
            {
                textBox2.text = textBox1.text;
                textBox2.enabled = true;
                bg.SetActive(true);
                textOnce = true;
            }
    
        }
        else
        {
            textBox2.text = textBox1.text;
            textBox2.enabled = false;
            bg.SetActive(false);
            textOnce = false;
        }
        if (textOnEvent == true)
        {
            textBox1.text = newLabel;
        }
        if (//Input.GetMouseButtonDown(0) &&
            KickStarter.playerInteraction.GetActiveHotspot() == null
            //|| KickStarter.playerInteraction.GetHotspotMovingTo() != null
            )
        {
            //textOnEvent = false;
        }
    }
    

    }` as you can see, there's a couple of things commented out for testing. This is the result:
    https://gfycat.com/NippyGrimKagu

    The right-click default interaction works wonderfully (besides the font color right now!) but it also only stops working when a cutscene (my soft-cutscene, which is just hiding the mouse during an interaction) is exited, as my script denotes. It gets stuck on auto-walk tos, and it would get stuck on look-at's that don't pause gameplay, for instance. Here's what happens if I add in the textOnEvent = false; code:
    https://gfycat.com/PracticalHighIndiancow

    It works as usual, and right-click interactions do not display the current interaction during a soft-cutscene. Again, this example of a soft cutscene: https://gfycat.com/HandmadeDaringAnnelida - no verbs hidden, just the cursor and the interaction text is held until the cutscene is broken. It just obviously needs a little more safe-guarding against situations where it's not exiting a cutscene such as auto-walktos and non-cutscene interactions. Is there any other hook I can tap into for this? Checking the cursor isn't walk-to also won't work, as anything that interferes with right-click default either breaks held auto-walkto sentences or doesn't display anything during right-click default, as video 2 exemplifies.

  • GetHotspotMovingTo() should be enough to check if the Player is "soft" moving to a Hotspot.

    Do you have Update if string is empty? unchecked in your Label's properties? The default verb label shouldn't disappear during the Interaction in that case, provided the Interaction blocks gameplay. Another approach may be to make all Interactions block gameplay, and manually turn off the Menus when necessary.

  • I tried disabling **Update if string is empty? ** but it still happens. I think issue is that when the interaction is run, the sentence returns to "Walk to" but I'm trying to keep the sentence built until either the interaction is finished or the "cutscene" ie: "pause gameplay"-based interaction hits a "play in background" node, therefore returning the gameplay to normal state.

    Using the textOnEvent bool is just a way of forcing a rebuilt sentence to stick on, but I need to be able to turn the bool off if an auto-walkto is interrupted, or an interaction isn't in cutscene/pause-gameplay mode. If there's a global check for an interaction ending, I could use that as a safeguard, but I think the main problem is that interactions don't build a sentence unless the player is walking to the hotspot, once it's run, it's cleared. Here's an example, I'm using "am null" for KickStarter.playerInteraction.GetActiveHotspot() == null and "NOT null" for KickStarter.playerInteraction.GetHotspotMovingTo() != null

    https://gfycat.com/JollyDownrightIguanodon

    Which would be expected, but there's obviously no way of telling if an interaction is running, and therefore how I'd need to toggle-off the bool that turns on when a hotspot is clicked.

    And, problem is that Input.GetMouseButtonDown(0)which could normally be used to clear the sentence when a walk-to is held by the bool, is actually clearing the rebuilt sentence in:
    `private void MyUse(Hotspot hotspot, AC.Button button)
    {

        textOnEvent = true;
    
        if (button != null)
        {
            int language = Options.GetLanguage();
            newLabel = KickStarter.cursorManager.GetLabelFromID(button.iconID, language) + " " + hotspot.GetName(language);
        }
    }`
    

    Here's the script I'm using: http://pasteall.org/1447355

    So, is there a way of having the sentence build if an interaction is run, or a hotspot is used? It's okay if the player is walking to a hotspot, but it clears as soon as the interaction is run, which is why I need to use the hook to rebuild it, but I can't find an effective way of clearing it once it's held.

  • Just for example purposes, here's what happens if I use if ( KickStarter.playerInteraction.GetActiveHotspot() != null ) { textOnEvent = false; } (which, as far as I can tell, also is true is hovering over a hotspot? I guess this is how 9-verb interprets an active hotspot?)
    As is here: http://pasteall.org/1447356
    So, now it works for interactions run if already at the hotspot, and it doesn't get stuck on walk-tos. But it doesn't work if walking-to the hotspot, and arriving there, then the interaction runs: https://gfycat.com/FarflungNastyBluetickcoonhound

    So, where the sentence is shown, held during the open/close routine and disappears when he speaks, that's what I'm trying to do. But I'm also trying to get it to do that when he walks to the hotspot, arrives at it, and the interaction runs. I'd also like to not have the text stuck on if the walk-to is interupted, or the interaction doesn't have a "pause gameplay" setting, obviously.

  • Finally, here's what happens if I further substitute in KickStarter.playerInteraction.GetActiveHotspot() == null
    It sometimes works, depending on if the mouse i hovering the hotspot, or if the interaction was run using left-click or right-click, as you'd imagine: https://gfycat.com/GratefulLittleEasteuropeanshepherd

  • "pause gameplay"-based interaction hits a "play in background" node, therefore returning the gameplay to normal state.

    The Play in background? option in the Dialogue: Play speech Action refers to the background of the ActionList itself, not the state of the game. It just means that it'll continue on with the ActionList once it's hit the speech Action for one frame. If it also happens to be the last Action, the ActionList will itself then end.

    There's obviously no way of telling if an interaction is running, and therefore how I'd need to toggle-off the bool that turns on when a hotspot is clicked.

    What constitutes as an Interaction being run isn't possible to define, since an Interaction can branch off into other ActionLists/assets etc. The OnEnterGameState event, however, can be used to detect when re-entering "gameplay" mode.

    To tell if an ActionList of the type "Interaction" has been triggered, however, you can hook into the OnBeginActionList event and do a cast check, i.e.:

    if (actionList is Interaction) { // Do stuff }
    

    I need to be able to turn the bool off if an auto-walkto is interrupted

    I can consider adding an event triggered when the pending Hotspot interaction is cancelled.

    is there a way of having the sentence build if an interaction is run, or a hotspot is used?

    The OnHotspotInteract event will be called whenever a Hotspot is used, regardless of the "walk to" state and always before it.

  • The Play in background? option in the Dialogue: Play speech Action refers to the background of the ActionList itself, not the state of the game. It just means that it'll continue on with the ActionList once it's hit the speech Action for one frame. If it also happens to be the last Action, the ActionList will itself then end.

    Yeah, and having the actionlist set to When Running: Pause Gameplay ensures the mouse disappears and control is taken from the player, and if the last dialog is set to Play in background then, as you say, the game returns to normal. In actual fact, When Running: Pause Gameplay doesn't actually pause the game, thank goodness, so I can use it to just take control from the player.

    To tell if an ActionList of the type "Interaction" has been triggered, however, you can hook into the OnBeginActionList event and do a cast check, i.e.:

    Great, I can use this to hopefully keep the text from disappearing by using yet another bool to determine if an interaction is running or not.

    But, is there a way of determining if the actionlist has ended? OnBeginActionList is great for not having the sentence wipe on first click, but it still gets stuck if the interaction hasn't entered a cutscene (as you know, I've got OnExitGameState cutscene used to un-hold the sentence)

    I can consider adding an event triggered when the pending Hotspot interaction is cancelled.

    Thank you so much! This would solve the stuck text if clicked away during walk-to :smiley:

  • Okay, this is crazy but after 4 hours of fiddling and twisting my brains around, this combination seems to work! Here's a clip: https://gfycat.com/PassionateMelodicKilldeer
    `using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;
    using AC;

    public class Verb1txtVerb2 : MonoBehaviour
    {

    public Text textBox1;
    public Text textBox2;
    public GameObject bg;
    private bool textOnce;
    private bool textOnEvent;
    private string newLabel;
    private bool amInteraction;
    
    private void Start()
    {
    
    }
    
    private void OnEnable()
    {
        EventManager.OnHotspotInteract += MyUse;
        EventManager.OnExitGameState += ExitGameState;
        EventManager.OnBeginActionList += OnBeginActionList;
    
    }
    
    private void OnDisable()
    {
        EventManager.OnHotspotInteract -= MyUse;
        EventManager.OnExitGameState -= ExitGameState;
        EventManager.OnBeginActionList -= OnBeginActionList;
    }
    private void ExitGameState(GameState gamestate)
    {
        if (gamestate == GameState.Cutscene)
        {
            textOnEvent = false;
            amInteraction = false;
        }
    }
    private void OnBeginActionList(ActionList actionList, ActionListAsset actionListAsset, int startingIndex, bool isSkipping)
    {
        if (actionList is Interaction)
        {
            amInteraction = true;
        }
    }
    private void MyUse(Hotspot hotspot, AC.Button button)
    {
        textOnEvent = true;
        if (button != null)
        {
            int language = Options.GetLanguage();
            newLabel = KickStarter.cursorManager.GetLabelFromID(button.iconID, language) + " " + hotspot.GetName(language);
        }
    }
    
    private void Update()
    {
    
        if (KickStarter.playerInteraction.GetHotspotMovingTo() != null
            )
        {
            if (textOnce == false)
            {
                textBox2.text = textBox1.text;
                textBox2.enabled = true;
                bg.SetActive(true);
                textOnce = true;
            }
    
        }
        else
        {
            textBox2.text = textBox1.text;
            textBox2.enabled = false;
            bg.SetActive(false);
            textOnce = false;
        }
    
        if (textOnEvent == true)
        {
            textBox1.text = newLabel;
        }
        if (KickStarter.playerInteraction.GetHotspotMovingTo() == null
            && amInteraction == false
            )
        {
            textOnEvent = false;
        }
    
    }
    

    }`
    I'm aware that unless a cutscene is exited, amInteracting will stay on, which I'll sort out soon, but for now, this actually works okay as far as I can tell.

    If you can add an event triggered when the pending Hotspot interaction is cancelled, that would be super handy as I can tidy the code up even more by just checking against that.

    Is there any way of checking if any interaction is finished, or any actionlist has come to it's last node? That would be useful for clearing the amInteracting bool if there is no cutscene specified.

  • Here's a couple of possibilities: AreActionsRunning(); IsListRunning() but I'm not too sure how to call those. Anything that can flag off the bool if there's no actionlist running (or it's on the last node of the actionlist, at either the start or end of the node)

  • You could feasibly store the Interaction ActionList that's being run from the OnBeginActionList event, and then read its AreActionsRunning() value in Update to check when it finished.

    A custom event fired when an ActionList ends would be useful, I'll consider it.

  • That would be very useful, thanks! I think I can handle the AreActionsRunning part in update, but I'm not quite sure how to store the current interaction actionlist in onbeginactionlist event?
  • First, declare your variable:

    Interaction myInteraction;
    

    Then, in OnBeginActionList:

    if (actionList is Interaction) myInteraction = actionList as Interaction;
    
  • Thanks! And I should be able to use 'if AC.ActionList.AreActionsRunning() != null' in update?
  • AreActionsRunning() returns a bool - you don't want to do a null check. Instead:

    if (myInteraction != null) 
    {
        if (!myInteraction.AreActionsRunning ())
        {
            // Interaction is set, but no Actions are running
        }
    }
    
  • edited January 21

    MASSIVE thank you to all of your incredible help and patience! Here's the finished script (for now!):
    `using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;
    using AC;

    public class Verb1txtVerb2 : MonoBehaviour
    {

    public Text textBox1;
    public Text textBox2;
    public GameObject bg;
    private bool textOnce;
    private bool textOnEvent;
    private string newLabel;
    private bool amInteraction;
    private bool amCutscene = true;
    Interaction myInteraction;
    
    private Animator _animator;
    public float heightofclick;
    
    private void Start()
    {
        _animator = GetComponent<Animator>();
    }
    
    private void OnEnable()
    {
        EventManager.OnHotspotInteract += MyUse;
        EventManager.OnExitGameState += ExitGameState;
        EventManager.OnEnterGameState += EnterGameState;
        EventManager.OnBeginActionList += OnBeginActionList;
    
    }
    
    private void OnDisable()
    {
        EventManager.OnHotspotInteract -= MyUse;
        EventManager.OnExitGameState -= ExitGameState;
        EventManager.OnEnterGameState -= EnterGameState;
        EventManager.OnBeginActionList -= OnBeginActionList;
    }
    private void EnterGameState(GameState gamestate)
    {
        if (gamestate == GameState.Cutscene)
        {
            amCutscene = true;
        }
    }
    private void ExitGameState(GameState gamestate)
    {
        if (gamestate == GameState.Cutscene)
        {
            textOnEvent = false;
            amInteraction = false;
            amCutscene = false;
        }
    }
    private void OnBeginActionList(ActionList actionList, ActionListAsset actionListAsset, int startingIndex, bool isSkipping)
    {
        if (actionList is Interaction)
        {
            amInteraction = true;
            myInteraction = actionList as Interaction;
        }
    
    }
    private void MyUse(Hotspot hotspot, AC.Button button)
    {
        textOnEvent = true;
        if (button != null)
        {
            int language = Options.GetLanguage();
            newLabel = KickStarter.cursorManager.GetLabelFromID(button.iconID, language) + " " + hotspot.GetName(language);
        }
    }
    
    private void Update()
    {
        bool doHold = false;
    
        if (KickStarter.playerInteraction.GetHotspotMovingTo() != null
            )
        {
            if (textOnce == false)
            {
                textBox2.text = textBox1.text;
                textBox2.enabled = true;
                bg.SetActive(true);
                textOnce = true;
            }
    
        }
        else
        {
            textBox2.text = textBox1.text;
            textBox2.enabled = false;
            bg.SetActive(false);
            textOnce = false;
        }
    
        if (textOnEvent == true)
        {
            textBox1.text = newLabel;
        }
        if (KickStarter.playerInteraction.GetHotspotMovingTo() == null
            && amInteraction == false
            )
        {
            textOnEvent = false;
        }
    
        if (myInteraction != null)
        {
            if (!myInteraction.AreActionsRunning())
            {
                //textOnEvent = false;
                amInteraction = false;
                // Interaction is set, but no Actions are running
            }
        }
    
        //Highlight Script
    
        if (KickStarter.playerInteraction.GetHotspotMovingTo() != null
            || amInteraction == true
            //||  textOnEvent == true
                )
        {
            doHold = true;
        }
        _animator.SetBool("Hold", doHold);
    
        if (Input.GetMouseButtonDown(0) &&
            KickStarter.playerInteraction.GetActiveHotspot() == null &&
            Camera.main.ScreenToViewportPoint(Input.mousePosition).y >= heightofclick
            && amInteraction == false
            && amCutscene == false
            )
        {
            _animator.SetTrigger("Flash");
        }
    
    }
    

    }`

    Hopefully this can be of use to anyone looking for a 1:1 Scumm-style sentence effect, I'll update this thread with any fixes or improvements I come across. Thanks again!

    PS: if you're able to add those couple of hooks, I'll refactor this with a slightly tidier method!

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.