Forum rules - please read before posting.

Doing text-based development using AC as the engine (more like SCUMM)?

This question is directed at folks who are already familiar with the tools Ron Gilbert created (SCUMM[1], yack[2], Dinky[3]) in building his Monkey Island games (among others).

At a high level, I would prefer to encode game logic in a small scripting language (in the style of yack/dinky) and then use that to generate C# code that is compiled and run by Adventure Creator. (I started working on a tool like this in the past when I was targeting Escoria.)

For this to work well, I need to be able to do a lot of things dynamically, and I'm not sure how well AC supports this today.

Putting the script-to-C# generation aside for the moment, let's say I just authored the .cs file directly for each hotspot. I would probably want my own adapter class that subclasses MonoBehaviour where Start() would use GetComponent<Hotspot>() to get the associated Hotspot and then wire up methods like OnExamine() or OnUse() (declared in the adapter class) to the appropriate callsites in Hotspot. OnExamine() could be designed to run code directly or possibly it would be required to return an ActionList.

I have been struggling to make this work because I don't have a good mental model for how things fit together in AC. For example, Hotspot has bool provideLookInteraction and Button lookButton (as opposed to provideLookInteraction being true whenever lookButton is non-null). And then Button itself does not seem to have an in-memory ActionList, but needs an ActionListAsset, though maybe CopyFromActionList() can solve my problem?

I'm curious if anyone has already successfully gone this route and has any wisdom to impart.

[1] https://en.wikipedia.org/wiki/SCUMM
[2] https://grumpygamer.com/loom
[3] https://github.com/grumpygamer/DeloresDev/tree/master/Scripts/Rooms

Comments

  • For example, I tried:

    ActionList actionList = gameObject.AddComponent<ActionList>();
    ActionSpeech speechAction = ActionSpeech.CreateNew_Player("I can't go downstairs yet.");
    actionList.actions.Add(speechAction);
    
    Button button = new Button();
    ActionListAsset actionListAsset = ScriptableObject.CreateInstance<ActionListAsset>();
    actionListAsset.CopyFromActionList(actionList);
    button.assetFile = actionListAsset;
    
    hotspot.lookButton = button;
    hotspot.provideLookInteraction = true;
    Debug.Log("hotspot has real look button");
    

    But that fails with the following runtime error:

    UnityException: Adding asset to object  failed.
    
  • Ah, I see Interaction is a subclass of ActionList and this works:

    Interaction actionList = gameObject.AddComponent<Interaction>();
    ActionSpeech speechAction = ActionSpeech.CreateNew_Player("I can't go downstairs yet.");
    actionList.actions.Add(speechAction);
    Button button = new Button();
    button.interaction = actionList;
    hotspot.lookButton = button;
    hotspot.provideLookInteraction = true;
    
  • Hmm, though when I define the actions for the Use verb like this:

    Interaction actionList2 = gameObject.AddComponent<Interaction>();
    ActionSpeech speechAction2 = ActionSpeech.CreateNew_Player("I can't use that.");
    Debug.LogError("Hotspot walkToMarker: " + hotspot.walkToMarker.name);
    ActionCharPathFind pathFindAction = ActionCharPathFind.CreateNew(KickStarter.player, hotspot.walkToMarker);
    actionList2.actions.Add(pathFindAction);
    actionList2.actions.Add(speechAction2);
    Button useButton = new Button();
    useButton.interaction = actionList2;
    hotspot.useButtons = new List<Button> { useButton };
    hotspot.provideUseInteraction = true;
    

    I get the speech action to work, but the player character does not walk to the hotspot.

  • Ah, apparently I didn't need ActionCharPathFind, but playerAction = PlayerAction.WalkToMarker:

    Interaction actionList2 = gameObject.AddComponent<Interaction>();
    ActionSpeech speechAction2 = ActionSpeech.CreateNew_Player("I can't use that.");
    actionList2.actions.Add(speechAction2);
    Button useButton = new Button();
    useButton.playerAction = PlayerAction.WalkToMarker;
    useButton.interaction = actionList2;
    hotspot.useButtons = new List<Button> { useButton };
    hotspot.provideUseInteraction = true;
    

    Incidentally, my OnHotspotReach event is finally firing, as well!

  • OK, in hopes that this helps someone, here is a simple working of my MonoBehaviour that individual Hotspot2D instances will extend and override:

    using AC;
    using System.Collections.Generic;
    using UnityEngine;
    
    #nullable enable
    
    public class CTFHotspot : MonoBehaviour
    {
        void Start()
        {
            Hotspot hotspot = GetComponent<Hotspot>();
            if (hotspot != null)
            {
                Marker walkToMarker = hotspot.walkToMarker;
                if (walkToMarker == null)
                {
                    Debug.LogError("CTFHotspot2D should have a walkToMarker");
                    return;
                }
            }
            else
            {
                Debug.LogError("CTFHotspot2D should have a Hotspot component");
                return;
            }
    
            EventManager.OnHotspotReach += OnHotspotReached;
    
            Init(hotspot);
        }
    
        private void Init(Hotspot hotspot)
        {
            var lookActions = OnLook();
            if (lookActions != null)
            {
                Interaction actionList = gameObject.AddComponent<Interaction>();
                actionList.actions.AddRange(lookActions);
    
                var lookButton = new Button();
                lookButton.playerAction = PlayerAction.WalkToMarker;
                lookButton.interaction = actionList;
                hotspot.lookButton = lookButton;
                hotspot.provideLookInteraction = true;
            }
    
            var useActions = OnUse();
            if (useActions != null)
            {
                Interaction actionList = gameObject.AddComponent<Interaction>();
                actionList.actions.AddRange(useActions);
    
                var useButton = new Button();
                useButton.playerAction = PlayerAction.WalkToMarker;
                useButton.interaction = actionList;
                hotspot.useButtons = new List<Button> { useButton };
                hotspot.provideUseInteraction = true;
            }
        }
    
        protected virtual List<Action>? OnLook() { return null; }
    
        protected virtual List<Action>? OnUse() { return null; }
    
        private void OnHotspotReached(Hotspot hotspot, AC.Button button)
        {
            Debug.Log($"Player has reached the Hotspot: {hotspot.name}");
        }
    
        private void OnDestroy()
        {
            EventManager.OnHotspotReach -= OnHotspotReached;
        }
    }
    

    Here is a sample use of the behavior that I add as a component to a Hotspot2D:

    using AC;
    using System.Collections.Generic;
    
    #nullable enable
    
    public class BedroomDoor : CTFHotspot
    {
        protected override List<Action>? OnLook()
        {
            return new List<Action>
            {
                ActionSpeech.CreateNew_Player("I can't go downstairs yet.")
            };
        }
    
        protected override List<Action>? OnUse() {
            return new List<Action>
            {
                ActionSpeech.CreateNew_Player("I can't use that.")
            };
        }
    }
    

    This sort of C# code could easily be generated from a simpler (and less verbose) scripting language.

  • Whoops, there was a bit of a mistake in the previous version where OnHotspotReach was not scoped correctly:

        void Start()
        {
            this.hotspot = GetComponent<Hotspot>();
    
            // ...
    
            EventManager.OnHotspotReach += OnSomeHotspotReached;
    
            Init(hotspot);
        }
    
        // ...
    
        protected virtual void OnHotspotReached(Hotspot hotspot, AC.Button button) { }
    
        private void OnSomeHotspotReached(Hotspot hotspot, AC.Button button)
        {
            if (hotspot == this.hotspot)
            {
                OnHotspotReached(hotspot, button);
            }
        }
    
    }
    
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.