Here are descriptions of the various file formats used by the game.
On Windows and Linux platforms, the encryption method is AES. This applies to the profile and to all files ending in ".bin".
The files are encrypted using AES in CBC mode with the 192-bit key 0D0607070C01080506090904060D030F03060E010E02070B
It is important to note that the AES algorithm requires data in 16-byte blocks. Since the game does not store the actual length in the file, you must instead look for 0xFD in the last block to indicate the end-of-file. When encrypting, if you wish to follow how the game encrypts, you should pad with up to 4 0xFD bytes, and the rest with 0x00 bytes.
If you have the mcrypt extension for PHP installed, the code is trivial:
$key = "\x0D\x06\x07\x07\x0C\x01\x08\x05\x06\x09\x09\x04\x06\x0D\x03\x0F\x03\x06\x0E\x01\x0E\x02\x07\x0B"; $encrypted = file_get_contents("pers2.dat"); $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, str_repeat("\x00", 16)); echo $decrypted;
Likewise, Python with the Python Cryptography Toolkit is equally simple:
def decode_pc(data): if len(data)%16 !=0: return "" # AES encryption key used (192 bits) key = "\x0D\x06\x07\x07\x0C\x01\x08\x05\x06\x09\x09\x04\x06\x0D\x03\x0F\x03\x06\x0E\x01\x0E\x02\x07\x0B" return AES.new(key, AES.MODE_CBC).decrypt(data)
Complete Python encryption/decryption routines in Python are on the 2D Boy forum.
GooTool's Java class for decryption/encryption follows. Note that Java ships by default to all users with a restricted-export encryption strength. Although the limit is easily lifted with a new file in the user's lib directory, this is not a very user-friendly requirement, so GooTool instead uses the BouncyCastle lightweight API.
package com.goofans.gootool.io; import com.goofans.gootool.util.Utilities; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import java.io.File; import java.io.IOException; /** * Encrypt/decrypt .bin files in AES format (Windows/Linux). * * @author David Croft * @version $Revision: 186$ */ public class AESBinFormat { private static final byte[] KEY = {0x0D, 0x06, 0x07, 0x07, 0x0C, 0x01, 0x08, 0x05, 0x06, 0x09, 0x09, 0x04, 0x06, 0x0D, 0x03, 0x0F, 0x03, 0x06, 0x0E, 0x01, 0x0E, 0x02, 0x07, 0x0B}; private static final byte EOF_MARKER = (byte) 0xFD; private AESBinFormat() { } public static byte[] decodeFile(File file) throws IOException { byte[] inputBytes = Utilities.readFile(file); return decode(inputBytes); } // Java Crypto API - can't use because user will have to install 192-bit policy file. // SecretKey key = new SecretKeySpec(KEY, "AES"); // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");//CBC // cipher.init(Cipher.DECRYPT_MODE, key); // byte[] decrypted = cipher.doFinal(bytes); private static byte[] decode(byte[] inputBytes) throws IOException { BufferedBlockCipher cipher = getCipher(false); byte[] outputBytes = new byte[cipher.getOutputSize(inputBytes.length)]; int outputLen = cipher.processBytes(inputBytes, 0, inputBytes.length, outputBytes, 0); try { outputLen += cipher.doFinal(outputBytes, outputLen); } catch (InvalidCipherTextException e) { throw new IOException("Can't decrypt file: " + e.getLocalizedMessage()); } for (int i = outputLen - 16; i < outputLen; ++i) { byte b = outputBytes[i]; if (b == EOF_MARKER) { outputLen = i; break; } } byte[] finalBytes = new byte[outputLen]; System.arraycopy(outputBytes, 0, finalBytes, 0, outputLen); return finalBytes; } public static void encodeFile(File file, byte[] input) throws IOException { byte[] bytes = encode(input); Utilities.writeFile(file, bytes); } private static byte[] encode(byte[] inputBytes) throws IOException { /* If input was multiple of 16, NO padding. Example: res\levels\BulletinBoardSystem\BulletinBoardSystem.level.bin */ /* Otherwise pad to next 16 byte boundary */ int origSize = inputBytes.length; if (origSize % 16 != 0) { int padding = 16 - origSize % 16; int newSize = origSize + padding; byte[] newInputBytes = new byte[newSize]; System.arraycopy(inputBytes, 0, newInputBytes, 0, origSize); inputBytes = newInputBytes; /* Write up to 4 0xFD bytes immediately after the original file. The remainder can stay as the 0x00 provided by Arrays.copyOf. */ for (int i = origSize; i < origSize + 4 && i < newSize; ++i) { inputBytes[i] = EOF_MARKER; } } BufferedBlockCipher cipher = getCipher(true); byte[] outputBytes = new byte[cipher.getOutputSize(inputBytes.length)]; int outputLen = cipher.processBytes(inputBytes, 0, inputBytes.length, outputBytes, 0); try { outputLen += cipher.doFinal(outputBytes, outputLen); } catch (InvalidCipherTextException e) { throw new IOException("Can't encrypt file: " + e.getLocalizedMessage()); } byte[] finalBytes = new byte[outputLen]; System.arraycopy(outputBytes, 0, finalBytes, 0, outputLen); return finalBytes; } private static BufferedBlockCipher getCipher(boolean forEncryption) { BlockCipher engine = new AESEngine(); BufferedBlockCipher cipher = new BufferedBlockCipher(new CBCBlockCipher(engine)); cipher.init(forEncryption, new KeyParameter(KEY)); return cipher; } }
On the Mac platform, the "encryption" method is a simple rotating XOR . This applies to the profile and to all files ending in ".bin".
Unlike the AES format, there is no requirement for 16-byte alignment of the file. Thus the input length equals the output length for both encryption and decryption.
The initial "salt" for the XOR is binary 00X00Y0Z XOR 0xAB, where X, Y and Z are bits from the file length. X is the first bit, Y is the second bit and Z is the third bit.
For each byte in the file you XOR it with the current salt to decrypt. The salt is then updated by rotating it one bit higher and XORing it with the (encrypted) byte that was just read.
This is explained better in the very simple code below:
package com.goofans.gootool.io; import com.goofans.gootool.util.Utilities; import java.io.File; import java.io.IOException; /** * Encrypt/decrypt .bin files in XOR format (Mac). * * @author David Croft * @version $Revision: 227$ */ public class MacBinFormat { public static byte[] decodeFile(File file) throws IOException { byte[] inputBytes = Utilities.readFile(file); return decode(inputBytes); } private static byte[] decode(byte[] inputBytes) throws IOException { int length = inputBytes.length; byte[] outputBytes = new byte[length]; int salt = (((length & 1) << 6) | ((length & 2) << 3) | (length & 4)) ^ 0xab; for (int i = 0; i < length; ++i) { byte inByte = inputBytes[i]; outputBytes[i] = (byte) (salt ^ inByte); salt = ((salt & 0x7f) << 1 | (salt & 0x80) >> 7) ^ inByte; } return outputBytes; } public static void encodeFile(File file, byte[] inputBytes) throws IOException { byte[] bytes = encode(inputBytes); Utilities.writeFile(file, bytes); } private static byte[] encode(byte[] inputBytes) throws IOException { int length = inputBytes.length; byte[] outputBytes = new byte[length]; int salt = (((length & 1) << 6) | ((length & 2) << 3) | (length & 4)) ^ 0xab; for (int i = 0; i < length; ++i) { byte inByte = inputBytes[i]; byte newByte = (byte) (salt ^ inByte); outputBytes[i] = newByte; salt = ((salt & 0x7f) << 1 | (salt & 0x80) >> 7) ^ newByte; } return outputBytes; } }
Sample encryption/decryption routines in Python are on the 2dboy forum.
Every Gooball starts with the ball tag with a series of attributes (name, shape, mass...)
Inside the ball tag may live the following tags:
part : defines the balls appearence (usually the first part is the body image, followed by eyes and other features).
marker : definition of the cursor when pointing at a ball, or detaching it
shadow : a shadow image/overlay that only shows above geometry.
particles : defined for various states such as sleeping or falling, it adds a particle effect above or beneath the ball
strand : the parts other balls walk on, mainly the definition of the structures physics this ball will form
detachstrand : image and length of detaching visualisation.
splat : drops of this goo, shown when the player clicks on or releases this ball and when it dies.
sound : the soundeffects for various events (attach, throw, death...)
sinvariance : animations of the bodyparts in different states (walking, falling, sleeping...)
All these tags are (strictly) optional and which are present depends on the ball's type.
A ball with no part tags will be invisible, a ball with no sound tags will be silent... etc.
For descriptions of these tags, and more detailed explainations on their attributes... follow the links.
Balls also have a state which indicates what the ball is currently doing, and events which are things that can happen to a ball.
These attributes are always (or usually) required for any normal functioning ball.
These attributes govern the actions of the Goos when they are "left alone"
These attributes govern the Goo Balls motion
These attributes control how the Goos respond to the Player.
These attributes control how the Goos interact with various elements of the level.. pipes, spikes etc.
These attributes govern how the Goos behave when they encounter other Goos.
These attributes control the Goos look, but do not significantly affect game play.
These attributes control whether the Goos are flammable, and how the behave when lit.
These attributes control whether the Goos are "pop-able", and what they contain
If you got here, that means you played my level: Stranded.
If not, you might want to play it before you read this, it has
The falling attachment attribute, when set to true on a gooball, allows strands to attach to it when it is falling/thrown. This means you can start a level with no existing strands within the scene limits! This can be quite interesting...
The first thing I want to show you is a single stand ball with the fallingattachment attribute set to true. One gooball can be attached to the other when it is falling, like so:
However! The ball that was attached to remains in a detached state even though it has a strand! Here is what I mean:
This means that particular gooball can be sucked by the pipe, this can give some quite amusing results:
This is why:
The unattached with strand gooball is eligible to enter the pipe, so it does, and drags the attached gooball with it! This can cause crashes (though not necessarily).
Now, let's check out the gooball I used in the level, the DynamAttach. This is how the attachment works:
However, if one of the two on the structure gets sucked by the pipe, the game WILL crash!
This is why I set this gooball to not be suckable.
The fact this gooball can both have a strand AND be in a unattached state, means the structure can roll around, which is quite funny! Also, if you click then release the gooball on this mobile structure, it will switch to an attached state. You will have noticed both of these while playing the level.
I hope you enjoyed my little explanation on this attribute! I'm looking forward to see such gooballs from you!
These tags define the visible elements of the balls. A ball can contain any number of part tags.
Balls with no parts area invisible.
This tag is optional in a ball.
However without it the ball cannot create strands with other balls, even if the ball's strands attribute is set.
This tag specifies attributes about detaching and flinging. It is required for balls to be detachable.
These attributes allow balls to have a range of different spring constants, however whilst the variation is predictable to the designer, it can appear almost random to the player and makes building structures rather frustrating.
When a strand is created, the spring constant is set based on the initial / "natural" length of the strand.
If the initial length is LESS than the minlen attribute - The spring constant is set to springconstmax
If the initial length is GREATER than the maxlen2 attribute - The spring
constant is set to springconstmin
Between minlen and maxlen2 the spring constant varies linearly from springconstmax to springconstmin
This produces some odd, perhaps "interesting", behaviour if max and min are set to substantially different values, however it makes the result on any strand very hard for the player to predict, and thus makes building "difficult"
As mentioned above, 2DBoy set max and min to the same value, which makes the spring constant "constant", whatever length the strand is.
Defines the graphics use for the mouse cursor when selecting and dragging Goo Balls.
Adds a particle effect to the ball in the give state.
In the original balls these tags are used mostly for sleeping ZZzz effects and "onfire" burning.
Image displayed around / behind the ball when it is on (or near) geometry objects.
Only the parts of the image actually over the geometry are displayed.
Sounds that are played when something happens to the ball.
These are named "states" in which the balls can exist. They describe what the ball is currently doing.
attached | Attached to a structure |
climbing | Moving along a strand |
detaching | Being detached, but has not been actually removed from the structure |
dragging | Being held by the player |
falling | Falling or Flying.. not held by player, not sitting on geometry. |
pipe | Moving along inside the pipe |
sleeping | Asleep |
standing | On geometry, but not walking. |
stuck | Is a "sticky" ball and is stuck to geometry |
stuck_attached | Stuck to geometry and attached to a structure |
stuck_detaching | Stuck to geometry and being detached from a structure |
tank | In the tank (Final Stats screen) |
walking | On geometry and walking along. |
There is one additional state onfire which can only be used in a particles tag.
These are named events which can happen to the balls. They describe what happened to it.
attach | Attaches to a structure |
attachcloser | Attaches to a structure and is closer to the pipe |
bounce | Bounces when hitting geometry |
collidediff | Collides with a different type of ball |
collidegeom | Collides with a geometry object |
collidesame | collides with the same type of ball |
death | dies |
deathfall | Unknown |
detached | Is detached from a structure |
detaching | Is clicked ready for detaching |
detonate | Explodes after being on fire |
drop | Is dropped (at low speed) by the player.. not thrown |
exit | Enters the pipe |
extinguish | Unknown may be unused |
ignite | Catches fire |
land | Lands on geometry, but does not bounce. |
marker | the player moves the mouse over this ball (Hover) |
pickup | The player clicks the ball to grab it. |
snap | a strand snaps, also used for Pilot and Bit "launch" ** |
suction | in range of the pipe |
throw | Released travelling at speed |
**Note
Balls with "fling" capability use the snap event to specify the sound when the ball is launched.
The sounds played as the arrow changes size are hardcoded to SOUND_GLOBAL_FLING0 -> 9
It is "unclear" what specifies the sound to play when a Pilot strand snaps.
Without animations balls seem rather lifeless and flat. They do not wobble or stretch or do anything that makes them appear to be made of Goo.
Obviously it is sometimes desirable to have them appear "lifeless", Bones, Bombs, Blocks etc...
But for balls made of Goo the animations are really the thing that brings them to life!
The tags hold information about the variation of the aniamtions (from ball to ball) and contain sinanim tags which actually describe the animations.
When each ball is created, the game selects a random value for each of the variance attributes [0 -> variance]
This is then applied to all the corresponding attributes in each of sinamin tags the sinvariance contains.
Setting all the variance attributes to 0, will make every ball animate in exactly the same way, at exactly the same time. This looks a bit odd.
You should take care that the variance values are less than the values set in the sinanim tags.
If the variance is equal (or greater than) the sinamin value this can result is very strange effects, such as negative scaling.. where the ball shrinks to a point, then expands again as its mirror image, then shrinks to a point and expands back to its normal appearance.
These tags define elements of the balls animation. Each element is a simple sinusoidal oscillation, but when several are combined (correctly) the resultinf animation can be quite complex.
These are not easy concepts to understand, and its difficult to explain how these animations will work and the effects they will produce when combined.
Your best bet is...
Take an original ball... simplify it, so it has only a single sinvariance and sinanim.
Set the sinvariance values to 0, and have try out some values in the sinanim.
Once you think you've "got" that, add some variance, or a second sinanim tag... and play with that...
Eventually you'll "get it", and be able to produce all sorts of weird and wonderful things.
Alternatively.. just clone an existing ball, and keep whatever 2DBoy had set!
The game fonts are bitmap files stored in res/fonts, along with a .txt file describing the font contents. These were generated using FontBuilder.exe from the PopCap framework. The format of these files is described below:
If you open up one of the text files generated by Fontilizer, you will see a whole bunch of numbers and stuff. In most cases you can leave these as they are. However, if you feel the need to edit them, here's a brief description of what some of them do.
The remaining items are either the values selected in the GUI when the font was originally generated, or references to other objects in the file to link them together.
Stored in the res/islands/ folder, these files describe the setup of a chapter specifies which levels it contains. It also has additional infomation about each level which is not contained in the level files, such as OCD, movies to play, which level must be complete before this level can be played... etc.
The <island> tag contains a level tag for each level in the chapter.
Levels with no "depends" are always playable, however the chapter they are in may not be open.
Chapters are "open" if the first level specified in the island.xml is available.
All audio files in World Of Goo are stored as standard OGG files. However, although it is not used in the original game, it is possible to have different audio files play depending on the language World Of Goo is being played in. Presumably this could be used to translate dialogue into different languages.
When loading an OGG file, World Of Goo also looks for a language-specific alternative. This alternative has the exact same file name as the original OGG file, only with a slightly different extension. The extension consists of the two-letter country code of the language World Of Goo is being played in, followed by a period, followed by the standard "ogg". The file must also be in the same folder as the original OGG file.
So, for example, a file named temp_main.en.ogg in the same directory as temp_main.ogg would play at the main menu when World Of Goo is being played in English. Similarly, renaming the file to temp_main.de.ogg would cause this to play when playing in German.
There are a few rules to keep in mind when using this feature:
This feature does not only apply for OGG files used in the original game; any OGG file loaded by World Of Goo will be overridden if a language-specific alternative exists. That is, whenever the game loads an OGG file through the a level's .resrc.bin file, a ball's resources.xml.bin file, Therefore, this will work in the same way for custom OGG files and custom levels/Goo Balls that load them. This, as mentioned earlier, can be used to, for instance, translate dialogue in a level or total conversion.
2 camera
0-1 music
0-1 loopsound
0-n signpost
0-1 pipe
0-n BallInstance
0-n Strand
0-2 levelexit
0-n endoncollision
0-n endonmessage
0-1 endonnogeom
0-n fire
0-1 targetheight
Specify a camera for the given aspect ratio. The game normally uses zoom levels of 1.0 or 0.889 depending on the type of level.
1-n poi
POIs specify how the camera moves when the level loads. The last POI dictates the position and zoom of the camera during play. When retrying the level, the game skips directly to the last POI.
Defines the background music for this level.
An additional background soundtrack for the level, played on top of the music. Typically used to provide atmospheric effects such as fire.
A signpost defines a clickable image. Note that the signpost image only includes the actual board. You'll want to add a corresponding signpost pole in a <SceneLayer> on the <scene> as well.
Defines the visual exit pipe. Note that this is purely cosmetic; a <levelexit> must exist to cause suction and allow balls to exit. Once sucked, the balls go along the defined pipe.
2-n Vertex
A vertex in the exit pipe. The game will draw the pipe between these vertices. There must be at least two.
The vertices should form horizontal or vertical lines, not diagonal.
It isn't allowed to enlongate a line with a third vertex on the same axis. (e.g. 0,0 ; 100,0 ; 200,0
)
An instance of a ball that is present at level start.
A strand between two balls that is already connected at level start.
The actual point at which a pipe sucks. The <pipe> element only defines the visual appearance of the exit.
If the level has no pipe, level completion can be triggered by collision of two geometry objects. This is used in the game levels ProductLauncher and ObservatoryObservationStation, both of which end when two geometries touch.
If the level has no pipe, the level can end when a specific text message is displayed. This is used by MOM to end when MOM_DESTROY_11 ("I love you, MOM. Goodbye.") is chosen, and in Deliverance on END_DELIVERANCE string is triggered. Since in both cases the strings are programmatically triggered, this seems to be a hack to allow level end on hard-coded triggers.
This element appears in the game code, but is not used by any current levels, so its functionality, attributes and status is unknown.
A circular fire trigger that can ignite certain balls (whose burntime is set). Note that Ivy balls can "burn" too - they simply display a different particle effect which looks like poison instead.
Triggers level end when the given height is used. Used by the game level RegurgitationPumpingStation to end the level when the structure flies to a certain height.
The game appears to have a concept of "live" balls. This excludes balls that are sleeping, that are being thrown, or that are falling. Only live balls can trigger expansion of the view area with autobounds, or level exit on targetheight.
One could conjecture that it only includes balls that are in a structure, but that is not true for example in World of Goo Corporation, where a ball walking to one side will trigger bounds expansion.
RGB = r,g,b integers (0-255 each).
2D = x,y floats
resource = string, an ID present in this level's resources file
The min/max x/y attributes are only optional on wogc* levels. The bounds can be overridden if autobounds is set on the level.
0-n SceneLayer
0-n button
0-n buttongroup
0-n circle
0-n compositegeom
0-n hinge
0-n label
0-n line
0-n linearforcefield
0-n motor
0-n particles
0-n radialforcefield
0-n rectangle
A tag can be applied to any geometry object (line, rectangle, circle and compositegeom*), and specifies additional properties of the object.
The tags you can apply are fixed, but you can have more than one by using a comma separator, for example walkable,detaching.
When a compgeom is tagged as deadly it does not kill "sticky" things.
StickyBombs & AnchorStickys never die. Pokeys will die if they just fall into it, but survive if they are trying to attach.
Tagging as mostlydeadly instead will always kill Pokeys and most other balls, but won't kill Boney, Bombs and Anchors.
Tagging it as both, deadly,mostlydeadly, makes it kill everything except StickyAnchors and StickyBombs.
The Solution! In addition to the deadly or mostlydeadly tags, also add detaching. This makes a compositegeom act exactly the same as any other geometry.. deadly kills everything!
This value attribute is present in one of the original game levels, but it looks to be there by mistake. In Second Hand Smoke the main platform is tagged kindasticky,walkable, however the kindasticky has absolutely no effect.
There is, however, a material called kindasticky, which is never used in any of the original levels. Most likely, 2D Boy meant to set the material of the platform to kindasticky, but accidentally put it in the tag field instead.
On the Mac, raster files are stored in a different file format, suffixed .png.binltl. These files are neither PNG format nor are they encrypted.
The format of the file is as follows. Note that all data types are little-endian.
Offset | Datatype | Description |
---|---|---|
0x00 | unsigned 16-bit int | width of the final image |
0x02 | unsigned 16-bit int | height of the final image |
0x04 | unsigned 32-bit int | size of compressed data |
0x08 | unsigned 32-bit int | size of uncompressed data |
0x12 | ... | compressed image data |
The image data is compressed using zlib deflate. When uncompressed it is a stream of RGBA bytes in that order, so four bytes per pixel.
The actual dimensions of the uncompressed image are always square, with each side being a power of two. The dimensions are the smallest power-of-two square that completely encloses the actual source image (whose size appears in the header).
Thus, a 18x12 image would be stored in a 32x32 square, but a 512x512 image would be in a 512x512 square.
The square should be cropped to the image size specified in the header. Pixels outside these bounds have undefined values.
The fx.xml.bin file stores the data of all particle effects in World of Goo.
They are used to create fire, smoke, rain, leaves falling in the wind, goo drops and trails, and a lot of other visual goo-dness.
There are 2 types of effect...
Point Source: Particles appear from a single point. eg. Fire, Trails, Explosions etc. Details here
Ambient: Particles appear at random positions and cover the whole screen. eg. Rain, Leaves, Snow Details here
The Level Editor Reference Guide has videos showing the different effects of each type.
root of fx.xml
children: All the available <ambientparticleeffect> and <particleeffect>
Ambient effects can only be used in levels in a <particles> item.
children: 1 or more <particle>
Point Source particle effects can be used in levels (in particles, fire and signpost items) and they can also be used in Goo Balls.
children: 1 or more <particle>
A single particle effect may contain a number of different elements which act in different ways. Smoke and Fire for example.
This tag sets the values for one particular type of particle in the effect.
children: [0-n] Any number (or none) <axialsinoffset>
Axial Sin Offset adds extra components to the motion of the particle on either the x or y axis. The original particle effects use at most 1 axialsinoffset on each axis, however you can have more than one on any axis if you wish.
Here is a list of all the Ambient Particle effects in the original World of Goo.
And below the "code" from the original fx.xml for each.
bigleaves1, smallleaves1, rainingleaves, rainingleavesRight, leavesRight,
snowSparse, snowDense, snowStorm, snowStormC3,
blackBallsRising, blackBallsRight, blackLeaves, blackLeavesHeavy,
rainStreaksHeavy, rainStreaksHeavyDistant, rainStreaksDown,
mistRight, breezeRight, breezeUpSlow, breezeDownSlow, breezeUp, mistUpSepia,
ish_BigLeaves, ish_SmallLeaves, ish_RainLeavesLeft, ish_RainLeavesUp, ish_RainLeavesUpRed,
ish_BreezeRight, ish_HeavyBreezeLeft, ish_HeavyBreezeUp, OOS_breezeRight
Up to Particle List bigleaves1:
<ambientparticleeffect name="bigleaves1" maxparticles="2"> <particle image="IMAGE_FX_LEAF1,IMAGE_FX_LEAF2,IMAGE_FX_LEAF3,IMAGE_FX_LEAF4,IMAGE_FX_LEAF5" rotspeed="-6,-1" rotation="-6,-1" scale="0.4,0.5" directed="false" additive="false" speed="1.0,4.0" movedir="-80" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="smallleaves1" maxparticles="2"> <particle image="IMAGE_FX_LEAF1,IMAGE_FX_LEAF2,IMAGE_FX_LEAF3,IMAGE_FX_LEAF4,IMAGE_FX_LEAF5" rotspeed="-6,-1" rotation="-6,-1" scale="0.3,0.4" directed="false" additive="false" speed="1.0,4.0" movedir="-80" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="rainingleaves" maxparticles="60"> <particle image="IMAGE_FX_LEAF1,IMAGE_FX_LEAF2,IMAGE_FX_LEAF3,IMAGE_FX_LEAF4,IMAGE_FX_LEAF5" rotspeed="-6,-1" rotation="-6,-1" scale="0.2,0.5" directed="false" additive="false" speed="1.0,4.0" movedir="-80" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="rainingleavesRight" maxparticles="60"> <particle image="IMAGE_FX_LEAF1,IMAGE_FX_LEAF2,IMAGE_FX_LEAF3,IMAGE_FX_LEAF4,IMAGE_FX_LEAF5" rotspeed="-6,-1" rotation="-6,-1" scale="0.2,0.5" directed="false" additive="false" speed="1.0,4.0" movedir="-30" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="leavesRight" maxparticles="20"> <particle image="IMAGE_FX_LEAF1,IMAGE_FX_LEAF2,IMAGE_FX_LEAF3,IMAGE_FX_LEAF4,IMAGE_FX_LEAF5" rotspeed="-6,-1" rotation="-6,-1" scale="0.2,0.5" directed="false" additive="false" speed="1.0,4.0" movedir="-30" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="snowSparse" maxparticles="5"> <particle image="IMAGE_FX_SNOWFLAKE1,IMAGE_FX_SNOWFLAKE2" rotspeed="-2,2" rotation="-180,180" scale=".75,1.25" directed="false" additive="false" speed="1.0,4.0" movedir="-90" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="snowDense" maxparticles="16"> <particle image="IMAGE_FX_SNOWFLAKE1,IMAGE_FX_SNOWFLAKE2" rotspeed="-2,2" rotation="-180,180" scale=".75,1.25" directed="false" additive="false" speed="1.0,4.0" movedir="-90" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="snowStorm" maxparticles="26"> <particle image="IMAGE_FX_SNOWFLAKE1,IMAGE_FX_SNOWFLAKE2" rotspeed="-2,2" rotation="-180,180" scale="1,2" directed="false" additive="false" speed="4.0,8.0" movedir="-10" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="snowStormC3" maxparticles="64"> <particle image="IMAGE_FX_SNOWFLAKE1,IMAGE_FX_SNOWFLAKE2" rotspeed="-2,2" rotation="-180,180" scale="1,2" directed="false" additive="false" speed="3.0,6.0" movedir="-80" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="blackBallsRising" maxparticles="6"> <particle image="IMAGE_FX_SNOWFLAKE_BLACK" rotspeed="-1,1" rotation="-180,180" scale="0.25,1" directed="false" additive="false" speed="1.0,3.0" movedir="90" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="blackBallsRight" maxparticles="10"> <particle image="IMAGE_FX_SNOWFLAKE_BLACK" rotspeed="-1,1" rotation="-180,180" scale="0.25,1" directed="false" additive="false" speed="3.0,5.0" movedir="0" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="blackLeaves" maxparticles="5"> <particle image="IMAGE_FX_SNOWFLAKE_BLACK" rotspeed="-2,2" rotation="-180,180" scale="0.25,1.0" directed="false" additive="false" speed="3.0,5.0" movedir="-20" movedirvar="10" acceleration="0,-0.03"> <axialsinoffset amp="5,15" freq="0.5,3" phaseshift="0.2,0.4" axis="x"/> <axialsinoffset amp="30,60" freq="0.5,3" phaseshift="0.2,0.4" axis="y"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="blackLeavesHeavy" maxparticles="45"> <particle image="IMAGE_FX_SNOWFLAKE_BLACK" rotspeed="-2,2" rotation="-180,180" scale="0.25,1.0" directed="false" additive="false" speed="3.0,5.0" movedir="-20" movedirvar="10" acceleration="0,-0.03"> <axialsinoffset amp="5,15" freq="0.5,3" phaseshift="0.2,0.4" axis="x"/> <axialsinoffset amp="30,60" freq="0.5,3" phaseshift="0.2,0.4" axis="y"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="rainStreaksHeavy" maxparticles="20" margin="384"> <particle image="IMAGE_FX_RAINSTREAK" rotation="0,0" scale="0.5,3" directed="true" additive="false" speed="40.0,80.0" movedir="-5" movedirvar="5" acceleration="0,-0.1"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="rainStreaksHeavyDistant" maxparticles="20"> <particle image="IMAGE_FX_RAINSTREAK" rotation="0,0" scale="0.05,1" directed="true" additive="false" speed="10.0,20.0" movedir="-5" movedirvar="5" acceleration="0,0"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="rainStreaksDown" maxparticles="50" margin="384"> <particle image="IMAGE_FX_RAINSTREAK" rotation="0,0" scale="1,2.5" directed="true" additive="false" speed="20.0,40.0" movedir="-70" movedirvar="5" acceleration="0,-0.3"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="mistRight" maxparticles="6" margin="500"> <particle image="IMAGE_FX_WINDWHITE" rotation="-180,180" scale="4,6" directed="true" additive="true" speed="30,50" movedir="0" movedirvar="5" acceleration="0,0"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="breezeRight" maxparticles="6" margin="500"> <particle image="IMAGE_FX_WINDWHITE" scale="4,6" directed="true" additive="true" speed="8,12" movedir="0" movedirvar="5" acceleration="0,0"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="breezeUpSlow" maxparticles="6" margin="500"> <particle image="IMAGE_FX_WINDWHITE" scale="4,6" directed="false" additive="true" speed="4,5" movedir="90" movedirvar="5" acceleration="0,0"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="breezeDownSlow" maxparticles="6" margin="500"> <particle image="IMAGE_FX_WINDWHITE" scale="4,6" directed="false" additive="true" speed="4,5" movedir="270" movedirvar="5" acceleration="0,0"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="breezeUp" maxparticles="6" margin="500"> <particle image="IMAGE_FX_WINDWHITE" scale="4,6" directed="false" additive="true" speed="12,16" movedir="90" movedirvar="5" acceleration="0,0"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="mistUpSepia" maxparticles="10" margin="500"> <particle image="IMAGE_FX_WINDSEPIA" rotation="0,0" scale="4,6" directed="false" additive="true" speed="1.0,4.0" movedir="90" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="ish_BigLeaves" maxparticles="8"> <particle image="IMAGE_FX_ISH_CHAR_0,IMAGE_FX_ISH_CHAR_1" rotspeed="-6,-1" rotation="-6,-1" scale="1.0,1.5" directed="false" additive="true" speed="1.0,4.0" movedir="-30" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="ish_SmallLeaves" maxparticles="8"> <particle image="IMAGE_FX_ISH_CHAR_0,IMAGE_FX_ISH_CHAR_1" rotspeed="-6,-1" rotation="-6,-1" scale="0.75,1.0" directed="false" additive="true" speed="1.0,4.0" movedir="-30" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="ish_RainLeavesLeft" maxparticles="10"> <particle image="IMAGE_FX_ISH_CHAR_0,IMAGE_FX_ISH_CHAR_1" rotspeed="-6,-1" rotation="-6,-1" scale="1.0,2.5" directed="false" additive="false" speed="10.0,30.0" movedir="180" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="ish_RainLeavesUp" maxparticles="10"> <particle image="IMAGE_FX_ISH_CHAR_0,IMAGE_FX_ISH_CHAR_1" rotspeed="-6,-1" rotation="-6,-1" scale="1.0,2.5" directed="false" additive="false" speed="3.0,8.0" movedir="90" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="ish_RainLeavesUpRed" maxparticles="40"> <particle image="IMAGE_FX_ISHR_CHAR_0,IMAGE_FX_ISHR_CHAR_1" rotspeed="-6,-1" rotation="-6,-1" scale="1.0,2.5" directed="false" additive="false" speed="3.0,8.0" movedir="90" movedirvar="10" acceleration="0,0"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </ambientparticleeffect>
<ambientparticleeffect name="ish_BreezeRight" maxparticles="6" margin="400"> <particle image="IMAGE_FX_WINDISH" scale="4,6" directed="true" additive="true" speed="8,12" movedir="0" movedirvar="5" acceleration="0,0"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="ish_HeavyBreezeLeft" maxparticles="12" margin="400"> <particle image="IMAGE_FX_WINDISH" scale="4,6" directed="true" additive="true" speed="8,12" movedir="180" movedirvar="5" acceleration="0,0"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="ish_HeavyBreezeUp" maxparticles="12" margin="400"> <particle image="IMAGE_FX_WINDISH" scale="4,6" directed="true" additive="true" speed="8,12" movedir="90" movedirvar="5" acceleration="0,0"> </particle> </ambientparticleeffect>
<ambientparticleeffect name="OOS_breezeRight" maxparticles="6" margin="1000"> <particle image="IMAGE_FX_WINDSEPIA" scale="8,10" directed="true" additive="true" speed="8,12" movedir="0" movedirvar="5" acceleration="0,0"> </particle> </ambientparticleeffect>
Here is a list of all the Point Source Particle effects in the original World of Goo.
And below the "code" from the original fx.xml for each.
unlockfuse, unlockburst, snowStormFromPoint, splash, gooFallWide2, gooSplatter,
gooSplatterSubtle, mistUp, fireStack1, fireStackSmaller1, fireStackSmaller2,
fireRobotHead, fireStackSquat, fireStackLanternBw, fireBallBurn, thruster,
fireArmBurn, poisonBallBurn, poisonArmBurn, blackSmokeRising, bubblesRisingFromPoint,
bubblesRisingFromPointSlow, wogcSmoke, wogcSmokeIsh, whiteSmokeRising, sleepyZzz,
ish_sleepyZzz, ishr_sleepyZzz, signpostAlert, signpostAlertMom, whistle,
worldMap_FactorySmoke, BurningManSmoke, RobotHeadSmoke, distantSmokestack,
worldMap_FactorySmokeWhite, cigSmoke, gentleFactorySmoke, puffyFactorySmoke,
gentleFactorySmokeSepia, matchSmoke, gooTankStream, gooDrips, polFountain1, polFountain2,
gooMist, timebugFizz, flashes, tubeAirFlowUp, tubeAirFlowLeft, geomExplosionDust,
ish_smallfire, ish_FlingTrail, ishr_FlingTrail, ish_gpu_bitspew, ish_bitPop, beautypop,
flowerDust, OOS_gooDrips, OOS_gooGlobs, BallExplode_Fuse, BallExplode_Bomb, BallExplode_ISH,
ISH_undeleteFizz, ish_bubbles
Up to Particle List unlockfuse:
<particleeffect name="unlockfuse" maxparticles="30" rate="0.5"> <particle image="IMAGE_FX_GOOGLOB" rotspeed="0.2" directed="false" scale="0.5,1.0" finalscale="0" fade="false" additive="false" lifespan="3,4" speed="0.2,0.4" movedir="0" movedirvar="180" acceleration="0,0"/> </particleeffect>
<particleeffect name="unlockburst" maxparticles="40"> <particle image="IMAGE_FX_GOODRIP1" directed="true" scale="0.35,0.75" finalscale="0" fade="false" additive="false" lifespan="1.4,2" dampening="0.98" speed="3,12" movedir="90" movedirvar="20" acceleration="0,0" /> <particle image="IMAGE_FX_GOOGLOB" directed="true" scale="0.25,0.8" finalscale="0" fade="false" additive="false" lifespan="1.4,2" dampening="0.98" speed="2,7" movedir="90" movedirvar="30" acceleration="0,-0.1" /> </particleeffect>
<particleeffect name="snowStormFromPoint" maxparticles="60" rate="0.22"> <particle image="IMAGE_FX_SNOWFLAKE1,IMAGE_FX_SNOWFLAKE2" rotspeed="-2,2" rotation="-180,180" scale="1,1.5" lifespan="6" directed="false" additive="false" speed="9.0,12.0" movedir="-10" movedirvar="20" acceleration="0,0" fade="true"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </particleeffect>
<particleeffect name="splash" maxparticles="20"> <particle image="IMAGE_FX_WATERDROP1" directed="true" scale="0.25,1" finalscale="0" fade="true" lifespan="1,1.6" dampening="0.98" speed="6,16" movedir="90" movedirvar="60" acceleration="0,-0.2" /> </particleeffect>
<particleeffect name="gooFallWide2" maxparticles="24" rate="0.025"> <particle image="IMAGE_FX_GOOFALLWIDE2" directed="false" scale="4.0,4.0" finalscale="3.0" fade="false" additive="false" lifespan="11" speed="0.3,0.5" movedir="90" movedirvar="0" acceleration="0,0.02"/> </particleeffect>
<particleeffect name="gooSplatter" maxparticles="10" rate="0.15"> <particle image="IMAGE_FX_GOODRIP1" directed="true" scale="0.6" finalscale="0.0" fade="true" additive="false" lifespan="1.2" speed="3.0,6.0" movedir="270" movedirvar="180" acceleration="0,-0.6"/> </particleeffect>
<particleeffect name="gooSplatterSubtle" maxparticles="10" rate="0.15"> <particle image="IMAGE_FX_GOODRIP1" directed="true" scale="0.0" finalscale="0.6" fade="true" additive="false" lifespan="1.2" speed="3.0,6.0" movedir="270" movedirvar="180" acceleration="0,-0.6"/> </particleeffect>
<particleeffect name="mistUp" maxparticles="10" rate="0.08" margin="400"> <particle image="IMAGE_FX_MIST1" rotspeed="-4,4" rotation="-180,180" scale="0.5,1" finalscale="14" fade="true" lifespan="0.8,1.5" directed="false" additive="false" speed="0.5,3.0" movedir="90" movedirvar="60" acceleration="0,0.1"> </particle> </particleeffect>
<particleeffect name="fireStack1" maxparticles="30" rate="0.3"> <particle image="IMAGE_FX_FIREMAIN1" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="2.05,3.05" finalscale="0.35" fade="true" additive="true" lifespan="1.7,1.7" speed="9.5" movedir="90" movedirvar="12" acceleration="0,0.12"/> <particle image="IMAGE_FX_FIREMAIN2" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="3.05,4.05" finalscale="0.1" fade="true" additive="true" lifespan="1.7,1.7" speed="9.5" movedir="90" movedirvar="5" acceleration="0,0.12"/> </particleeffect>
<particleeffect name="fireStackSmaller1" maxparticles="20" rate="0.3"> <particle image="IMAGE_FX_FIREMAIN1" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="1.35,1.95" finalscale="0.35" fade="true" additive="true" lifespan="1,1" speed="7.5" movedir="90" movedirvar="12" acceleration="0,0.12"/> <particle image="IMAGE_FX_FIREMAIN2" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="2.05,2.85" finalscale="0.1" fade="true" additive="true" lifespan="1,1" speed="7.5" movedir="90" movedirvar="5" acceleration="0,0.12"/> </particleeffect>
<particleeffect name="fireStackSmaller2" maxparticles="20" rate="0.3"> <particle image="IMAGE_FX_FIREMAIN1" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.4,0.9" finalscale="0.1" fade="true" additive="true" lifespan="0.6,0.8" speed="3.5" movedir="90" movedirvar="12" acceleration="0,0.12"/> <particle image="IMAGE_FX_FIREMAIN2" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.4,0.9" finalscale="0.02" fade="true" additive="true" lifespan="0.6,0.8" speed="3.5" movedir="90" movedirvar="5" acceleration="0,0.12"/> </particleeffect>
<particleeffect name="fireRobotHead" maxparticles="20" rate="0.3"> <particle image="IMAGE_FX_FIREMAIN1" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.9,1.25" finalscale="0.35" fade="true" additive="true" lifespan="1,1" speed="6.5" movedir="90" movedirvar="12" acceleration="0,0.12"/> <particle image="IMAGE_FX_FIREMAIN2" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="1.5,2.0" finalscale="0.1" fade="true" additive="true" lifespan="1,1" speed="6.5" movedir="90" movedirvar="5" acceleration="0,0.12"/> </particleeffect>
<particleeffect name="fireStackSquat" maxparticles="20" rate="0.3"> <particle image="IMAGE_FX_FIREMAIN1" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="1.35,1.95" finalscale="0.35" fade="true" additive="true" lifespan="1,1" speed="4" movedir="90" movedirvar="12" acceleration="0,0.12"/> <particle image="IMAGE_FX_FIREMAIN2" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="2.05,2.85" finalscale="0.1" fade="true" additive="true" lifespan="1,1" speed="4" movedir="90" movedirvar="5" acceleration="0,0.12"/> </particleeffect>
<particleeffect name="fireStackLanternBw" maxparticles="16" rate="0.3"> <particle image="IMAGE_FX_FIREMAIN1BW" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.2,0.3" finalscale="0.1" fade="true" additive="true" lifespan="1,1" speed="1" movedir="90" movedirvar="12" acceleration="0,0.01"/> <particle image="IMAGE_FX_FIREMAIN2BW" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.2,0.4" finalscale="0.05" fade="true" additive="true" lifespan="1,1" speed="1" movedir="90" movedirvar="5" acceleration="0,0.01"/> </particleeffect>
<particleeffect name="fireBallBurn" maxparticles="12" rate="0.2"> <particle image="IMAGE_FX_FIREMAIN1" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.3,0.6" finalscale="0.1" fade="true" additive="true" lifespan="0.5,0.8" speed="1.5" movedir="90" movedirvar="12" acceleration="0,0.12"/> <particle image="IMAGE_FX_FIREMAIN2" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.3,0.6" finalscale="0.02" fade="true" additive="true" lifespan="0.5,0.8" speed="1.5" movedir="90" movedirvar="5" acceleration="0,0.12"/> </particleeffect>
<particleeffect name="thruster" maxparticles="30" rate="1.5" > <particle image="IMAGE_FX_FIREMAIN1" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.08,0.14" finalscale="0.3" fade="true" additive="true" lifespan="0.25,0.45" speed="10" movedir="90" movedirvar="4" acceleration="0,0"/> <particle image="IMAGE_FX_FIREMAIN2" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.1,0.1" finalscale="0.4" fade="true" additive="true" lifespan="0.38,0.40" speed="10" movedir="90" movedirvar="2" acceleration="0,0"/> </particleeffect>
<particleeffect name="fireArmBurn" maxparticles="16" rate="0.3"> <particle image="IMAGE_FX_FIREMAIN1" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.1,0.2" finalscale="0.5" fade="true" additive="true" lifespan="0.2,0.3" speed="0.5" movedir="0" movedirvar="180" acceleration="0,0"/> <particle image="IMAGE_FX_FIREMAIN2" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.1,0.2" finalscale="0.6" fade="true" additive="true" lifespan="0.2,0.3" speed="0.5" movedir="0" movedirvar="180" acceleration="0,0"/> </particleeffect>
<particleeffect name="poisonBallBurn" maxparticles="12" rate="0.2"> <particle image="IMAGE_FX_GOOGLOB" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="0.3,0.6" finalscale="0.0" fade="true" additive="false" lifespan="0.25,0.8" speed="0.1,6.5" movedir="0" movedirvar="180" acceleration="0,0.12"/> </particleeffect>
<particleeffect name="poisonArmBurn" maxparticles="30" rate="0.5"> <particle image="IMAGE_FX_GOOGLOB" rotspeed="0.2" directed="false" scale="0.5,1.0" finalscale="0" fade="false" additive="false" lifespan="3,4" speed="0.2,0.4" movedir="0" movedirvar="180" acceleration="0,0"/> </particleeffect>
<particleeffect name="blackSmokeRising" maxparticles="40" rate="0.3"> <particle image="IMAGE_FX_SMOKEBLACK" rotspeed="-4,4" rotation="-180,180" scale="0" finalscale="6" directed="false" additive="false" speed="6,9" fade="true" lifespan="1.6,2" movedir="90" movedirvar="15" acceleration="0,0.1"> </particle> </particleeffect>
<particleeffect name="bubblesRisingFromPoint" maxparticles="20" rate="0.1"> <particle image="IMAGE_FX_BUBBLE1" rotspeed="-4,4" rotation="-180,180" scale="0" finalscale="1.1" directed="false" additive="false" speed="1,8" fade="true" lifespan="1.8,2.0" movedir="90" movedirvar="30" acceleration="0,0.1"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </particleeffect>
<particleeffect name="bubblesRisingFromPointSlow" maxparticles="20" rate="0.1"> <particle image="IMAGE_FX_BUBBLE1" rotspeed="-4,4" rotation="-180,180" scale="0" finalscale="1.5" directed="false" additive="false" speed="0.1,1.25" fade="true" lifespan="1.8,2.0" movedir="90" movedirvar="60" acceleration="0,0.1"> <axialsinoffset amp="5,25" freq="0.5,4" phaseshift="0.2,0.4" axis="x"/> </particle> </particleeffect>
<particleeffect name="wogcSmoke" maxparticles="30" rate="0.1"> <particle image="IMAGE_FX_SMOKEBLACK" rotspeed="-1,1" rotation="-180,180" scale="4" finalscale="6" directed="false" additive="false" speed="2,4" fade="true" lifespan="4,5" movedir="90" movedirvar="15" acceleration="0,0.01"> </particle> </particleeffect>
<particleeffect name="wogcSmokeIsh" maxparticles="30" rate="0.1"> <particle image="IMAGE_FX_SMOKEGREEN" rotspeed="-1,1" rotation="-180,180" scale="4" finalscale="6" directed="false" additive="false" speed="2,4" fade="true" lifespan="4,5" movedir="90" movedirvar="15" acceleration="0,0.01"> </particle> </particleeffect>
<particleeffect name="whiteSmokeRising" maxparticles="30" rate="0.3"> <particle image="IMAGE_FX_SMOKEWHITE" rotspeed="-4,4" rotation="-180,180" scale="4,5" finalscale="6" directed="false" additive="false" speed="6,9" fade="true" lifespan="1.6,2" movedir="90" movedirvar="15" acceleration="0,0.1"> </particle> </particleeffect>
<particleeffect name="sleepyZzz" maxparticles="7" rate="0.06"> <particle image="IMAGE_FX_SLEEPYZZZ" rotspeed="0" directed="false" scale="0.2" finalscale="0.8" fade="true" additive="false" lifespan="0.8" speed="0.5,0.9" movedir="90" movedirvar="90" acceleration="0,0.01"> <axialsinoffset amp="5,10" freq="2,3" phaseshift="0.2,0.4" axis="x"/> </particle> </particleeffect>
<particleeffect name="ish_sleepyZzz" maxparticles="7" rate="0.06"> <particle image="IMAGE_FX_ISH_SLEEPYZZZ" rotspeed="0" directed="false" scale="0.2" finalscale="0.8" fade="true" additive="false" lifespan="0.8" speed="0.5,0.9" movedir="90" movedirvar="90" acceleration="0,0.01"> <axialsinoffset amp="5,10" freq="2,3" phaseshift="0.2,0.4" axis="x"/> </particle> </particleeffect>
<particleeffect name="ishr_sleepyZzz" maxparticles="7" rate="0.06"> <particle image="IMAGE_FX_ISHR_SLEEPYZZZ" rotspeed="0" directed="false" scale="0.2" finalscale="0.8" fade="true" additive="false" lifespan="0.8" speed="0.5,0.9" movedir="90" movedirvar="90" acceleration="0,0.01"> <axialsinoffset amp="5,10" freq="2,3" phaseshift="0.2,0.4" axis="x"/> </particle> </particleeffect>
<particleeffect name="signpostAlert" maxparticles="4" rate="0.05"> <particle image="IMAGE_FX_SIGNPOSTEXCLAIMATION" rotspeed="0" rotation="-20,20" directed="false" scale="0.5, 0.8" finalscale="1.0" fade="true" additive="false" lifespan="0.6" speed="1,3" movedir="90" movedirvar="45" acceleration="0,0.01"> <axialsinoffset amp="5,10" freq="5,8" phaseshift="0.2,0.4" axis="x"/> </particle> </particleeffect>
<particleeffect name="signpostAlertMom" maxparticles="4" rate="0.05"> <particle image="IMAGE_FX_SNOWFLAKE_BLACK" rotspeed="0" rotation="-20,20" directed="false" scale="0.5, 0.8" finalscale="1.0" fade="true" additive="false" lifespan="0.6" speed="1,3" movedir="90" movedirvar="45" acceleration="0,0.01"> <axialsinoffset amp="5,10" freq="5,8" phaseshift="0.2,0.4" axis="x"/> </particle> </particleeffect>
<particleeffect name="whistle" maxparticles="10" rate="0.2"> <particle image="IMAGE_FX_NOTE1,IMAGE_FX_NOTE2,IMAGE_FX_NOTE3" rotspeed="0" rotation="-20,20" directed="false" scale="0.5, 0.8" finalscale="1.0" fade="true" additive="false" lifespan="0.6" speed="1,3" movedir="90" movedirvar="45" acceleration="0,0.01"> <axialsinoffset amp="5,10" freq="5,8" phaseshift="0.2,0.4" axis="x"/> </particle> </particleeffect>
<particleeffect name="worldMap_FactorySmoke" maxparticles="38" rate="0.6"> <particle image="IMAGE_FX_SMOKEBLACK" rotspeed="-4,4" rotation="-180,180" scale="0.5,1.2" finalscale="3" directed="false" additive="false" speed="1,3" fade="true" lifespan="1.6,2" movedir="120" movedirvar="15" acceleration="0.2,0.0"> </particle> </particleeffect>
<particleeffect name="BurningManSmoke" maxparticles="36" rate="0.4"> <particle image="IMAGE_FX_SMOKEBLACK" rotspeed="-4,4" rotation="-180,180" scale="0.5,1.2" finalscale="6" directed="false" additive="false" speed="1,3" fade="true" lifespan="1.2,2.0" movedir="120" movedirvar="15" acceleration="0.15,0.05"> </particle> </particleeffect>
<particleeffect name="RobotHeadSmoke" maxparticles="48" rate="0.15"> <particle image="IMAGE_FX_SMOKEBLACK" rotspeed="0,1" rotation="-180,180" scale="0.5,1.2" finalscale="6" directed="false" additive="false" speed="0.1,1" fade="true" lifespan="2.0,2.2" movedir="90" movedirvar="15" acceleration="0.0,0.05"> </particle> </particleeffect>
<particleeffect name="distantSmokestack" maxparticles="18" rate="0.2"> <particle image="IMAGE_FX_SMOKEBLACK" rotspeed="-4,4" rotation="-180,180" scale="0.25,0.5" finalscale="3" directed="false" additive="false" speed="0.2,2" fade="true" lifespan="1,1.4" movedir="90" movedirvar="5" acceleration="0.0,0.1"> </particle> <particle image="IMAGE_FX_SMOKEBLACK" rotspeed="-4,4" rotation="-180,180" scale="0.25,0.6" finalscale="3" directed="false" additive="false" speed="0.1,2" fade="true" lifespan="0.6,1.0" movedir="90" movedirvar="15" acceleration="0.0,0.1"> </particle> </particleeffect>
<particleeffect name="worldMap_FactorySmokeWhite" maxparticles="38" rate="0.6"> <particle image="IMAGE_FX_SMOKEWHITE" rotspeed="-4,4" rotation="-180,180" scale="0.5,1.2" finalscale="3" directed="false" additive="false" speed="1,3" fade="true" lifespan="1.6,2" movedir="120" movedirvar="15" acceleration="0.2,0.0"> </particle> </particleeffect>
<particleeffect name="cigSmoke" maxparticles="30" rate="0.3"> <particle image="IMAGE_FX_WINDWHITE" scale="0.05,0.1" finalscale="0.8" directed="true" additive="false" speed="2,4" fade="true" lifespan="1.6,2" movedir="90" movedirvar="3" acceleration="0.01,0.0"> </particle> </particleeffect>
<particleeffect name="gentleFactorySmoke" maxparticles="45" rate="0.125"> <particle image="IMAGE_FX_WINDBLACK" scale="0.1,0.2" finalscale="2.1" directed="true" additive="false" speed="3,4" fade="true" lifespan="6.8,6.8" movedir="90" movedirvar="0.08" acceleration="0.01,0.0"> </particle> </particleeffect>
<particleeffect name="puffyFactorySmoke" maxparticles="32" rate="0.2"> <particle image="IMAGE_FX_SMOKEBLACK" rotspeed="-4,4" rotation="-180,180" scale="0.5,0.7" finalscale="3" directed="false" additive="false" speed="0.5,1" fade="true" lifespan="2,2.2" movedir="0" movedirvar="180" acceleration="0.0,0.0"> </particle> </particleeffect>
<particleeffect name="gentleFactorySmokeSepia" maxparticles="45" rate="0.125"> <particle image="IMAGE_FX_WINDSEPIA" scale="0.1,0.2" finalscale="2.1" directed="true" additive="false" speed="3,4" fade="true" lifespan="6.8,6.8" movedir="90" movedirvar="0.08" acceleration="0.01,0.0"> </particle> </particleeffect>
<particleeffect name="matchSmoke" maxparticles="10" rate="0.08"> <particle image="IMAGE_FX_SMOKEBLACK" scale="0.1,0.25" finalscale="4" directed="false" rotspeed="-4,4" additive="false" speed="1,3" fade="true" lifespan="1.6,2" movedir="90" movedirvar="3" acceleration="0.01,0.0"> </particle> </particleeffect>
<particleeffect name="gooTankStream" maxparticles="40" rate="0.4"> <particle image="IMAGE_FX_GOODRIP1" directed="true" scale="1,1" fade="false" additive="false" lifespan="0.6,0.6" speed="20,30" movedir="270" movedirvar="1" acceleration="0,0"/> </particleeffect>
<particleeffect name="gooDrips" maxparticles="8" rate="0.06"> <particle image="IMAGE_FX_GOODRIP1" directed="true" scale="0.2,0.5" fade="false" additive="false" lifespan="1.9,1.9" speed="4,6" movedir="270" movedirvar="0" acceleration="0,-0.01"/> </particleeffect>
<particleeffect name="polFountain1" maxparticles="20" rate="0.25"> <particle image="IMAGE_FX_GOODRIP1" directed="true" scale="0.0,0.0" finalscale="1.2" fade="true" additive="false" lifespan="0.8,0.8" speed="17,18" movedir="115" movedirvar="0.1" acceleration="0,-0.5"/> </particleeffect>
<particleeffect name="polFountain2" maxparticles="30" rate="0.25"> <particle image="IMAGE_FX_GOODRIP1" directed="true" scale="0.0,0.0" finalscale="1.6" fade="true" additive="false" lifespan="0.8,0.8" speed="12,13" movedir="150" movedirvar="0.1" acceleration="0,-0.65"/> </particleeffect>
<particleeffect name="gooMist" maxparticles="10" rate="0.6"> <particle image="IMAGE_FX_SMOKEBLACK" scale="0.1,0.25" finalscale="2.4" directed="false" rotspeed="-4,4" additive="false" speed="0,1" fade="true" lifespan="0.3,0.6" movedir="0" movedirvar="180" acceleration="0.0,0.0"> </particle> </particleeffect>
<particleeffect name="timebugFizz" maxparticles="8" rate="0.1"> <particle image="IMAGE_FX_SMOKEWHITE" rotspeed="2,4" directed="false" scale="0.4" finalscale="0.7" fade="true" additive="false" lifespan="0.8" speed="0.1,0.2" movedir="0" movedirvar="180" acceleration="0,-0.01"> </particle> </particleeffect>
<particleeffect name="flashes" maxparticles="3" rate="0.1"> <particle image="IMAGE_FX_FLASH" rotspeed="0" rotation="-180,180" directed="false" scale="4.0, 8.0" finalscale="0.0" fade="true" additive="true" lifespan="0.06" speed="25,400" movedir="0" movedirvar="180" acceleration="0,0"> </particle> </particleeffect>
<particleeffect name="tubeAirFlowUp" maxparticles="8" rate="0.1"> <particle image="IMAGE_FX_WINDWHITE" scale="0.5,0.75" finalscale="5" directed="true" additive="true" speed="15,20" fade="true" lifespan="1.2,1.4" movedir="90" movedirvar="3" acceleration="0.0,0.0"> </particle> </particleeffect>
<particleeffect name="tubeAirFlowLeft" maxparticles="8" rate="0.1"> <particle image="IMAGE_FX_WINDWHITE" scale="0.5,0.75" finalscale="5" directed="true" additive="true" speed="15,20" fade="true" lifespan="1.2,1.4" movedir="180" movedirvar="3" acceleration="0.0,0.0"> </particle> </particleeffect>
<particleeffect name="geomExplosionDust" maxparticles="100"> <particle image="IMAGE_FX_SMOKEBLACK" scale="1.0,3.0" directed="false" additive="false" dampening="0.95" speed="3,4" fade="true" lifespan="1,3" movedir="90" movedirvar="90" acceleration="0,0"> </particle> </particleeffect>
<particleeffect name="ish_smallfire" maxparticles="16" rate="0.3"> <particle image="IMAGE_FX_FIREMAIN1ISH" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="2,2.5" finalscale="0.3" fade="true" additive="true" lifespan="1,1" speed="1" movedir="90" movedirvar="12" acceleration="0,0.09"/> <particle image="IMAGE_FX_FIREMAIN1ISH" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="2,2.5" finalscale="0.05" fade="true" additive="true" lifespan="1,1" speed="1" movedir="90" movedirvar="5" acceleration="0,0.09"/> </particleeffect>
<particleeffect name="ish_FlingTrail" maxparticles="60" rate="0.5"> <particle image="IMAGE_FX_ISH_CHAR_0,IMAGE_FX_ISH_CHAR_1" rotspeed="-2,-0.2" directed="false" scale="1.0,1.5" finalscale="0" fade="false" additive="true" lifespan="3,4" speed="0.2,0.4" movedir="0" movedirvar="180" acceleration="0,0"/> </particleeffect>
<particleeffect name="ishr_FlingTrail" maxparticles="60" rate="0.5"> <particle image="IMAGE_FX_ISHR_CHAR_0,IMAGE_FX_ISHR_CHAR_1" rotspeed="-2,-0.2" directed="false" scale="1.0,1.5" finalscale="0" fade="false" additive="true" lifespan="3,4" speed="0.2,0.4" movedir="0" movedirvar="180" acceleration="0,0"/> </particleeffect>
<particleeffect name="ish_gpu_bitspew" maxparticles="30" rate="0.07"> <particle image="IMAGE_FX_ISH_CHAR_0" rotspeed="-9,-3" directed="false" scale="1.0,1.5" finalscale="4" fade="true" additive="true" lifespan="2,4" speed="1,3" movedir="0" movedirvar="180" acceleration="0,0"/> </particleeffect>
<particleeffect name="ish_bitPop" maxparticles="80"> <particle image="IMAGE_FX_FIREMAIN1ISH" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="2,2.5" finalscale="0.3" fade="true" additive="true" lifespan="1,1" speed="1" movedir="90" movedirvar="12" acceleration="0,0.09"/> <particle image="IMAGE_FX_FIREMAIN1ISH" directed="false" rotspeed="-4,-2" rotation="-180,180" scale="2,2.5" finalscale="0.05" fade="true" additive="true" lifespan="1,1" speed="1" movedir="90" movedirvar="5" acceleration="0,0.09"/> </particleeffect>
<particleeffect name="beautypop" maxparticles="80"> <particle image="IMAGE_FX_GOODRIP1" directed="true" scale="0.35,0.75" finalscale="0" fade="false" additive="false" lifespan="1.4,2" dampening="0.98" speed="3,12" movedir="90" movedirvar="180" acceleration="0,0" /> <particle image="IMAGE_FX_GOOGLOB" directed="true" scale="1.25,1.8" finalscale="0" fade="false" additive="false" lifespan="1.4,2" dampening="0.98" speed="2,7" movedir="90" movedirvar="180" acceleration="0,-0.1" /> </particleeffect>
<particleeffect name="flowerDust" maxparticles="30" rate="0.25"> <particle image="IMAGE_FX_GOOGLOB" rotspeed="0.2" directed="false" scale="0.5,1.0" finalscale="0" fade="false" additive="false" lifespan="2,3" speed="0.2,0.4" movedir="0" movedirvar="180" acceleration="0,0"/> </particleeffect>
<particleeffect name="OOS_gooDrips" maxparticles="16" rate="0.035"> <particle image="IMAGE_FX_GOODRIP1" directed="true" scale="0.2,0.7" finalscale="0.0" fade="true" additive="false" lifespan="4, 8" speed="0.2,1.2" movedir="270" movedirvar="30" acceleration="0,-0.02"/> </particleeffect>
<particleeffect name="OOS_gooGlobs" maxparticles="30" rate="0.25"> <particle image="IMAGE_FX_GOOGLOB" rotspeed="0.2" directed="false" scale="0.5,1.0" finalscale="0" fade="false" additive="false" lifespan="2,3" speed="0.6,0.8" movedir="0" movedirvar="180" acceleration="0,0.01"/> </particleeffect>
<particleeffect name="BallExplode_Fuse" maxparticles="80"> <particle image="IMAGE_FX_EXPLODESTREAK1" directed="true" scale="0.01,0.1" finalscale="3" fade="true" additive="true" lifespan="0.15,0.55" speed="3,12" movedir="0" movedirvar="180" acceleration="0,0" dampening="0.1" /> <particle image="IMAGE_FX_RAINSTREAK" directed="true" scale="0.01,0.1" finalscale="4" fade="true" additive="false" lifespan="0.25,0.75" speed="3,12" movedir="0" movedirvar="180" acceleration="0,0" dampening="0.1" /> <particle image="IMAGE_FX_FIREMAIN1,IMAGE_FX_FIREMAIN2" rotspeed="-1,-0.1" rotation="-180,180" directed="false" scale="0.25,0.8" finalscale="0" fade="true" additive="true" lifespan="1.4,2" dampening="0.98" speed="2,7" movedir="90" movedirvar="30" acceleration="0,-0.1" /> </particleeffect>
<particleeffect name="BallExplode_Bomb" maxparticles="80"> <particle image="IMAGE_FX_EXPLODESTREAK1" directed="true" scale="0.01,0.1" finalscale="4" fade="true" additive="true" lifespan="0.5,1.0" speed="10,30" movedir="0" movedirvar="180" acceleration="0,0" dampening="0.1" /> <particle image="IMAGE_FX_RAINSTREAK" directed="true" scale="0.01,0.1" finalscale="5" fade="true" additive="false" lifespan="0.5,1.0" speed="10,30" movedir="0" movedirvar="180" acceleration="0,0" dampening="0.1" /> <particle image="IMAGE_FX_FIREMAIN1,IMAGE_FX_FIREMAIN2" rotspeed="-1,-0.1" rotation="-180,180" directed="false" scale="1.0,2.0" finalscale="0" fade="false" additive="true" lifespan="1.4,2" dampening="0.98" speed="2,7" movedir="90" movedirvar="30" acceleration="0,-0.05" /> </particleeffect>
<particleeffect name="BallExplode_ISH" maxparticles="80"> <particle image="IMAGE_FX_EXPLODESTREAK1ISH" directed="true" scale="0.01,0.1" finalscale="3" fade="true" additive="true" lifespan="0.15,0.75" speed="10,32" movedir="0" movedirvar="180" acceleration="0,0" dampening="0.05" /> <particle image="IMAGE_FX_EXPLODESTREAK1ISH" directed="true" scale="0.01,0.1" finalscale="4" fade="true" additive="false" lifespan="0.15,0.55" speed="10,22" movedir="0" movedirvar="180" acceleration="0,0" /> <particle image="IMAGE_FX_FIREMAIN1ISH" rotspeed="-1,-0.1" rotation="-180,180" directed="false" scale="0.25,0.8" finalscale="0" fade="false" additive="true" lifespan="1.4,2" dampening="0.98" speed="2,7" movedir="90" movedirvar="30" acceleration="0,-0.1" /> </particleeffect>
<particleeffect name="ISH_undeleteFizz" maxparticles="6" rate="0.04"> <particle image="IMAGE_FX_FIREMAIN1ISH,IMAGE_FX_FIREMAIN2" rotspeed="1,2" directed="false" scale="1.0,2.0" finalscale="0" fade="true" additive="true" lifespan="1,1.5" speed="0.6,1.3" movedir="0" movedirvar="180" acceleration="0,0.05"/> </particleeffect>
<particleeffect name="ish_bubbles" maxparticles="30" rate="0.25"> <particle image="IMAGE_FX_FIREMAIN1ISH" rotspeed="0.2" directed="false" scale="0.5,1.0" finalscale="0" fade="false" additive="false" lifespan="2,3" speed="0.6,0.8" movedir="0" movedirvar="180" acceleration="0,0.01"/> </particleeffect>
The user's profile file is named "pers2.dat" and the location depends on the platform and version of World of Goo.
Prior to version 1.10 it was stored in %ProgramData%, e.g. C:\ProgramData\2DBoy\WorldOfGoo
From version 1.10 it is stored in the user's profile under %LOCALAPPDATA%, e.g. C:\Users\david\AppData\Local\2DBoy\WorldOfGoo
Prior to version 1.10 it was stored in the All Users profile under %ALLUSERSPROFILE%, e.g. C:\Documents and Settings\All Users\Application Data\2DBoy\WorldOfGoo
From version 1.10 it is stored in the user's profile under %LOCALAPPDATA%, e.g. C:\Documents and Settings\david\Local Settings\Application Data\2DBoy\WorldOfGoo
The profile is stored in the user's home directory under Library/Application Support/WorldOfGoo/
In the Linux native port, the profile is stored at ${HOME}/.WorldOfGoo/pers2.dat
Users playing under PlayOnLinux or wine may have their profile located in one of the following locations depending on version:
On the iPad, the profile is serialised into a string which is stored in a single preferences entry.
With wog.app installed in /Applications/, the property list file is found at /var/mobile/Library/Preferences/com.2dboy.worldofgoo.plist.
With wog.app installed normally through the App Store, it will presumably be located in the application's sandbox in /var/mobile/Applications/GUID/Library
The actual plist contains a single entry, "pers2.dat", with binary data.
davids-iPhone-2:/var/mobile/Library/Preferences root# plutil com.2dboy.worldofgoo.plist { "pers2.dat" = <342c6d72 7070312c 30392c70 726f6669 6c655f30 31323338 [...many bytes snipped...] 5f2c5f2c 302e>; }
As with other iPad files, the pers2.dat contents are unencrypted.
The profile is encrypted using the platform's standard .bin file encryption, i.e. AES for Windows and Linux, and XOR for Mac OS X, except the plaintext is padded with zero bytes. The character encoding used is unknown; most likely it is the native character set of the operating system.
At the highest level, it contains a sequence of strings, terminated by the sequence ",0." (without quotes). Each string is encoded with a decimal integer signifying its length, followed by a comma, followed by the string contents.
For example, "foo" would be encoded as "3,foo" (quotes for clarity only).
Note that the number specifies the number of BYTES that follow, not characters, so be careful not to use Unicode functions at this stage. This will particularly show up in version 1.40+ where the Unicode representation of non-ASCII characters in profile names has been fixed. See also the notes at the bottom of this page regarding encoding.
The strings in the data file describe a dictionary, with alternating keys and values. Keys in use are:
countrycode and fullscreen are not present in the iPad version.
Obviously, the player profile strings are the most interesting. These strings consist of a number of fields, seperated by commas. The first four fields are:
The player flags field is an integer that combines several bits:
Flag | Meaning |
---|---|
1 | online play is enabled |
2 | World of Goo Corporation has been unlocked |
4 | World of Goo Corporation has been destroyed |
8 | the whistle has been found |
16 | the Terms & Conditions have been accepted (this unlocks Deliverance) |
So a player that has finished the first two chapters, for example, would have flags set to 2|8 = 10 or 1|2|8 = 11 (here '|' indicates bitwise-or).
Then follows, for each level:
Note that these three stats are "best ever" and are independent for each stat. i.e. they were NOT necessarily achieved during the same level attempt.
The number of levels in this list is not necessarily equal to the fourth field in the header (levels played).
A level id with an integer value indicates the end of level data. This is usually 0, meaning no fields follow. However if it is a positive integer, it is the number of levels that the user has skipped, and will be followed by a list of the level IDs.
Then, the World of Goo Corporation tower data follows, which is a string prefixed by an underscore character (_) which is how it can be distinguished from more level data.
It may be empty if WoGC has not been unlocked yet. Otherwise, it contains first the location of balls, and then the configuration of strands (connections between balls). All data is seperated by colon characters (:).
Each ball description consists of six fields; for example:
b:Drained:-61.88:211.98:-0.96:1.75. The fields are:
Each strand description consists of seven fields; for example: s:Drained:288,20,9.000,140.0,0. The fields are:
Next, the online player key follows, which is prefixed by an underscore; the player key is 32 characters long and consists of lower-case hexadecimal digits. If the player has never connected to the internet, the key is empty.
Since this key is used to authenticate players online, it should be kept private. For other purposes e.g. Soultaker's site, he proposes to take an MD5 hash code of the string and use that instead, so players can be identified without compromising their scoreboard standing. For example: "d41d8cd98f00b204e9800998ecf8427e" would be transformed to "74be16979710d4c4e7c6647856088456". If you do not want players to be globally identifiable, use an HMAC instead.
The final field is a decimal integer, representing the number of newly collected goo balls since the player last visited World of Goo Corporation. This number is displayed on the chapter and title screen overlay.
Credit to Soultaker for first documenting this file format.
In the iPad version, two further fields follow. If the user has never connected to the Game Centre, they are both "_". Otherwise, the first is an underscore followed by their Game Centre Player Key (corresponding to GKAuthenticatedPlayerKey in com.apple.gamed.plist), for example "_G:100002345". The second field is an underscore followed by their Game Centre name, for example "_davidc".
The name stored in the profile is technically UTF-8. In the case of version 1.40 and above, this is correct. However there is a bug in previous versions of World of Goo. It appears that the top bit of the continuation bytes is cleared when the profile is saved.
For example, the character ä, Unicode code point U+00E4, should be encoded in UTF-8 as C3 A4, but in pers2.dat you actually find C3 24.
Given that (a) the first byte of the UTF-8 representation allows you to determine the number of continuation bytes by looking in the high-order bits and (b) the continuation bits should ALL begin with the high bit set (binary 10xx xxxx), it is possible to write code to reconstruct the correct UTF-8 representation: use the first byte to determine the number of continuation bytes, and then for this number of subsequent bytes, set the top bit. Then use standard UTF-8 routines to decode it.
This has been confirmed working for 2-byte UTF-8 characters. It is presumed that it will work with 3- and 4-byte representations; at least it can do no harm since these should all have the top bit set in the continuation bytes anyway.
Sample code from GooTool 1.0.3:
// Go through and restore high-order bits for UTF-8 sequences (#0000270) int nskip = 0; for (int i=0; i<buf.length; ++i) { if (nskip > 0) { buf[i] |= 0x80; // set top bit nskip--; } else if ((buf[i] & 0xE0) == 0xC0) { // 110yyyyy: U+0080 to U+07FF nskip = 1; } else if ((buf[i] & 0xF0) == 0xE0) { // 1110zzzz: U+0800 to U+FFFF nskip = 2; } else if ((buf[i] & 0xF8) == 0xF0) { // 11110www: U+010000 to U+10FFFF nskip = 3; } } return new String(buf, "UTF-8");
NB. This is a description of the in-game BINARY file format for animations. You are advised not to use this format, but to contribute to the discussion on an XML format for these files.
This is some sketchy information about the anim.binltl file format. These contain the BinImageAnimation and associated structs. These are used to animate single elements in a scene. They store the description of the animation keyframes used, but not the actual item to be animated. This means a single BinImageAnimation (such as rot_1fps) can be used in multiple places.
BinImageAnimations are also used for each item animated in a movie, though in that case they're stored within the movie.binltl file.
The file is unencrypted binary data, roughly corresponding to the C structs used in the game. Pointers are stored as offsets from the start of the file.
Note that when the BinImageAnimation appears within a movie file, the pointers in BinImageAnimation after relative to the start of the file, but the pointers they point to are relative to the start of the BinImageAnimation.
float - 32-bit floating point in IEEE 754 floating-point "single format" bit layout
int - 32-bit little endian integer
char * - stored in files as a sequential list of null-terminated strings
enum = stored in the file as ints
enum TransformType { XFORM_SCALE = 0, XFORM_ROTATE = 1, XFORM_TRANSLATE = 2 }; enum InterpolationType { INTERPOLATION_NONE = 0, INTERPOLATION_LINEAR = 1 };
struct keyframe { +0x00: float x; +0x04: float y; +0x08: float angle; +0x0c: int alpha; +0x10: int color; +0x14: int nextFrameIndex; +0x18: int soundStrIdx; +0x1c: InterpolationType interpolation; }; struct BinImageAnimation { +0x00: int mHasColor; +0x04: int mHasAlpha; +0x08: int mHasSound; +0x0c: int mHasTransform; +0x10: int mNumTransforms; +0x14: int mNumFrames; +0x18: TransformType *mTransformTypes; +0x1c: float *mFrameTimes; +0x20: keyframe ***mXformFrames; +0x24: keyframe **mAlphaFrames; +0x28: keyframe **mColorFrames; +0x2c: keyframe **mSoundFrames; +0x30: const char *pStringTable; };
Note that mXformFrames has an extra indirection, this is because there is one keyframe per frame per transform type (mNumTransforms).
There is a single BinImageAnimation at the start of the file. All other items are referenced by "pointers" within this structure (offsets from the start of the file).
When the BinImageAnimation appears inside a BinMovie, the offsets are relative to the start of the BinImageAnimation, so you must compare them with 0 before shifting them.
res\anim\rot_1rps.anim.binltl:
hasColor = false hasAlpha = false hasSound = false hasTransform = true numTransforms = 1 numFrames = 3 transformTypesOffset = 52 frameTimesOffset = 56 xformFramesOffset = 68 alphaFramesOffset = 84 colorFramesOffset = 96 soundFramesOffset = 108 stringTableOffset = 120 frameTimes[0] = 0.0 frameTimes[1] = 0.5 frameTimes[2] = 1.0 transformTypes[0] = ROTATE frameOffset = 72 framePointer = 124 transformFrames[t=0,ROTATE][frame=0] = KeyFrame{x=-1.0, y=-1.0, angle=0.0, alpha=-1, color=-1, nextFrameIndex=1, soundStrIndex=0, interpolationType=LINEAR} framePointer = 156 transformFrames[t=0,ROTATE][frame=1] = KeyFrame{x=-1.0, y=-1.0, angle=180.0, alpha=-1, color=-1, nextFrameIndex=2, soundStrIndex=0, interpolationType=LINEAR} framePointer = 188 transformFrames[t=0,ROTATE][frame=2] = KeyFrame{x=-1.0, y=-1.0, angle=360.0, alpha=-1, color=-1, nextFrameIndex=-1, soundStrIndex=0, interpolationType=LINEAR} framePointer = 0
NB. This is a description of the in-game BINARY file format for movies. You are advised not to use this format, but to contribute to the discussion on an XML format for these files.
This is some sketchy information about the movie.binltl file format. These contain animated movies used for the loading screen, cut-scenes and credits. They consist of a number of actors, each of which is animated according to a BinImageAnimation.
The file is unencrypted binary data, roughly corresponding to the C structs used in the game. Pointers are stored as offsets from the start of the file.
float - 32-bit floating point in IEEE 754 floating-point "single format" bit layout
int - 32-bit little endian integer
char * - stored in files as a sequential list of null-terminated strings
enum = stored in the file as ints
enum ActorType { eActorType_Image = 0, eActorType_Text = 1 }; enum AlignmentH { ALIGN_LEFT = 0, ALIGN_CENTER = 1, ALIGN_RIGHT = 2 }; enum AlignmentV { ALIGN_TOP = 0, ALIGN_MIDDLE = 1, ALIGN_BOTTOM = 2 };
struct BinActor { +0x00: ActorType mType; +0x04: int mImageStrIdx; +0x08: int mLabelTextStrIdx; +0x0c: int mFontStrIdx; +0x10: float mLabelMaxWidth; +0x14: float mLabelWrapWidth; +0x18: AlignmentH mLabelJustification; +0x1c: float mDepth; }; struct BinMovie { +0x00: float length; +0x04: int numActors; +0x08: BinActor *pActors; +0x0c: BinImageAnimation **ppAnims; +0x10: const char *pStringTable; };
Note that pActors is a pointer to the array of BinActors
ppAnims has an extra indirection which is strictly superfluous, however it allows the BinImageAnimation structure and associated animation data to be stored as a single contiguous block. Which is the format 2DBoy used for the original files.
There is a single BinMovie at the start of the file. All other items are referenced by "pointers" within this structure (offsets from the start of the file).
A BinActor is an animated scene element. All BinActor string indexes reference the BinMovie string table. There is exactly one BinImageAnimation per BinActor.
Some complete dumps (large files!) of movies, including their actors and BinImageAnimations can be found here: Chapter5End and credits
Here are just the actors from res\movie\credits\credits.movie.binltl:
length = 63.0 numActors = 34 actorsOffset = 20 animsOffset = 1108 stringsOffset = 1244 actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_BLACK32', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=0.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_GIRL3', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=1.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_GIRL2', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=2.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_GIRL1', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=3.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_GIRL0', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=4.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_DATE', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=5.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_2DBOY', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=6.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_SPECIALFRIENDS', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=7.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_D9', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=8.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_PAULHUBANS', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=9.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_D8', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=10.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_ALLANBLOMQUIST', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=11.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_D7', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=12.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_RONCARMEL', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=13.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_AND', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=14.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_D6', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=15.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_D5', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=16.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_GEAR', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=17.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_GEAR', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=18.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_GEAR', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=19.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_KYLEGABLER', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=20.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_AND', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=21.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_D4', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=22.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_D3', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=23.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_D2', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=24.0} actor = BinActor{actorType=TEXT, imageStr='', labelStr='$MOVIE_CREDITS_D1', fontStr='FONT_BIGWHITE_52', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=25.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_TREE', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=26.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_TREE', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=27.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_TREE', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=28.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_TREE', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=29.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_TREE', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=30.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_TREE', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=31.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_WOGLOGO01', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=32.0} actor = BinActor{actorType=IMAGE, imageStr='IMAGE_MOVIE_CREDITS_LENSFLARE', labelStr='', fontStr='', labelMaxWidth=-1.0, labelWrapWidth=-1.0, labelJustification=1, depth=33.0}
When searching for an image, World of Goo will usually load filename.png. However, if there is a file called filename.es.png and the game is being played in Spanish, that image will be substituted. This works for all language codes.
In the original game, this is mostly used for image-based signposts and text such as "Island 1: The Goo-Filled Hills".
There is also a fature coded into the game for Profanity Pack users, which loads filename.profanity.png instead of filename.png, and properties/profanity.xml.bin instead of properties/text.xml.bin. As 2D Boy has not yet released the profanity pack, it is unknown how this feature would be activated.