Game File Formats

Here are descriptions of the various file formats used by the game.

Encryption formats

Profile

Graphic formats

Sound Files

Animation file formats

XML file formats

Levels

Balls

Game-wide properties

Islands

AES Encryption format

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.

PHP code

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;

Python code

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.

Java code

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;
  }
}

XOR Encryption format

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.

Balls XML

General Overview

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.

Contents

Core Attributes

These attributes are always (or usually) required for any normal functioning ball.

name : Required
This is the unique identifier for the ball type. It must be unique in the game. It should also exactly match the folder name in which the files are held, and the id entry in the resources file.
shape : Required : Examples: rectangle,100,100 circle,30 circle,50,0.25
This defines the shape and size of the solid part of the Goo Ball, it's geometry.
To specify a square or rectangular ball rectangle,{width},{height}
To specify a round ball circle,{diameter}
An optional variation number may be included as the final parameter. This number defines the variation in size of the balls and thickness of the strands. It can be in the range 0 -> 1, although values above 0.5 are not recommended.
If 0 is set (or is omitted) all balls will be exactly the size specified, If 1 is specified the balls will range randomly from (almost) 0 -> 2 x size The "original" balls used numbers in the range 0.1 -> 0.3
The variation can be included on both circle and rectangle balls.
mass : Required: Float : Examples 10,20,100,600
Defines the normal mass of the ball. See also towermass and dragmass
NB: Must be greater than 1 to avoid game engine crashes
strands : Required : Integer : Examples 0,1,2,3,4
The maximum number of strands this ball will attempt to connect when being attached to a structure.
This does not affect the number of strands which can subsequently be attached to it.
If 0, this goo will not attach to a structure.
If 1, this goo will attach to a structure with only 1 strand.
If 2 (or more), this goo will only attach if 2 (or more) strands can be formed.
material : {Material Name} : Default (unknown)
The material the ball is made of.
towermass : Required : Float : Examples 3,5,10,200
When a ball is "climbing" on a structure, it can have a different mass to when it is walking on the ground.
Most of the original balls have a towermass approx. 1/3 -> 1/10 of their normal mass
Note: This value can also be negative... with "interesting" results.
NB: Must be greater than 1 (or less than -1) to avoid game engine crashes
dragmass : Optional : Float
Defines the masss of the ball while the being dragged by the player.
Only used on Blocks and Windows balls, and only affects how the ball reacts while being dragged.
NB: Must be greater than 1 to avoid game engine crashes, "low" values (<5) cause a glitch when the ball is dragged near an open pipe.

Behaviour Attributes

These attributes govern the actions of the Goos when they are "left alone"

climber : true / false : Default = true
Will jump onto and climb on strands NB: Interacts with climbspeed to determine if ball actually climbs or not.
If climber set to false.. the ball will not climb or grab onto strands
If set to true (or omitted).. climbing behaviour is determined by climbspeed
antigrav : float : Default = Not antigrav
Antigrav force multiplier when this ball is attached and in a forcefield with antigrav=true
In a gravity field : Produces an upward (against gravity direction) force = antigrav * mass
eg. Balloon : mass=20 antigrav=4.5 produces a force of 90, just sufficient to lift 3xcommon balls mass=30)
In a force field : mass is irrelevant and will lift antigrav Goo Balls regardless of the type or mass.
eg. Balloon : antigrav=4.5 will lift a structure made of 4 balls of any type. Will not lift a structure of 5.
This value can also be negative, which makes the ball heavier when it is attached.
Has a special use on Ugly see Genetic Sorting Machine
isantigravunattached : true / false : Default = true
Antigravity force also applies when unattached
static : true / false : Default = false
The ball is fixed in position and cannot be moved. Used mostly for Anchors and Dispensers.
staticwhensleeping : true / false : Default = false
Fixed in position and immune to "kicking" when asleep
wakedist : float : Default = -∞ (no Wake)
Wakes sleeping Goos within the given distance
jump : float,float : Default = 0 (no jump)
Will jump in the air when they "walk" into another Goo Ball
The speed of the jump is random in the range given {min},{max}
jumponwakeup true / false : Default = false
Will jump in the air when woken, if jump values are specified.
autoattach true / false : Default = false
Attaches automatically. If strands>1 then requires 2 strands to attach
Will also form new strands (up to strands) with other attached balls after is it attached.
autodisable : true / false : Default = false
If set true, causes the ball to "freeze solid" once it stops moving, in effect locking it in place. The ball is automatically "unfrozen" if you pick it up and move it, or detach it from the tower.
Used only on Blocks and Windows in the original Balls
Causes the "floating block" effect when another block is removed quickly from a pile.
If visualdebug is enabled in a level, disabled / frozen ball show in red.
decay : float : Default = 0 (No Decay)
If specified, sets the "life span" of the Ball in seconds.
Once this time runs out the Ball will automatically die.
Timed from the ball's "creation", beginning of level, spawn / dispense, popping of its MOM

Movement Attributes

These attributes govern the Goo Balls motion

walkspeed : Float : Required
Nominal speed the Goo Ball will walk along the ground. Actual walking speed is affected by speedvariance
Set to 0 will prevent the ball from walking. Original Balls ranged from 0.05 -> 0.15
climbspeed : Float : Required
Nominal speed the Goo Ball will climb along strands. Actual climbing speed is affected by speedvariance
Set to 0 will prevent the ball from climbing. Original Balls ranged from 0.9 -> 2.8
speedvariance : Float : Required
The variation in speed of walking and climbing from the "nominal" value. Can be in the range 0 -> 1
Set to 0, all balls will walk / climb at exactly the same speed
Set to 1, some will not move, some will "race" at 2 x nominal speed
Original Balls used either 0 or 0.2
dampening : Float : Default=0
Dampening factor for falling / flying movement.
Used on balloon-type balls to reduce fast movement and make them "feel" like the have air resistance.
Values above about 0.2 cause a glitch when throwing the ball quickly.
Original Balls used values around 0.1
walkforce : Float : Default={unknown} but seems to work for most balls
Some balls require additional force to be able to "walk"
Set to 0 to prevent walking,
Bone are set to 3000, common and common_black (and others) are set to 500
common_albino is not set... but is still able to walk OK.
thrust : Float : Default = 0 (No Thrust)
If set, then when attached, the ball will extert a force on the structure.
The thrust value is the magnitude of the force. Its initial direction is set when it is attached to the structure. Specifically, If the ball attaches 2 strands to the structure, then the thrust will be along the bisector of those 2 strands. If the ball attaches any other number of strands, the initial thrust direction will be random.
The direction is also affected by whether the thrust value is positive or negative. Postive values will usually push on the structure, negative values will tend to pull Image showing Thrust Direction for >0 and <0
Normally, the direction of the thrust is then fixed. Even if the structure moves or rotates, the thrust will remain in its initial direction. However, if the ball is also detachable, and has quite a large maxlen for its detachstrand then the direction of the thrust can be changed. Image showing fixed direction and detaching
A thrust value of 7.5 will (just) make a mass=30 ball "weightless" in a gravity field of 10.

Player Interaction

These attributes control how the Goos respond to the Player.

draggable : true / false : Default = true
These balls can be picked up by player
detachable : true / false : Default = True
Can be detached from a structure
hingedrag : true / false : Default = false
If False (or omitted) the Goo Ball is grabbed by its centre point
If True the ball is grabbed by whereever it is clicked, and will rotate about that point.
Set True on Blocks and Window Balls
fling : {Max Arrow Length},{Force Multiplier} : Default = (no fling)
fling is what happens to Bit and Pilot balls when you grab them.
The first number sets the maximum length of the shooting arrow.
The second number is a multiplier to calculate the force when the player releases the mouse button.
Original Balls used 200,2.5, setting 2.5,200 gives the same speed, but the arrow is almost invisible.
Because the multiplier calculates a force, lighter balls go faster than heavier balls with the same settings.
The image for the shooting arrow is set by the detachstand tag
NB: Balls must draggable to use fling

Level Interaction

These attributes control how the Goos interact with various elements of the level.. pipes, spikes etc.

invulnerable : true / false : Default = false
These Goos are NOT killed by objects tagged 'mostlydeadly' but they are killed by objects tagged 'deadly'
suckable : true / false : Default = true
If set to false, these Goos cannot be collected by the pipe
autobounds : true / false : Default = true
If set to false, these Goos have NO effect on the autobounds / explored area.
autoboundsunattached : true / false : Default = false
These Goos affect the autobounds / explored area even when unattached
sticky : true / false : Default = false
These Goos are always Sticky, and will stick to all geometry objects except those tagged detaching
Used on AnchorSticky and AnchorStickyInvisible
stickyattached : true / false : Default = false
These Goos are Sticky when they are attached to a structure. Used on BombSticky and Pokey
stickyunattached : true / false : Default = false
These Goos are Sticky when they are not attached to a structure. Only used on BombSticky

Other Ball Interaction

These attributes govern how the Goos behave when they encounter other Goos.

grumpy : true / false : Default = false
When set to true this Goo will not accept strands.
collidewithattached : true / false : Default = false
When set to true this ball will collide with attached balls. Used on Beauty, Ugly, Blocks and Windows
collideattached : true / false : Default = false
When set to true, this ball will collide with other attached balls when it is attached
Used on Balloons and Pilot
stuckattachment : true / false : Default = false
Set to true, allows strands to attach to this ball when it is unattached .. if...
this ball is also sticky, or sticky at the moment (see stickyattached and stickyunattached)
and is currently "stuck" to something.
Set True on AnchorSticky and AnchorStickyInvisible
Set False on StickyBomb and Pokey so you can't attach to them when they are not attached.
fallingattachment : true / false : Default = false
When set true, allows strands to connect to this ball when it is unattached and falling.
Never used (set to true) on any original ball.
For more information, see this reference page.
stacking : true / false : Default = false
When set to true, these balls can be stacked. Used on Blocks and Windows
Note: Stacking behaviour breaks down in range of an open pipe, and the blocks slide through each other.
This attribute also has another function. When set to true, the ball will explode / shatter when it dies, as if it were a geometry instead of a Goo Ball.
maxattachspeed : float : Default = unknown (but less than 1000)
The maximum speed the ball can be moving be attached to a structure.
Set to 1000 for Balloons, Bone and Pokey (All other balls default)

Cosmetic Attributes

These attributes control the Goos look, but do not significantly affect game play.

blinkcolor : RGB Colour : Default = No Blink
If set to a valid RGB colour, the Goo balls will randomly blink their eyes in this colour
If Invalid or omitted the Goo Balls will no blink.
hideeyes : true / false : Default = true
Shrink and hide part marked as eye when cursor is "far away"
Set to false the eyes remain open and "alert" at all times.
alwayslookatmouse : true / false : Default = false
Set to true the pupils follow the cursor
Set to false the pupils follow the direction of movement or stay still.
statescales : {statename},{scalefactor} : Default = {All States},{1}
Allows the Goos to change size when they are in a particular state.
Multiple states and scales allowed attached,1.75, detaching,1.3, tank,1.0
Each state must be a valid recognised value [ state list ]
attenuationselect / deselect / drag / drop : {Time},{ScaleFactor},{ScaleFactor},{ScaleFactor}....
These attributes define how the ball changes size in response to mouse actions.
The first number is the duration (in seconds) of the animation
The rest are a list of scalefactors, the ball will smoothly change between all these factors in the given Time.
attenuationselect : "Mouse Over/Hover" : Default ~ 0.2,1,1.25
attenuationdeselect : "Mouse Out" : Default ~ 0.2,1.25,0.8,1
attenuationdrag : "Mouse Down" : Default ~ 0.2,1.75,1
attenuationdrop : "Mouse Up" : Default ~ 0.2,1,1
To prevent the ball from changing size in this way set all 4 attributes to 0,1,1 (Blocks and Windows)
isbehindstrands : true / false : Default = false
If set to true, the Goo Ball is displayed behind any strands. Used on Beauty, Ugly and UndeletePill
distantsounds : true / false : Default = true
If true or omitted, sounds reduce in volume when ball is far away
If false the sound is the same volume wherever you are in the level
Only used on UtilProductLauncherScreamer to produce the "Gun Rising" sound effect.

Burn Attributes

These attributes control whether the Goos are flammable, and how the behave when lit.

burntime : float : Default = 0 (No Burn)
Number of seconds between ignition and explosion / death. Set to 0 to make the ball non-flammable
detonateforce : float : Default = 0
breaking Force at Goo location when it explodes. Set to 0 the Goo will not "explode" and will just die.
detonateradius : float : Required if detonateforce > 0
The breaking force reduces linearly from detonateforce at the center to 0 at this distance.

* This is related to the 'break=?' tag of geometry... but Shhh!


If geometry in the level has the break=X tag, it will explode when that much force hits the center of the object.
explosionparticles : {particle effect} : Default={None}
Additional particle effect on explosion, the ball also dies so "death splat" will also display.
Must be a Point-Source Non-Repeating particle effect.
flammable : true /false : Unused/Irrelavant.
Completely overridden by burntime in determing flammability. Only set FALSE on water

Popping / MOM Attributes

These attributes control whether the Goos are "pop-able", and what they contain

contains : {Number},{BallType} : Default = None (Not pop-able)
this ball contains other balls, which will pop into when it touches an object with 'ballbuster' tag.
A ball can contain a number of different balls 16,BeautyProduct,2,BeautyProductEye
The game will crash if this attribute creates a circular reference of ball type
ie. A ball cannot contain itself and a parent ball cannot contain a child ball which contains the parent ball.
popduration : float : Default = 1 (or 2)
Time takens for the "mother" ball to shrink away to nothing. Gradually over this time it releases its child balls.
Original Balls set to around 0.25, but can be longer.
popparticles : {particle effect} : Required if contains is set
Particles emitted when the ball is "busted" (also replaces death SPLAT with this effect)
Must be a Point-Source Non-Repeating particle effect
popsound : {Sound Resource} : Required if contains is set
Sound to play on pop
popdelay : {min delay},{max delay} : Default=0,0
Gives a random delay between min and max (in seconds) between the ball being "busted" and it popping.
Note: popparticles display immediately on "busting", popsound plays after the delay.

Dispenser Attributes

spawn : {BallType} : Default= None (No dispense)
Creates / Dispenses a ball of the given type when it is clicked

FallingAttachment

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

SPOILERS!!!

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!

Parts

<part>

These tags define the visible elements of the balls. A ball can contain any number of part tags.
Balls with no parts area invisible.

name : string : Required
This is the unique name for the part. It is usually "meaningful" like body or eye.
It is used as a reference for the sinanim tags.
image : list-of-{Image Resource} : Required
Image to use for this part.
If multiple images given in a comma-separated list then one is selected at random for each ball.
x & y : float or min,max : Required
x and y define the position of this part within the ball. Usually the "body" part is set to x=0 and y=0
They can each be a single number, in which case the part is place in a fixed position.
However, each of these attributes can specify a range instead. If 2 numbers are specified then they are treated as a min and max value. The position of the part on each ball will be set randomly within this range.
layer : Integer : Default = 0
Defines the drawing layer / order of this part. Higher values are drawn over the top of lower values.
Unlike depth in the level model.. this must be an integer number and cannot be negative.
state : list-of-{State Names} : Default {All States}
This is a comma-seperated list of all the ball states for which this part should be drawn.
If omitted, then this part is always drawn.
scale : Float : Default = 1
Allow the size of this part to be changed relative to the others.
rotate : true / false : Default = false
Specifies whether this part rotates about the centre of the ball as it walks / moves.
Usually set True, but often left false on body and hilite images.
stretch : {speed},{direction scale},{across scale} : Default = No Stretching
This defines how the part will stretch as the ball moves when it is falling or being dragged.
The first number is a speed. The amount of stretching varies from "no stretching" when the ball is not moving to "maximum stretching" when it is moving at this speed.
The second number is the maximum amount of stretching in the direction of motion
The third number is the maximum amount of stretching perpendicular to the direction of motion.
The original balls uses values like 24,1.5,0.75 and 16,2,0.5
These produce the effect of the balls "stream-lining" when they are moving quickly.
eye : true / false : Default = false
Specifies that this part is an eye, and tells the game that following attributes should also be used.
If this is set to false (or omitted) the following attributes are ignored.
pupil : {Image Resource} : Required (if eye is true)
The image for the pupil
pupilinset : Integer : Default = 0
The distance from the outside edge of the eye to the pupil.
If this value is too low (size of the pupil or less) then the pupil will appear outside the eye image.
If set to a large value the pupil remains fixed in the centre of the eye.
Original balls used values 12-14, except for large balls like Beauty which used values up to about 60
xrange and yrange : min,max : Default (no Motion)
These attributes specify the range of motion of the part when the ball is moving, or bouncing off things.
The values are specified relative to the ball the same as the part position / range (x,y)
If omitted the part remains in a fixed position on the ball.
These are normally used only on "eyes" to give the "googly-eye" look, however they can be used on any part.
eg Beauty's lips and Ivy's leaves.

Strands

<strand>

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.

type : 'spring' or 'rope' or 'rigid' : Required
This specifies what "real world" object the strand acts like.
Set to spring it will act like a spring and will resist being stretched or compressed.
Set to rope it will resist being stretched, but will simply collapse if compressed
Most of the original strands are set to spring, only Balloons, Pilot and Water are set to rope
NB: rigid type is valid, however so far it does not appear to function correctly, the strands are not rigid at all. If you want to experiment with 'rigid' strands you must also set the mass attribute.
image : {Image Resource} : Required
The image of the strand
inactiveimage : {Image Resource} : Required
The image used to show where the new strands will be added
minlen : Float : Default = 0
This is the minimum "natural" length of a strand.
If the new strand is shorter than this length when it is created the strand will expand to reach this minimum length.
The original balls use a value approx. 1/2 of maxlen1
NB: Must be LESS than both maxlen attributes... otherwise errors occur in the game.
maxlen2 : Float : Default = 0 (No strands can be created)
The maximum length of a new strand between the ball being "dragged" and another attached ball.
Strands can be created "slightly" longer but they will shrink down over a few seconds. (see also shrinklen)
maxlen1 : Float : Default = 0 (Ball-Ball strands cannot be created)
The maximum length of a new strand which connects two balls that are both already attached.
ie. where a new strand is made between two balls, but the ball you were holding "disappears".
The Original balls had values of approx. 0.7 * maxlen2. Values substantially different to this will cause "unfamiliar" building behaviour, but that's not necessarily a bad thing.
shrinklen : Float : Default = 140
Once the strand is connected will attempt to shrink to this length.
If this value is set to less than minlen the strands will shrink, but will not go shorter than minlen
Used "noticably" on Pokey balls
thickness : Float : Default = {1/2 Ball Size}
This can be used to limit or reduce the thickness of the displayed strand image.
Setting a large value will not increase the thickness of the strand beyond about 1/2 the size of the ball, as set by the shape attribute.
The actual thickness of the strand also depends on the size and type of the ball it is connecting to.
springconstmax and springconstmin : Float : Range 0 to about 20
Defines the spring constant of the strand. The original balls used values around (6-10).
Low values (1 to 5) produce weak and wobbly strands.
High Values (11-20) produce very strong solid strands.
Values above 20 cause the "Shaking Bug" to occur, even with only a few balls attached.
The operation of these values is quite complex and can produce "unexpected" behaviour. The original balls used the same value for both min and max, it is highly recommended that you do the same.
* See Note below for a full description how these attributes actually function.
walkable : true / false : Default = True
Determines if balls can climb along the strands and will "land" on them when dropped or thrown.
dampfac : Float : Default = 0
Dampening Factor that applies to the strand's length.
Set to low values (<0.1) strands will continue to wobble for a long time after they are attached or hit by a flying ball.
Set to high values (>0.7) this wooble is reduce very quickly.
Most original balls used values around 0.9 Balloons used 0.002
maxforce : Float : Default = 0 (Strands almost always break immediately)
This sets the maximum force a strand can provide before it breaks.
If the force on the strand rises above this value then the strand will begin to stretch very easily and quickly.
If the force remains above this value for 1 second, the strand will break.
Most original balls were set to 600 or 800, Balloons are 200, Ivy's 300
burnspeed : Float : Default = 0 (No Burn)
Sets the speed at which the strand burns along its length.
If set to a low value (<1) then the strand burns slowly. When the ball that started them burning explodes the strands disappear. If that happens before the fire reaches the other end of the strand the next ball will not light.
ignitedelay : Float : Default = 0
The delay (in seconds) between the ball catching fire, and the strand beginning to burn.
NB: Must be less than burntime of the ball.
burntimage : {Image Resource} : Default {Strand Image}
As the strand burns its image is replace by this image.
If omitted the normal strand image is used, and the strand does not change appearance as it burns.
fireparticles : {Point-Source Particle Effect} : Required
Specifies the particle effect to use as the strand burns.
NB: If omitted the strand will NOT burn
rope : true / false : Unused / Unknown
Set true on water (but not Pilot or Balloons)
Appears to have no effect. Possibly a precursor to type
geom : true / false : Unused / Unknown
Set False on Drained and DrainedISH

<detachstrand>

This tag specifies attributes about detaching and flinging. It is required for balls to be detachable.

image : {Image Resource} : Default {None}
The strand image to display as the user tries to detach the ball
For balls with fling set (Bit, Pilot), this specifies the image to use for the direction arrow.
maxlen : Float : Default = 0
The distance the player must pull the ball, before it actually detaches.
Low values (or 0 or ommited) makes the balls detach immediately, annoying when accidentally clicked.
High Values will cause the detach-strand to pull (a lot) on the structure before the ball detaches.
Original balls used 60. Has no effect on fling balls.

* How the springconst attributes actually work

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.

Other effects

<marker>

Defines the graphics use for the mouse cursor when selecting and dragging Goo Balls.

drag : {Image Resource} : Required
Image displayed when selecting or dragging a Goo Ball
Required even if the ball is not draggable
detach : {Image Resource} : Required
Image displayed when attempting to detach a Goo Ball from a structure.
Required even if the ball is not detachable (or even attachable)
rotspeed : Float : Default = 0 (No rotation)
Rotation speed of the marker (radians per second)
Original balls used 2 or -2, except for Window Balls which used 0

<particles>

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.

effect : {Point-Source Particle Effect} : Required
The particle effect to display.
state : list-of-{State Names} : Required (You must specify at least one state)
The name of a state, or a comma-separated list of several states, for which this effect will be displayed.
overball : true / false : default = false
If set to true, the particle effect will be drawn over the ball.
Is set to false, the effect will be drawn behind the ball.

<shadow>

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.

image : {Image resource} : Required
The image to display.
additive : true / false : Default = false
Set to true, will make white shadow images act as a "glow" rather than a shadow.
Only used on albino balls and timebugs

<sound>

Sounds that are played when something happens to the ball.

event : {Event Name} : Required
The name of the event for which the sound should be played
sounds : list-of-{Sound Resource} : Required
A sound, or a comma-separated list of several, to be played when the event occurs.
If more than one sound is given, one will be randomly selected.

<splat>

image : list-of-{Image Resource} : Required
An image, or a comma-separated list of several, which are fly out from the ball when it is picked up or dropped by the player, or when it dies.
If the ball is a 'MOM' then the popparticles effect is show instead when it dies.

States and Events

States

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.

Events

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.

Animations

Advanced - Not for the faint hearted

Bringing balls to life!

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!

<sinvariance>

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.

amp : Float : Required : "Sane" Range 0 -> 1
The amplitude variance
Original Balls used values from 0 -> 0.1
freq : Float : Required
The frequency variance in Hz (oscillations per second)
Original Balls used values from 0 -> 1.5
shift : Float : Required : Range 0 -> 1
The phase shift variance, 0.5 = 180° 1 = 360°
Original Balls mostly used 0, but some have 0.5 or 0.8

<sinanim>

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.

part : list-of-{Part Name} : Required
The name or names of the ball <part>s to which this animation is applied
state : list-of-{State Name} : Required
The state, or a comma-separated list of several, for which this animation is applied
For your sanity, we recommend that you select the same state / states for every sinanim within a sinvariance
type : scale or translate : Required
The type of animation
axis : x or y : Required
The axis, relative to the ball's default upright position, along which the animation is applied
amp : Float : Required
The default amplitude of the oscillation, the actual amplitude will be modified by the variance.
For scale type animations this is the scale factor amplitude
For translate type animations this is a pixel distance and can be positive or negative.
freq : Float : Required
The default frequency of the oscillation in Hz, the actual frequency will be modified by the variance
shift : Float : Required : Range 0 -> 1
The default phase shift for this oscillation, the actual phase shift will be modified by the variance.
The shift is in the range 0 = 0° 1 = 360°

Experimention is the key to understanding

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! Wink

Font

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.

Define CharList
This simply lists all the characters in the font.
Define WidthList
This defines the width, in pixels, of each character in the font. There may be cases where you would want to edit this... if, for instance, one letter had a really odd flourish that you didn't want taken into account for spacing, or if you wanted the characters to overlap in some strange manner.
Define RectList
This defines the size and position of each character in the image. Don't think you'd wanna mess with this.
Define OffsetList
This defines a set of integer pairs specifying how many pixels in X,Y you want to offset each character. This can be used to move individual characters around without modifying the image.
Define KerningPairs
This defines the different pairs of characters that have special kerning numbers set for them.
Define KerningValues
This defines how special sets of characters are kerned when they turn up. This is imported directly from the Truetype font and in most cases should be left alone.
LayerSetCharWidths Main (' ') (X)
This value in the spew of data below is used to set the width of a space character. If you feel the font needs bigger or smaller spaces, edit this.
LayerSetImage Main 'X'
The X here is the file name used for the font. You may need to edit this manually if you are creating a special font.

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.

Island.xml

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.

<island>

name : String : Optional (Unused)
Text describing or naming this level. This text is not used anywhere within the game, so is used for identification purposes only.
map : {Level Name} : Required
The level id (folder) of the map for this chapter.
NB: There are some strict criteria regarding the contents of that level, described later.
icon : Global Image Resource : Required
The image to display on the level start screens for this chapter.

<level>

The <island> tag contains a level tag for each level in the chapter.

id : {Level Folder} : Required
The name / folder name of the level.
name : Global Text Resource : Required
The text for the Level Name
text : Global Text Resource : Required
The text for the Level Subtitle
ocd : balls/moves/time,number : Optional
Sets the OCD criteria for this level
eg. balls,11 moves,4 time,13
If omitted then there is no OCD, the games displays "This one is mine, The Sign Painter"
depends : list-of-{Level Id} : Optional
The id (or ids) of levels which must be completed before this level can be played.
Can be levels in this chapter, or in a "previous" chapter.
If omitted the level can always be played. (see Note below)
cutscene : list-of-3-movies : Optional
The names of the movies to play at the end of the level.
This list must always contain 3 valid items, x may be used when less than 3 "real" movies are requried.
examples : levelFadeOut,Chapter1End,gooTransition_out x,whistleUnlock,gooTransition_out
The 3 movies will play in order, however each one is overlayed onto a different background. You should take account of this when you create / design you movie.
The 1st Movie is overlay onto the level which has just been completed.
The music and sounds from that level will fade out in about 1 second at the start of the movie.
The 2nd movie is played over a solid black background.
The 3rd movie is played over the island you are returning to, or the main menu if you just completed the island.
he music / sounds from the island begin to play at the start of this movie, and will play at the same time as any sounds or music in the movie.
oncomplete : Recognised Command : Optional
When this level is completed the command given will be executed.
Mostly these commands affect flags in the profile info, which unlock a game feature or mode.
[Commands and Functions]
skipeolsequence : True / False : Optional
If this level uses the normal "level exit and pipe" end condition, this makes the game skip the "tank and scores" sequence, and jumps straight to any cutscene movies, when the "Continue" handle is pulled.
Used only in Genetic Sorting Machine

Note on playable levels.

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.

Language Specific Audio Files

This page is currently under construction and the information here may not be complete and/or accurate.

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:

  • The file must simply be an OGG file with a modified extension.
  • The file must have a name identical to the OGG file it overrides (excluding the extension).
  • The file must be in the same folder as the OGG file is overrides.

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.

See Also:

Language Specific Image Files

Level level.xml

<level>

Attributes

allowskip (boolean) (optional, default: true)
Whether the user may skip this level.
autobounds (boolean) (optional, default: false)
If true, the camera bounds are limited to the Explored Area. If false, the <scene> bounds are fixed.
ballsrequired (integer) (optional, default: 1)
Number of balls required in the pipe to exit the level. Not used for levels where there is no levelexit
cursor1color (also 2, 3, 4) (RGB) (optional, default null)
1 for the default cursor colour. 2, 3, 4 are used in manymouse mode in Linux & Wii versions. Used by the levels in Chapter 4 where they are set to 0,255,0; 0,255,255; 0,0,255; 255,0,0.
letterboxed (boolean) (optional, default: false)
Whether to letterbox the display (16:9). false on levels, true on islands.
retrytime (float) (optional, default: 0 )
Retry button will be lit after there are fewer available balls (i.e. suckable, in the pipe/@filter list, not attached or detachable) than ballsrequired or the given time span (i.e. retrytime seconds) has elapsed if retrytime isn't set to zero.
strandgeom (boolean) (optional, default: false)
Whether ball strands interact with geometry. If true, prevents building strands through geometry.
textcolor (RGB) (optional, default: 255,255,255)
Colour of various text effects.
"metres to go", "Made It!", ball count shown at the pipe, !'s from signposts and waking Goo Balls
texteffects (boolean) (optional, default: true)
If set to false, prevents the display of the "metres to go" and "Made it!" text effects.
timebugprobability (float) (optional, default: ~0.5)
The probability per move that a new time bug will appear. It seems that moving the same balls repeatedly will not trigger a new time bug; there must be some clever logic to prevent people stocking up on time bugs..
visualdebug (boolean) (optional, default: false)
When enabled, shows a translucent overlay detailing geometry, balls (including their alignment), and the level exit.
zoomoutlimit (float) (optional, default 0 )
Minimum zoom in this level. Any camera zoom smaller than this will be set to this value.

Child Elements

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


<camera>

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.

Attributes

aspect (string) (required)
The aspect ratio under which this camera will be used. Valid values: normal or widescreen.
endpos (2D) (optional, default null)
The camera position upon level completion, while the score shows.
endzoom (float) (optional, default null)
The camera zoom level upon level completion, while the score shows.

Child Elements

1-n poi


<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.

Attributes

pause (float) (required)
How long to display this POI before travelling to the next (or, for the final POI, before allowing play).
pos (2D) (required)
The camera centre position (in world coordinates) for this POI.
traveltime (float) (required)
How long the camera takes to move to the next POI. Irrelevant for the final POI.
zoom (float) (required)
The camera zoom level for this POI.


<music>

Defines the background music for this level.

Attributes

id (resource) (required)
The resource ID of the music to play in the background.


<loopsound>

An additional background soundtrack for the level, played on top of the music. Typically used to provide atmospheric effects such as fire.

Attributes

id (resource) (required)
The resource ID of the sound to play in the background.


<signpost>

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.

Attributes

alpha (float) (required)
The opacity of the signpost image, where 1 is completely opaque. Note that all game files currently use a value of 1.
colorize (RGB) (required)
Specifies the colour factor of each RGB component. Most levels use 255,255,255 to use the full colour. "Darker" levels like UpperShaft use 128,128,128 to remove some of the vibrant colour. "Silhouette" levels like ObservatoryObservationStation use 0,0,0 to remove all colour and display only in greyscale.
depth (float) (required)
The visual depth of the signpost image. Affects layering and perspective.
image (resource) (required)
The resource ID of the signpost image.
name (string) (required)
An ID for the signpost that may be referenced elsewhere.
particles (string) (optional, default null)
A particle effect to apply at the signpost's coordinates/depth. Must be a valid particle generator. The game uses this exclusively to apply the effect signpostAlertMom (little black rising bubbles) to MOM's signposts.
scalex (float) (required)
X-scaling of the signpost image.
scaley (float) (required)
Y-scaling of the signpost image.
text (string) (required)
The contents of the signpost to display - a valid string from text.xml.
x (float) (required)
X-position (in world coordinates) of the signpost image.
y (float) (required)
Y-position (in world coordinates) of the signpost image.


<pipe>

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.

Attributes

depth (float) (required)
The visual depth of the pipe.
id (string? integer?) (required)
An unique ID for this pipe in the level. Always 0 in game files.
type (string) (optional, default )
The type of balls this pipe will suck. Note that this is only decorative; it affects the visuals of the pipe. The filter on the levelexit affects the actual sucking. Known values (caps): ISH (only Bit and Pilot balls), BEAUTY (red pipe, only BeautyProduct and BeautyProductEye balls), and BLACK (only common_black balls, used by WeatherVane). These values appear to correspond directly to the IMAGE_GLOBAL_PIPE_xx_yy resources in the global resources.xml

Child Elements

2-n Vertex


<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)

Attributes

x (float) (required)
X-position (in world coordinates) of this vertex.
y (float) (required)
Y-position (in world coordinates) of this vertex.


<BallInstance>

An instance of a ball that is present at level start.

Attributes

angle (float) (required)
Angle of the ball, North=0 (degrees).
discovered (boolean) (optional, default true)
Whether the ball is discovered (false=sleeping ball).
id (string) (required)
Unique ID for the ball in this level, to be referenced by the <Strand>. The game uses integers beginning from 0, but any string is fine as long as it's unique.
type (string) (required)
The ball type ID of this ball instance (same as the directory name under res/balls/).
x (float) (required)
X-position (in world coordinates) of this ball.
y (float) (required)
Y-position (in world coordinates) of this ball.


<Strand>

A strand between two balls that is already connected at level start.

Attributes

gb1 (string) (required)
ID of the first ball.
gb2 (string) (required)
ID of the second ball.


<levelexit>

The actual point at which a pipe sucks. The <pipe> element only defines the visual appearance of the exit.

Attributes

filter (string) (required)
A comma-separated list of the balls that will be accepted into this pipe. Empty string to allow all balls. Note that balls that have suckable="false" are automatically excluded.
id (string) (required)
ID of the exit. Generally "theExit".
pos (2D) (required)
X,Y coordinates (in world coordinates) of the exit.
radius (float? integer?) (required)
The suck radius (coordinates unknown - 75 seems unreasonable?) within which balls will be accepted. Always 75 in game levels.


<endoncollision>

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.

Attributes

delay (float) (required)
Number of seconds to delay after the collision before the level ends.
id1 (string) (required)
ID of the first geometry object.
id2 (string) (required)
ID of the second geometry object.


<endonmessage>

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.

id (string) (required)
Text string ID or programmatic trigger to end the level on.


<endonnogeom>

This element appears in the game code, but is not used by any current levels, so its functionality, attributes and status is unknown.


<fire>

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.

depth (float) (required)
Visual depth of the particle generator.
particles (float) (required)
ID of the particle generator. Must be a valid particle generator in fx.xml.
radius (float) (required)
Radius (in world coordinates) within which balls will ignite. Does not affect the particle generator/visuals in any way, so it's possible to have a fire whose ignite radius doesn't correspond to its particle generator at all.
x (float) (required)
X-position (in world coordinates) of both the particle generator, and the ignite radius.
y (float) (required)
X-position (in world coordinates) of both the particle generator, and the ignite radius.


<targetheight>

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.

y (float) (required)
Y-height (in game coordinates) that a live ball must reach to trigger the end of the level.


Live Balls

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.

Level scene.xml

RGB = r,g,b integers (0-255 each).
2D = x,y floats
resource = string, an ID present in this level's resources file

<scene>

The min/max x/y attributes are only optional on wogc* levels. The bounds can be overridden if autobounds is set on the level.

Attributes

backgroundcolor (RGB) (required)
Background colour of the level that shows through when nothing is in front of it. Always black in shipped levels.
minx (float) (optional)
Lower bound for the X-coordinate (in world coordinates) of the camera.
miny (float) (optional)
Lower bound for the Y-coordinate (in world coordinates) of the camera.
maxx (float) (optional)
Upper bound for the X-coordinate (in world coordinates) of the camera.
maxy (float) (optional)
Upper bound for the Y-coordinate (in world coordinates) of the camera.

Child Elements

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

tag attribute

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.

What do the different tags do?

ballbuster
Pops any container ball and releases its children. Container balls are those that have a contains attribute. The existing ones in the game are: Beauty, Ugly, ZBombMom and Bit ("contains" a Pilot).
break=1
Can be destroyed by bombs, Bits & PixelProducts & ZBombs, a subtype of break=X.
break=2
Can be destroyed by bombs, Bits & PixelProducts but not ZBombs, a subtype of break=X.
break=X
Specifies the "damage" needed to break this object. If a Goo Ball explodes, it deals "damage" to all geometry within the explosion radius, linearly scaled from the explosion force at the center to 0 at the edge. If the center of a breakable geometry object takes enough damage in one hit, it breaks. NOTE: This experimentation was done after the latest version of WooGLE was released, you you need to modify it by hand.
deadly
Kills all balls on contact (* see note on compositegeom below)
mostlydeadly
Kills all balls except those with the attribute invulnerable=true (Bone, Bombs, and Anchors)
(* see note on compositegeom below)
detaching
Detaches any ball from a structure, even the un-detachable.
geomkiller
Destroys non-static geometry objects on contact. Note: If 2 geomkiller objects come into contact with each other, only one will explode - which one seems consistent but "unpredictable"
kindasticky
Removed in v0.70 (**see below)
stopsign
Balls that make contact with this will reverse direction, so it's effectively a "stop sign" indicating that they should not walk off a cliff etc. This has no effect on thrown, flying, jumping, falling or climbing balls. Note: If a ball falls into a stopsign area, and lands on a walkable surface it will be thrown (violently) to the nearest edge of the stopsign shape, even if that's straight down into (inside) the floor.
unwalkable
Balls will not walk or roll on this surface, they will just sit still.
walkable
Balls will walk or roll on this surface (default if tag field is left blank)

nodrag
Balls cannot be dragged within this volume. Automatically becomes non-solid. Discovered by MyGod long after Daft as Brush left, so it needs to be added in by hand in WooGLE.


* Notes about compositegeom objects and tags

  1. Only the tag applied to the compgeom itself has any effect. Tags applied to child items are ignored.
  2. The game doesn't process the deadly tag 100% correctly on compositegeom objects. This gets a bit complicated...

    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!

** Note on kindasticky

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.

Mac .png.binltl File Format

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.

Particles - fx.xml.bin

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.

<effects>

root of fx.xml
children: All the available <ambientparticleeffect> and <particleeffect>

<ambientparticleeffect>

Ambient effects can only be used in levels in a <particles> item.
children: 1 or more <particle>

attributes:
name : Required : String
Unique name / identifier for this effect
maxparticles : Required : Integer : minimum= 1
Maximum number of particles that are active at any time.
margin : Optional : Integer : Default: ~50 Examples= 384; 400; 1000
The distance outside the screen edge where the particles are created and destroyed.
Used mainly with large images to prevent them suddenly appearing or disappearing while still on screen.
If set to 0, particles are destroyed when their centre point reaches the screen edge, and a replacement particle is immediately created and appears at random somewhere else on edge of the screen.
Can be negative, but this looks strange because the particles only appear in the middle of the screen and not near the edges.

<particleeffect>

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>

attributes:
name : Required : String
identifies this effect
maxparticles : Required : Integer : minimum= 1
maximum of particles active at any time.
rate : Optional : Float : minimum= 0.00001
Rate at which new particles are produced, seems to be per "frame".
Set to 0.02, new particles are created at exactly 1 per second
If a new particle is created when there are maxparticles on screen, then the oldest particle is removed.

<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>

attributes:
image : [list-of]{Image Resource} : Required : String
the resource id that represents the image of this particle type.
If you specify a single image then the game will use this for every particle.
If you specify multiple images as a comma-separated list, the game will select one at random for each new particle.
The resources for particle effect images are stored in /properties/resources.xml.bin
additive : Optional : true / false : Default= False
sets the particle image colors additive: the more particles overlap, the brighter it gets.
dampening : Optional : Float
Motion dampening factor from 0 to 1, can be used to make the particles slow (to a stop) once created.
directed : Optional : true / false : Default = false
Particle image is always aligned with the direction of motion. used on Rain, some Mist, smoke and goo splatter effects.
When set to True, the rotation and rotspeed attributes (see below) are ignored.
fade : Optional : true / false : Default= false
The particle fades out over its lifespan
lifespan : {min lifespan},{max lifespan} : Optional : Float
The particle's lifetime (in seconds) it will be destroyed after that time.
The fade and finalscale attributes are affected by this value.
NB: If maxparticles is reached, older particles will disappear regardless of whether they have reached their lifespan.
scale : {min scale},{max scale} : Required : Float
Sets the (initial) size of a particle, can be changed over time to "finalscale"
finalscale : Optional : Float : Default not set.
if set, the particle grows/shrinks from its original scale to finalscale over its lifespan.
speed : {min speed},{max speed} : Required : Float
range of the particle's (initial) speed, can be changed over time by acceleration and dampening
acceleration : x-axis , y-axis : Optional : Default= ( 0,0 ) no acceleration
50x per second these values are added to the speed of the particle.
Each value can be positive or negative.
Negative values on the y axis around -0.01 gives a reasonable impression of gravity
movedir : Optional : Integer : Default = 0 (Right)
Initial direction the particle moves in in degrees.
0 = Right, 90 = Up, 180 = Left, 270 = Down
movedirvar : Required : Integer
Variation of the direction in degrees. The initial movedir varies randomly by + or - this amount.
rotation : {min rotation},{max rotation} : Optional : Integer : Default= 0 : Example= "-180,180"
inital rotation of the particle (image) in degrees.
Ignored if the particle has directed=true
rotspeed : {min rotspeed},{max rotspeed} : Optional : Float
range of rotation speed, in radians per second... or something close

<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.

attributes:
amp : {min amplitude},{max amplitude} : Required :
range of amplitude in pixels
axis : "x" or "y" : Required
The axis for the motion
freq : {min frequency},{max frequency} : Required
The speed of oscillation in Radians per second
A value of 6.28 completes a whole cycle in 1 second.
phaseshift : {min shift},{max shift} : Required :
range of phase shift in radians 0 -> 6.28

Ambient Effects

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>

Up to Particle List smallleaves1:
<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>

Up to Particle List rainingleaves:
<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>

Up to Particle List rainingleavesRight:
  <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>

Up to Particle List leavesRight:
<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>

Up to Particle List snowSparse:
<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>

Up to Particle List snowDense:
<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>

Up to Particle List snowStorm:
<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>

Up to Particle List snowStormC3:
<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>

Up to Particle List blackBallsRising:
<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>

Up to Particle List blackBallsRight:
<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>

Up to Particle List blackLeaves:
<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>

Up to Particle List blackLeavesHeavy:
<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>

Up to Particle List rainStreaksHeavy:
<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>

Up to Particle List rainStreaksHeavyDistant:
<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>

Up to Particle List rainStreaksDown:
  <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>

Up to Particle List mistRight:
<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>

Up to Particle List breezeRight:
<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>

Up to Particle List breezeUpSlow:
<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>

Up to Particle List breezeDownSlow:
<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>

Up to Particle List breezeUp:
<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>

Up to Particle List mistUpSepia:
<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>

Up to Particle List ish_BigLeaves:
<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>

Up to Particle List ish_SmallLeaves:
<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>

Up to Particle List ish_RainLeavesLeft:
<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>

Up to Particle List ish_RainLeavesUp:
<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>

Up to Particle List ish_RainLeavesUpRed:
<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>

Up to Particle List ish_BreezeRight:
<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>

Up to Particle List ish_HeavyBreezeLeft:
<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>

Up to Particle List ish_HeavyBreezeUp:
<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>

Up to Particle List OOS_breezeRight:
<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>

Point Source Effects

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>

Up to Particle List unlockburst:
<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>

Up to Particle List snowStormFromPoint:
  <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>

Up to Particle List splash:
<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>

Up to Particle List gooFallWide2:
<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>

Up to Particle List gooSplatter:
<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>

Up to Particle List gooSplatterSubtle:
<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>

Up to Particle List mistUp:
<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>

Up to Particle List fireStack1:
<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>

Up to Particle List fireStackSmaller1:
 <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>

Up to Particle List fireStackSmaller2:
  <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>

Up to Particle List fireRobotHead:
   <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>

Up to Particle List fireStackSquat:
   <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>

Up to Particle List fireStackLanternBw:
   <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>

Up to Particle List fireBallBurn:
  <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>

Up to Particle List thruster:
  <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>

Up to Particle List fireArmBurn:
  <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>

Up to Particle List poisonBallBurn:
  <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>

Up to Particle List poisonArmBurn:
  <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>

Up to Particle List blackSmokeRising:
<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>

Up to Particle List bubblesRisingFromPoint:
  <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>

Up to Particle List bubblesRisingFromPointSlow:
    <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>

Up to Particle List wogcSmoke:
  <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>

Up to Particle List wogcSmokeIsh:
  <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>

Up to Particle List whiteSmokeRising:
<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>

Up to Particle List sleepyZzz:
<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>

Up to Particle List ish_sleepyZzz:
<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>

Up to Particle List ishr_sleepyZzz:
<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>

Up to Particle List signpostAlert:
  <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>

Up to Particle List signpostAlertMom:
  <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>

Up to Particle List whistle:
  <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>

Up to Particle List worldMap_FactorySmoke:
  <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>

Up to Particle List BurningManSmoke:
<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>

Up to Particle List RobotHeadSmoke:
<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>

Up to Particle List distantSmokestack:
  <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>

Up to Particle List worldMap_FactorySmokeWhite:
  <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>

Up to Particle List cigSmoke:
<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>

Up to Particle List gentleFactorySmoke:
<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>

Up to Particle List puffyFactorySmoke:
<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>

Up to Particle List gentleFactorySmokeSepia:
<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>

Up to Particle List matchSmoke:
  <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>

Up to Particle List gooTankStream:
  <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>

Up to Particle List gooDrips:
  <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>

Up to Particle List polFountain1:
  <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>

Up to Particle List polFountain2:
  <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>

Up to Particle List gooMist:
  <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>

Up to Particle List timebugFizz:
  <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>

Up to Particle List flashes:
  <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>

Up to Particle List tubeAirFlowUp:
  <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>

Up to Particle List tubeAirFlowLeft:
  <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>

Up to Particle List geomExplosionDust:
    <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>

Up to Particle List ish_smallfire:
   <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>

Up to Particle List ish_FlingTrail:
  <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>

Up to Particle List ishr_FlingTrail:
  <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>

Up to Particle List ish_gpu_bitspew:
  <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>

Up to Particle List ish_bitPop:
<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>

Up to Particle List beautypop:
  <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>

Up to Particle List flowerDust:
  <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>

Up to Particle List OOS_gooDrips:
 <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>

Up to Particle List OOS_gooGlobs:
  <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>

Up to Particle List BallExplode_Fuse:
<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>

Up to Particle List BallExplode_Bomb:
<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>

Up to Particle List BallExplode_ISH:
<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>

Up to Particle List ISH_undeleteFizz:
<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>

Up to Particle List ish_bubbles:
   <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>

Profile file format

Profile location

The user's profile file is named "pers2.dat" and the location depends on the platform and version of World of Goo.

Windows Vista and Windows 7

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

Windows XP

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

Mac OS X

The profile is stored in the user's home directory under Library/Application Support/WorldOfGoo/

Linux

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:

  • ${HOME}/.PlayOnLinux/wineprefix/WorldOfGoo/drive_c/windows/profiles/${USERNAME}/Application Data/2DBoy/WorldOfGoo
  • ${HOME}/.PlayOnLinux/wineprefix/WorldOfGoo/drive_c/windows/profiles/All Users/Application Data/2DBoy/WorldOfGoo
  • ${HOME}/.wine/drive_c/windows/profiles/${USERNAME}/Application Data/2DBoy/WorldOfGoo
  • ${HOME}/.wine/drive_c/windows/profiles/All Users/Application Data/2DBoy/WorldOfGoo

iPad

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.

Profile Encryption

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.

Profile Format

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: values are two-letter ISO 3166-1 country codes, or EU for the European Union, or XX for unknown.
  • fullscreen: indicates whether the game runs in full-screen or in windowed mode; value is either "true" or "false"
  • mrpp: most recent player profile; value is a decimal integer between 1 and 3.
  • profile_0/profile_1/profile_2: the three player profile slots.

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:

  • player name
  • player flags (see below)
  • total play time (in seconds)
  • number of levels played/completed

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:

  • level id (same as the directory name in res/levels/)
  • greatest number of balls collected
  • least number of moves used
  • least time taken (in seconds)

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:

  • "b" indicating ball (fixed)
  • "Drained" indicating ball type (fixed)
  • x-coordinate as a decimal number
  • y-coordinate as a decimal number
  • x-velocity as a decimal number
  • y-velocity as a decimal number

Each strand description consists of seven fields; for example: s:Drained:288,20,9.000,140.0,0. The fields are:

  • "s" indicating strand (fixed)
  • "Drained" indicating ball type (fixed)
  • first endpoint (zero-based index of a ball in the list above)
  • second endpoint (same as above)
  • spring constant: read from the ball.xml and always "9.0"
  • "natural" length: length of strand when it was created
  • "0" or "1": indicates whether or not a ball was absorbed to create this strand, which is released if the strand is destroyed.

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".

Name encoding

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");

anim.binltl Animation file format

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.

File format

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.

Simple types

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
};

Complex types

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).

File order

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.

Example

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

movie.binltl Movie file format

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.

File format

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.

Simple types

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
};

Complex types

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.

File order

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.

Example

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}

Language Specific Image Files

This page is currently under construction and the information here may not be complete and/or accurate.

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.

See also:

Language Specific Audio Files