Art is Art and Water is Water

June 20, 2017

Uncovering the Sounds of SkiFree

Filed under: Game Design — foone @ 9:27 pm

ski

So I was looking into the disassembly of SkiFree (like you do), the classic skiing (pronounced she-ing) game for Windows 3.1 as part of the Microsoft Entertainment Pack #3. I noticed an odd string constant: “nosound”. The game checks for a command line parameter to turn off the sound, which is a very strange thing for a game with no sound effects or music to do!

I followed the disassembly to see where this option was being referenced, and to my surprise I found code to load sound effects, as well as trigger them at different points in the game. The only reason why the game appears to have no sound is that no WAV files were shipped in the EXE. So the obvious next step is, what happens if we edit some WAV files in? Will it play them as sound effects at the appropriate time?

Yes, in fact!

So I figured out which sounds meant what, by using a set of sounds that were just a robot voice saying “SOUND ONE” through “SOUND NINE”. I asked a musician friend to see if they could generate some appropriate sound effects. While I waiting for them to get back to me, I check the official site for information.

It turns out that back in 1993 the original creator of SkiFree (Chris Pirih) was working on a version 2.0 of the game, to add better physics, network play, sound effects, robot opponents, the works. Sadly they ran into difficulties with the physics implementation and then misplaced the source code, so that game was never completed.

However in 2005 they were able to locate a 1.03 version of the source, which is an intermediate step between the original 1.0 release and the planned 2.0 release. With this source they were able to build a 32bit version of the game, to allow it to run on modern systems (64bit windows is backwards compatible to 32bit programs, but not to 16bit programs like the original SkiFree). This version apparently included some of the improvements planned for 2.0, but no one had noticed the sound system for 12 years because it was effectively disabled by the lack of embedded sound effects. I was able to confirm this by partially disassembling the original 16bit 1.0 version, and found no trace of a sound system there.

I emailed Chris Pirih about my discovery, and to ask if they’d ever created sound effects to be used with SkiFree 2.0, and if they still had them. To my surprise, they emailed me back within a few hours, providing a full set of sound files for SkiFree!

sfx

They also included a header file which mapped the sound effect names to the resource numbers, to allow me insert them in the correct order. So I updated my earlier test with the new sounds, and created a new demo video of it in action.

The new sounds don’t play entirely properly, because SkiFree is using a very limited API to play back sounds. It only supports playing one sound effect at a time, so often sound effects will be cut off because another sound effect plays after it. The “argh” sound of the yeti is particularly cropped, because it’s a long sound that is cut off every time the yeti takes a step.

I’ve briefly looked into fixing this, but it’ll require switching to a different sound API, as SkiFree uses sndPlaySound, a very simple high-level API which can’t be used for any sort of sound effects that require mixing. That’s beyond the scope of my quick-hackery for today, it’ll probably require adding in an external wrapper DLL to manage the sound system. Hopefully that can be done without too much effort, I’ll have to see.

As for these sounds, I suspect some of them are placeholders that would have been replaced before SkiFree 2.0 was complete. It’s still neat to see them in action, even if they may not fit in perfectly.

If you’d like to try out this version of SkiFree with the sounds edited back in, you can download ski32sounds.zip. I’ve made no code changes in this version, just added the sound resources. This should work fine on both 32bit and 64bit windows systems.

One final thing to consider about sound in SkiFree: Even if the original version had been intended to have sound, it wouldn’t have been possible for two reasons:

  1. The first version of Windows to include a sound API was the Windows 3.0 with Multimedia Extensions OEM-only release, which came out a month after SkiFree 1.0
  2. There was no room on the disk. SkiFree 1.0 was released in an entertainment pack that included 7 games. Adding sound effects could have easily quadrupled the size of the game, requiring an extra disk or kicking another game out of the collection.

 

Advertisements

August 1, 2016

MURIDOS Devlog 4: Backgrounds 2

Filed under: MURIDOS — foone @ 11:11 pm

The next step: Investigate the palettes being generated and see if there’s something going wrong there.

I completely rewrote the palette generation (not hard when it’s just a big unix command line), reexamined all the images, and compared against the existing palettes: perfect match. I double checked the palette loading code, to make sure there’s no issue there, with swapping channels or some similar issues. Nope, everything looks kosher.

So let’s pretend we don’t have a palette issue and move onto the next step: DUM loading, DUM being the custom image format I created for this project. It’s very simple! 5 bytes of header, giving it a unique identifier (D8 for “Dum 8-bit”) and width/height, followed by zlib-compressed image data.

This is a straightforward if tedious process. Using the 5-lines of python that comprise the encoder as a reference, I write 70 lines of simple C++ to load DUM files. And then run the prototype to test it, and hey, look!

layers2

Suddenly our colors look a lot better from last time. My hunch was accurate: something about the TGA conversion process or TGA loading process was corrupting the colors. DUM has no conversion, and therefore has no problem.

The only remaining issue with backgrounds is that the loader needs to be expanded to be more efficient. Currently it loads the entire image into RAM, creates an equally large allegro image, and does all the decompression in one shot. This requires more ram that is necessary, and won’t work on real DOS hardware, where we may not be able to access all the video ram at once, thanks to bank switching. For this early test it’s fine, but it may become an issue in the future once we start running on real DOS hardware.

For next time, we’ll look into loading objects into our empty levels.

July 28, 2016

MURIDOS Devlog 3: Backgrounds

Filed under: MURIDOS — foone @ 9:21 pm

We’ve got a blank window with correct palette, so what’s the next step? Backgrounds!

It’s not as simple as a plain background, like a single image. Or even a single layer of tiles! no, this is a multilayer system. The first step is converting the output of the room-dumper script into something that’s easy to load with C++ code.

 -50: x=90, y=38:  0, 0:bg_tiles1
 -50: x=90, y=39:  1,16:bg_tiles1
8999: x=50, y=32:  2,20:bg_tiles1
8999: x=50, y=33:  6,20:bg_tiles1

This means “layer at priority -50,  coordinates 90,38, draw tile at 0,0 from the bg_tiles1 set”. But thankfully the background tiles are all the same size (16×16 pixels), so we don’t have to encode that.

The scheme I ended up using, for tiles:

rect4290

a 16bit integer, with the first 4 bits indicating which of the 5 tilesets are used. The next 4 bits indicate the x-coordinate (The tilesheets are 8-12 tiles across), and the next 8 are used for the y-coordinate. Naturally these are tile coordinates (so they’re interpreted as times 16) instead of pixel coordinates.

Then those raw blocks are grouped into rectangles, which have a width/height and an x&y offset. This will be more efficient than having all the layers be the same size, as most layers will only encompass a small subset of the map, and we don’t want to waste time processing empty tiles. We just need to sort the layers so that they render

So here’s a display of level 1-1, with the layers outlined:

layers

It’s as simple as that! we go through the layers, blitting out of the selected tiles to the screen (eventually backbuffer).

The only problem so far is that those colors are completely and totally wrong. And some layers seem to be missing. More investigation is needed!

July 27, 2016

MURIDOS devlog 2: Initial code

Filed under: MURIDOS — foone @ 10:30 pm

So now that we’ve got all the resources helpfully extracted, it’s a matter of putting them to use. We have all the code from the original game, but that’s not yet helpful;

  1. It’s not a language we can directly use (it’s Game Maker 7 code) or emulate
  2. It’s only half the code of the game. It handles interaction between objects and when to spawn them, but all the code that draws them or detects collisions, draws backgrounds and sprites, plays sound and handles input? That’s all built into game maker.

So let’s put the code aside for now. We need to build something from scratch, and we need to use it in DOS. This limits our options. Running SDL/pygame/opengl? out of the question!

So I’m going to be using Allegro. Specifically, Allegro 4.2 as that was the last version that supported DOS. This gives us much of the functionality we’d get out of something like SDL, and we can compile for DOS using DJGPP. We can also compile for Linux/Windows natively, which means we can easily develop without having to keep a DOS VM on hand.

Now using Allegro with DJGPP means we’ll be targeting a 32bit extender (so a 386+) and VGA graphics. This may seem like overkill for the type of game MURI is in tribute to, and it is. MURI is definitely inspired by 16bit EGA games like Captain Comic, Commander Keen, and Duke Nukem. But the reality of how this game will be played is that it’s most likely going to be in DOSBox or in (relatively) modern DOS gaming machines. So neither 386+ or EGA will really hold us back in terms of what computers can play it. It’s a minor inaccuracy, but one we’ll have to live with.

So to begin with, we start with a simple allegro example. We set up graphics and keyboard, display a simple screen, and exit. The first things added are loading the palette:

palette

Which has several similar but not exactly identical colors. This is another reason we’re not targeting real EGA: EGA is limited to 16 colors and we have 26. We could merge some to get it down to 16, but even then we have the problem that these aren’t the 64 EGA colors.

So if we’re using VGA, we have 256 colors to work with, so there’s no reason to not use the colors as-is.

Next time, we get backgrounds working!

July 26, 2016

MURIDOS devlog 1: Resources

Filed under: MURIDOS — foone @ 7:30 pm

So, extraction should be done. I’ve got all the original art (objects and backgrounds), levels, sounds, and code extracted out of the MURI Game Maker file. This should have been a simple task, but Game Maker 7 is a mess.

There’s a “save all source” option, but it gives you the source of all user-defined functions and nothing else. There’s also plenty of source associated with event handlers, which have to be manually extracted. I said “screw it” to the manual process and wrote a script to extract them by automating opening all the various windows and clicking on all the buttons.

As for images, backgrounds were simple as there’s only a few and they can be done manually. Object images was much harder, and required automation again to extract them.

For extracting level data I went the opposite route and modified the source, so that when it loaded a new level it also created a pair of text files listing where all the tiles were and their properties, as well as the objects loaded into the level.

Sounds were done manually, although they’re temporary. MURI uses WAV soundfiles for the effects, done in a style to emulate the PC speaker. Well, I’m making a DOS port. I happen to have a PC speaker! So I’ll need to reverse engineer how they work, and encode that into an equivalent series of instructions for the PC speaker. Time to break out the (virtual) oscilloscope!

There was also a lot of cleanup to make later steps simpler. Unifying all the files into 256-color images with the same palette (the raw images are true color, even if they only ever use ~20 colors). I created a very simple image format called DUM to easily encode the images, as no existing image formats really fit my needs. It’s basically just a header and gzipped pixels, no palette or other metadata.

(Most of this was done nearly a year ago. I’m just now getting back to this project)

July 17, 2015

Silent installation of JDK 8 on windows

Filed under: Uncategorized — foone @ 6:39 pm

This doesn’t appear to be documented or posted anywhere I could find with google, but this is the insane syntax you need:

jdk-8u51-windows-x64.exe /s ADDLOCAL=”ToolsFeature,PublicjreFeature” /INSTALLDIRPUBJRE=”C:\java\jre” /L “C:\java.txt” INSTALLDIR=C:\java\jdk

INSTALLDIR existed in Java 6/7 but had a slash before it. For some reason in JDK 8, you must do it slashless.

(If you wonder “Why install the JRE/JDK to C:\Java instead of C:\Program Files”, the answer is because You’re Running Jenkins and vcvarsall.bat hates you)

(Also: If you want to wait while the JDK installs, do “start /wait jdk.exe ..”. but if you’re in cygwin you can’t, because start isn’t a program, it’s a command inside cmd.exe, so do “cmd.exe /c start /wait jdk.exe …”)

July 12, 2011

Backstory of Starmap

Filed under: Uncategorized — foone @ 11:56 am

The Modified Neutrino Theory was published by $NAME(indian) in 2062, and the first example of the Catalyzed Neutrino Decay reactor was successfully built by $UNIVERSITY in 2073. By the end of the century, the world was barely recognizable. CND generators provided nearly limitless energy and were able to be miniaturized small enough to power an electric car. With the vast majority of the planet’s energy needs switched over to CND, pollution from power plants, automobiles, and airplanes was a thing of the past. Lightweight autonomous rigid airships using modified neutrino decay devices to heat the air within their envelope cluttered the sky, transferring cargo that once was delivered by truck, train, and ship.

The only area of technology not revitalized by the discovery of this new energy source was space exploration. Because CND generators work by using the curvature of space near massive objects to redirect and capture neutrinos, CND generators only worked up to low Earth orbit, beyond which they could only supply a trickle of power, decreasing to a barely measurable amount once a spacecraft left orbit.

Despite the setbacks CND generators (and the abandonment of the partially completed Space Solar Power Array which was being built at L1), in 2126 a massive project was undertaken to build a moon base. The initial phase involved launching hundreds of small mining robots to the moon using a mass accelerator built up the Serra do Imeri mountain range in Brazil. The robots were powered by CND devices which were useless in transit but reactivated once they reached the moon (although at 1/6th the power level they would have had on Earth). The first goal of the project was building a massive network of large CND devices under the Moon’s surface, to provide power for the automatic factories and pressurized domes which would be built later.

It was shortly before the completion of the Lunar CND Array that the Periodic Neutrino Anomaly was detected. Every 24.3 days, there were two bursts of neutrinos 68.3 minutes apart. Both their polarity and the sequence in which they hit the Lunar CND Array showed that they couldn’t possibly be solar in origin, and after statistical analysis of stationary CND devices on Earth they were discovered there too. After parallax analysis, it was determined that they were coming from a point near L4, and the partially completed Wide Lunar Array telescope was used to example the area for the next anomaly.

It was the discovery that defined Human history even more than the invention of CND: A small spacecraft of alien origin was repeatedly appearing, deploying solar sails, and then disappearing 68.3 minutes later, having returned to approximately its starting point. Radio and PNC (Polarized Neutrino Communications) signals received no response or reaction from the spacecraft, believed to be an observation probe of some sort. The ESS Armstrong (built for the lunar colonization project) was quickly re-purposed for a mission to observe the probe up close. 68 scientists from 12 nations arrived in the general area of the probe (there was some uncertainty as to where it would appear, as observations of the last 13 cycles showed the probe always appeared within a disc 2km in diameter.

On close inspection, the alien object was confirmed to be some sort of observation probe, but all the telescopes and sensors that could be identified were pointing back towards the disc, none towards Earth or any of the other planets. While maneuvering to better observe the disappearance and eventual reappearance of the probe, all contact with Earth and the lunar outpost suddenly ceased. After a few paniced minutes, contact was re-established over radio. Somehow all neutrinos were being blocked in a cylinder-shaped area beyond the disc. Even solar neutrinos weren’t detectable within this area. The scientists on the ship and on Earth were no closer to finding an answer to how this was possible or why it was happening when the ship reappeared. The ESS Armstrong continued observing it from within the cylindrical area (labeled the L4 Neutrino Anomaly), but something entirely unexpected happened when the probe disappeared again, right on schedule: The Armstrong vanished too.

To be continued at some point.

March 31, 2011

March Game: Pipecraft

Filed under: Month Games — foone @ 11:35 pm

Pipe Mania?
For my March game I made a sort of Pipe Mania inspired game, using CraftyJS, a very neat library I sorely underutilized in this game. It uses a component-based design which I had some problems with early on, so I basically just wrote around it (because I was low on time) rather than figuring out how I should really use it. I’m hoping to correct that for my next CraftyJS game. I also made use of CoffeeScript, which is an excellent language that’s really just a sanity wrapper on JS. It fixes some JS misfeatures and changes the syntax to something that’s more like Python/Ruby. It compiles JS pretty directly, though for-loops end up looking pretty weird.

I also used some of my Tasari sprites, which is a series of unfinished RTS games I worked on from around 1998-2002. (These sprites are so old they were originally drawn for a Visual Basic 5 game!). Tasari 1 was a VB RTS, Tasari 2 lasted about 20 minutes and was a C++ translation of Tasari 1, and Tasari 3 was a fully 3D mess that got nowhere because I didn’t understand model formats, so I just hardcoded all the models into the source. It was deeply ugly.

As for the gameplay itself, it’s pretty simple Pipe Mania with some tweaks in strange directions: You place randomly selected straight/90-degree turn/crossover pieces, and if you replace an existing piece there is a time penalty. You have a time limit, and one of the ways I tweaked it was adding a “sink” tile. Instead of having to create a series of pipes that will survive for Xty seconds/Xty tiles, you have to connect from start to finish within the time limit + travel time of the water/electricity/flooz. It also has levels (A whole 3 of them, including a tutorial level!) instead of just being a blank grid with increasingly difficult time limits.

I’m not sure if it’s the different library, the fact I know JS better now, the lack of multiplayer, or the calming influence of CoffeeScript, but unlike my last JS game I don’t feel like pulling my hair out. (My last JS game was last March, which shows you how much I hated it: It took me a whole year to get back into JS)

PS: I called it “Pipecraft” because I’d developed it in my CraftyJS demo folder, which I’d just named “craft”. Since I’ve played too much Minecraft and based it on Pipe Mania, Pipecraft it is.

March 7, 2011

Minecraft: Beds in multiplayer

Filed under: Minecraft — foone @ 11:31 am

So the 1.3 Beta release of Minecraft was released towards the end of February 2010, and it adds a nifty new feature: Beds. Beds are easily made (if you don’t mind smacking up a few sheep) and let you skip the night phase of the game. This totally changes how safety works in Minecraft since you don’t have to worry about getting killed by monsters except at the very beginning of the morning, before they despawn. (Or when they spawn next to your bed because you didn’t build your house correctly according to the slightly-odd mob spawning mechanics).

But a more interesting aspect of them is the spawnpoint-changing aspect which has been promised in a future update assumed by players. Notch has previously said he’s not sure about changing the spawnpoint because it’d change the gameplay. There’s not so much danger in wandering far from your spawn point or dying if it doesn’t make you respawn far away. But assuming this is going to be implemented, there’s also some griefing issues with it in MP.

Say a player makes a house and a safe bed inside it on a server. They then later log out, and a griefer signs on. The griefer could find their bed, destroy it, and replace it with lava/a TNT cube/a fall-to-bedrock drop. When the player logs in again, if they then die before they return to their house and discover the sabotage, they’ll spawn, quickly die, and then repeat the process forever.

There’s a few ways to fix this problem, none of them without their own issues. (And it is a problem that should be fixed. The existing spawn-point invulnerability proves that: In Minecraft, you should be able to kill other players and destroy their things, but not permanently and unfixably.)

1. Beds are invulnerable/the area around them is.
This is a bad idea, because you could log into a public server, quickly build a bed/surrounding dirt dome in a key spot, sleep in it, and then log out forever. It’s now undestroyable (except by admins). This sort of thing can already be done with obsidian, but obsidian is very hard to get, limiting the amount of damage a griefer could do with it (plus it’s not completely indestructible, just annoying to deal with).
(I list this first because it’s the most obviously unworkable answer)

2. Players can pick where to spawn, their bed or the default spawnpoint.
This is really just a workaround for spawnpoint griefing: If someone does something nasty to your spawn point, you can give up and revert to the (ungriefable (in theory)) default spawnpoint. Adding the UI for this shouldn’t be hard, it’d just change the interface from [Respawn] [Exit to menu] to [Respawn] [Respawn at center] [Exit to menu]. Alternatively this option could only be given (or just forced on the player) after they had died several times at their non-default spawn point (which opens up the possibility of griefing the player in a way that won’t kill them, like putting an obsidian-tomb around their bed.

3. Beds only work as a spawn point so long as the bed is intact.
It would be simple for minecraft to check that the bed is still there before spawning a player, but it wouldn’t fully solve the problem of spawnpoint griefing. Firstly, the griefer could just surround the spawnpoint with lava (without destroying the bed). So the player would spawn near lava, with no way to pass through it without dying . Secondly, the griefer could surround the bed with obsidian, so the player can spawn but not escape their bed. Despite these issues, this is probably the best solution as it incorporates #2. If a player spawns in their bed and can’t leave it(/without dying), they can simply destroy their bed and get back to the default spawn. There is the issue of player knowledge, however: It may not be obvious that that is an option.

This does assume that a bed can’t be placed in a situation where spawning in it would kill the player, but the bed is intact. For example, putting blocks directly over a bed might cause the player to spawn in them and “drown”. This specific issue could be avoided by having the game also check for enough room to spawn in (which it already does for default spawns), and having lava destroy beds (which I believe it does) so you can’t have a lava river running over the bed.

An unrelated minor issue of having beds change the player’s spawn point in multiplayer is that it would reduce the social aspect of multiplayer servers, since right now everyone has to build houses clustered around the spawn point, making them near each other. if players are free to wander 20,000 generic distance units away from the spawnpoint in search of a perfect spot and then live there, they won’t be forced into close quarters with each other. This has obvious negative effects on player interaction and server memory requirements. It also makes griefing possible if option #3 is implemented, as a griefer could find a player’s house and destroy their bed, making it hard for the player to get back to their home (or even impossible, if the player can’t remember existing where off in the endless wilderness their home is.

Given all that and the lack of any clear statement from Notch and the Mojangs that backs up the widely assumed “fact” that beds are going to change spawn points but it hasn’t been implemented yet for reasons of programming/testing time, I think beds changing spawn points was left out for reasons of game balance (and may not be coming, ever!) rather than time issues. There’s enough issues with them changing the gameplay as it is (You don’t have to spend nearly as much time making a beautiful creeper-proof castle if you can just skip over their active cycle with a small mound of dirt and a bed), making them change the spawnpoints too would introduce too many changes at once. Maybe in a later update once beds have been better balanced and their contributions to gameplay better understood and accepted.

February 28, 2011

February Game ZapRogue

Filed under: Month Games — foone @ 10:54 pm

ZapRogue

My february game is ZapRogue, which isn’t really a roguelike at all.
I was playing with some sample code from the Roguelike Wiki
and built it into a monster-hunting game, since my original ideas failed to work thanks to issues with curses and 256 color support. Oh well.

You move with the numpad (make sure numlock is off!) and you press Z to shoot LASERBEAMS! which fly in the direction you last moved.
Enemies are randomly placed and have random ranges of activation (so sniping them from afar won’t always work). The level should be randomized, but I ran out of time, so it’s static.

(I did do and release a January game I just haven’t taken the time to write it up yet)

Older Posts »

Blog at WordPress.com.