Jump to content

Home

Serge

Members
  • Posts

    264
  • Joined

  • Last visited

  • Days Won

    14

Posts posted by Serge

  1. On 3/2/2024 at 5:32 AM, Jake said:

    I think it was Hit the Road I was thinking of that was made on/for general midi as opposed to a Roland thing. Zaarin, whose name I can never remember on this forum, would know better than me. (@s-island)

     

    Both DOTT and Hit the Road were both still made for a Roland thing 🙂 - just not MT-32, but Roland SoundCanvas (55, I believe, but Zaarin knows better). I don't think there has been a MIDI-based SCUMM game ever that was originally composed (or arranged) for SoundBlaster or Adlib. But of course, that doesn't mean people aren't allowed to prefer them. 😁

    • Like 3
  2. 11 minutes ago, danielalbu said:

    🎙️ Tune in TODAY for the YouTube premiere of my conversation with acclaimed author Annie Fox!

     

    Personal aside: Annie and David were both instrumental in me getting into programming, writing not one, but three of the first books I read on the topic. A few years later I first played Maniac Mansion and Zak McKracken, and although I noticed "David Fox" in the credits, I didn't even realize the connection to those early programming books of theirs until a few years ago. 😁

    • Like 1
  3. On 2/9/2024 at 10:28 AM, Laserschwert said:

    Works great, thanks again! What seems to be missing in a lot of the files are tempo changes (or even an initial tempo), so I guess those were originally SYSEX commands interpreted by iMUSE?

     

    Nope... iMUSE doesn't have any tempo specific hooks or markers. It's possible to change the tempo from SCUMM, but that was rarely done. All of the MIDIs (just checked) have initial tempo as standard MIDI meta events, as well as tempo changes when applicable (see fourth purple line):

    image.png

    iMUSESequencer scan for standard MIDI system messages in all files from FOA - all of them have at least one tempo (initial or change) - labelled "set-tempo" here (excerpt):

     

    image.png

    However, only the first track in an iMUSE MIDI will have an initial tempo - because none of the other tracks are independently played - they will all follow in some kind of sequence from the first track. Hence, Misty doesn't include any tempo in the split MIDI files, except for the file created from the first track - because it has no way of knowing what that initial tempo should be for the others: If the tempo changes halfway through track 1, and iMUSE then happens to jump to track 2, then the tempo of track 2 will be that changed tempo, not the initial tempo.

     

    ETA: Haven't checked FOA specifically, because I'm not that familiar with FOAs MIDIs - which ones use iMUSE heavily and which don't. But here's an example from the DOTT opening titles - DOTT uses a different version of iMUSE ("v2"), so it actually also does include initial tempo on all tracks (which Misty would then also include in the split MIDIs). But other than that, this is an example of the tempo being changed, and then conditionally jumping to track ("chunk") 6 (actually, 7 - unlike Misty, iMUSE Sequencer (and iMUSE itself) counts tracks from 0). Tracks 2-5 are used for delaying the theme when a platform is too slow at loading the graphics for the next part of the title sequence.

     

    image.png

    • Thanks 2
  4. Even more quickly added (hour of dev), even less tested - I considered adding an option to split each MIDI file into new tracks based on channels (which we did for HighLand), but decided I don't have the time right now. 🙂

     

    https://github.com/Jither/iMUSE-Sequencer/releases/tag/Misty-1.1.0

     

    New "verb" is split - it also allows remapping at the same time. You can, of course, just use the output format that is built in, which will store the output files in the same folder with the same name and tracknumber appended. There's another example of a format in the examples. Beware that if you don't include "{track}" in the format, it'll generate the same name for every file, and they'll overwrite each other - a format like "{folder}/{name}.mid" will even overwrite the original file without warning. 😉 This is all very hacky development for now.

    • Like 3
    • Thanks 1
  5. 1 hour ago, Laserschwert said:

    To be fair, I don't need to replicate all the interactivity anyway. I just pick and choose whatever sounds the best (and usually I just want to have the most complete track possible, so muting channels isn't even desired).

     

    Well, what's done here (Stan's Theme) is setting up all the variations that happen for each repeat of the theme - adding instruments, removing them, transposing them, and changing them for other instruments. 🙂

     

    Anyway, here's the first release... Like I said, it's "cooked together" - very quickly done, but seems to leave the MIDIs exactly the same, except for remapping all instrument references.

     

    https://github.com/Jither/iMUSE-Sequencer/releases/tag/Misty-1.0.0

     

    Just decompress the .zip to a folder. The command line provides help (and examples) for each verb (two of them - one for the actual remapping, one for getting the mapping in a more readable format with instrument names). Just type misty or misty help. It then lists the verbs - you can get help for those by typing misty <verbname> help. (Also works to just type misty remap - since it will find that the required input file parameter is missing, so it will show the help for the verb - it doesn't, however, work that way for misty mapinfo - because the mapinfo verb can be called with no parameters).

     

    So, misty remap file.mid is the simplest usage, which will use the default mapping file that's included (the one you sent, but with a single changed line - which allows the mapping info "verb" to find the instrument names). You can create multiple mapping files and specify a different one with -m path-to-file or --mapping path-to-file. After remapping, it outputs to a file with the same name, in the same folder, except for replacing the extension with ".remapped.mid". (Note that it will *not* warn or prompt if the output path already exists - it will just overwrite it). You can also just specify a second file name that it should output to, rather than letting it create a default output path.

     

    ETA: Forgot: Requires .NET 8.0 runtime - https://dotnet.microsoft.com/en-us/download/dotnet/8.0 - the one named ".NET Runtime 8.0.x" will do, but you might as well install the one named ".NET Desktop Runtime 8.0.x" - just adds support for GUI apps (SCUMMRev 8, if it ever appears, will be one of those).

    • Thanks 1
    • Chef's Kiss 1
  6. 1 hour ago, Laserschwert said:

    Wow, this is coming along nicely, and fast! Thanks a lot for this. I didn't realize iMUSE would send instrument change commands as well. It would be great to have those properly converted to GM patch changes as well.

     

    For now, they most likely won't be. It'll just change the instruments in the actual messages that involve instruments. Changing them to patch changes would rarely make sense anyway, since they'll often also be transposed etc.

     

    iMUSESequencer does allow dumping all the iMUSE messages, though (iMUSESequencer dump -i filename.mid😞

     

    image.png

    The numbers on the left are <bar>.<beat>.<ticks> [<seconds>] (<total ticks>). So, here you can see that it changes the instrument on track 7 to 85 at bar 13, beat 4, tick 398 - if the current hook ID (set by SCUMM) is 1. You'll also note it disables that track at the same time (hook-part-enable with state 0) - and that there are no hook-part-enable that enable it (they all have "state: 0". That's because in this case, iMUSE will disable the track based on the hook, and a command in the SCUMM script for the room will reenable the track later. iMUSE in general is a bit like untangling spaghetti of MIDI hooks and markers intertwining with SCUMM calls to determine what's actually going on - often SCUMM will do something that might as well have been done through hooks in the MIDI - and vice versa. 🙂

     

  7. On 1/31/2024 at 3:06 PM, Laserschwert said:

    A command line tool would be great! It would be enough to just use the next best General MIDI instrument, just to get a quick overview of the track. Does the attached file help? (this one seems to leave out the ones that are identical)

     

    Looks right. Almost done (although I have done zero testing yet while writing all the code...), and for good measure I made a quick verb on the command that lists out the mapping with instrument names (if it has a "comment" about what standards are used on both sides - only support GM and MT-32 so far, though):

     

    image.png'

     

    ETA: Does seem to be working - at least from doing byte comparisons on input and output. It also allows inputting raw ROL chunks (and other chunks) - i.e., it just removes the LEC headers from those. Still need to test the part of the functionality where it remaps iMUSE instrument changes in addition to the standard Program Change messages. So, will probably be ready tomorrow.

    • Like 2
    • Thanks 1
  8. 1 hour ago, Laserschwert said:

    Also, since DMs aren't working for me anymore, @Serge, is there a tool to quickly convert the instrument mappings of MT-32 MIDIs to General MIDI? I only found MT2GM.exe, which won't run under modern 64-bit Windows.

     

    Not that I know of - also, it's not a 1:1 process - MT-32 has many more variations of e.g. strings or pianos than General MIDI does. But I could probably relatively quickly do a simple command line tool that takes a MIDI input file and outputs a remapped file, based on a text list of mappings. I.e., a text list like:

     

    1   : 5           # remap (MT-32) instrument 1 to (GM) instrument 5
    2   : 4           # remap (MT-32) instrument 2 to (GM) instrument 4
    3   : 3
    5   : 2           
    ...
    126 : 81
    127 : 86

     

    SCUMMRev 1 did it (it's what we used as a basis for conversion of HighLand MIDIs) - but SCUMMRev 1 hasn't existed for almost 25 years. 🙂

    • Thanks 1
  9. 7 hours ago, Laserschwert said:

    There are some weird cases, though, where the AdLib tracks actually offer more content. At least with a quick comparison between the Roland and AdLib End Credits (ripped via ScummRev), I noticed that the short string marcatos during the Raider's March are only present in the AdLib version. So, I guess to get the most out of the files, I'll have to compare both versions to see if anything can be added from the AdLib tracks.

     

    Yeah, that continued to be the case in later games. For example, as far as I recall, there's an organ part in the Adlib version of the DOTT opening or end credits, which isn't in the GM version (which is particularly strange, because it's not a matter of a channel missing or something - since the GM version does have the organ, just not all the notes that the organ plays).

     

    Also worth noting that iMUSE limited the number of channels that could actually be used at a time - which means even if a part is in the MIDI, it may not actually play in the game. The MT-32 iMUSE driver only allowed 8 channels (+ percussion), and the GM driver only allowed 9, even though both MT-32 and GM allowed 15 channels + percussion. Since, of course, multiple MIDIs may be playing at a time during transitions, the choice of which channels to include in the game are based on priority - each part specifies its priority at the beginning. But it also does happen that the number of channels in a single file exceeds those limits - meaning there are parts that will never play in the game.

     

    ETA: Should you need the original sound numbers (e.g. because the titles I gave them are short - for script output purposes - so you might need to compare to some other lists of FOA sounds, which are likely to be in numeric order - you can find them in the symbols file I posted in another thread - https://gist.github.com/Jither/7ed7b65fb77acc0f3cd2bd28ec4c5a9f - look for the section starting with sounds {. 😉

    • Thanks 1
  10. Here are all the sound files from the CD version of FOA ripped with SCUMM Revisited's potential successor... They all play back fine in iMUSE Sequencer sending to an MT-32, so the instruments are correct, and so are the tempo changes (those that aren't actually implemented in script through iMUSE commands, rather than MIDI). The MT-32 versions are obviously the ones named "ROL.MID". The ones without ".MID" are raw rips of the block, which include iMUSE's headers, and hence won't be read by any standard MIDI reader. The content of those headers can be found in the .txt files.

     

    https://www.dropbox.com/scl/fi/jn128u4vvb5v3ug109y4h/foa.zip?rlkey=z16uhvdzbr62ieflw73lqc49l&dl=0 (note: this file will likely be gone in a week)

     

    Note that iMUSE simply stores its "bits" as separate tracks in a single file - Standard MIDI format 2 (0 = single track, 1 = the typical multiple simultaneous tracks, 2 = multiple independent tracks). That may be the ones VGMRips has, verbatim. That's also how these MIDs are stored - they're just raw rips from the files. But then, I'm pretty sure the SCUMMRev ones were too - as far as I recall, it simply changed the format 2 bit to format 1, in order to allow it to be read by more applications. The only difference in any MIDI compliant app would be that it would interpret it as multiple tracks to be played at the same time.

     

    ETA: As for the HighLand MIDIs, their main problem in terms of orchestrating isn't that we sequenced them based on taste - it's that they're remapped to General MIDI instruments, which does not cover all the instruments available on the MT-32 - and even MT-32 timbres with the same name (say, "Clarinet") may sound very different from the typical GM instrument with the same name - or the soundfont we used as target. Of course, can't go back, since the MT-32 has three acoustic pianos, four electric pianos, three string sections, etc. - which would have been mapped to fewer GM instruments. Also, the MT-32 allowed custom timbres through sysex - which had to be mapped to some mostly fitting GM instrument. Hence, the original MT-32 MIDIs should always be the reference. The HighLand MIDIs were for easy listening on a GM/GS device.

     

    ETA2: The files that contain actual music, as opposed to sound effects, should mostly be named "-theme" (the names are from the names they get in decompiled scripts) - but I cannot guarantee that I've been 100% consistent when naming them, so some of them may be "-sound" - or "sound-xxx" (used for sounds I haven't named yet).

    • Like 1
    • Thanks 1
    • Chef's Kiss 1
  11. 6 minutes ago, EOakford said:

    I've managed to put together a list of defines relating to Fate of Atlantis, based on what Serge has provided. Check it out. The decompilation is of the playable demo of the game, which actually has the whole fight and death code present. Crucially, some versions of the demo do not have this code (since it's usually in room 10, which was removed), which results in the game crashing when Indy drowns in room 82. The version which does have this code is Version A (according to DREAMM), and is identified as  Get it here. It's available on ScummVM's site as well, but I've got the precise version here.

     

    I'm not in SCUMM Decompiler mode at the moment, but here's its symbols for FOA as they look at the moment, in SCUMM Decompiler's symbol syntax. Still far from complete, but might have some you're missing. Do note that some (like variables and classes) will be missing, simply because this file inherits from symbol files shared between games (see the top "include" directives) - those define common variables, classes, states, verbs, etc.

     

    Also note that some of them may not actually be correct, since the first FOA symbols were gathered when the decompiler didn't have a concept of e.g. room local variables.

     

    https://gist.github.com/Jither/7ed7b65fb77acc0f3cd2bd28ec4c5a9f

     

    • Like 1
  12. A very old different attempt (end titles, though - and without most of the percussion, and some panning issues in places that I never fixed) - towards the end of this video. 🙂 Was actually thinking about getting back to it with newer samples. But I really like your arrangement so far. 🙂

     

     

    • Like 5
  13. Another quick example today - not 100% annotated, some scripts and verbs are still unnamed.

     

    But to show the flexibility of SCUMM, here's the "logo" room from Last Crusade. This room, like in many of the other games, also contains the boot script as well as the global scripts that handle input, default responses, global scripts for room entry/exit etc. (And yeah, the script from the intro cutscene is also there, even if it takes place in the college halls). It's these scripts that allow a game like Loom to have a completely different interface. The SCUMM sentence building interface with verbs and inventory isn't a SCUMM feature as such - it's implemented *in* SCUMM. The setup concepts in this boot script would pretty much be the same all the way up to CMI.

     

    The scripts ending in "build-sentence" are the ones that handle key presses, clicking on verbs or the rest of the screen etc. - in different contexts.

     

    The term "verb" in SCUMM has two different meanings, and both meanings are not necessarily what you'd think:

    • In their "visual" meaning, verbs are just text or graphics on screen that can (mostly) be clicked. This means that the inventory items are also "verbs" - as are the arrows to scroll through the inventory, and the sentence line itself. And - as discussed earlier - the dialog choices.
    • In their "non-visual" meaning, verbs are scripts that are owned by a specific game object. They may (and often do) contain scripts that respond to a specific verb used on an object ("open door"). But they can be anything relating to that object. Which is why the choice of inventory icon for objects in later games is also chosen by implementing a verb on the object. Another "verb" sets the "quick-verb" - the verb that's used when right-clicking an object. Verb scripts are also used to define the response when the actor cannot reach an object. Etc. In a way, they allow simple "object oriented programming". The important thing is that they're not restricted to being responses to open/close/talk etc.

     

    https://scumm.jither.net/?Jither/036e506282f14a7932d85808e13c7fcc

     

    Note that the boot script has a few "special" string assignments for the save/load dialog and error messages. These use string format tokens (%c etc.) that don't follow the standard SCUMM syntax of %token%. I've finally updated the syntax highlighting to support this special case. So if much of the script just displays grey text (because it thinks a string token never ends), clear your cache and reload it. 🙂

    • Like 2
    • Thanks 1
  14. Pretty sure it's a "made up" sculpture. Douglas Crockford famously wrote that while making the NES version, they claimed to Nintendo that it was a Michelangelo. Michelangelo only made a couple of female nudes, and they got lucky, and one of them (Dawn) looks somewhat like it. If you squint your eyes and turn your head 30 degrees. And you're partially blind. According to Crockford, it was Winnick who looked through an art book to find the Michelangelo "reference". Surely Winnick would have known if it was originally based on a closer reference. 🙂

     

    (It was still removed from the NES version).

    • Thanks 2
  15. 2 hours ago, JPL said:

     

    Yeah, thanks! That's pretty much what I gathered was happening. What I'm having the most difficulty finding are the actual color values (or indices, for some existing palette I can sample) that the default palettes are being set to in these few specific rooms. I had an idea to go to one of them (eg the Yellow Crystal room in the Mayan pyramid) in ScummVM and take a 1:1 unfiltered / original pixels screenshot, sample one of the shifted colors, and then wrote a Python script to search all the existing background images' palettes for that exact color - thinking that it's defined in some other palette.

     

    But that didn't return any results! So last night I tried a more manual, lower tech solution on one image, as a test to see how it looked: I sampled a few (3-4) colors along one of the shifted ramps in the ScummVM screenshot (ie the actual colors specified by the game data, AFAICT), and then opened up the unshifted palettized image in DPaint IIe in DOSBox, set those same indices to those colors, and then used the "Spread" palette function to fill in the rest of the ramp with the approximately-correctly shifted colors. When I pasted this result back into GNU IMP over the screenshot, there were definitely some small discrepancies in color, but it looked "pretty close", and it got me closer than any other method I've tried to date. So I might just try that approach for the remaining 6-7 images that need these palette shifts. Which involves playing fairly deep into Zak (skipping to the room #s via the debug console doesn't work, because they're all variations on the same room!) which I hadn't done in years.

     

    What is that Palette Viewer program you've screenshot there? I've been amassing all kinds of old quirky SCUMM tools throughout this process and that one doesn't look familiar.

     

    https://quickandeasysoftware.net/software/scumm-revisited

     

    The one in the dump specifically is SCUMM Revisited 5, but as far as I recall, there are palette viewers in all 3 versions (but they don't all support the same games or features - I was rather haphazard with coding back then).

     

    The default palette is in the game data - for Zak FM it's the PA chunk. All SCUMM Revisited versions open Zak FM, but 2 may be slightly more useful in this case, since it also list the hex values for each color. Although, of course, you can use a color picker to get the colors too - 5 is generally more useful for viewing rooms, since it also allows showing or hiding object images individually (click "View Room" when a RO or ROOM chunk is seleced) etc. In general, the colors may be more reliable than ScummVM, since ScummVM may apply filters and color profiles(?) that will change the actual output. The palette viewer (and image and room viewers) use the colors straight from the game files.

     

    All that should really be needed is to rearrange that default palette according to the calls to the palette mutation script. I.e. copy the colors in row 3 to row 12 if the parameters are "12 3". Of course, beware that if another line says e.g. "10 12", you'd need to copy the original colors in row 12 to row 10 - not the colors that were copied from row 3 earlier.

     

    Oh, and it should be entirely possible to skip to a specific room - while, yes, they're variations on a single room, they all do have their own room number. I haven't annotated all of Zak, so a lot of the rooms in this script are unnamed, but this shows the general idea:

     

    https://scumm.jither.net/?Jither/8f01826cd23b042264fc50db2b59a438

     

    I.e., room 29 has four "variations" (pseudo rooms) - which have the room numbers 189, 190, 191, and 182.

    • Thanks 1
  16. 11 hours ago, JPL said:

     

    That's consistent with what I'm seeing; I'm down to looking through decompiled scumm scripts, and the numbers it's giving for room color changes are definitely all single digit numbers, and looking at the palettes in GNU IMP they're pretty much all even 16-color ramps. It'd be easy to swap one of those ramps (from another room's palette, possibly?) in for another, but the question is which one... it's kinda hard to just eyeball. My guess is that the alt palette / ramps are *somewhere* in existing game data, ie they didn't just make new palette ramps just for these swaps. I'm just not quite good enough at decompiling and reading these old scripts to know which ones they might be.

     

    The palette mutation script looks pretty much like this:

    https://scumm.jither.net/?Jither/4ad155c14594f848e3e4cc8d2c39355e

     

    The room-color command here doesn't actually set a color, it puts an index to the actual palette (the PA chunk for Zak FM) into the room palette. By default (when entering a room), the room palette looks like 0, 1, 2, 3, 4, 5... 254, 255 - meaning the room will just use the colors in the actual palette as-is: If the background or an object image has the value 78 for a pixel, it will use color 78 from the PA chunk. So, changing a value in the room palette using e.g. room-color 215 in-slot 78 doesn't actually change any colors as such - it just makes color 78 in room graphics use color 215 in the actual palette stored in the game files.

     

    The palette mutation script works with the palettes in a 16x16 grid:

     

    palette.png

     

    It is generally called in one of two ways: With 2 arguments or with 4 arguments. You'll see cases in room 47 (which contains the system scripts) where it's called with a third 0 argument, but that has the same effect as calling it with two.

     

    The two argument version changes a row of 16 palette entries to point to a different row than the default. The four argument version allows to change a subset of a row - in practice, it's only used to swap the two halves of a single row.

     

    So, for example:

    mutate-palette 3 12

     

    ... will remap 16 palette indices starting from row 12 - i.e. index 192 (16 times 12) to instead point to the 16 colors starting at index 48 (16 times 3) in the actual palette. So, it can be read as "change the palette at row 12 to use the colors at row 3".

     

    mutate-palette 6 6

    This will change the room palette to the default for this row. The 16 palette indices starting from 96 (16 times 6) will point to the actual palette colors starting from 96. In other words, the colors are unchanged from the PA palette (for this row). So, it can be read as "change the palette at row 6 to use the colors at row 6".

     

    mutate-palette 5 5 0 8

    This will remap 8 palette indices starting from 88 (16 times 8) to instead point to the colors starting at 80 (16 times 0). So, it can be read as "change the palette of the last 8 colors in row 5 to point to the first 8 colors in row 5".

     

    The script where this occurs follows up with mutate-palette 5 5 8 0 in combination, the two result in swapping the 8 "left" colors of row 5 with the 8 "right" colors of row 5.

     

    Does that make sense? 🙂

     

    5 hours ago, Lagomorph01 said:

    Wasn’t SMI the first game to do so? I remember the ship in the Governor’s Mansion really coming at you, and a friend of mine actually got startled by it.

     

    Yeah, you're right. 🙂

    • Like 1
    • Thanks 1
  17. Just a bit of what I remember from Zak FM, not sure if it helps any - don't have the scripts around... Generally, the palette was modified by moving rows of 16 palette entries around. E.g. replacing colors 16-31 with the colors in 128-143 - always a complete row, i.e. the first index to move (and move to) would be a multiple of 16. And always 16 entries to be moved. The one special room that I recall is the Egypt maze, which has one or two cases where the first 8 colors of a row are swapped with the last 8 colors.

     

    Something you may or may not want to include: MI2 introduced parallax scrolling in the foreground, although I think they only did it for the ship hull in the foreground in Woodtick. FOA has quite a few more instances of it: The lamp post in New York, a palm tree and roof in the Azores, a rope in the Algiers Dig Interior, etc. I guess those might be considered part of the "background", although the parallax nature of them complicates things when representing the background as a still image, of course. 😁 Because objects (generally) can't really change their position within a room, these parallax effects were implemented with actors and costumes, rather than object images. I've attached the cels for those costumes (I may not have identified all of them), just in case. Ship hull in woodtick; roof in the Azores; palm in the Azores; pillar in the orichalcum machine room; rock in the lava pool room; rope in Algiers Dig interior; lamp post in New York.

    image.png

    limb-0-cel-0.png

    limb-0-cel-0.png

    limb-0-cel-1.png

    limb-0-cel-0.png

    limb-0-cel-0.png

    limb-0-cel-0.png

    • Like 2
  18. The Fate of Atlantis and Woodtick scripts have been updated with a couple of new decompiler features:

     

    The macro restoration has become more robust, meaning it can restore more of the macro calls correctly. So, even closer to the original readability. That includes turning:

     

    start-sound largo-theme
    flush-sound-q

     

    ... into the original macro call:

     

    quick-start-sound largo-theme

     

    But more substantially, turning:

     

    for foo = 120 to 128 ++ {
       verb foo off
    }
    
    verb dialog-up-arrow off
    verb dialog-down-arrow off
    screen-escape-array = 0, 0, 0, 0, 0, 0, 0, 0, 0
    say-screen-escape is 0, 0, 0, 0, 0, 0, 0, 0, 0
    cursor off
    userput off

     

    ... which disables the dialog interface when a dialog ends - into the "slightly" more concise original call:

     

    cancel-dialog

     

    The FOA script also shows quite a few examples of a commonly used macro (although it happens to not be used at all in the Woodtick script) - run-script - which would start a script and then wait for it to finish running before continuing the current script. If you compare to the previous version of the decompiled FOA script, you'll see that the macro just calls start-script and then loops over a call to break-here until the script that was started has stopped running.

     

    The other major change in the decompiler is adding declaration of room local variables.

     

    A "bit" of an explanation of that one, since it's not actually documented in any (publicly available or otherwise) official documentation of SCUMM:

     

    Room local variables

    SCUMM (obviously) had support for global variables from the start. The first 50-100 (circa) of these were system variables declared and understood by the compiler and engine - for things like the selected actor, the soundcard, the machine speed, the cursor position, etc. The rest were game specific and declared in SCUMM scripts.

     

    Mostly each of these variables had a specific relatively local purpose and were named as such - e.g. something like visited-iceland. A few, however, were reused for all kinds of purposes across scripts - this helped reduce the amount of memory allocated for global variables. The first couple of these - which were already there in Maniac Mansion - were named foo and bar (the latter was renamed around MI1 for possibly obvious reasons - hint: MI1 and MI2 both have a room named bar 😉).

     

    Global variables could be integer numbers or arrays (or bits or strings - which had their own dedicated space. In SCUMM 6 - from DOTT - nibble, byte, and word variables were also added).

     

    When scripts local to a room were added for Last Crusade, local variables - local to the scripts - were also introduced.

     

    However, sometimes (quite often) you need variables that can be shared between all the scripts in a single room, but are still local to that room. Unlike script-local variables, this wasn't implemented as a compiler and engine feature, but rather, was implemented by a bit of (ab)use of functionality provided by the two:

     

    Global variables were declared in a dedicated SCUMM script, defining the name of each variable, and the "slot" (just a relative pointer to its location in memory). Using one of two syntaxes:

     

    variable selected-actor       @ 1

     

    Which would declare that the variable selected-actor would be in slot 1. (This was the typical way the system variables were declared in the script).

     

    Or:

     

    start-variables 100
    variable foo
    variable bar
    variable some-array[10]
    variable visited-iceland

     

    Which would place foo in slot 100, bar in slot 101 etc. some-array would simply take up the next 10 slots, 102-111, with visited-iceland being in slot 112.

     

    So, in order to add "room local variables", you'd do this:

    1. Add an array to your global variables, named, say, room-local, with, say, 14 items (this was the typical number)
    2. At the top of each room needing room local variables, simply use the above syntax:
      start-variables room-local
      
      variable time-to-nuke-largo
      variable looked-at-money
      variable out-of-money-lines

      ... which would place time-to-nuke-largo at the same slot as room-local - in other words, it would be at the same location in memory as room-local[0]. In a different room, you could then have another set of variables, pointing to the same array.

     

    This is exactly what can be seen in the woodtick script (and similarly in the new-york-street script). So, you could have up to 14 variables that were specific to a room, but only took up 14 variable slots of memory across the game.

     

    To make it a bit cleaner, another feature was used: Every room in SCUMM can have an enter and exit script, which defines a script to be executed when you enter/exit that room. However, there was also two global enter scripts - and two global exit scripts - assigned during the boot script. Which would be run respectively before and after the individual room's enter/exit scripts - for every room. In one of these, you could place a for loop, which would iterate over the room-local array and set each item to 0. That way, whenever the player entered a new room, the room local variables would start out with a clean, default, 0 value.

     

    Just another example - much like the dialog system - of the kind of flexibility SCUMM allowed without actually needing changes to the compiler or interpreter. 🤓

    • Like 1
    • Chef's Kiss 3
  19. On 7/3/2023 at 6:20 PM, rzil said:

    Also, I haven't managed to figure out how the instructions for using colon, plus and comma in say-line until your example, so thanks again.

     

    Just remembered this one. Note that there are a few places where you might see multiple of those line-concatenators in a row - e.g. in the MI2 end credits. My decompiler currently output them like this:

     

    https://scumm.jither.net/?Jither/5c3c0dfe9484834ca1ec91c2898da759


    Comma followed by plus. That's perfectly serviceable for reading - and makes sense - but I'm relatively sure it wouldn't be compilable. The actual scripts were written with a literal 0xfe 0x01 at the end of the string, and only the + outside of it. Just mentioning it for completeness sake, because I was just looking at the end credits again. 🙂 Also worth noting that these characters wouldn't be backslash-escaped in the source file - they'd be written in verbatim. In Borland Brief and codepage 437, that wouldn't look like total garbage, but rather something along the lines of a filled square followed by a smiley (minus the colors here 😉😞

     

    "Additional Art■☺️"+

     

    The SCUMM5 compiler only supported escaping double-quotes - \" - even if actual double quotes for output in the games were usually written using the backtick `. As far as I recall (but never looked closely), it didn't support escaping the backslash itself either.

     

    Another somewhat related thing I never mentioned above - and which is still missing from the decompiled scripts: There's (clearly) no statement separator in SCUMM, other than new-line. Splitting a long statement over multiple lines actually requires "escaping the newline" - i.e. a \ at the end of the line.

  20. Ah well, one more update...

     

    Early macro "compression" is implemented (along with a ton of other stuff - including label annotations). The macro "compression" restores those set-dialog calls that make things a whole lot more readable (it also restores a lot of other macro calls, although not all of them yet).

     

    Also updated my SCUMM syntax highlighter and turned it into a small web app, which also makes for a much more enjoyable reading experience. 😉

     

    So, here's an updated Woodtick script:

    https://scumm.jither.net/?Jither/077e9e90ad4d4f30127f2eddc3730fdb

     

    And as a bonus, a half-done script from my "complete Fate of Atlantis annotations" project. It looks a bit scary towards the top, because this particular room also includes the entire scripting for fighting in FOA. And although all that kind of "low level" stuff can also be done in SCUMM, it's sometimes a bit verbose about it... Also, haven't quite grogged how the crate moving works, so that part (and other parts toward the bottom of this very long script) are a bit rough in terms of naming, still...

     

    Fate of Atlantis - New York Street

    https://scumm.jither.net/?Jither/4f98f905073d408deb89ed8b8b42fe0f

     

     

    • Like 1
    • Thanks 2
    • Chef's Kiss 1
  21. 12 minutes ago, ThunderPeel2001 said:

    Yep, nice rant. I was biting my tongue. Allow me offer my own rant, mirroring just about everything you've said (in a less learned manner), on the subject that I wrote yesterday...

     

    https://thunderpeel2001.blogspot.com/2023/07/why-ai-is-bad-joke-or-why-i-think-bill.html

     

     

    Yeah, pretty much all my points, explained in more depth. 🙂 Although, we are at the dawn of a new era - whether "AI" is stupid or not. The only thing that really matters is whether decision makers are stupid or not. And we know the answer to that one.

    • Like 1
  22. 54 minutes ago, ThunderPeel2001 said:

     

    My baby-like understanding says that ChatGPT has zero knowledge of any subject. It's auto-complete on steroids. It literally knows nothing at all about anything. (And there's quite a few AI researchers and linguists saying this, including Noam Chomsky, but they're getting drowned out by the hype machine.)

     

    Every actual AI researcher worth their salt says this. 🙂 Rant incoming...

     

    Toddler-like understanding: GPT is a language model. All it does is predict the next word in a sentence (or, in ChatGPT's case, a conversation) - based on a model it's built from a ginormous text corpus. The reason this works is that letting the "AI" optimize for predicting words - actually makes it build a structure where "math on words" becomes possible in a multidimensional space - where 'Guybrush' minus 'Monkey Island' plus 'Day of the Tentacle' ends up with a result in the vicinity of Bernard (but also relatively close to Hoagie). This is called "word embedding" and is probably the number one principle of current "AI".

     

    The thing is, knowing that the most likely next word in the sentence "The main character of The Secret of Monkey Island is..." is "Guybrush Threepwood" - is not the same as knowing that Guybrush Threepwood is the main character of The Secret of Monkey Island.

     

    Another thing is that "AI" in general has a "utility function" - the "scoring mechanism" for whether they do a good job or not. The ideal utility function for an AI is mostly hard or impossible to actually implement, so researchers usually go for something easier. For example, you might think that "speak the truth" is an ideal utility function for ChatGPT. But "truth" is hard to quantify - you could hire a number of experts to train it - scoring it based on whether its output is actually correct. But you'd need a lot of experts to train it sufficiently. So, OpenAI settle (like all AI developers must) for less - in this case, simply a subjective ranking of which of multiple outputs the reviewer likes the most. Of course, the reviewer will not be an expert on all matters - so they'll tend to simply rate on which response is the most pleasing to read, the most convincing, etc.

     

    In other words, you're not training the AI to pick its words to be truthful - you're training it to pick words to sound authoritative on the matter. In general, AI studies of recent years have shown, that the larger corpus, the more training, and the more processing power you throw at an "AI", the more it will, indeed, increase its score according to its actual utility function - although we've already reached the point of diminishing returns. However, at the same time, you also reach a point - and we've already reached it for the large AIs - where its score according to its ideal utility function drops steeply - and even goes below 0 - as in, the algorithm will "actively" go directly against its ideal utility function (e.g. "truth") while still scoring high on its actual utility function (e.g. "good answer"). A classic example of this is that ChatGPT 3 would happily give people a random poem sounding "old" if asked to write in the style of a Shakespeare sonnet - why? Because most humans can't tell the difference anyway.

     

    This may sound like "lying like a human". But all of this doesn't reflect any kind of understanding on the part of the AI - it just reflects the humans who are training it.

     

    Some people will realize all of this, and still claim that "the evolution of AI is going so fast that in just a few years...". Thing is, the evolution isn't going fast. The vast majority of breakthroughs in the field happened between 1960 and 1989. For example, back-propagation - a major component of the learning of any "AI" - was described in 1962 - and implemented before the end of that decade. Word embeddings as described above were first realized and implemented in the mid-1980's.

     

    (Almost) all that's happened in the past 10 years is throwing more data and computing power at the problem - both resources which are finally approaching their breaking point. On the computing power side, ChatGPT (pre-4) requires a server with 8 GPU's (and we're not talking gamer GPU's here). That server will be dedicated to just you for the amount of time that it takes for it to send you a full response to a prompt. And in that time it will devour about the same amount of power as a couple of old washing machines. 😉 It's a hugely inefficient way to solve most of the problems people use it for - and in most cases, it's also very ill-suited for those problems.

     

    Rant over. Here's a bit of SCUMM... 😂

     

    Quote

    The SCUMM script language used a combination of English-like keywords and numerical codes to represent actions and objects in the game. For example, the code "walk guybrush to x=15 y=10" would move the character Guybrush to a specific location on the screen. 

    > [Yeah... no.] Can you give me a longer example of a SCUMM script?

    Sure, here's an example of a SCUMM script that might be used in a point-and-click adventure game:
     

    // This script is for the first room in the game
    
    // Define variables
    int doorOpen = 0;
    int keyTaken = 0;
    
    // Define objects
    object door "door"
    {
        state "closed"
        {
            verb "open"
            {
                if (keyTaken == 0)
                {
                    print "The door is locked."
                }
                else
                {
                    setState "open";
                    doorOpen = 1;
                }
            }
        }
        state "open"
        {
            verb "close"
            {
                setState "closed";
                doorOpen = 0;
            }
        }
    }
    
    object key "key"
    {
        state "onTable"
        {
            verb "take"
            {
                takeObject;
                keyTaken = 1;
            }
        }
    }
    
    // Room logic
    if (doorOpen == 0)
    {
        print "You are in a small room. There is a door to the north and a key on a table."
    }
    else
    {
        print "You are in a small room. The door to the north is open."
    }


    [It goes on to explain its made up script]

    This is a very simple script, but it gives an idea of how the SCUMM script language was used to create interactive point-and-click adventure games.

    > I may be wrong, but that doesn't look like the SCUMM scripting language at all.

    You're correct, I apologize for the confusion. The script I provided is not an example of the SCUMM script language, but an example of a possible script that could be written in a similar style. SCUMM was a proprietary language and its exact syntax, commands and structures were not exposed to the public. While it was a simple high-level language, it was not similar to the example I provided.

     

    • Like 1
    • Chef's Kiss 2
  23. 11 hours ago, Huz said:

    Some would call this a series of lucky guesses. I would call it SKILL :doo1:

     

    I've got that beat, in terms of series of lucky guesses/SKILL (yeah yeah, sticking with the letters I already guessed isn't exactly skill in Mojole...)

     

    👕 I beat #MojoleXtreme #477 and all I got was this stupid t-shirt. 4/6
    💛💚🖤🖤🖤💛
    🖤💚💛🖤🖤💛
    🖤💚🖤🖤💛🖤
    💚💚💚💚💚💚
    https://funzone.mixnmojo.com/Mojole/

  24. Probably the last update for a while - but the decompiler now mostly does iMUSE properly. Which means the Woodtick script is dangerously close to the original script, other than:

    • Label and non-standard chore names
    • loc-0 in a single verb
    • No comments, obviously.
    • Some numeric constants (like break-here counts or x/y positions) that had defined names or short macros at the top rather than being written literally in the commands.
    • Generally, dialog trees - like Guybrush's dialogue with Largo - would be included from a separate script file.
    • Macros - i.e., some single line macro calls - like set-dialog - are inlined in their "full glory" here.

    Here it is (same place as earlier versions): https://gist.github.com/Jither/077e9e90ad4d4f30127f2eddc3730fdb

     

    But yes, this (particularly the code in woodtick-music-control) is what scripting iMUSE in MI2 looked like. There's one example of a macro in there:

     

    start-sound woodtick-theme
    flush-sound-q

     

    ... which had a shorter quick-start-sound woodtick-theme macro. Other than that, the iMUSE calls are identical to how they originally looked.

     

    Very short iMUSE scripting primer

    All iMUSE commands could either be called immediately, or queued to be called later. The latter was used for e.g. waiting for a cue marker (trigger) in the MIDI data before calling a command. So, e.g. q-sound-trigger woodtick 1 would block the queue until trigger 1 was reached in the music - then the next command in the queue (added with q-sound-command) would immediately be called.

     

    Somewhat in the "reverse" direction, hooks were conditional commands stored within the MIDI data - calling set-hook from SCUMM would enable a specific hook, so that it would be triggered when the playback reached it, e.g. jumping to a different place in the music, enabling or disabling parts (instruments) or even transposing the music to a different key.

     

    For a possibly slightly more intuitive (and full) view of the implementation of the music in Woodtick, here's the music-related parts (including scripting from other rooms in Woodtick) - rewritten in MUSK. MUSK is my own SCUMM-like language that makes iMUSE more of a first class citizen with some syntax constructs dedicated to it - for use in the iMUSE Sequencer. 🙂

     

    https://github.com/Jither/iMUSE-Sequencer/blob/main/ImuseSequencer/Scripts/woodtick.musk

    • Like 1
    • Thanks 1
×
×
  • Create New...