Ignifuga Game Engine

Image

Ignifuga is a multi platform (Windows/Linux/OS X/iOS/Android) 2D hardware accelerated engine based on Python and Cython, inspired by similar offerings like Cocos2D, Cocos2D for iPhone, and AndEngine. All your game logic code along with the engine’s and supporting tools is converted to C during the build process, and compiled into one big standalone binary for each of the supported platforms (please refer to the FAQ for more information). The project is currently in heavy development as the engine for The Gaucho, and it should be fairly usable already, but we don’t give any guarantees, so the expected performance may range from not even working to attaining consciousness and starting the third world war for all we know. At the very least, we hope that it lives up to its name and it doesn’t catch fire.

Why Ignifuga? Who is he/her/it? What’s in a name?

Ignifuga means fireproof in spanish. That doesn’t explain much, and it has little to do with game engines, I know. But it’s a catchy name, I already had the art from a dead-and-now-revived project, and the Linux project has a penguin in their logo, so don’t come here judging me!

Who’s this guy that’s staring at me?

Don’t mind him, it’s just The Gaucho, who hangs around the site looking after his chicken.

Can the chicken talk?

Well, of course not, but she can tweet!

 

She's also very active, and much more eloquent, in our Google Group / Mailing List

Get The Code

Source

Use the bootstrap script to get started

Issue Tracker

Please report bugs and issues here.

Wiki

An ever growing collection of tips, tricks and tutorials that should tide you over until we put proper documentation in place.

Getting Started

Let's get started by making it clear that there's no "hello world" to be found here as Ignifuga doesn't believe in social norms or programming conventions or saying hello. What you'll find below is a walkthrough over the steps required to build the engine and a quick explanation of what makes the demo tick, which hopefully will be enough to get you experimenting.

The easiest way to get going on Ubuntu (Precise/12.04 or Quantal/12.10) or OS X (Lion/10.7.x or Mountain Lion/10.8.x) is to use the bootstrap script. One note before you go rushing ahead and then get dissapointed. If you are developing on OS X, you have to install XCode from the App Store, start it, accept the license, go to XCode->Preferences->Downloads and install the Command Line Tools (hey! OS X is super easy, or so they say!). Then you can proceed.

Download the bootstrap.py script with your browser, or via the command line:

# On Linux
wget https://bitbucket.org/gabomdq/ignifuga/raw/tip/tools/bootstrap.py
# On OS X 
curl https://bitbucket.org/gabomdq/ignifuga/raw/tip/tools/bootstrap.py > bootstrap.py
Then run it with:
python bootstrap.py

By default, on Ubuntu the bootstrap utility will download a bunch of development packages and require your sudo password. On OS X, Mac Ports will be used if available, and if it is not available it will be auto installed as an unprivileged user (so no need for you to provide the sudo password). The Android SDK and NDK will also be installed. If you want to disable any of this default behaviour or change some of the install paths, run

python bootstrap.py --help 
to view the available options.

Once the process completes, all dependencies should be set up (if not, please make sure to fill a bug report), and you can build the engine itself...doing so will create a statically compiled interpreter that you can use for playing around… Inside ignifuga/dist/[platform]/bin/ you'll find a Python binary that's quite like the standard Python runtime, but it also contains the whole Ignifuga machine (SDL, libRocket, one or more chickens) inside it. First, get a list of available target platforms:

schafer -A
Then run something like:
schafer -P linux64
Or
schafer -P osx

And that's it! Fame, fortune and a flock of attractive individuals of the opposite or same sex (or both) should quickly follow.

Ok, I know you are wondering what you can do in the meantime while you patiently wait for fame, fortune, etc. As a baseline reference of a few things our engine can do, check out our demo project. It's not the flashiest of demos, but it will help you get an idea of how things work. You can download it via Mercurial, or better yet (given the size of the repository's history) directly as a zip or bz2 tarball

cd ~
wget https://bitbucket.org/gabomdq/ignifuga-demo/get/tip.tar.bz2
tar jxvf tip.tar.bz2
# Note: The folder name varies with each commit!
cd gabomdq-ignifuga-demo-4cc3532ad91e
./demo.py

demo.py is very simple:

#!./ignifuga-python

from ignifuga.Gilbert import Gilbert, BACKENDS
from ignifuga.Log import Log

def run():
    Log(0)
    Gilbert().init(BACKENDS.sdl, 'intro', 'demo')

if __name__ == '__main__':
    run()

The first line states that this Python file should run using the ignifuga-python binary (i.e. the engine!) in the same directory, which is no other than a renamed copy of the Python runtime Schafer generates if you run schafer -P linux64. This binary is compiled for Linux 64 bits (Ubuntu 12.04 more specifically), but you can replace that file for any of the other variants Schafer produces (For example, for Windows you could generate the binary with schafer -P mingw32 on Ubuntu, then use it on a Windows machine to develop without having to rebuild the engine

Next up we import three objects from Ignifuga, Gilbert (the main Singleton, Controller, Overlord, whatever you want to call it), BACKENDS which actually only contains "sdl" as the only backend (but hey, extra points for me for planning ahead!), and Log which is the logging facility (though between us you can use print if you are in a hurry).

Finally, we initialize the logging facility asking for a log level of zero (the higher the level, the less messages you'll see logged), and initiate the game by asking Gilbert to initialize using the SDL backend, loading the "intro" scene from the demo.json file (which needs to go in data/scenes/demo.json)

That's it, the rest is mostly data driven from demo.json!

For the lazy among you, the demo repository contains a bunch of pre compiled static apps for Linux 32/64, OS X, Win 32, Android and iOS. In the special case of iOS you won’t be able to install the demo_ios.ipa package unless you are using a jailbroken device, but if you are a member of the Apple iOS Developer Program you can regenerate the IPA and sign it with your own provisioning certificate following the instructions in the README.

These static apps are all built using Schafer as well, and they are examples of what you would distribute if you were to ship a product with Ignifuga.The demo repository holds demos in binary form

As an example of how simple is to get a project ready for deployment, the contents of build_android look like this:

#!/bin/bash
schafer -P android -m demo.py -p org.ignifuga.demoandroid -a data -a images -a fonts -a sound --android-keystore="/home/gabo/android.keystore/keystore" --android-keyalias="mdqinc.com key 1"

This asks Schafer to build a deployment package for Android (optionally building the Android version of Ignifuga if it's not been built already), and to include in it the contents of the data, images, fonts and sound directories. It also requests that the package be signed for direct uploading into the Google Play Store.

The deployment procedure will also gather any other Python source files present in the current directory and sub directories, and treat them as sources for your game. The -m parameter merely tells Schafer which is the "main" file, the entry point. All these Python files will be Cythonized and linked into the final product, so no source is distributed

Let's now turn our attention to the demo.json file, where most of the data driven magic actually happens

{
    "intro":{
        "resolution":{
            "width":1920,
            "height":1200
        },
        "keepAspect":true,
        "autoCenter": true,
        "size":{
            "width":1920,
            "height":1200
        },
        "entities":{
            "title":{
                "components":[
                    {
                        "type":"Sprite",
                        "file":"images/title.png",
                        "z":1,
                        "alpha": 0.0,
                        "interactive":false
                    },
                    {
                        "type": "Action",
                        "duration": 0.5,
                        "alpha": 1.0,
                        "runNext": {
                            "duration": 2.0,
                            "runNext": {
                                "duration":1.0,
                                "alpha":0.0,
                                "onStop": "Gilbert.changeScene('menu')"
                            }
                        }
                    }
                ]
            }
        }
    },
    "menu":{
        "resolution":{
            "width":2048,
            "height":1536
        },
        "size":{
            "width":2048,
            "height":1536
        },
        "keepAspect": true,
        "autoScale": true,
        "autoCenter": true,
        "userCanScroll": false,
        "userCanZoom": false,
        "components":[
            {
                "type":"Rocket",
                "id": "gui",
                "file":"data/rocket/menu.rml",
                "interactive":false,
                "fonts": ["fonts/Asap-Regular.otf"]
            },
            {
                "type":"Music",
                "id": "menumusic",
                "file":"sound/menu.ogg",
                "loop": 2,
                "onStop": "print 'music stopped'",
                "onStart": "print 'music started'",
                "onLoop": "print 'music looping'",
                "active": true,
                "fadeIn": 5000,
                "fadeOut": 3000,
                "stopOnDeactivation": false
            },
            {
                "type":"Sound",
                "id": "chickensound",
                "file":"sound/chicken.ogg",
                "active": false
            },
            {
                "type":"Sound",
                "id": "cowsound",
                "file":"sound/cow.ogg",
                "active": false
            },
            {
                "type":"Action",
                "targets": ["bkg", "dp", "igni", "sun"],
                "duration":1.0,
                "loop":0,
                "relative": true,
                "alpha":1.0,
                "runNext": {
                    "duration": 2.0,
                    "runNext": {
                        "targets": ["bkg"],
                        "duration":0.3,
                        "loop":0,
                        "relative": true,
                        "alpha":0.3,
                        "runNext": {
                            "targets": ["bkg"],
                            "duration":0.3,
                            "loop":0,
                            "relative": true,
                            "alpha":-0.1
                        }
                    }
                }
            }
        ],
        "entities":{
            "spine":{
                "components":[
                    {
                        "type":"Spine",
                        "id": "spinec",
                        "atlasFile":"data/spine/spineboy.atlas",
                        "skeletonFile":"data/spine/spineboy-skeleton.json",
                        "animationFile":"data/spine/spineboy-walk.json",
                        "interactive":false,
                        "float": true,
                        "x": 200,
                        "y": 400,
                        "z": 200
                    }
                ]
             },
             "bkg":{
                "components":[
                    {
                        "type":"Sprite",
                        "id": "bkg-sprite",
                        "file":"images/menu/UI1_gradient.png",
                        "z": 0,
                        "x": 0,
                        "y": 0,
                        "interactive":false,
                        "alpha": -0.3
                    }
                ]
            },
            "bkg-menu":{
                "components":[
                    {
                        "type":"Sprite",
                        "id": "bkg-sprite",
                        "file":"images/menu/UI1_Piso.png",
                        "z": 1,
                        "x": 0,
                        "y": 1153,
                        "interactive":false
                    }
                ]
            },
            "dp":{
                "components":[
                    {
                        "id": "dpidle1",
                        "type":"Sprite",
                        "file":"images/dp/dp_idle.png",
                        "z": 2,
                        "x": 894,
                        "y": 722,
                        "width": 260,
                        "height": 485,
                        "interactive":false,
                        "red": 0.07,
                        "green": 0.07,
                        "blue": 0.07,
                        "alpha": 0,
                        "loopMax": 1,
                        "onStop": "self.paused=True;idleaction1.active=True;",
                        "remainActiveOnStop": true
                    },
                    {
                        "type":"Action",
                        "id": "idleaction1",
                        "duration":5.0,
                        "active": false,
                        "onStop": "dpidle1.loop=0;dpidle1.paused=False;"
                    }
                ]
            },
            "igni":{
                "components":[
                    {
                        "id": "idle",
                        "type":"Sprite",
                        "file":"images/ignifuga/igni_idle.png",
                        "z": 10,
                        "x": 1800,
                        "y": 1045,
                        "interactive":false,
                        "width": 128,
                        "height": 128,
                        "red": 0.07,
                        "green": 0.07,
                        "blue": 0.07,
                        "alpha": 0,
                        "loopMax": 3,
                        "onStop": "self.paused=True;idleAction.active=True;",
                        "remainActiveOnStop": true
                    },
                    {
                        "type":"Action",
                        "id": "idleAction",
                        "duration":2.0,
                        "active": false,
                        "onStop": "idle.active=False;jump.reset();jump.paused=False;jump.active=True"
                    },
                    {
                        "id": "jump",
                        "type":"Sprite",
                        "file":"images/ignifuga/igni_jump.png",
                        "z": 10,
                        "interactive":false,
                        "active": false,
                        "width": 128,
                        "height": 128,
                        "red": 0.07,
                        "green": 0.07,
                        "blue": 0.07,
                        "alpha": 1.0,
                        "loopMax": 1,
                        "onStart": "jumpAction.active=True",
                        "onStop": "self.paused=True;",
                        "remainActiveOnStop": true
                    },
                    {
                        "type":"Action",
                        "id": "jumpAction",
                        "active": false,
                        "y": -15,
                        "relative": true,
                        "duration": 0.4,
                        "runNext": {
                            "y": 15,
                            "relative": true,
                            "duration": 0.4,
                            "onStop": "jump.active=False;walk.reset();walk.paused=False;walk.active=True"
                        }
                    },
                    {
                        "id": "walk",
                        "type":"Sprite",
                        "file":"images/ignifuga/igni_walk.png",
                        "z": 10,
                        "fliph": true,
                        "x": 1800,
                        "y": 1045,
                        "interactive":false,
                        "active": false,
                        "width": 128,
                        "height": 128,
                        "red": 0.07,
                        "green": 0.07,
                        "blue": 0.07,
                        "alpha": 1.0,
                        "onStart": "walkAction.active=True"
                    },
                    
                    {
                        "id":"walkAction",
                        "type":"Action",
                        "active": false,
                        "fliph": true,
                        "runNext": {
                            "relative": true,
                            "duration":10.0,
                            "x": -500,
                            "runNext":{
                                "fliph": false,
                                "runNext":{
                                    "duration":2.0,
                                    "x": 100,
                                    "relative": true,
                                    "runNext": {
                                        "fliph": true,
                                        "onStop": "walk.active=False;idle.reset();idle.paused=False;idle.active=True;igni.x=2048 if igni.x<-128 else None;"
                                    }
                                }
                            }
                        }
                    }
                ]
            },
            "sun":{
                "components":[
                    {
                        "type":"Sprite",
                        "file":"images/menu/UI1_Sol.png",
                        "z": 1,
                        "x": 887,
                        "y": 1537,
                        "interactive":false,
                        "alpha": 0
                    },
                    {
                        "type":"Action",
                        "duration":4.0,
                        "loop":0,
                        "y": 220,
                        "easing": "outelastic"
                    }
                ]
            },
            "title":{
                "components":[
                    {
                        "type":"Sprite",
                        "file":"images/menu/UI1_Title.png",
                        "z": 1,
                        "x": 2048,
                        "y": 530,
                        "interactive":false
                    },
                    {
                        "type":"Action",
                        "duration":1.5,
                        "loop":0,
                        "x": 689,
                        "easing": "outback"
                    }
                ]
            },
            "subtitle":{
                "components":[
                    {
                        "type":"Sprite",
                        "file":"images/menu/UI1_SubTitle.png",
                        "z": 1,
                        "x": 2048,
                        "y": 700,
                        "interactive":false
                    },
                    {
                        "type":"Action",
                        "duration":2.0,
                        "loop":0,
                        "x": 697,
                        "easing": "outback"
                    }
                ]
            }
        }
    }
}

This file is a simplified version of the initial title and menu from The Gaucho Game. It defines two scenes, "intro" and "menu".

"intro" is simple enough, it contains a single entity which has attached a Sprite component (the title image), and two chained Action components that fade in and then fade out the entity. When the fading out is over, the Gilbert singleton is called upon to change the current scene and show the menu

The menu scene is a bit more complex, as it uses many of the engine's features. You'll see it has a Rocket component (which wraps libRocket, the HTML+CSS rendering engine), sound, sprites, tweening, and components attached directly to the scene which can affect more than one entity at the same time.

You'll also see bits of Python code, and a nice bit of development sugar as you can reference components or entities by their id (the methods of the walkAction Action can be referenced by walkAction.something).

Finally, let's have a quick look at menu.rml, the Python scriptable HTML look-alike that libRocket understands


    
        
        
    
    
    
        
    

]]>

It looks quite like standard HTML and CSS, with the exception that Python is used instead of Javascript. You'll also see here our own pQuery at work, a jQuery clone implemented in Python (while jQuery typically uses the $ symbol, we use the underscore _), used to select components and activate them to play sounds.

There's certainly a lot more to Ignifuga than what's shown here, such as the RFoo/Websockets terminal integration, live reloading of assets, remote screen, etc, but I hope this should be enough to at least wet your appetite and get you started experimenting. Head on to our wiki for more information on how to best use these features.

Doubts/questions/critics are most welcome via our Google Group, G+ Page or Twitter account

Features

2D Game Engine

Python/Cython based – compiles all the code to a static binary, no external dependencies.

Targets

  • Linux 32 and 64 bits
  • Windows 32 (64 bits support almost done)
  • OS X (10.6 and newer, i386 and x86_64)
  • Android (all ARM CPUs devices, OS version 2.0 and newer)
  • All iOS devices from the original iPhone to the new iPad 3 and iPhone 5 (iOS 3.0 and newer, including 6.x, with the iOS SDK v5.0 or newer, armv6, armv7 and armv7s devices)
  • More coming soon…

Component Based Design

Add your own components, use or extend the default set:

  • Actions (dynamic modification of any of the entity components and properties)
  • Sprites: static and animated, hardware accelerated, automatically compressed (see the Grossman tool for details). Fast and automatic scaling, red/green/blue/alpha modification, bluring,etc.
  • Skeletal animation: Based on Spine's runtime
  • Text (with TTF font support)
  • Sound (with ogg support)
  • Music (with ogg support)

Spine integration

Spine is a 2D skeletal animation tool, its runtime is directly integrated in the engine.

libRocket integration

Build your GUI using HTML+CSS!

Remote Websockets Console

Alternatively, you can use the Remote Console with regular sockets (i.e. via Telnet) or with websockets (using javascript from a web browser).

Remote Screen

Provides a 1:1 live MJPEG stream of the game for screencast purposes, browser embedding, etc.

pQuery

Our own version of jQuery for Python, that works on Ignifuga Scenes/Entities/Components and Rocket RML Elements using the exact same syntax.

Remote Python Console

RFoo integration, connect remotely to a running instance of Ignifuga and experiment with your game interactively in real time.

Heavily Data Driven

Almost everything can be done from a json definition file

Hot Reloading

On the fly, pun intended, reloading of assets, this includes images and scene definition files.

A healthy mix of static/dynamic development

Despite compiling everything to C and then to machine code, you can still take advantage of Python’s dynamic nature and develop using almost regular Python in your dev system, then compile everything into a neat bundle for distribution

The Grossman Tool

This tool processes sets of sprites into a monolithic compressed texture, generating no hassle animated sprites with optimized hit maps with transparency support

The Schafer Tool

This tool automates the whole build process for all the supported platforms. It also features a ’s “bare” mode, which builds a custom static Python interpreter with the minimum possible contents (no SDL, no libRocket, etc) plus your source code, on all the supported platforms, very useful for easy distribution of Python based projects entirely unrelated to games or chickens!

FAQ

  • What’s a Ignifuga, how do I eat it?

    Ignifuga means fireproof in spanish, it is also the name of a fictitious chicken, and a multi platform Python/Cython/SDL based 2d game engine created by Gabriel Jacobo and contributions from the open source community.

  • What are the supported development platforms?

    Ignifuga is developed and mostly used by me on Ubuntu Precise (12.04) and Ubuntu Quantal (12.10), 64 bits version. Other flavours of Ubuntu (32 bits, or older versions) may work fine as well. From Ubuntu you can currently build binaries for ARM Android, Intel Linux 32 and 64 bits and Windows 32 bits (with 64 bits support coming as soon as Greenlet supports it).
    We also support OS X 10.7.x and 10.8.x as a development system for building OS X (fat binaries for i386 and x86_64), iOS (iPhone/iPad, fat binaries for armv6, armv7 and armv7s), ARM Android and Win32 binaries.

  • What are the supported target systems?

    Schafer, the builder utility for Ignifuga, currently supports building for Linux 32 and 64 bits, Windows 32 bits via MingW32, ARM Android, OS X 10.6 or later, iOS 3.0 or later with SDK 5. Windows 64 support is coming soon.

  • What language do I need to know to use Ignifuga?

    Ignifuga is Python / Cython oriented. You don’t need to trouble yourself with C, Java, Objective C, Klingon or Esperanto.

  • What are the dependencies?

    Ignifuga has a bootstrap utility which will automatically install the engine's source code and the external dependencies under Ubuntu and most of what you need under OS X. See the Get The Code section for more details. Basically you need all the standard build utilities (Make, GCC, etc) as well as the Mingw tools to build for Windows. To create apps for Android you’ll need the NDK and SDK and all the baggage that comes with it (Java, Ant and all the guys in that happy, happy family that you are precisely trying to avoid by programming in Python). For iOS/OS X you need XCode and an Apple Developer account.

  • Do I need to distribute Python? SDL? Some random library you guys thought of adding just for kicks?

    No. Schafer will take care of it for you, it will take sources from all the projects that Ignifuga is built on, mix them, smash them, add some pepper in there, and it’ll give you a single binary that you can then use to become a famous billonaire (fame and billons not included in the package).

  • Is my Python source code interpreted at runtime? Is it protected?

    All your Python / Cython code is converted to C by Cython and then compiled into the monolithic app that’s produced as a final result. So your code is as safe as any other compiled app, but this is not in any way limiting the dynamic nature of Python as you can also have interpreted code in the scene definition files and in the Rocket RML files.

  • My mom says Python is slow. So, how can I make the next Crysis using your engine?

    Ignifuga is Cython based, which gives you almost C like speed, while allowing you to use Python as you please, with the associated ease of development/experimenting/fast iteration. You can see some performance result here or you can build the Bunnymark yourself and try it out (for the lazy ones among you, we are getting 2600 bunnies on a Asus TF101 Transformer and 1600 on an iPad 2). For a comparison of these numbers, see what Haxe NME is getting. As the engine matures we will try to push it to the limits of each platform, optimizing code by replacing Python bits by Cython code where a good performance gain is possible. However, this engine doesn’t aim to be the fastest thing out there, it aims to be easy to use, to be flexible, to support a wide range of targets and secretly it aims to find love, like we all do. If you feel up for it, there’s a profiling facility included that generates CacheGrind files for your viewing pleasure.

  • What if I don’t like showing the Ignifuga splash screen in all my awesome games?

    As Ignifuga approaches stable (1.0) status, we will implement a commercial licensing option where you’ll give us some money and we will give you a hug.

  • Just a hug?

    Ok, you drive a hard bargain…let’s call it a hug and a license to remove the splash screen.

  • That's more like it, how much money are we talking about?

    The price has not been determined yet, but it will probably be on the lower end of the typical price range for indie game engines

  • When will Ignifuga become stable?

    It will become stable when (I always wanted to say this…) when it becomes stable. There’s no date set in stone (or even mud!) right now.

  • How can I help?

    When it becomes available, visit the store and buy something, buy the book if such a thing ever comes out, send us a donation or even better yet, file a bug (with some real problem, don’t go making stuff up just because we told you to file a bug), or contribute some (quality) code.

License

The Ignifuga Game Engine code is distributed under a modified BSD license. The only difference with a standard BSD license is a restriction to ensure that users don’t remove the splash screen showed by the Engine when it starts up. Users that want to remove this splash screen will need to acquire a commercial license from us, which will be available when the engine gets to a more mature stage of development.

The License terms are as follows:

Ignifuga Game Engine License

Copyright (c) 2010-2012, Gabriel Jacobo
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
* Neither the name of Gabriel Jacobo, MDQ Incorporeo, Ignifuga Game Engine nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
* You must NOT, under ANY CIRCUMSTANCES, remove, modify or alter in any way the presentation, duration, code functionality and graphic and audio material, and all other perceptible properties related to the “splash screen”, which should always be the first screen shown by any derived work done with, based on or that uses in any way the licensed code. The splash screen should ALWAYS be shown and should clearly state the “Ignifuga Game Engine” name, the www.ignifuga.org URL and MDQ Incorporeo company logo EXACTLY as the original author provided.

THIS LICENSE AGREEMENT WILL AUTOMATICALLY TERMINATE UPON A MATERIAL BREACH OF ITS
TERMS AND CONDITIONS

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GABRIEL JACOBO NOR MDQ INCORPOREO NOR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.