Random Nikos questions
Moderators: Ice Cream Jonsey, joltcountry
- Ice Cream Jonsey
- Posts: 30175
- Joined: Sat Apr 27, 2002 2:44 pm
- Location: Colorado
- Contact:
Random Nikos questions
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.
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!
- The Happiness Engine
- Posts: 868
- Joined: Thu Aug 02, 2012 4:16 pm
- RealNC
- Posts: 2301
- Joined: Wed Mar 07, 2012 4:32 am
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:
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.
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"
}
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.
-
- Posts: 2255
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
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)?RealNC wrote: Example:
Code: Select all
writefile "hugor-control" { writeval "fade 250 255 0" }
- RealNC
- Posts: 2301
- Joined: Wed Mar 07, 2012 4:32 am
Oh, oops. Yeah. I'm not actually familiar with the Hugo language itself.Roody_Yogurt wrote:writeval in that instance, stores the value of the string "fade 250 255 0", not the string itself
The game should write the actual text to the control file, just like it would write text to a normal text file.
- RealNC
- Posts: 2301
- Joined: Wed Mar 07, 2012 4:32 am
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:
If you'd want opcodes that open a website in the player's web browser and alter the volume of the currently playing music:
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.
Code: Select all
writeval fade_opcode, 250, 255, 0
Code: Select all
writeval openurl_opcode, "http://example.com"
writeval music_vol_opcode, 40
I have tested ANY of that, but I think this would work just fine.
-
- Posts: 2255
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
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).
-
- Posts: 2255
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
- RealNC
- Posts: 2301
- Joined: Wed Mar 07, 2012 4:32 am
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.
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.
-
- Posts: 2255
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
- Ice Cream Jonsey
- Posts: 30175
- Joined: Sat Apr 27, 2002 2:44 pm
- Location: Colorado
- Contact:
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.
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!
- RealNC
- Posts: 2301
- Joined: Wed Mar 07, 2012 4:32 am
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:
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:
This also works:
However, this does not work:
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:
If an opcode has multiple return values, then you readval multiple times.
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."
}
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.
}!<-- The opcode is executed here.
Code: Select all
writefile "HrCtlAPI"
{
writeval opcode, param1, param2
! Do something else
! ...
writeval param3
}
Code: Select all
writefile "HrCtlAPI"
{
writeval opcode, param1, param2
writeval some_other_opcode
}
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"
{
writeval opcode, param1, param2, param3
}
readfile "HrCtlAPI"
{
readval result
}
- RealNC
- Posts: 2301
- Joined: Wed Mar 07, 2012 4:32 am
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:
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
{
local hugor_test, major, minor, patch, os_type
readfile HGR_CONTROL_FILE
{
hugor_test = readval
}
if hugor_test = 12121
{
print "Running in Hugor."
}
else
{
print "Not running in Hugor."
pause
quit
}
writefile HGR_CONTROL_FILE
{
writeval HGR_OP_GET_OS
}
readfile HGR_CONTROL_FILE
{
os_type = readval
}
print "Operating system: ";
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
{
writeval HGR_OP_GET_VERSION
}
readfile HGR_CONTROL_FILE
{
major = readval
minor = readval
patch = readval
}
print "Hugor version: "; number major; "."; number minor; "."; number patch
pause
quit
}
- RealNC
- Posts: 2301
- Joined: Wed Mar 07, 2012 4:32 am
Obviously that was meant to be "result = readval". Where's my edit button?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" { readval result }
- RealNC
- Posts: 2301
- Joined: Wed Mar 07, 2012 4:32 am
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.
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.
-
- Posts: 2255
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
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.
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.
- RealNC
- Posts: 2301
- Joined: Wed Mar 07, 2012 4:32 am
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.)
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.)
-
- Posts: 2255
- Joined: Mon Apr 29, 2002 6:23 pm
- Location: Milwaukee
- RealNC
- Posts: 2301
- Joined: Wed Mar 07, 2012 4:32 am
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?Roody_Yogurt wrote:I see in Test.hug that you've set up another file just for Hugor-checking
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.