I’m a big PC gamer. I have a recumbent bike set up in front of an old computer in my basement. It’s a great excuse both to get some exercise and to play some classic or indy games I may not get around to when I’m on my main gaming rig. Unfortunately, you can’t really use a mouse and keyboard on a bike, so that basically cut out a big section of otherwise ideal games. At least, it used to.

One of my favourite genres is the point and click adventure, and I’m glad it’s been making a comeback in recent years. Since Myo Script has built in mouse control, I use it to make a whole bunch of connectors to control a whole bunch of awesome adventure games with my Myo. I’m talking classics like Monkey Island and modern takes like Broken Age, the Blackwell series, and To the Moon. Now they’re playable with a single arm, which is ideal for when I’m biking.

These scripts are also ideal for showing off a neat extension to the function binding concept we discussed a few weeks ago. Let’s dive in!

Here’s the script:

  


scriptId = 'com.thalmic.adventuregames.Wadjeteye Published Adventures'
        scriptTitle = "Wadjeteye Published Adventures"
        scriptDetailsUrl =  "https://market.myo.com/app/54790c55e4b05772f1d7693a/"

        description = [[
        Adventure Game Script

        Play adventure games with your Myo armband! Play from your couch or excersize bike, or anywhere away from easy reach of a mouse.

        Find an adventure game that isn't supported? Talk to us (@thalmicdev)
        ]]

        link = [[ ]]

        controls = [[
        Controls:
         - Move arm to control mouse
         - Fist to left click
         - Fingers spread to right click
         - Wave in to hit spacebar
         - Wave out to hit escape
         - Double tap to recenter mouse
         ]]

        knownIssues = [[
        - Currently only detects Windows games
         ]]

        function mouseClutch(clutch)
            myo.controlMouse(clutch)
        end

        function leftClick(edge)
            --myo.vibrate("short")
            myo.mouse("left",edge)
        end

        function rightClick(edge)
            myo.mouse("right",edge)
        end

        function escape(edge)
            myo.keyboard("escape",edge)
        end

        function space(edge)
            myo.keyboard("space",edge)
        end

        function centerMouse(edge)
            if (edge == "down") then
                myo.centerMousePosition()
            end
        end

        mouseClutched = false
        function clutchMouse(edge)
            if (edge == "up") then
                mouseClutched = not mouseClutched
            end
            myo.vibrate("short")
            myo.controlMouse(not mouseClutched)
        end

         STANDARD_BINDINGS = {
            fist            = leftClick,
            fingersSpread   = rightClick,
            waveOut         = escape,
            wavein          = space,
            doubleTap       = clutchMouse
        }
        --STANDARD_BINDINGS = true

        bindings = STANDARD_BINDINGS

        supportedApps = {}

        supportedApps["Primordia.exe"] = STANDARD_BINDINGS
        supportedApps["Gemini Rue.exe"] = STANDARD_BINDINGS
        supportedApps["a-golden-wake.exe"] = STANDARD_BINDINGS
        supportedApps["Resonance.exe"] = STANDARD_BINDINGS

        function onForegroundWindowChange(app, title)
            myo.debug("onForegroundWindowChange: " .. app .. ", " .. title)

            appFound = supportedApps[app] ~= nil
            if (appFound) then
                bindings = supportedApps[app]
                myo.debug("Adventure Games active")
                myo.setLockingPolicy("none")
            end
            myo.controlMouse(appFound)
            return appFound
        end

        function activeAppName()
            return scriptTitle
        end

        function onPoseEdge(pose, edge)
            --pose = conditionallySwapWave(pose)
            myo.debug("onPoseEdge: " .. pose .. ": " .. edge)
            fn = bindings[pose]
            if fn then
                keyEdge = edge == "off" and "up" or "down"
                fn(keyEdge)
            end
        end

        function conditionallySwapWave(pose)
            if myo.getArm() == "left" then
                if pose == "waveIn" then
                    pose = "waveOut"
                elseif pose == "waveOut" then
                    pose = "waveIn"
                end
            end
            return pose
        end`  

This script lets you control a bunch of awesome adventure games published by Wadjeteye Games, like Resonance, A Golden Wake, Gemini Rue, and my favourite, Primordia. It’s set up to let you control a bunch of similar games while having slightly different bindings for each. That turned out not to be necessary. We’ll probably cover that approach in more detail another time, but for now I want to focus in on the onPoseEdge definition:

  


function onPoseEdge(pose, edge)  
        --pose = conditionallySwapWave(pose)
        myo.debug("onPoseEdge: " .. pose .. ": " .. edge)
        fn = bindings[pose]
        if fn then
            keyEdge = edge == "off" and "up" or "down"
            fn(keyEdge)
        end
    end  

This is pretty close to the Saint’s Row approach, with one key difference. We use keyEdge to let us have a single function for each pose, with either “down” or “up” getting passed in depending on if the pose is starting or ending (ie, edge is “on” or “off”). The upside is that in your function you can just pass keyEdge in as the edge value for your myo.keyboard()  or myo.mouse() call, and thus the user can hold a key down as long as needed. You could still do this with the Saint’s Row approach, but you’d need two functions, and if you changed the key you’d have to make sure you did it twice. Messy.

Anyway, the downside is, of course, that weird looking keyEdge definition:



  
keyEdge = edge == "off" and "up" or "down"  

what
What.

What’s going on there?

Well, in many other language there exists a concept of an “in-line if statement” (or “ternary if”), which is basically designed to let you set a variable based on a condition, all in one line instead of like, five. For instance, in Java this

  


opacity = connected ? 1 : 0.25

is the same as this



if (connected) { 
            opacity = 1; 
        } else { 
            opacity = .25
        }

Clearly the first is much more concise. Lua doesn’t exactly have this facility, unfortunately. What it does have is a specific way of evaluating logical operators. In Lua, x and y actually evaluates to either false (if x or y is false) or y (if they are both true). Similarly, y or z evaluates to y if y is true, z if y is false and z is true, and false if neither are true.

Confused? I made some tables:

x and y
y or z

Of course, "up" and “down” (x and y) don’t change, and are always true (In fact, this will not work correctly if y is false. So our expression of x and y or z  actually looks like this:

x and y or z

And for our specific example:
edge

Not so scary, I hope. Basically, it’s just

  


value = test_condition and value_if_true or value_if_false  

With that onPoseEdge, and a function like this bound to a pose:

  


function escape(edge) 
            myo.keyboard("escape",edge) 
        end

The user can hold easily any button as long as they like. This great for things like click and drag as well.

Anyway, that’s it for #MyoCraft this week! Send me ideas for future posts at MyoCraft@thalmic.com. And if you haven’t played any adventure games since the classic days of Lucas Arts and Sierra, well, check out the Blackwell Series, Primordia, and To the Moon. Playable with your Myo armband thanks to those connectors!

See you next week!

Newsletter

Enter your email address and get all latest content delivered to your inbox every now and then.