Spoo4k

PS - Somehow I broke the sound just now so it doesn’t play part of the time. Looking into it, but Java MIDI sucks.

“Spoo4k” (pronounced just like spook) is my entry into the 4k competition (well, my unofficial just-for-fun entry because I’m a judge). The game is basically done but any suggestions for improvement would be great. I’ll probably change the AWT-shapes to bitmap images at some point, by the way.

Webstart:
http://www.otcsw.com/webstart/Spoo4k/Spoo4k.jnlp

Applet:
http://www.otcsw.com/applet/Spoo4k/

The basic idea is that you’re a guy in a spooky house which is inhabited by ghosts. All of your friends are also in the house, but they’ve been paralyzed by the ghosts. Because you’ve got your full life energy, you’re more tempting to the specters. So, your job is to go near the other people to lure the ghost off them, then bring the ghost to the bottom right corner where it will disappear. Careful, though, because the more a ghost touches you, the more life force you lose. If you get down to 0, you collapse just like your friends, and all hope is lost. Fortunately, however, ghosts are afraid of light, and you’ve got your lantern with you. Hold space to keep it on. While the lantern is on, you’ve got extra visibility and the ghosts will run away instead of coming close to you. By going back and forth between the lantern, you can keep the ghosts following you but also at a safe distance. Get all the ghosts away to win.

Your life is measured by the red bar, and your lantern batteries by the yellow bar. The green bar represents how many ghosts you’ve led away. When it gets full you win. If your red bar goes to 0 you lose.

Press to turn on your flash light, to walk around.
Press to quit or to restart.

Extra notes:
This game actually went through a very interesting evolution. I wasn’t at all sure what I wanted to do for the 4k competition, and finally decided (since I’ve been playing around with it so often lately anyway) that a velocity/attraction game would be pretty cool. I was thinking some sort of puzzler/action game where a missile gets shot at you and you need to arrange black holes intelligently so that the missiles get flung back at the rocket launcher. So I started coding that, and ended up with a pretty cool little gravity engine that could fling missiles around in a nifty fashion. Then I experimented with repelling fields as well, which were straightforward and would probably be good for the gameplay. Because at that time I was having one gravity well follow the mouse around, I realized that the missiles actually seemed to behave sort of like some sort of critter when I repelled them. So I decided maybe a game where you need to get a bunch of mice out of a maze would be cool, and your mouse would represent either cheese or a cat. But then I realized that I still had the possibility to have more than one “black hole” in the level at one time, so after noticing that the “mice” would sort of attract to the other ones until the stronger black hole got closer, it reminded of ghosts or something floating around a person. And the rest is history, of course.

Yeah the atmosphere is quite spooky. The flickering lantern is a good effect.

Maybe you could alter the game play a bit so you can kind of herd the ghosts towards the green dot with the flashlight, as well as lure them over using your own body?
Also, for some reason the player moves slower with the flashlight on, maybe it’s on purpose?
The sound volume could be higher too on my computer.

Cool game!

Good game,

There is some bug on the ghosts AI when there are two friends near. They don't follow the player, and keep haunting the friends.

@CommanderKeith

  • What exactly do you mean by herding them with the flashlight? Because currently it already does that, unless you mean that they automatically run towards the exit when the flashlight is on. Basically you can think of the ghosts as asteroids and the people as planets. The player has the most mass so the ghosts get pulled most towards him. When the flashlight is on, the pull is just turned negative, so it becomes a push instead. So if you, the exit, and a ghost are all on a straight line and the ghost is between you and the exit, then the flashlight will herd it directly to the exit. Do you mean some separate functionality from that? As for walking more slowly when the flashlight is on, that’s not intentional at all actually, and is quite weird. My CPU doesn’t seem to go any higher when I have it on, so it’s not lag-based. I’ll look into it. Literally all it does though is change the sign of your mass to negative (so you repel instead), draw a yellow rectangle with alpha, and stretch the light image wider.

@rdcarvallo

  • Yup, I knew that already, take a look above I posted it up there. Has to do with two people together having mass exceeding the player’s. This should be fixed just fine if I randomize the level better/design levels.

A highly updated and improved version uploaded with the same webstart link. Now has an actual level you can play and has become a real game. Just like before, the goal is in the bottom right.

Please let me know if any of the sounds are annoying. Also this should be totally beatable. Any suggestions on tweaks would be great. I’m currently over by 800 bytes… we’ll see if optimizers help. Still slowly but surely reducing my byte count, but I may need to eliminate something (although I don’t know what).

just ran a cut down version of your jar (minus the metainfo) through the 4KJO tool and your compressed class size has been reduced from 4538 bytes to 3092 bytes. this means a jar with only your class + data has gone from 4873 to 3419 bytes. ( optimised Jar )

embedding the “M” data file directly into the class byte code saves another 64 bytes giving 3355 bytes in total. ( embedded data optimised Jar )

to run i noticed i needed the JDK JRE for the sound bank.

this is the 4KJO script that generated the non-embeded optimisted JAR:


<config>
	<classOptimiser>
		<findBestOptimiser>false</findBestOptimiser>
		<usePack200ForEach>false</usePack200ForEach>
		<usePack200atEnd>true</usePack200atEnd>
		<useDummyCompressedSize>true</useDummyCompressedSize>
		<optimisers>
			<count>3</count>
			<orderPosition0>JOGA OPTIMIZER</orderPosition0>
			<orderPosition1>PROGUARD OPTIMIZER</orderPosition1>
			<orderPosition2>JARG OPTIMIZER</orderPosition2>
		</optimisers>
	</classOptimiser>
	<jarCompressor>
		<useKzipOnly>false</useKzipOnly>
		<useBWJDeflateOnly>true</useBWJDeflateOnly>
		<BWJflateFindBestSplitSize>false</BWJflateFindBestSplitSize>
		<KzipFindBestSplitSize>false</KzipFindBestSplitSize>
		<useDefOptAfterEach>false</useDefOptAfterEach>
		<useDefOptAtEnd>true</useDefOptAtEnd>
		<kzipIterations>30</kzipIterations>
		<kzipSplits>30</kzipSplits>
		<BWJflateBlockSize>2048</BWJflateBlockSize>
	</jarCompressor>
</config>

Woah! :o You’re my hero! Now I have to add in a ton more functionality! :stuck_out_tongue:

Hm, yeah I was worried about the sound bank. :-\ You mean the sound wouldn’t play or you couldn’t run it at all? Anyone have any suggestions on what to do about this Soundbank lacking?

The Window looks misplaced on my WinXP (1280*800).
The Bottom-Line is behind the Taskbar :frowning:

But other than that, i won ;D

Yeah to save bytes I was just moving it to position 200,200 on the screen and it’s 800x600, so that means it’ll get overlapped a bit on your smaller resolution. I’ll put a fix for this at some point or I’ll just make it an applet (more likely).

no problems :slight_smile:

yeah, when i tried playing it on my laptop (no jdk) it refused to run and threw an exception related to sound.

Most get around this by either not having sound at all or creating their own syntherziser in game.

Application Error : Unable to launch the Application

Console =

#### Java Web Start Error:
#### null

Exception details given when clicking the Details button =


java.lang.NullPointerException
	at com.sun.media.sound.AbstractPlayer.loadAllInstruments(Unknown Source)
	at F.<init>(F.java:143)
	at F.main(F.java:69)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at com.sun.javaws.Launcher.executeApplication(Unknown Source)
	at com.sun.javaws.Launcher.executeMainClass(Unknown Source)
	at com.sun.javaws.Launcher.doLaunchApp(Unknown Source)
	at com.sun.javaws.Launcher.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)


OK I read that its related to JDK JRE or somthing - I have jdk1.6.0_10 but I guess it doesnt have priority or somthing?

Okay, I put an if statement in there that should prevent that from happening.

There is now a very updated Applet version of the game.
http://www.otcsw.com/applet/Spoo4k/

For some reason the MIDI thing behaves a lot different in an Applet, namely that multiple sounds will not play at once.

Can you give me a quick step-by-step on exactly what you typed to do all this? I want to make sure I’m not being dumb before I start posting exceptions.

Namely the command line stuff for 4KJO and also how you put the text file in the class file. Is it possible to put the image file in there too?

[quote]Can you give me a quick step-by-step on exactly what you typed to do all this
[/quote]
no problems :slight_smile: The tool was originally only going to be an in-house tool so is not very friendly. (or well made :stuck_out_tongue: )

  1. I removed the metainf directory from the your published JAR
  2. I ran 4KJO using default settings on this modified JAR: 4KJO -saveConfigFile c:\temp\out.xml -output c:\temp\spoo4k_opt.jar -verbosity 1 c:\temp\spoo4k_modified.jar
    This generated the first optmised jar ( you can simply use the script aboveusing the -config switch to generate that jar.

to run I just used java -cp c:\temp\spoo4k_opt.jar F

For the embedded version I used an undocumented feature of the tool: the “-addData” command line switch.

  1. Decompiled your F.class
  2. modified the getResourceAsInputStream statement from “M” to “F.class” so that it will use the class itself as input.
  3. added a simple statement to move the inputstream pointer to the start of the embedded data:

	        //search for the magic number
	        while (!(is.read()=='|' && is.read()=='|'));

  1. modified the M file by pre-appending a unique magic number to the “||” to the data.
  2. created a new jar containing the modified F class.
  3. ran the tool using default settings:
 4KJO -saveConfigFile c:\temp\out_embed.xml -output c:\temp\spoo4k_embed_opt.jar -verbosity 1 -addData c:\temp\M_modified c:\temp\spoo4k_embedded.jar

The only seperate file i saw in the JAR you had published was the M data file, i did not see another file (text or image) but, yes you can embedd both, however only as one file so you will have to concatenate both files into one and then embed that file.

Hope this helps.

All right, that makes sense.

How do I get an image file into my class file? Just edit it with a text editor and then copy/paste that big jumble?

I am not sure i follow you…

see the “-addData” section in my previous post on how i embedded data into the class file.

All you need to do is add two extra bytes to the start of your image data and then point the tool (using “-addData”) to that image data. (you will need to add code to “seek” the input stream to the embedded data. (see previous post ) )

If you wand to add multiple discrete data files to the class i.e. a map and a image then you will need to read and output the bytes of each file sequentially into a new file. this new file will likewise need the magic number extra 2 bytes of “||” at the beginning.

You will of course need to then read the embedded data in your class in the same order as you appended the data files into one.

Ah, I see, I misunderstood, I thought you were doing that part manually. So what’s the business with the “magic number” then? Is that added automatically by 4KJO?

Nope, it is not done manually… it is actually quite an involved process… since Java 5 they do not allow you to simply append extra data to the end of the .class file. So the tool has to parse the class file and insert a valid Attribute containing the data. I learnt a lot about class files doing that i can tell you!

The magic number is just a unique 2 byte sequence which is used to make the process of seeking the input stream to the start of your embedded data simpler and thus better to compress.

In my 2007 entry the magic number consisted of byte 0 = 124, byte 1 = 124 which, as ascii is “||”

Unfortunately this feature is undocumented for a reason… It does not automatically add a magic number to the data to embed. A silly over site. However a simple program to append a magic number is below:


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class embedder {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws IOException
	{
		FileInputStream fis = new FileInputStream(new File(args[0]));
		FileOutputStream fos = new FileOutputStream(new File(args[1]));
		
		fos.write('|');
		fos.write('|');
		
		int data =fis.read();
		while (data !=-1)
		{
			fos.write(data);
			data =fis.read();
		}
		fos.close();
	}

}