Random Nikos questions

This is a discussion / support forum for the Hugo programming language by Kent Tessman. Hugo is a powerful programming language for making text games / interactive fiction with multimedia support.

Hugo download links: https://www.generalcoffee.com/hugo
Roody Yogurt's Hugo Blog: https://notdeadhugo.blogspot.com
The Hugor interpreter by RealNC: http://ifwiki.org/index.php/Hugor

Moderators: Ice Cream Jonsey, joltcountry

User avatar
Ice Cream Jonsey
Posts: 28878
Joined: Sat Apr 27, 2002 2:44 pm
Location: Colorado
Contact:

Random Nikos questions

Post by Ice Cream Jonsey »

So, I was thinking the other day that Hugo doesn't support fading the screen to black.

Would it be possible to construct code in a Hugo game that Hugor would understand, but would otherwise be ignored by other Hugo interpreters? Without having to recompile or extend the Hugo language, I mean.

For instance, if I were to kick off some opcodes in a Hugo game and Hugor picked them up, could Hugor then do a thing. (Like fade the screen to black.) This is all theoretical at this point, just curious if it's even possible.
the dark and gritty...Ice Cream Jonsey!

User avatar
The Happiness Engine
Posts: 868
Joined: Thu Aug 02, 2012 4:16 pm

Post by The Happiness Engine »

embed a way to interpret comments?

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

Yeah, it's possible. We'd need to come up with a secret handshake. Something the game initiates, doesn't have ill effects on other interpreters, but Hugor recognizes it as a handshake and behaves in a way that signals to the game that it detected it.

For example, the game might try to read a file that doesn't exist, but has a specific name, like "hugor-interpreter-secret-hello-handshake/?", a file that's very unlikely to exist. Other interpreters will indicate an error. Hugor can indicate success. That way, the game knows it's running in Hugor, and Hugor knows the game wants to know if it's running in Hugor.

Hugor might even allow reading from this non-existent file, by feeding the game with metadata it might want to know when it actually reads from the file (like Hugor version, operating system, whatever else.)

After the fact the game is running is Hugor is established, the game could then open a file for writing, again with a special name, like "hugor-control" (this doesn't need to be an unlikely file name, since we already know the terp is Hugor). The game can then write commands to that file. (Obviously, nothing will ever be actually written to such a file.)

Example:

Code: Select all

writefile "hugor-control"
{
    writeval "fade 250 255 0"
}
Which would do a fade, lasting 250ms, starting from an alpha value of 255 and an end value of 0.

Basically, since you just write commands, you can come with any command structure you want using this mechanism. Error reporting would also be possible if we want, by having the game reading from the same file. So writing to "hugor-control" executes commands, reading from it gets you the result of the last executed command.

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

The Happiness Engine wrote:embed a way to interpret comments?
The compiler strips all comments.

Roody_Yogurt
Posts: 2179
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

Post by Roody_Yogurt »

RealNC wrote: Example:

Code: Select all

writefile "hugor-control"
{
    writeval "fade 250 255 0"
}
writeval in that instance, stores the value of the string "fade 250 255 0", not the string itself, so I guess the hacked Hugor terp would just be checking for a certain value (if you already know what the value of the string will be)?

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

Roody_Yogurt wrote:writeval in that instance, stores the value of the string "fade 250 255 0", not the string itself
Oh, oops. Yeah. I'm not actually familiar with the Hugo language itself.

The game should write the actual text to the control file, just like it would write text to a normal text file.

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

Hm, actually I think we can just write dictionary entries. That would be simpler from the game's side. We'd basically have our own Hugor-specific assembly-like language. The commands would be numeric opcodes, and the parameters would be also numeric, and string parameters would be dictionary entries:

Code: Select all

writeval fade_opcode, 250, 255, 0
If you'd want opcodes that open a website in the player's web browser and alter the volume of the currently playing music:

Code: Select all

writeval openurl_opcode, "http://example.com"
writeval music_vol_opcode, 40
The opcodes would be defined somewhere in the game as numbers. When an opcode takes a string parameter, the value of the string is passed (the interpreter will then look it up in the game's dictionary table.)

I have tested ANY of that, but I think this would work just fine.

Roody_Yogurt
Posts: 2179
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

Post by Roody_Yogurt »

I was also thinking that if the intent of the fade to black is to present cool transitions, you could make a movie (ala the Thundercats logo) transition that plays nearly fullscreen. Probably would take some clever design to make something that looks cool and fits the game but is definitely doable (and wouldn't require an interpreter hack).

Roody_Yogurt
Posts: 2179
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

Post by Roody_Yogurt »

Also, both of those opcode suggestions sound cool. I recently added mouse-click support to newmenu.h so I can imagine how I could also add clickable web links to menu pages.

And the ability to fade out music is something we've all wanted for a while.

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

The core issue here is, how far can we push this? Stuff that is optional seems fine; audio fade in/out can be mapped to just music 0 / sound 0 when not running in Hugor, visual fades are just eye candy, and opening URLs doesn't affect the playability of the game itself in any way.

We could do other stuff, which doesn't have a compatible fallback. Like playing any amount of music and/or sound tracks at the same time. We can pretty much do just about anything. Heck, we can put 2D or even 3D rendering in there if we want. And since these all go through the control file API, we wouldn't be modifying the engine or compiler. But we'd end up with something that call itself a Hugo interpreter, but really isn't :-/

So I think for now, the best thing to do is just pick a few things that would be nice to have (and I assume that would be mostly Robb needing some helpful opcodes for CG) and go from there.

Roody_Yogurt
Posts: 2179
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

Post by Roody_Yogurt »

In the very least, it'd be nice to have that Hugor-check just so I can have Roodylib figure out if Hugor is being used and act accordingly (for things like hiding cursors where I can just put the cursor in a window and pause with Hugor but requiring a while loop for the regular interpreter).

User avatar
Ice Cream Jonsey
Posts: 28878
Joined: Sat Apr 27, 2002 2:44 pm
Location: Colorado
Contact:

Post by Ice Cream Jonsey »

Yeah, I am thinking, if we had a Hugor check as one, and two opcodes for fade out and fade in of graphics*, that would be an excellent starting point. (Not sure how difficult fading out audio would be.)

So, I am envisioning a full-screen fade. I don't have a copy of compiled Cyberganked at the moment, but it opens by putting a company logo up for a second, then a color splash screen that takes up the entire display, and then the same splash screen in CGA colors. Something that effectively lets me:

- Fade in, print logo, fade out
- Fade in, print color splash screen, wait for spacebar
- Immediately display CGA splash screen, wait for spacebar then fade out after spacebar is hit, then it continues with the game

Would be awesome and one of those little things that makes the game look a bit more modern. Ditto with fading out the previous song before starting a new one.
the dark and gritty...Ice Cream Jonsey!

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

I started work on this, and here's the design so far:

Virtual Control File
The virtual control file is named "HrCtlAPI". The Hugo engine does not allow more than 8 characters in a file name, and enforces alphanumeric letters. That's unfortunate, but "HrCtlAPI", with *exactly* that capitalization (it's case-sensitive) is safe and very unlikely to be ever used in any other game.

Handshake
For detecting Hugor, the game must read exactly one value from the control file before writing anything in it. The magic value is 12121. So:

Code: Select all

readfile "HrCtlAPI"
{
    hugor_test = readval
}
if hugor_test = 12121
{
    print "We are running in Hugor."
}
The magic value is an additional safety measure. In the very unlikely event that a file named "HrCtlAPI" actually exists on disk, and the game is not running in Hugor, when you don't get 12121 as the only value from that file, then you know you're not running in Hugor. If you want to be completely paranoid, you might also test whether 12121 is actually the *only* value in the file. If you're able to read more values from the file, even if the first value was 12121 by some crazy chance, then you're not running in Hugor.

When trying to read more values, the read will fail (just like when trying to read past the end of a real file.)

This handshake is only possible when reading from the control file for the first time, and without having previously written to it. So the first access to the control file is special and should probably be done at game startup.

However, from Hugor's perspective, this is not mandatory. You can write opcodes to the control file before doing this handshake and just assume you're running in Hugor. But that's obviously not portable; other interpreters (but also older versions of Hugor) will actually create a file named "HrCtlAPI" on disk and your opcodes will be written there and obviously have no effect.

Long story short: do the handshake *first* if you care about other interpreters or older versions of Hugor.

Executing Opcodes
To execute a Hugor opcode, you write the value of the opcode, plus parameter (if any) to the control file, in *one* write operation. So each opcode should be in its own writefile { } block. The opcode is *only* executed when the block exits. You cannot write multiple opcodes in the same writefile block.

This works:

Code: Select all

writefile "HrCtlAPI"
{
    writeval opcode, param1, param2, param3
    ! The opcode is NOT executed yet.
&#125;!<-- The opcode is executed here.
This also works:

Code: Select all

writefile "HrCtlAPI"
&#123;
    writeval opcode, param1, param2
    ! Do something else
    ! ...
    writeval param3
&#125;
However, this does not work:

Code: Select all

writefile "HrCtlAPI"
&#123;
    writeval opcode, param1, param2
    writeval some_other_opcode
&#125;
Or at least, you should not expect it to work. It might, or it might not. Let's call it "undefined behavior."

So every writefile block should write exactly one opcode and the parameters (if any) for that one opcode.

The reason for this is two-fold:

First, Hugo does not allow you to read values inside a writefile block, so there's no way to get the return value of an opcode from inside the writefile block. To get the return value (if the opcode has a return value), you need to a readfile.

Second, if the opcode was executed immediately, you wouldn't get the chance to calculate parameters and write them later on inside the writefile block. This way, you can writeval the opcode, do something else, and then writeval parameters. It is guaranteed that the opcode will only be executed when the writefile block closes ("}").

Getting Opcode Return Values
Since you cannot read values inside a writefile block, to get the return values of an opcode, you need a readfile block and read the results:

Code: Select all

writefile "HrCtlAPI"
&#123;
    writeval opcode, param1, param2, param3
&#125;

readfile "HrCtlAPI"
&#123;
    readval result
&#125;
If an opcode has multiple return values, then you readval multiple times.

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

And here's the first version that implements the above design:

http://83.212.107.13/~realnc/hugor/test ... upport.zip

A couple of opcodes are implemented. Fading is not in yet. This only implements the core mechanism and just a few very easy opcodes.

You can test the above build with this Hugo program:

Code: Select all

#include "hugolib.h"

constant HGR_CONTROL_FILE "HrCtlAPI"

constant HGR_OP_GET_VERSION 100
constant HGR_OP_GET_OS 200
constant HGR_OP_ABORT 300

constant HGR_OS_UNKNOWN 0
constant HGR_OS_WINDOWS 1
constant HGR_OS_MACOSX 2
constant HGR_OS_LINUX 3

routine Main
&#123;
    local hugor_test, major, minor, patch, os_type

    readfile HGR_CONTROL_FILE
    &#123;
        hugor_test = readval
    &#125;
    if hugor_test = 12121
    &#123;
        print "Running in Hugor."
    &#125;
    else
    &#123;
        print "Not running in Hugor."
        pause
        quit
    &#125;

    writefile HGR_CONTROL_FILE
    &#123;
        writeval HGR_OP_GET_OS
    &#125;
    readfile HGR_CONTROL_FILE
    &#123;
        os_type = readval
    &#125;
    print "Operating system&#58; ";
    select os_type
        case HGR_OS_UNKNOWN
            print "unknown"
        case HGR_OS_WINDOWS
            print "Windows"
        case HGR_OS_MACOSX
            print "Mac OS X"
        case HGR_OS_LINUX
            print "Linux"

    writefile HGR_CONTROL_FILE
    &#123;
        writeval HGR_OP_GET_VERSION
    &#125;
    readfile HGR_CONTROL_FILE
    &#123;
        major = readval
        minor = readval
        patch = readval
    &#125;
    print "Hugor version&#58; "; number major; "."; number minor; "."; number patch

    pause
    quit
&#125;

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

RealNC wrote:Getting Opcode Return Values
Since you cannot read values inside a writefile block, to get the return values of an opcode, you need a readfile block and read the results:

Code: Select all

readfile "HrCtlAPI"
&#123;
    readval result
&#125;
Obviously that was meant to be "result = readval". Where's my edit button?

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

Screen fade support and opening of URLs is now in. (No audio fade yet.)

The design changed too. The opcodes now always return at least one value that indicates whether it succeeded or failed. I'll document the new behavior later. For now, you can grab this:

http://83.212.107.13/~realnc/hugor/test ... upport.zip

A test Hugo program with source is included.

Note: The URL opcode opens pretty much anything, not just http URLs. Local files, email addresses, whatever.

Roody_Yogurt
Posts: 2179
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

Post by Roody_Yogurt »

Cool, I just played with this. Already started on adding some of it to Roodylib.

A couple of things, though. Trying to get the OS on Windows is resulting on a 0 readval for me.

Initially, I thought that I'd have to call HGR_OP_ABORT whenever I wanted to reset the ops if I wanted to check for Hugor again. I see in Test.hug that you've set up another file just for Hugor-checking which is nice so my code will switch over to that, but just the same, calling HGR_OP_ABORT caused Hugor to crash for me.

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

ABORT is for killing the process. No graceful engine shutdown, or even application shutdown. It's mainly there for debugging.

All opcodes now return one value that indicates whether there was an error or not. That means that GET_OS will return two values. The first is a HGR_RESULT_* code, the second is the actual value you want (one of the HGR_OS_* values.)

Roody_Yogurt
Posts: 2179
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

Post by Roody_Yogurt »

Ah, cool, that fixes it, thanks!

User avatar
RealNC
Posts: 2244
Joined: Wed Mar 07, 2012 4:32 am

Post by RealNC »

Roody_Yogurt wrote:I see in Test.hug that you've set up another file just for Hugor-checking
This was needed, since the game needs to check for this not only during game startup, but also after restoring from a saved game. I'm not sure if this is possible with Hugo though. Is there a way to run code right after restore?

If not, then things will break if you save the game in Hugor, then restore it in another interpreter, as the game will be assuming it's running in Hugor.

Post Reply