This document is under active development and discussion!

If you find errors or omissions in this document, please don’t hesitate to submit an issue or open a pull request with a fix. We also encourage you to ask questions and discuss any aspects of the project on IRC. New contributors are always welcome!

1. What is Fifengine?

Flexible Isometric Free Engine (FIFE) is a multi-platform isometric game engine written in C++. It comes with Python bindings allowing users to create games using Python as well as C++. The engine is extendable and enables you to add any feature you can imagine to your project.

It’s a very flexible game creation framework and not tied to any genre, but geared towards an RTS or RPG using an isometric or top-down view style.

This manual describes how to use FIFE to power your game.

Features

This chapter describes all the features the engine currently supports in the master branch. Features are organized into categories for easy reference.

As we continue to add features to FIFE, we will update this list to keep it up to date.

Just because a feature is listed here does not mean that feature is 100% bug free. You may from time to time stumble upon a bug. Please file any bugs you find using our Issue Tracker on Github.
Audio
  • Supported Formats

  • Large file streaming

  • Looping

Development Helpers
  • Logger

    • Module specific logging functionality

    • Logging according to priority levels

    • Logging output to file and stdout

Editor tools
  • Map Editor

    • Multi Map Interface (edit multiple maps at the same time)

    • Object Selector

    • Deleting instances from map layer

    • Loading existing maps

    • Placing instances on map layer

    • Saving maps

    • Undo/Redo support for instances

    • Object Editor plugin

    • Light Editor plugin

    • Camera Editor plugin

  • Image Atlas Creator

    • Create/Edit image atlases

    • cross-platform

    • written in C++ using the Qt framework

Event handling

The following event types are supported and can be bound to scripting functionality:

  • Mouse events

  • Keyboard events

  • Widget events

  • Custom commands

Fonts

Support for following font formats:

Graphical User Interface
  • Ingame console with support for executing Python code / scripts

  • Fully customizable GUIs via

Graphics
  • General

    • Support for all formats implemented by SDL_image:

      • BMP, GIF, JPEG, LBM, PCX, PNG, PNM, TGA, TIFF, WEBP, XCF, XPM, XV

    • Color key support

    • Take ingame screenshots via hotkey

    • Pooling of image resources, resulting enhanced performance and reduced memory consumption

    • Image atlases (many images contained in one file)

  • Animations

    • Multiple definable key frame animations per object

  • Effects

    • Lighting (OpenGL renderer only)

    • Fog of War (OpenGL renderer only)

  • Maps

    • 3D geometry definition (defined by tilt, rotation and scale)

    • Support for different tile and object grids with independent geometries

    • Multiple layers per map

    • All variations of square and hex shape geometries

    • Multiple cameras / views per map

    • Custom XML-based map file format

  • Pathfinding

  • Exchangable pathfinding backends:

    • Route path finder

Scripting
  • Python based scripting system (out of the box)

  • Scripts be can executed from the console

Renderer
  • Support for different renderers (RendererBackends):

    • SDL

    • OpenGL

  • Various resolutions

  • Bit-depth (16, 24, 32bit)

  • Window mode (fullscreen & windowed)

SDL
  • Colorkey for fast transparency effects

OpenGL
  • Transparency for tiles & objects

  • Colorkey for fast transparency effects

  • Lighting effects

  • Fog of War

View
  • Custom Isometric views defined by angle and tilt of camera

  • Top down/side views

  • Correct z-order sorting of map instances

  • Support for different renderers:

    • Blocking renderer

    • Cell selection renderer

    • Coordinate renderer

    • Floating text renderer

    • Grid renderer

    • Instance renderer

    • Quadtree renderer

    • Light renderer (OpenGL only)

  • Static layer support which renders an entire layer as one texture

Virtual file system
  • Support for reading files on platforms with different byte orders

  • Read support for ZIP archives

  • Lazy loading of files for decreased load times

Games using Fifengine

The following projects are using Fife as their engine.

We work closely with both Unknown Horizons to continue to improve fife.

Unknown Horizons

A realtime strategy game with economy simulation. This project is in very active development.

Zero-Projekt

A turn-based post apocalyptic RPG. Check out some videos from Zero-Project here (development ceased).

PARPG

A post-apocalyptic roleplaying game inspired by Fallout, Planescape: Torment & Arcanum (development ceased). PAPRG Blog

FIFE RPG

Rewrite of PARPG. Build with the intention to create an RPG framework, which helps create new RPG games.

Jagged Alliance 2 - Fife Demo

A small game demo for using the Jagged Alliance 2 assets in FIFE engine.

SteamFolkTales

-

Fife Tactics

-

If you are developing a game with Fifengine and want it posted here, please let us know!

Media

You’ll find various media items in this section, including screenshots and videos.

Screenshots

There is a media page that can be found here: http://www.fifengine.net/media.html

Also check out some screenshots on our archived wiki here: http://archivewiki.fifengine.net/Screenshots

Videos

Unknown Horizons
Steamfolk Tales
Zero Project
Jagged Alliance 2 - Asset Demo

1.1. License

The source code (*.cpp, *.h & *.py) of Fifengine is licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1.

Can I use FIFE to make a commercial product?

You can create commercial games with FIFE without needing to pay us any fee.

The following basic rules apply concerning the used LGPL:

You can use proprietery libraries in combination with FIFE if you don’t integrate their code directly into the FIFE C++ core or the FIFE Python scripts.

Third-Party Content Licenses

Third-party content, such as assets, images, and sounds might come from different sources.

Therefore each client directory comes with a separate LICENSE file that states the origin of the content, the author and the actual license it was published under.

2. Getting started

2.1. Installation

This chapter explains how to install fifengine.

3. Core Concepts

This chapter provides an overview of FIFE’s architecture and how the major subsystems connect. Understanding the core architecture helps when working with maps, rendering, input, and other engine features.

3.1. Engine

The Engine class is the central entry point to all engine subsystems. It initializes, manages, and provides access to all major components.

3.1.1. Initialization

Before using any engine features, the Engine must be initialized:

engine = fife.Engine()
engine.init()

During initialization, the engine creates and connects the following subsystems:

SoundManager

Handles audio playback

EventManager

Manages input and game events

TimeManager

Controls timing and frame rate

Model

Contains the game world (maps, layers, objects)

VFS

Virtual file system for loading assets

RenderBackend

Graphics rendering (SDL or OpenGL)

IGUIManager

GUI system integration

3.1.2. Game Loop

FIFE uses a pump-based game loop. Call pump() repeatedly in your main loop:

# Initialize engine pumping
engine.initializePumping()

# Main game loop
running = True
while running:
    engine.pump()  # Process one frame
    # Update game logic here

# Clean up
engine.finalizePumping()
engine.destroy()

The pump() method processes all pending events, updates the model, and renders the frame.

3.1.3. Accessing Subsystems

The Engine class provides getter methods for each subsystem:

sound_manager = engine.getSoundManager()
event_manager = engine.getEventManager()
time_manager = engine.getTimeManager()
model = engine.getModel()
vfs = engine.getVFS()
render_backend = engine.getRenderBackend()
gui_manager = engine.getGuiManager()

3.2. Engine Settings

The EngineSettings class controls initialization parameters before calling engine.init().

3.2.1. Display Settings

settings = engine.getSettings()

# Screen dimensions
settings.setScreenWidth(1024)
settings.setScreenHeight(768)

# Display mode
settings.setFullScreen(True)
settings.setBitsPerPixel(32)
settings.setVSync(True)
settings.setRefreshRate(60)

# Render backend
settings.setRenderBackend("OpenGL")  # or "SDL"

3.2.2. Window Settings

settings.setWindowTitle("My Game")
settings.setWindowIcon("icon.png")

3.2.3. Audio Settings

settings.setInitialVolume(0.8)  # 80% volume

3.2.4. OpenGL-Specific Settings

# Texture filtering
settings.setGLTextureFiltering("nearest")  # or "linear"

# Texture compression
settings.setGLCompressImages(True)

# Mipmapping
settings.setGLUseMipmapping(True)

# Framebuffer effects
settings.setGLUseFramebuffer(True)

3.3. Subsystem Overview

FIFE’s architecture consists of several interconnected subsystems:

Model

The game world containing maps, layers, and instances. See Model and Instances.

View

Rendering pipeline including cameras and renderers. See Rendering.

Video

Low-level graphics backend (SDL/OpenGL).

Audio

Sound management and playback. See Audio.

EventChannel

Input handling and event distribution. See Input and Events.

GUI

Graphical user interface integration. See GUI.

Pathfinder

Pathfinding and movement. See Pathfinding.

VFS

Virtual file system for asset loading. See Virtual Filesystem.

3.4. Resource Managers

FIFE uses resource managers to cache and manage loaded assets:

ImageManager

Caches loaded images

AnimationManager

Caches animations

SoundClipManager

Caches sound clips

These managers implement lazy loading, so resources are loaded only when first accessed.

image_manager = engine.getImageManager()
animation_manager = engine.getAnimationManager()
sound_clip_manager = engine.getSoundClipManager()

3.5. Time Management

The TimeManager provides timing information for frame-rate independent updates:

time_manager = engine.getTimeManager()
dt = time_manager.getTimeDelta()  # Time since last frame in seconds

Use dt to scale movement and animation speeds for consistent behavior regardless of frame rate.

3.6. Engine Change Listener

Monitor engine-level changes such as screen mode switches:

class MyEngineChangeListener(fife.IEngineChangeListener):
    def onScreenModeChanged(self, newmode):
        print("Screen mode changed")

listener = MyEngineChangeListener()
engine.addChangeListener(listener)

# Later, remove it
engine.removeChangeListener(listener)

3.7. Changing Screen Mode at Runtime

Change the display mode without re-initializing the engine:

# Get available screen modes from device capabilities
device_caps = engine.getDeviceCaps()
modes = device_caps.getNearestScreenMode(width, height, bpp, fullscreen)

# Apply the new mode
engine.changeScreenMode(modes)

The changeScreenMode() method handles re-creation of internal objects that depend on the display.

3.8. Device Capabilities

Query supported display configurations:

device_caps = engine.getDeviceCaps()

# Get available screen modes
modes = device_caps.getAvailableScreenModes()

# Get available render drivers
drivers = device_caps.getAvailableRenderDrivers()

3.9. Additional Engine Accessors

The Engine provides access to additional subsystems:

# Logging subsystem
log_manager = engine.getLogManager()

# Special renderers (owned by engine, do not delete)
off_renderer = engine.getOffRenderer()
target_renderer = engine.getTargetRenderer()

# Cursor control
cursor = engine.getCursor()

3.10. Engine Settings Reference

3.10.1. Display Index

For multi-monitor setups, select which display to use:

settings.setDisplay(0)  # Primary display (default)
settings.setDisplay(1)  # Secondary display

3.10.2. SDL Render Driver

Select a specific SDL render driver:

settings.setSDLDriver("software")  # Platform-dependent values

3.10.3. Video Driver

Set the video driver (platform-dependent):

settings.setVideoDriver("x11")  # Linux X11

3.10.4. Color Key Transparency

Enable global colorkey-based transparency:

settings.setColorKeyEnabled(True)
settings.setColorKey(255, 0, 255)  # Magenta as transparent color

3.10.5. Monochrome Rendering

Render in grayscale (OpenGL only):

settings.setGLUseMonochrome(True)

3.10.6. Alpha Test Value

Set the alpha threshold for depth buffer discarding (OpenGL only):

settings.setGLUseDepthBuffer(True)
settings.setGLAlphaTestValue(0.1)  # Discard fragments with alpha < 0.1

3.10.7. SDL Fake Alpha Removal

Remove fake alpha in SDL backend:

settings.setSDLRemoveFakeAlpha(True)

3.10.8. Joystick Support

Enable or disable joystick and gamepad input:

settings.setJoystickSupport(True)  # Enable gamepad support

3.10.9. Frame Limit

Limit the frame rate at the settings level. Also documented in Rendering.

settings.setFrameLimitEnabled(True)
settings.setFrameLimit(60)  # 60 FPS max
if settings.isFrameLimitEnabled():
    print("Frame limit:", settings.getFrameLimit())

3.10.10. Mouse Sensitivity

Configure mouse input sensitivity and acceleration via settings (also configurable at runtime through EventManager):

settings.setMouseSensitivity(1.5)       # Speed factor (1.0 = default)
settings.setMouseAccelerationEnabled(True)  # Enable mouse acceleration

3.10.11. Native Image Cursor

Use a native OS cursor backed by an image file:

settings.setNativeImageCursorEnabled(True)
if settings.isNativeImageCursorEnabled():
    print("Using native cursor")

3.11. Log Modules

FIFE uses a hierarchical logging system. Each subsystem is a log module:

  • LM_AUDIO - Audio subsystem

  • LM_MODEL - Game model

  • LM_VIDEO - Video/rendering

  • LM_VIEW - View system

  • LM_CAMERA - Camera

  • LM_PATHFINDER - Pathfinding

  • LM_GUI - GUI system

  • LM_VFS - Virtual file system

Configure logging levels to filter debug output during development.

4. Coordinate Systems

FIFE uses several different coordinate systems to handle positions across the game world, layers, and screen display. Understanding these coordinate systems is essential for positioning objects, handling input, and working with the camera.

This chapter explains each coordinate system, how they relate to each other, and provides examples for converting between them.

4.1. Overview

FIFE has four main coordinate systems:

Model Coordinates

Integer-based 3D coordinates representing discrete grid positions (cells)

Exact Model Coordinates

Floating-point 3D coordinates for precise positioning

Layer Coordinates

Model coordinates mapped to a specific layer

Screen Coordinates

2D pixel coordinates on the screen after camera transformation

The base types are defined in model/metamodel/modelcoords.h:

typedef DoublePoint3D ExactModelCoordinate;  // Double precision (x, y, z)
typedef Point3D ModelCoordinate;             // Integer precision (x, y, z)
typedef DoublePoint3D AudioSpaceCoordinate;  // Audio positioning

4.2. Model Coordinates

Model coordinates represent positions on the game grid using integers. They correspond to discrete cell positions and are useful when you need to reference specific tiles or cells on a layer.

4.2.1. Creating Model Coordinates

# Create a model coordinate at cell (5, 3, 0)
coord = fife.ModelCoordinate(5, 3, 0)

# Access components
print(coord.x)  # 5
print(coord.y)  # 3
print(coord.z)  # 0

4.2.2. Use Cases

  • Referencing specific cells on a grid

  • Placing objects at exact tile boundaries

  • Grid-based pathfinding

  • Layer grid calculations

4.3. Exact Model Coordinates

Exact model coordinates use double-precision floating-point values for smooth, continuous positioning. They allow objects to be placed between grid cells, enabling smooth movement and animations.

4.3.1. Creating Exact Model Coordinates

# Create an exact coordinate at (5.5, 3.7, 0.0)
coord = fife.ExactModelCoordinate(5.5, 3.7, 0.0)

# Access components
print(coord.x)  # 5.5
print(coord.y)  # 3.7
print(coord.z)  # 0.0

4.3.2. Use Cases

  • Smooth object movement between cells

  • Precise positioning for animations

  • Camera positioning

  • Map coordinate calculations

4.4. Layer Coordinates

Layer coordinates are model coordinates that are associated with a specific layer. The Location class ties coordinates to a layer, providing the bridge between coordinate systems.

4.4.1. The Location Class

The Location class represents a position on a specific layer:

# Create a location on a layer
location = fife.Location(layer)

# Set position using layer coordinates (cell precision)
location.setLayerCoordinates(fife.ModelCoordinate(5, 3, 0))

# Set position using exact layer coordinates (precise)
location.setExactLayerCoordinates(fife.ExactModelCoordinate(5.5, 3.7, 0.0))

4.4.2. Getting Coordinates from a Location

# Get the layer this location is on
layer = location.getLayer()

# Get exact layer coordinates
exact_coords = location.getExactLayerCoordinates()
print(f"Exact: ({exact_coords.x}, {exact_coords.y}, {exact_coords.z})")

# Get cell precision coordinates
cell_coords = location.getLayerCoordinates()
print(f"Cell: ({cell_coords.x}, {cell_coords.y}, {cell_coords.z})")

# Get map coordinates
map_coords = location.getMapCoordinates()
print(f"Map: ({map_coords.x}, {map_coords.y}, {map_coords.z})")

4.4.3. Coordinate Mapping Between Layers

Convert coordinates to a different layer:

# Get exact coordinates mapped to a specific layer
other_exact = location.getExactLayerCoordinates(target_layer)

# Get cell coordinates mapped to a specific layer
other_cell = location.getLayerCoordinates(target_layer)

4.4.4. Cell Offset Distance

Get the offset from the nearest cell center:

offset = location.getCellOffsetDistance()
# Returns distance between exact position and cell center

4.4.5. Checking Location Validity

A location is valid if: - A layer is set - The layer has a cell grid assigned

if location.isValid():
    # Location is ready to use
    pass

4.4.6. Calculating Distances

The Location class provides methods for calculating distances:

# Distance in map coordinates
map_distance = location.getMapDistanceTo(other_location)

# Distance in layer coordinates
layer_distance = location.getLayerDistanceTo(other_location)

4.5. Screen Coordinates

Screen coordinates represent positions on the game window in pixels. The camera transforms model coordinates to screen coordinates for rendering.

4.5.1. The Camera and ScreenPoint

The Camera class handles screen coordinate transformations. Screen coordinates use ScreenPoint, which is a Point3D where z represents depth.

4.5.2. Converting Between Map and Screen Coordinates

# Convert map coordinates to screen coordinates
screen_point = camera.toScreenCoordinates(exact_map_coords)

# Convert screen coordinates back to map coordinates
map_point = camera.toMapCoordinates(screen_point)

# Convert map to virtual screen coordinates (sub-pixel precision)
virtual_screen = camera.toVirtualScreenCoordinates(exact_map_coords)

4.5.3. Virtual Screen Coordinates

Virtual screen coordinates provide sub-pixel precision before final screen conversion:

# Map to virtual screen
virtual = camera.toVirtualScreenCoordinates(map_coords)

# Virtual screen to screen
screen = camera.virtualScreenToScreen(virtual)

# Screen to virtual screen
virtual = camera.screenToVirtualScreen(screen)

4.5.4. Mouse Click Example

When handling mouse clicks, you typically convert screen coordinates to map coordinates:

def onMousePress(self, event):
    # Get screen coordinates from mouse event
    screen_point = fife.ScreenPoint(event.getX(), event.getY(), 0)

    # Convert to map coordinates
    map_point = camera.toMapCoordinates(screen_point)

    # Convert to layer coordinates for a specific layer
    # (map coordinates are layer-independent)

4.6. Coordinate Conversion Summary

This table shows how to convert between coordinate systems:

Model to Exact

ExactModelCoordinate(float(x), float(y), float(z))

Exact to Model

ModelCoordinate(int(x), int(y), int(z)) (truncates)

Layer to Screen

camera.toScreenCoordinates(location.getExactLayerCoordinates())

Screen to Map

camera.toMapCoordinates(ScreenPoint(x, y, z))

Map to Layer

Use cell grid’s getLayerCoordinates(map_coords) method

Layer to Map

Use cell grid’s getExactLayerCoordinates(layer_coords) method

4.7. Practical Examples

4.7.1. Moving an Instance

def move_instance(instance, target_x, target_y):
    # Get current location
    current = instance.getLocation()

    # Create target coordinates
    target = fife.ExactModelCoordinate(target_x, target_y, 0)

    # Create new location on same layer
    new_location = fife.Location(current.getLayer())
    new_location.setExactLayerCoordinates(target)

    # Move the instance
    instance.setLocation(new_location)

4.7.2. Finding Instance at Mouse Position

def get_instance_at(screen_x, screen_y, camera, layer):
    # Convert screen coordinates to screen point
    screen_point = fife.ScreenPoint(screen_x, screen_y, 0)

    # Get matching instances
    instances = []
    camera.getMatchingInstances(screen_point, layer, instances)

    return instances[0] if instances else None

4.7.3. Placing an Object on a Grid

def place_object_at(object_id, layer, cell_x, cell_y):
    # Create location at specific cell
    location = fife.Location(layer)
    location.setLayerCoordinates(fife.ModelCoordinate(cell_x, cell_y, 0))

    # Create instance
    instance = layer.createInstance(object_id, location)
    return instance

4.8. Camera Properties Affecting Coordinate Transformations

The camera’s properties affect how coordinates are transformed:

Rotation

Rotates the view around the target point

Tilt

Adjusts the viewing angle (0 = top-down, 45 = isometric)

Zoom

Scales the view

zToY

Affects the z-axis to y-axis transformation ratio

These properties can be set via the camera:

camera.setRotation(45)      # Rotate 45 degrees
camera.setTilt(60)          # 60 degree tilt angle
camera.setZoom(1.5)         # Zoom to 1.5x
camera.setZToY(32)          # 1 z-unit = 32 y-pixels

Understanding these coordinate systems is key to working with FIFE’s rendering and input systems effectively.

4.9. Point Utilities

The Point and Point3D classes provide vector math utilities.

4.9.1. Point2D Operations

p = fife.Point(3, 4)

# Vector operations
p2 = p + fife.Point(1, 2)   # (4, 6)
p3 = p - fife.Point(1, 1)   # (2, 3)
p4 = p * 2                   # (6, 8)
p5 = p / 2                   # (1, 2)

# Length (magnitude)
length = p.length()  # 5.0

# Normalize to unit vector
p.normalize()

# Rotate around origin (degrees)
p.rotate(90)

# Rotate around a specific point
origin = fife.Point(0, 0)
p.rotate(origin, 45)

# Set coordinates
p.set(10, 20)

4.9.2. Point3D Operations

p = fife.Point3D(1, 2, 3)

# Same operations as Point2D, plus z-component
length = p.length()
p.normalize()
p.set(10, 20, 30)

4.9.3. Type Conversions

Convert between integer and double point types:

# Double to int (rounds)
int_point = fife.doublePt2intPt(double_point)

# Int to double
double_point = fife.intPt2doublePt(int_point)

4.9.4. Typedefs

Point

PointType2D<int32_t> - 2D integer point

DoublePoint

PointType2D<double> - 2D double point

Point3D

PointType3D<int32_t> - 3D integer point

DoublePoint3D

PointType3D<double> - 3D double point

4.10. Rect Utilities

The Rect class provides rectangle operations for screen areas:

rect = fife.Rect(x, y, width, height)

# Edge coordinates
right = rect.right()    # x + width
bottom = rect.bottom()  # y + height

# Point containment
if rect.contains(fife.Point(px, py)):
    print("Point is inside rect")

# Rectangle intersection
if rect.intersects(other_rect):
    print("Rectangles overlap")

# In-place intersection (modifies rect)
rect.intersectInplace(other_rect)

4.10.1. Typed Rectangles

Rect

RectType<int32_t> - Integer rectangle

FloatRect

RectType<float> - Float rectangle

DoubleRect

RectType<double> - Double rectangle

5. Working with Maps

This chapter explains how maps work in Fife, including the XML file format and lighting system.

5.1. Map Format

Fife uses a custom XML-based map format. Maps are structured hierarchically with layers, cells, and instances.

5.1.1. Map Structure Overview

A map consists of the following components:

Map

The top-level container that holds all layers, cameras, and settings.

Layer

Contains a grid of cells with instances. Multiple layers can exist per map.

Cell

A single tile or position on the grid.

Instance

A game object placed on the map at a specific location.

5.1.2. XML File Structure

The basic map XML structure looks like this:

<?xml version="1.0" encoding="utf-8"?>
<fife version="0.4.0">
  <map id="mymap">
    <name>My Map</name>
    <grid type="square">
      <cell_scale>1.0</cell_scale>
      <x_scale>1.0</x_scale>
      <y_scale>1.0</y_scale>
      <rot>0</rot>
      <tilt>0</tilt>
      <x_offset>0</x_offset>
      <y_offset>0</y_offset>
    </grid>
    <elevation id="el1">
      <name>Ground Floor</name>
      <layer id="layer1">
        <name>Ground Layer</name>
        <grid type="square">
          <cell_scale>1.0</cell_scale>
          <x_scale>1.0</x_scale>
          <y_scale>1.0</y_scale>
          <rot>0</rot>
          <tilt>30</tilt>
        </grid>
        <instances>
          <instance object="myobject" x="0" y="0" id="inst1"/>
        </instances>
      </layer>
    </elevation>
  </map>
</fife>

5.1.3. Grid Types

Fife supports different tile and object grids with independent geometries:

  • Square - Standard square tile grid

  • Hexagonal - Hexagonal grid for more natural movement

Grids are defined by:

  • cell_scale - Size of each cell

  • x_scale / y_scale - Scaling factors

  • rot - Rotation angle

  • tilt - Camera tilt angle for 3D-like view

5.1.4. Layers

Each map can have multiple layers. Layers are useful for separating different types of content:

  • Ground layer for terrain

  • Object layer for buildings and decorations

  • Agent layer for game characters

  • Static layer for performance (renders entire layer as one texture)

5.1.5. Instances

Instances are game objects placed on the map. Each instance references an object (archetype) that defines its properties.

<instance object="tree" x="5" y="3" id="tree1"/>
<instance object="building" x="10" y="7" id="building1" rotation="90"/>

5.1.6. File Loaders

Fife provides multiple loaders for map files:

  • MapLoader - Main loader that orchestrates map loading from XML

  • ObjectLoader - Loads object and instance data from XML

  • AnimationLoader - Loads animation definitions from XML

  • AtlasLoader - Loads image atlas definitions

Maps are typically saved in the same XML format using MapSaver.

5.2. Lighting System

Fife includes a lighting system for creating atmospheric lighting effects in your game.

The lighting system is only available when using the OpenGL renderer. The SDL renderer does not support lighting effects.

5.2.1. Light Sources

Fife supports three types of light sources:

5.2.1.1. Image-Based Lights

Use images as light sources. This allows for complex, custom lighting effects.

lightrenderer = map.getLightRenderer()
lightrenderer.addImageLightInfo(
    layer=my_layer,
    image="my_light.png",
    x=5, y=5
)
5.2.1.2. Animation-Based Lights

Use animations as light sources. Useful for flickering or animated lighting effects.

lightrenderer.addAnimationLightInfo(
    layer=my_layer,
    animation="my_light_animation",
    x=5, y=5
)
5.2.1.3. Simple Procedural Lights

Create simple lights programmatically with full control over properties.

lightrenderer.addSimpleLightInfo(
    layer=my_layer,
    x=5, y=5,
    color=[255, 255, 128],  # Warm yellow light
    intensity=200,
    radius=5.0,
    subdivisions=32,
    xstretch=1.0,
    ystretch=1.0
)

5.2.2. Light Parameters

The simple light type supports these parameters:

x, y

Position of the light on the map

color

RGB color value (0-255 per channel)

intensity

Brightness of the light (0-255)

radius

How far the light reaches

subdivisions

Quality of the light circle (more = smoother)

xstretch

Horizontal stretch factor

ystretch

Vertical stretch factor

5.2.3. Blending Modes

Light sources use additive blending by default. You can configure the blend modes for custom effects:

lightrenderer.setBlendMode(
    src_factor="src_alpha",
    dst_factor="one"  # Additive blending
)

5.2.4. Fog of War

Fife supports fog of war effects in the OpenGL renderer. This creates areas of the map that are hidden from the player until they explore them.

Fog of war is only available when using the OpenGL renderer.

5.2.5. Light Groups

You can group lights together for easier management and organization. This is useful when you have many lights in a scene and want to control them as a group.

lightrenderer.setGroupIndex(my_group_index)
lightrenderer.addSimpleLightInfo(...)

5.2.6. Editor Support

The Fife Map Editor includes a Light Editor plugin for visual creation and editing of light sources. This provides a WYSIWYG interface for placing and configuring lights on your map.

6. Rendering

This chapter covers the camera, renderers, and graphics pipeline.

FIFE’s rendering system consists of three main components: the render backend, cameras, and renderers. The render backend handles low-level graphics operations, cameras define what to display, and renderers draw specific aspects of the view.

6.1. Render Backend

The RenderBackend is the foundation of FIFE’s graphics system. It provides hardware-acstraction for all drawing operations.

6.1.1. Available Backends

FIFE supports two render backends:

SDL

Software rendering, widely compatible

OpenGL

Hardware-accelerated, supports advanced effects

Set the backend before initializing the engine:

settings = engine.getSettings()
settings.setRenderBackend("OpenGL")  # or "SDL"

6.1.2. Screen Mode

Configure the display before initializing the engine:

settings = engine.getSettings()
settings.setScreenWidth(1024)
settings.setScreenHeight(768)
settings.setBitsPerPixel(32)
settings.setFullScreen(True)
settings.setVSync(True)

6.1.3. OpenGL-Specific Settings

The OpenGL backend supports additional features:

# Texture filtering (improves image quality)
settings.setGLTextureFiltering("linear")  # none, linear, bilinear, trilinear, anisotropic

# Mipmapping (reduces aliasing at distance)
settings.setGLUseMipmapping(True)

# Texture compression (reduces memory usage)
settings.setGLCompressImages(True)

# Framebuffer effects (for post-processing)
settings.setGLUseFramebuffer(True)

# NPOT textures (non-power-of-two sizes)
settings.setGLUseNPOT(True)

# Depth buffer (for 3D effects)
settings.setGLUseDepthBuffer(True)

6.2. Camera

The Camera defines what portion of the game world is visible on screen. Each camera can have different settings for tilt, rotation, and zoom.

6.2.1. Creating a Camera

import fifengine

# Get the map
map = model.getMap("mymap")

# Define viewport (area of screen to render to)
viewport = fifengine.Rect(0, 0, 800, 600)

# Create camera
camera = fifengine.Camera("mycam", map, viewport, engine.getRenderBackend())

6.2.2. Camera Properties

6.2.2.1. Tilt

Controls the viewing angle. 0 is top-down, 45 is classic isometric:

camera.setTilt(45)  # Isometric view
camera.setTilt(60)  # Steeper angle
6.2.2.2. Rotation

Rotates the view around the camera target:

camera.setRotation(0)    # Default
camera.setRotation(90)   # Quarter turn
camera.setRotation(45)   # Diagonal view
6.2.2.3. Zoom

Scales the view (1.0 is default):

camera.setZoom(1.0)   # Normal zoom
camera.setZoom(2.0)   # Zoomed in
camera.setZoom(0.5)   # Zoomed out
6.2.2.4. zToY

Controls the z-axis transformation ratio:

camera.setZToY(32)  # 1 z-unit = 32 pixels vertically

6.2.3. Setting Camera Location

Position the camera in the game world:

# Create a location
location = fifife.Location(layer)
location.setLayerCoordinates(fife.ModelCoordinate(10, 10, 0))

# Move camera to location
camera.setLocation(location)

6.2.4. Camera Attachment

Attach the camera to follow an instance:

# Attach to instance
camera.attach(instance)

# Detach
camera.detach()

6.2.5. Coordinate Transformations

The camera transforms between map coordinates and screen coordinates:

# Map to screen
screen_point = camera.toScreenCoordinates(map_coords)

# Screen to map
map_point = camera.toMapCoordinates(screen_point)

# Virtual screen coordinates (sub-pixel precision)
virtual = camera.toVirtualScreenCoordinates(map_coords)

# Convert between virtual and screen
screen = camera.virtualScreenToScreen(virtual)
virtual = camera.screenToVirtualScreen(screen_point)

See Coordinate Systems for details on coordinate transformations.

6.2.6. Camera Position

Set camera position directly without a Location object:

camera.setPosition(fife.ExactModelCoordinate(10.5, 20.3))
pos = camera.getPosition()

6.2.7. Camera Enabled State

Enable or disable a camera (disabled cameras are not rendered):

camera.setEnabled(False)  # Disable rendering
camera.setEnabled(True)   # Enable rendering

6.2.8. Z-to-Y Transformation

Control the z-axis transformation. Enable or disable the custom zToY factor:

camera.setZToY(32)             # 1 z-unit = 32 pixels vertically
camera.setZToYEnabled(True)    # Enable custom z-to-y
camera.setZToYEnabled(False)   # Use original matrix value
is_enabled = camera.isZToYEnabled()
original = camera.getOriginalZToY()  # Original value from matrix

6.2.9. Cell Image Dimensions

Set the screen size of cell images and retrieve reference scale:

camera.setCellImageDimensions(64, 32)
dims = camera.getCellImageDimensions()        # For current layer
dims = camera.getCellImageDimensions(layer)   # For specific layer

# Reference scale factors
scale_x = camera.getReferenceScaleX()
scale_y = camera.getReferenceScaleY()

6.2.10. Camera Hit Testing

Find instances at screen coordinates:

# Single point hit test
instances = []
camera.getMatchingInstances(
    fife.ScreenPoint(x, y, 0), layer, instances, alpha=0
)

# Rectangle selection
rect = fife.Rect(x, y, width, height)
instances = []
camera.getMatchingInstances(rect, layer, instances, alpha=128)

# Location-based matching (sorted by camera view)
location = fife.Location(layer)
location.setLayerCoordinates(fife.ModelCoordinate(5, 3))
instances = []
camera.getMatchingInstances(location, instances)

6.2.11. Camera Lighting

Apply per-camera lighting color:

# Set lighting color (RGB floats 0.0-1.0)
camera.setLightingColor(0.5, 0.5, 1.0)  # Blue tint

# Get current lighting
color = camera.getLightingColor()  # Returns [r, g, b]

# Reset to default
camera.resetLightingColor()

6.2.12. Camera Overlays

Apply color, image, or animation overlays to the entire camera view:

# Color overlay (RGBA)
camera.setOverlayColor(255, 0, 0, 128)  # Semi-transparent red
color = camera.getOverlayColor()
camera.resetOverlayColor()

# Image overlay
camera.setOverlayImage(image_id, fill=True)  # Fill viewport
img_id = camera.getOverlayImage()
camera.resetOverlayImage()

# Animation overlay
camera.setOverlayAnimation(anim_ptr, fill=True)
anim = camera.getOverlayAnimation()
camera.resetOverlayAnimation()

6.2.13. Reset Renderers

Reset the active layer information on all renderers. Call this after changing layers or when renderers need to re-evaluate their context:

camera.resetRenderers()

6.2.14. Viewport

The viewport defines the screen area for rendering:

# Set viewport
camera.setViewPort(fife.Rect(100, 50, 600, 400))

# Get viewport
viewport = camera.getViewPort()

Multiple cameras can render to different screen areas.

6.3. Renderers

Renderers draw specific aspects of the view. Each renderer operates in a pipeline, executed in order per frame.

6.3.1. Available Renderers

FIFE includes the following built-in renderers:

InstanceRenderer

Renders game objects and instances

GridRenderer

Renders the tile grid overlay

CellRenderer

Renders individual cells

CellSelectionRenderer

Renders selected cells

CoordinateRenderer

Renders coordinate labels

BlockingInfoRenderer

Renders blocking information

FloatingTextRenderer

Renders floating text above instances

LightRenderer

Renders lighting effects (OpenGL only)

QuadtreeRenderer

Renders spatial partitioning info (debugging)

OffRenderer

Renders off-screen objects

TargetRenderer

Renders to target surfaces

GenericRenderer

Custom rendering with generic shapes

6.3.2. Accessing Renderers

Get renderers from the camera:

instance_renderer = camera.getRenderer("InstanceRenderer")
light_renderer = camera.getRenderer("LightRenderer")
grid_renderer = camera.getRenderer("GridRenderer")

6.3.3. Enabling/Disabling Renderers

Control which renderers are active:

renderer = camera.getRenderer("GridRenderer")
renderer.setEnabled(True)   # Enable
renderer.setEnabled(False)  # Disable

6.3.4. Pipeline Position

Renderers execute in pipeline order. Adjust position to change render order:

renderer.setPipelinePosition(5)  # Render 5th in pipeline

Lower numbers render first (background), higher numbers render later (foreground).

6.3.5. Layer Activation

Renderers can be limited to specific layers:

renderer.addActiveLayer(my_layer)      # Add one layer
renderer.clearActiveLayers()           # Clear all
renderer.activateAllLayers(my_map)     # Activate all from map

6.3.6. InstanceRenderer

Renders game objects on screen:

renderer = camera.getRenderer("InstanceRenderer")

6.3.7. GridRenderer

Renders the tile grid for debugging:

renderer = camera.getRenderer("GridRenderer")
renderer.setEnabled(True)

6.3.8. LightRenderer

Renders dynamic lighting effects (OpenGL only):

lightrenderer = camera.getRenderer("LightRenderer")

# Add simple light
lightrenderer.addSimpleLightInfo(
    layer=layer,
    x=5, y=5,
    color=[255, 255, 128],
    intensity=200,
    radius=5.0
)

# Add image-based light
lightrenderer.addImageLightInfo(
    layer=layer,
    image="light.png",
    x=5, y=5
)

# Add animated light
lightrenderer.addAnimationLightInfo(
    layer=layer,
    animation="flicker.anim",
    x=5, y=5
)

See Working with Maps for lighting system details.

6.3.9. GenericRenderer

For custom drawing, use the GenericRenderer:

renderer = camera.getRenderer("GenericRenderer")

The GenericRenderer allows drawing lines, points, and other shapes directly.

6.4. Screenshots

Capture the current frame:

render_backend = engine.getRenderBackend()
render_backend.captureScreen("screenshot.png")
render_backend.captureScreen("small.png", 320, 240)

6.5. Frame Rate Control

Limit the frame rate to reduce CPU usage:

settings = engine.getSettings()
settings.setFrameLimitEnabled(True)
settings.setFrameLimit(60)  # 60 FPS

Enable VSync to synchronize with monitor refresh rate:

settings.setVSync(True)

6.6. RenderBackend Enums

6.6.1. GLConstants

Stencil buffer constants used with changeRenderInfos() (OpenGL only):

KEEP

Keep current stencil value

ZERO

Set stencil to 0

REPLACE

Replace stencil with reference value

INCR

Increment stencil value

DECR

Decrement stencil value

INVERT

Bitwise invert stencil value

NEVER

Never pass stencil test

LESS

Pass if reference < stencil

LEQUAL

Pass if reference ⇐ stencil

GREATER

Pass if reference > stencil

GEQUAL

Pass if reference >= stencil

EQUAL

Pass if reference == stencil

NOTEQUAL

Pass if reference != stencil

ALWAYS

Always pass stencil test

6.6.2. OverlayType

Controls how texture overlays are applied (OpenGL only):

OVERLAY_TYPE_NONE

(0) No overlay

OVERLAY_TYPE_COLOR

(1) Single color overlay

OVERLAY_TYPE_COLOR_AND_TEXTURE

(2) Color and texture combined

OVERLAY_TYPE_TEXTURES_AND_FACTOR

(3) Multiple textures with blend factor

6.6.3. TextureFiltering

Texture filtering modes (OpenGL only):

TEXTURE_FILTER_NONE

(0) No filtering

TEXTURE_FILTER_BILINEAR

(1) Bilinear filtering

TEXTURE_FILTER_TRILINEAR

(2) Trilinear filtering

TEXTURE_FILTER_ANISOTROPIC

(3) Anisotropic filtering

6.6.4. RenderDataType

Controls the rendering data type for batched drawing (OpenGL only):

RENDER_DATA_WITHOUT_Z

(0) No z-buffer data

RENDER_DATA_TEXTURE_Z

(1) Texture with z-buffer

RENDER_DATA_TEXCOLOR_Z

(2) Texture color with z-buffer

RENDER_DATA_MULTITEXTURE_Z

(3) Multiple textures with z-buffer

6.7. Drawing Primitives

The RenderBackend provides 2D drawing functions for overlays, debug visuals, and UI elements.

6.7.1. Basic Shapes

rb = engine.getRenderBackend()

# Draw a pixel
rb.putPixel(x, y, r, g, b, a=255)

# Draw a line
rb.drawLine(fife.Point(x1, y1), fife.Point(x2, y2), r, g, b, a=255)

# Draw a thick line
rb.drawThickLine(fife.Point(x1, y1), fife.Point(x2, y2), width, r, g, b, a=255)

# Draw connected lines
points = [fife.Point(0, 0), fife.Point(10, 10), fife.Point(20, 0)]
rb.drawPolyLine(points, width, r, g, b, a=255)

# Draw bezier curve
rb.drawBezier(points, steps, width, r, g, b, a=255)

6.7.2. Rectangles

# Draw rectangle outline
rb.drawRectangle(fife.Point(x, y), width, height, r, g, b, a=255)

# Draw filled rectangle
rb.fillRectangle(fife.Point(x, y), width, height, r, g, b, a=255)

6.7.3. Circles

# Draw circle outline
rb.drawCircle(fife.Point(cx, cy), radius, r, g, b, a=255)

# Draw filled circle
rb.drawFillCircle(fife.Point(cx, cy), radius, r, g, b, a=255)

# Draw circle segment (0 angle is right side)
rb.drawCircleSegment(fife.Point(cx, cy), radius, start_angle, end_angle, r, g, b, a=255)

# Draw filled circle segment
rb.drawFillCircleSegment(fife.Point(cx, cy), radius, start_angle, end_angle, r, g, b, a=255)

6.7.4. Triangles and Quads

# Draw triangle
rb.drawTriangle(fife.Point(x1,y1), fife.Point(x2,y2), fife.Point(x3,y3), r, g, b, a=255)

# Draw quad
rb.drawQuad(p1, p2, p3, p4, r, g, b, a=255)

# Draw vertex point
rb.drawVertex(fife.Point(x, y), size, r, g, b, a=255)

6.7.5. Scissor Test

Clip rendering to a specific region:

rb.enableScissorTest()
# ... draw clipped content ...
rb.disableScissorTest()

6.8. Color Class

The Color class represents RGBA colors:

# Create a color (default: black, fully opaque)
color = fife.Color(r=255, g=128, b=0, alpha=255)

# Get channel values
r = color.getR()
g = color.getG()
b = color.getB()
a = color.getAlpha()

# Set channel values
color.setR(100)
color.setG(200)
color.setB(50)
color.setAlpha(128)

# Set all at once
color.set(255, 255, 255, 255)

# Compare colors
if color1 == color2:
    print("Same color")

6.9. Animation

The Animation class manages sequences of images with timing.

6.9.1. Creating Animations

Animations are typically loaded from XML files, but can be created programmatically:

anim = fife.Animation()

# Add frames with durations (in milliseconds)
anim.addFrame(image_ptr_1, 100)  # Show for 100ms
anim.addFrame(image_ptr_2, 100)
anim.addFrame(image_ptr_3, 150)

6.9.2. Querying Frames

# Get frame index by timestamp
index = anim.getFrameIndex(timestamp)

# Get frame image by index
frame = anim.getFrame(index)

# Get frame by timestamp
frame = anim.getFrameByTimestamp(timestamp)

# Get all frames
frames = anim.getFrames()

# Get frame duration
duration = anim.getFrameDuration(index)  # milliseconds

# Total frame count
count = anim.getFrameCount()

# Total animation duration
total = anim.getDuration()  # milliseconds

6.9.3. Action Frame

The action frame is when the associated action takes effect (e.g., a punch hitting):

anim.setActionFrame(2)   # Action happens on frame 2
action_frame = anim.getActionFrame()

6.9.4. Animation Direction

Direction associates an animation with movement direction:

anim.setDirection(0)      # Default direction
direction = anim.getDirection()

6.10. Image

The Image class wraps SDL surfaces and GPU textures.

6.10.1. Image Constructors

Images are created by the RenderBackend or loaded via ResourceLoader. You can also create them directly:

# From SDL_Surface (takes ownership)
image = fife.Image(surface)

# From raw RGBA pixel data
image = fife.Image(pixel_data, width, height)

# From a resource loader
image = fife.Image("name", loader)

6.10.2. Image Rendering

Render an image to the current target:

# Basic render with optional alpha
image.render(rect, alpha=255)

# Render with color overlay (RGB bytes)
image.render(rect, alpha=255, rgb=[r, g, b])

# Render with z-depth (OpenGL only)
image.renderZ(rect, vertexZ, alpha=255)

# Render with overlay image and z-depth
image.renderZ(rect, vertexZ, overlay_image, alpha=255)

6.10.3. Detaching Surface

Remove ownership of the SDL_Surface from the Image so it is not freed on destruction:

surface = image.detachSurface()
# Now caller is responsible for freeing the surface

6.10.4. Surface Replacement

Replace the underlying SDL_Surface:

image.setSurface(new_surface)

6.10.5. Force GPU Load

Force an image into GPU memory (OpenGL backend):

image.forceLoadInternal()

6.10.6. Image Properties

width = image.getWidth()
height = image.getHeight()
area = image.getArea()  # Returns Rect(0, 0, width, height)

6.10.7. Pixel Access

r, g, b, a = image.getPixelRGBA(x, y)

6.10.8. Image Shifts

Offset the rendering position of an image:

image.setXShift(10)
image.setYShift(-5)
shift_x = image.getXShift()
shift_y = image.getYShift()

6.10.9. Saving Images

image.saveImage("output.png")

6.10.10. Shared Images

Use texture atlasing by sharing image data:

image.useSharedImage(shared_image_ptr, region_rect)
if image.isSharedImage():
    rect = image.getSubImageRect()

6.10.11. Copying Sub-Images

target_image.copySubimage(xoffset, yoffset, source_image_ptr)

6.11. Background Color

Set the render backend background color:

rb = engine.getRenderBackend()
rb.setBackgroundColor(0, 0, 128)  # Dark blue background
rb.resetBackgroundColor()          # Reset to black

6.12. Render-to-Texture

Attach an Image as the render target instead of the main screen. All subsequent rendering commands draw to the image:

# Attach render target (discard = clear the image first)
rb.attachRenderTarget(target_image, discard=True)

# ... render commands draw into target_image ...

# Detach and resume rendering to main screen
rb.detachRenderTarget()
Render-to-texture is only available with the OpenGL backend.

6.13. GUI Geometry

The render backend provides a method to render geometry required by the GUI subsystem:

# This is an internal method used by FifechanManager
rb.renderGuiGeometry(vertices, indices, translation, texture)

6.14. Rendering Pipeline

The rendering pipeline processes each frame in this order:

  1. RenderBackend.startFrame() - Begin frame

  2. For each layer (back to front):

    • Camera updates render lists

    • Each renderer in pipeline order calls render()

  3. RenderBackend.endFrame() - Present frame

Renderers are called once per visible layer, allowing per-layer customization.

7. Model and Instances

This chapter explains objects, instances, layers, and maps. The model system is the core data structure that represents the game world.

7.1. Overview

The model system follows a hierarchy:

Model

Top-level container managing all maps

Map

Contains layers and cameras

Layer

Contains instances arranged in a grid

Instance

A placed object in the game world

7.2. Map

A Map is the top-level container for game world data. Maps contain layers, which in turn contain instances.

7.2.1. Creating a Map

model = engine.getModel()
my_map = model.createMap("mymap")

7.2.2. Managing Layers

# Create a cell grid (square or hex)
grid = fife.SquareGrid()  # or fife.HexGrid()

# Add a layer to the map
layer = my_map.createLayer("ground_layer", grid)

# Get existing layers
layers = my_map.getLayers()
ground = my_map.getLayer("ground_layer")

7.2.3. Map Cameras

Maps manage their own cameras:

# Add camera to map
camera = my_map.addCamera("main_camera", viewport)

# Get camera
camera = my_map.getCamera("main_camera")

# Get all cameras
cameras = my_map.getCameras()

7.2.4. Map Updates

Maps update their state each frame:

# Check if map changed
if my_map.isChanged():
    changed_layers = my_map.getChangedLayers()

7.2.5. Map Speed

Control time progression on a map:

my_map.setTimeMultiplier(1.0)   # Normal speed
my_map.setTimeMultiplier(2.0)   # Double speed
my_map.setTimeMultiplier(0.5)   # Half speed

7.2.6. Map TimeProvider

Access the map’s time provider for scaled game time:

time_provider = my_map.getTimeProvider()
game_time = time_provider.getGameTime()

7.2.7. Map Layers

Delete layers and get layer count:

count = my_map.getLayerCount()
my_map.deleteLayers()  # Remove all layers

7.2.8. Map Bounding Box

Get the min/max coordinates of all instances across all layers:

min_coord, max_coord = my_map.getMinMaxCoordinates()

7.2.9. Map Filename

Get or set the filename for the map:

my_map.setFilename("saves/mymap.sav")
filename = my_map.getFilename()

7.2.10. Map Camera Count

Get the number of active cameras on this map:

count = my_map.getActiveCameraCount()

7.2.11. Instance Transfer

Transfer instances between layers (used internally by the pathfinder):

# Register instance for transfer
my_map.addInstanceForTransfer(instance)

# Unregister
my_map.removeInstanceForTransfer(instance)

# Initialize cell caches after all transfers
my_map.initializeCellCaches()

# Clean up cell caches
my_map.finalizeCellCaches()

7.2.12. Trigger Controller

Access the map’s trigger system:

trigger_ctrl = my_map.getTriggerController()

7.3. Layer

A Layer is a grid of cells containing game instances. Layers allow organizing game objects by type (ground, objects, units).

7.3.1. Creating Instances

# Create instance at exact position
instance = layer.createInstance(my_object, fife.ModelCoordinate(5, 3), "my_instance")

# Create instance at precise position
instance = layer.createInstance(my_object, fife.ExactModelCoordinate(5.5, 3.2), "precise")

7.3.2. Querying Instances

# Get all instances
instances = layer.getInstances()

# Get instance by ID
instance = layer.getInstance("my_instance")

# Get instances by type
instances = layer.getInstances("tree")

# Get instances at location
location = fife.Location(layer)
location.setLayerCoordinates(fife.ModelCoordinate(5, 3))
instances_at = layer.getInstancesAt(location)

# Get instances in rectangle
rect = fife.Rect(0, 0, 10, 10)
instances_in = layer.getInstancesIn(rect)

# Get instances in circle
instances = layer.getInstancesInCircle(fife.ModelCoordinate(5, 5), 3)

# Get instances in line
instances = layer.getInstancesInLine(
    fife.ModelCoordinate(0, 0),
    fife.ModelCoordinate(10, 10)
)

7.3.3. Removing Instances

layer.removeInstance(instance)  # Remove without deleting
layer.deleteInstance(instance)  # Remove and delete

7.3.4. Layer Properties

7.3.4.1. Visibility
layer.setInstancesVisible(True)
layer.setInstancesVisible(False)
layer.toggleInstancesVisible()
7.3.4.2. Transparency
layer.setLayerTransparency(128)  # Semi-transparent (0-255)
7.3.4.3. Static Layers

Static layers render as a single texture for performance:

layer.setStatic(True)  # Enable static rendering
layer.isStatic()       # Check if static
Static layers only work correctly with the OpenGL backend. Do not use static layers for instances with animations.
7.3.4.4. Walkable Layers

Only walkable layers can have pathfinding:

layer.setWalkable(True)
if layer.isWalkable():
    # Can create CellCache for pathfinding
    pass
7.3.4.5. Pathing Strategy

Configure how pathfinding works on this layer:

# Edges only (4-direction movement)
layer.setPathingStrategy(fife.CELL_EDGES_ONLY)

# Edges and diagonals (8-direction movement)
layer.setPathingStrategy(fife.CELL_EDGES_AND_DIAGONALS)
7.3.4.6. Sorting Strategy

Control how instances are sorted for rendering:

layer.setSortingStrategy(fife.SORTING_CAMERA)              # By camera view
layer.setSortingStrategy(fife.SORTING_LOCATION)            # By location
layer.setSortingStrategy(fife.SORTING_CAMERA_AND_LOCATION) # Combined
7.3.4.7. Instance Queries

Get instances within a circle segment (angle range):

# center, radius, starting angle, ending angle, use_exact_coordinates
instances = layer.getInstancesInCircleSegment(
    center_location, radius, start_angle, end_angle, True
)

Get bounding coordinates of all instances on the layer:

min_coord, max_coord = layer.getMinMaxCoordinates()
7.3.4.8. Z-Offset

Get the z-offset for instances on this layer:

offset = layer.getZOffset()
7.3.4.9. Blocking Queries

Check if a cell contains a blocking instance:

if layer.cellContainsBlockingInstance(cell_coord):
    print("Cell is blocked")

# Get all blocking instances
blockers = layer.getBlockingInstances()
7.3.4.10. Layer Count

Get number of layers on the parent map:

count = layer.getLayerCount()
7.3.4.11. Instance Activity

Activate or deactivate instances on a layer to skip updates:

layer.setInstanceActivityStatus(False)  # Pause all instances
layer.setInstanceActivityStatus(True)   # Resume updates
7.3.4.12. Interact Layers

Interact layers merge pathfinding data from multiple layers. Useful for buildings with floors:

# Mark this as an interact layer
layer.setInteract(True)
if layer.isInteract():
    walkable_id = layer.getWalkableId()

# Add another layer's interact data to this layer
layer.addInteractLayer(other_layer)
interact_layers = layer.getInteractLayers()

# Remove an interact layer
layer.removeInteractLayer(other_layer)
7.3.4.13. CellCache

The CellCache stores pathfinding data. Create it before using the pathfinder:

layer.createCellCache()
cache = layer.getCellCache()

# When done (rarely needed)
layer.destroyCellCache()

7.4. Instance Change Tracking

FIFE tracks changes to instances using a bitmask system. The InstanceChangeType enum defines possible changes:

ICHANGE_NO_CHANGES

(0x0000) No changes

ICHANGE_LOC

(0x0001) Location changed

ICHANGE_ROTATION

(0x0002) Rotation changed

ICHANGE_SPEED

(0x0004) Speed changed

ICHANGE_ACTION

(0x0008) Action changed

ICHANGE_TIME_MULTIPLIER

(0x0010) Time multiplier changed

ICHANGE_SAYTEXT

(0x0020) Say text changed

ICHANGE_BLOCK

(0x0040) Blocking state changed

ICHANGE_CELL

(0x0080) Cell changed

ICHANGE_TRANSPARENCY

(0x0100) Transparency changed

ICHANGE_VISIBLE

(0x0200) Visibility changed

ICHANGE_STACKPOS

(0x0400) Stack position changed

ICHANGE_VISUAL

(0x0800) Visual changed

Use getChangeInfo() to retrieve the change bitmask from the last update:

changes = instance.getChangeInfo()
if changes & fife.ICHANGE_LOC:
    print("Location changed")

7.5. Instance Change Listener

Listen for any instance property changes:

class MyChangeListener(fife.InstanceChangeListener):
    def onInstanceChanged(self, instance, info):
        if info & fife.ICHANGE_LOC:
            print("Instance moved:", instance.getId())

listener = MyChangeListener()
instance.addChangeListener(listener)

7.6. Instance Delete Listener

Be notified when an instance is deleted:

class MyDeleteListener(fife.InstanceDeleteListener):
    def onInstanceDeleted(self, instance):
        print("Instance deleted:", instance.getId())

listener = MyDeleteListener()
instance.addDeleteListener(listener)

7.7. Instance

An Instance is a placed object in the game world. Instances are created from object archetypes and can perform actions.

7.7.1. TimeProvider

The TimeProvider class manages hierarchical time scaling. Each level (model, map, instance) has its own time provider that multiplies with its parent.

7.7.2. Creating a TimeProvider

# Create with a master provider (or None for root)
provider = fife.TimeProvider(master_provider)

7.7.3. Time Multiplier

# Set local multiplier (0.5 = half speed, 2.0 = double speed)
provider.setMultiplier(2.0)

# Get local multiplier
local = provider.getMultiplier()

# Get total multiplier including all parents
total = provider.getTotalMultiplier()  # E.g., master=2.0, local=0.5 -> 1.0

7.7.4. Game Time

# Get current scaled game time in milliseconds
game_time = provider.getGameTime()

7.8. Object

Objects define the template properties that instances inherit.

# Get or create an object archetype
object = model.getObject("tree")

7.8.1. Object Namespace

Objects can be organized into namespaces:

namespace = object.getNamespace()  # e.g., "vegetation"
object.setId("oak_tree")           # Set object identifier

7.8.2. Object Properties

7.8.2.1. Blocking and Static
object.setBlocking(True)   # Instances block movement
object.setStatic(True)     # Object does not move
7.8.2.2. Cell Stack Position

Set default stack position for instances:

object.setCellStackPosition(3)  # Default stack order
pos = object.getCellStackPosition()
7.8.2.3. Pather Assignment

Assign a pather algorithm to instances of this object:

object.setPather(my_pather)  # Set custom pather
pather = object.getPather()  # Get current pather
7.8.2.4. Cost and Speed

Set pathfinding cost properties on the object:

object.setCostId("terrain")   # Cost category
object.setCost(2.5)           # Cost multiplier
object.setSpeed(0.8)          # Speed modifier

# Query properties
if object.isSpecialCost():
    print("Cost:", object.getCost(), "ID:", object.getCostId())
if object.isSpecialSpeed():
    print("Speed modifier:", object.getSpeed())
7.8.2.5. Z-Step Range

Limit how many z-levels an object can climb:

object.setZStepRange(1)  # Can climb 1 z-level
range = object.getZStepRange()  # -1 if unlimited
7.8.2.6. Walkable Areas

Restrict which areas an object can walk on:

object.addWalkableArea("main_path")
object.addWalkableArea("forest_trail")
areas = object.getWalkableAreas()
object.removeWalkableArea("forest_trail")

# Set the area this object contributes to
object.setArea("building_zone")

7.8.3. Multi-Part Objects

Objects can consist of multiple parts for large buildings or structures:

# Mark object as a multi-part component
object.setMultiPart(True)

# Add part identifiers
object.addMultiPartId("tower_base")
object.addMultiPartId("tower_top")

# Get all part IDs
part_ids = object.getMultiPartIds()

# Set rotation anchor for the multi object
object.setRotationAnchor(fife.ExactModelCoordinate(0.5, 0.5))

# Enable restricted rotation (only use predefined angles)
object.setRestrictedRotation(True)

# Get obvious rotation from any angle
snapped = object.getRestrictedRotation(47)  # Returns nearest valid angle

Add rotation-dependent coordinates for parts:

# Add coordinates for specific rotations
object.addMultiPartCoordinate(0, fife.ModelCoordinate(0, 0))
object.addMultiPartCoordinate(90, fife.ModelCoordinate(1, 0))

# Get coordinates for a rotation
coords = object.getMultiPartCoordinates(90)
all_coords = object.getMultiPartCoordinates()  # All rotations

7.8.4. Instance Location

# Set location
location = fife.Location(layer)
location.setLayerCoordinates(fife.ModelCoordinate(5, 3))
instance.setLocation(location)

# Get location
current_location = instance.getLocation()

# Set facing direction
instance.setFacingLocation(target_location)

# Get facing location
facing = instance.getFacingLocation()

# Get reference to previous location (before last move)
old_loc = instance.getOldLocationRef()

7.8.5. Instance State

Check if an instance needs active updates:

if instance.isActive():
    print("Instance requires updates each frame")

7.8.6. Instance Say Text

Get the current say text pointer (None if not speaking):

text = instance.getSayText()
if text:
    print("Saying:", text)

7.8.7. Instance Actions

Instances can perform actions defined by their object:

# Play action once
instance.actOnce("idle")

# Play action repeatedly
instance.actRepeat("idle")

# Play action with specific direction
instance.actOnce("walk", direction_location)
instance.actRepeat("idle", 90)  # 90 degree rotation

7.8.8. Instance Movement

Move instances between locations:

# Move with action animation
instance.move("walk", target_location, speed=5.0)

# Movement speed: units = distance per second in layer coordinates
instance.move("run", target_location, speed=10.0)

# Follow another instance
instance.follow("walk", leader_instance, speed=5.0)

# Follow a route
instance.follow("walk", route, speed=5.0)

7.8.9. Instance Properties

7.8.9.1. Blocking

Blocking instances prevent movement through them:

instance.setBlocking(True)
instance.isBlocking()
7.8.9.2. Rotation
instance.setRotation(90)   # Set rotation offset
instance.getRotation()     # Get current rotation
instance.getOldRotation()  # Get previous frame's rotation
7.8.9.3. Override Blocking

Allow instances to pass through blocking objects:

instance.setOverrideBlocking(True)  # Can pass through blockers
if instance.isOverrideBlocking():
    print("Can override blocking")
7.8.9.4. Speed Control
instance.setTimeMultiplier(2.0)  # Double speed
instance.getMovementSpeed()      # Current speed

Get total time multiplier including map and model speeds:

total = instance.getTotalTimeMultiplier()  # Includes all parent multipliers
7.8.9.5. Instance Runtime

Get scaled runtime in milliseconds:

runtime = instance.getRuntime()  # Time scaled by time multiplier
7.8.9.6. Action Runtime

Save and restore action animation state:

# Get current action time offset
offset = instance.getActionRuntime()

# Restore action state later
instance.setActionRuntime(offset)
7.8.9.7. Instance Refresh

After directly modifying an instance’s location (bypassing setLocation()), call refresh() to update internal state:

instance.refresh()  # Update time provider and other internals
7.8.9.8. Transparency
instance.setTransparency(128)  # Semi-transparent (0-255)

7.8.10. Instance Say Text

Display text above an instance:

# Show text for 3 seconds (3000ms)
instance.say("Hello!", 3000)

# Show text indefinitely
instance.say("Important message!")

# Clear text
instance.say("")

7.8.11. Instance Listeners

Listen for instance events:

class MyActionListener(fife.InstanceActionListener):
    def onInstanceActionFinished(self, instance, action):
        print("Action finished:", action.getId())

    def onInstanceActionCancelled(self, instance, action):
        print("Action cancelled")

    def onInstanceActionFrame(self, instance, action, frame):
        pass

listener = MyActionListener()
instance.addActionListener(listener)

7.8.12. Cell Stack Position

Control rendering order within a cell (higher values render on top):

instance.setCellStackPosition(5)  # Render above stack position 0
pos = instance.getCellStackPosition()

7.8.13. Cost and Speed Overrides

Override pathfinding cost and movement speed per instance:

# Set custom pathfinding cost
instance.setCost("movement", 2.0)  # Costs 2x normal
instance.resetCost()                # Use object default

# Check if instance has custom cost
if instance.isSpecialCost():
    cost = instance.getCost()
    cost_id = instance.getCostId()

# Get movement speed modifier
speed = instance.getSpeed()
if instance.isSpecialSpeed():
    print("Has custom speed:", speed)

7.8.14. Multi-Cell Instances

Instances can occupy multiple cells:

if instance.isMultiCell():
    instances = instance.getMultiInstances()
    for inst in instances:
        print(inst.getLocation())

7.8.15. Overlays

Add visual overlays to instances:

# Static color overlay (applies regardless of action)
colors = fife.OverlayColors()
instance.addStaticColorOverlay(0, colors)

# Query static color overlay
if instance.isStaticColorOverlay(0):
    overlay = instance.getStaticColorOverlay(0)
    instance.removeStaticColorOverlay(0)

# Action-specific color overlay
instance.addColorOverlay("walk", 0, colors)

# Query color overlay
if instance.isColorOverlay("walk"):
    pass

# Animation overlay (action, order, animation)
instance.addAnimationOverlay("walk", 0, 0, animation_ptr)

# Query animation overlay
if instance.isAnimationOverlay("walk"):
    anim = instance.getAnimationOverlay("walk", 0)
    instance.removeAnimationOverlay("walk", 0)

# Convert action visual to overlays (e.g., for tinting)
instance.convertToOverlays("walk")

7.9. CellGrid

CellGrids define the coordinate system for layers.

7.9.1. Square Grid

Standard square tiles:

grid = fife.SquareGrid()
grid.setScale(32, 32)  # Cell size in pixels
layer = my_map.createLayer("ground", grid)

7.9.2. Hexagonal Grid

Hex-based tiles for natural movement:

grid = fife.HexGrid()
grid.setScale(32, 32)
layer = my_map.createLayer("ground", grid)

7.9.3. Grid Queries

# Get cell coordinates from exact position
cell = grid.getCellCoordinates(exact_position)

# Get exact position from cell
exact = grid.getExactLayerCoordinates(cell)

# Get cell dimensions
dims = grid.getCellDimensions()

7.10. Map Change Listeners

Monitor changes across the entire map:

class MyMapListener(fife.MapChangeListener):
    def onMapChanged(self, map, changed_layers):
        for layer in changed_layers:
            print("Layer changed:", layer.getId())

    def onLayerCreate(self, map, layer):
        print("Layer created:", layer.getId())

    def onLayerDelete(self, map, layer):
        print("Layer deleted:", layer.getId())

listener = MyMapListener()
my_map.addChangeListener(listener)

7.11. Layer Change Listeners

Monitor changes on a specific layer:

class MyLayerListener(fife.LayerChangeListener):
    def onLayerChanged(self, layer, changed_instances):
        for instance in changed_instances:
            print("Instance changed:", instance.getId())

    def onInstanceCreate(self, layer, instance):
        print("Instance created:", instance.getId())

    def onInstanceDelete(self, layer, instance):
        print("Instance deleted:", instance.getId())

listener = MyLayerListener()
layer.addChangeListener(listener)

7.12. Saving and Loading Maps

Maps are typically saved in FIFE’s XML format using loaders and savers:

# Load a map
model = engine.getModel()
map_loader = fife.MapLoader(model, vfs, image_manager)
my_map = map_loader.load("mymap.xml")

# Save a map
map_saver = fife.MapSaver(model)
map_saver.save(my_map, "mymap.xml")

See Working with Maps for details on the map file format.

8. Input and Events

This chapter covers the event system, mouse and keyboard input, and command handling.

FIFE uses an event-driven architecture where user input and game events are distributed through listeners.

8.1. Event Manager

The EventManager is the central hub for all input handling. It processes SDL events and dispatches them to registered listeners.

8.1.1. Getting the Event Manager

event_manager = engine.getEventManager()

8.2. Event Listeners

FIFE provides several listener interfaces for different event types:

IKeyListener

Keyboard input

IMouseListener

Mouse input

ITextListener

Text input (for text fields)

IDropListener

File drop events

ICommandListener

Custom command events

ISdlEventListener

Raw SDL events

8.3. Mouse Events

8.3.1. Mouse Listener Interface

Implement IMouseListener to handle mouse events:

class MyMouseListener(fife.IMouseListener):
    def mousePressed(self, event):
        print(f"Mouse pressed at ({event.getX()}, {event.getY()})")

    def mouseReleased(self, event):
        print(f"Mouse released at ({event.getX()}, {event.getY()})")

    def mouseMoved(self, event):
        print(f"Mouse moved to ({event.getX()}, {event.getY()})")

    def mouseDragged(self, event):
        print(f"Mouse dragged to ({event.getX()}, {event.getY()})")

    def mouseEntered(self, event):
        print("Mouse entered window")

    def mouseExited(self, event):
        print("Mouse exited window")

    def mouseWheelMovedUp(self, event):
        print("Wheel moved up")

    def mouseWheelMovedDown(self, event):
        print("Wheel moved down")

Register the listener:

listener = MyMouseListener()
event_manager.addMouseListener(listener)
event_manager.addMouseListener(listener)  # Add to front of queue

# Later, remove it
event_manager.removeMouseListener(listener)

8.3.2. Mouse Event Types

MOVED

Mouse cursor moved without button press

PRESSED

Mouse button pressed

RELEASED

Mouse button released

CLICKED

Mouse button clicked (press and release)

DRAGGED

Mouse moved while button is held

WHEEL_MOVED_UP

Scroll wheel up

WHEEL_MOVED_DOWN

Scroll wheel down

WHEEL_MOVED_RIGHT

Scroll wheel right

WHEEL_MOVED_LEFT

Scroll wheel left

ENTERED

Mouse entered window

EXITED

Mouse exited window

UNKNOWN_EVENT

Unknown mouse event type

8.3.3. Mouse Button Types

LEFT

Left mouse button

RIGHT

Right mouse button

MIDDLE

Middle mouse button (wheel)

X1

Extra button 1

X2

Extra button 2

EMPTY

No button

UNKNOWN_BUTTON

Unknown button type

8.3.4. MouseEvent Properties

def mousePressed(self, event):
    x = event.getX()
    y = event.getY()
    button = event.getButton()
    timestamp = event.getTimeStamp()

    # Check modifier keys
    shift = event.isShiftPressed()
    ctrl = event.isControlPressed()
    alt = event.isAltPressed()
    meta = event.isMetaPressed()

    # Check button type
    if button == fife.MouseEvent.LEFT:
        print("Left button pressed")
    elif button == fife.MouseEvent.RIGHT:
        print("Right button pressed")

8.3.5. Widget Consumption

Check if a mouse event was consumed by a GUI widget:

def mousePressed(self, event):
    if event.isConsumedByWidgets():
        return  # Don't process if GUI handled it

8.3.6. Event Name and Debugging

Get event metadata for debugging:

name = event.getName()          # Returns "MouseEvent"
debug = event.getDebugString()  # Human-readable description
attr = event.getAttrStr()       # Detailed attribute string

8.3.7. Example: Click to Move Instance

class ClickToMoveListener(fife.IMouseListener):
    def __init__(self, camera, instance):
        self.camera = camera
        self.instance = instance

    def mousePressed(self, event):
        if event.getButton() == fife.MouseEvent.LEFT:
            # Convert screen coordinates to map coordinates
            screen_point = fife.ScreenPoint(event.getX(), event.getY(), 0)
            map_point = self.camera.toMapCoordinates(screen_point)

            # Move instance
            location = fife.Location(self.instance.getLocation().getLayer())
            location.setExactLayerCoordinates(map_point)
            self.instance.move("walk", location, 5.0)

8.4. Keyboard Events

8.4.1. Key Listener Interface

Implement IKeyListener to handle keyboard events:

class MyKeyListener(fife.IKeyListener):
    def keyPressed(self, event):
        key = event.getKey()
        print(f"Key pressed: {key.getAsString()}")

    def keyReleased(self, event):
        key = event.getKey()
        print(f"Key released: {key.getAsString()}")

Register the listener:

listener = MyKeyListener()
event_manager.addKeyListener(listener)

# Later, remove it
event_manager.removeKeyListener(listener)

8.4.2. Key Event Types

PRESSED

Key was pressed down

RELEASED

Key was released

UNKNOWN

Unknown key event type

8.4.3. Key Properties

def keyPressed(self, event):
    key = event.getKey()

    # Get key code
    code = key.getValue()

    # Get string representation
    name = key.getAsString()

    # Check if numpad
    is_numpad = event.isNumericPad()

    # Check modifier keys
    shift = event.isShiftPressed()
    ctrl = event.isControlPressed()
    alt = event.isAltPressed()

    # Check specific keys
    if key.getValue() == fife.Key.ESCAPE:
        print("Escape pressed")
    elif key.getValue() == fife.Key.ENTER:
        print("Enter pressed")

8.4.4. Key Constants

Common key constants:

Key.ENTER

Enter/Return

Key.ESCAPE

Escape

Key.SPACE

Spacebar

Key.TAB

Tab

Key.UP

Arrow up

Key.DOWN

Arrow down

Key.LEFT

Arrow left

Key.RIGHT

Arrow right

Key.LSHIFT

Left Shift

Key.RSHIFT

Right Shift

Key.LCTRL

Left Control

Key.RCTRL

Right Control

Key.LALT

Left Alt

Key.RALT

Right Alt

Key.F1-F12

Function keys

8.5. Text Events

For text input fields, use the text listener:

class MyTextListener(fife.ITextListener):
    def textInput(self, event):
        text = event.getText()
        print(f"Text entered: {text}")

listener = MyTextListener()
event_manager.addTextListener(listener)

8.6. Command Events

Custom commands can be dispatched through the event system:

class MyCommandListener(fife.ICommandListener):
    def onCommand(self, command):
        command_code = command.getCode()
        if command_code == 1:
            print("Custom command received")

listener = MyCommandListener()
event_manager.addCommandListener(listener)

# Dispatch a command
command = fife.Command()
command.setCode(1)
event_manager.dispatchCommand(command)

8.7. Key Filters

Filter specific keys to be processed by the engine or your application:

class MyKeyFilter(fife.IKeyFilter):
    def isFiltered(self, key):
        # Filter out specific keys
        return key.getValue() == fife.Key.F12

filter = MyKeyFilter()
event_manager.setKeyFilter(filter)

8.8. Drop Events

Handle file drag-and-drop:

class MyDropListener(fife.IDropListener):
    def fileDropped(self, event):
        path = event.getPath()
        print(f"File dropped: {path}")

listener = MyDropListener()
event_manager.addDropListener(listener)

8.9. Mouse Sensitivity

Adjust mouse behavior:

# Set mouse sensitivity (0.0 = default, positive = faster, negative = slower)
event_manager.setMouseSensitivity(1.5)

# Enable mouse acceleration
event_manager.setMouseAccelerationEnabled(True)

8.10. Clipboard Support

Access system clipboard:

# Check if clipboard has text
if event_manager.isClipboardText():
    # Get clipboard text
    text = event_manager.getClipboardText()

# Set clipboard text
event_manager.setClipboardText("Hello, clipboard!")

8.11. Event Consumption

Events can be consumed to prevent further processing:

def mousePressed(self, event):
    if is_handled:
        event.consume()  # Prevent other listeners from receiving this event

8.12. SDL Events

For low-level access to SDL events:

class MySdlListener(fife.ISdlEventListener):
    def onSdlEvent(self, event):
        # Raw SDL event
        event_type = event.type
        print(f"SDL event type: {event_type}")

listener = MySdlListener()
event_manager.addSdlEventListener(listener)

8.13. Event Queue

The event manager processes events automatically during engine.pump(). You should register your listeners before the game loop starts.

Listeners are called in the order they were registered. Use addListenerFront() to add listeners to the front of the queue.

8.14. Adding Listeners to Front of Queue

Add listeners that receive events before previously registered ones:

event_manager.addKeyListenerFront(listener)
event_manager.addMouseListenerFront(listener)
event_manager.addCommandListenerFront(listener)
event_manager.addTextListenerFront(listener)
event_manager.addDropListenerFront(listener)
event_manager.addSdlEventListenerFront(listener)

8.15. Joystick and Gamepad Support

FIFE supports joystick and gamepad input through SDL.

8.15.1. Enabling Joystick Support

settings = engine.getSettings()
settings.setJoystickSupport(True)  # Enable before engine.init()

8.15.2. Joystick Manager Methods

event_manager = engine.getEventManager()

# Get number of connected joysticks
count = event_manager.getJoystickCount()

# Get a specific joystick by instance ID
joystick = event_manager.getJoystick(instance_id)

8.15.3. Gamepad Mappings

Load and save SDL gamepad controller mappings:

# Load mappings from file
event_manager.loadGamepadMapping("gamecontrollerdb.txt")

# Save mapping for a specific GUID
event_manager.saveGamepadMapping(guid, "mymapping.txt")

# Save all mappings used this session
event_manager.saveGamepadMappings("all_mappings.txt")

# Get/Set mapping as string
mapping = event_manager.getGamepadStringMapping(guid)
event_manager.setGamepadStringMapping(mapping)

8.15.4. Joystick Listener

class MyJoystickListener(fife.IJoystickListener):
    # Implement joystick event callbacks
    pass

event_manager.addJoystickListener(listener)
event_manager.addJoystickListenerFront(listener)  # Front of queue
event_manager.removeJoystickListener(listener)

9. Audio

This chapter explains the sound engine, sound emitters, and audio playback.

FIFE’s audio system is built on OpenAL and supports positional 3D audio.

9.1. Sound Manager

The SoundManager is the central audio controller. It manages emitters, sound effects, and the listener position.

9.1.1. Getting the Sound Manager

sound_manager = engine.getSoundManager()

9.1.2. Volume Control

# Set master volume (0.0 = silence, 1.0 = normal)
sound_manager.setVolume(0.8)
volume = sound_manager.getVolume()

# Mute/unmute
sound_manager.mute()
sound_manager.unmute()

9.1.3. Global Playback Control

sound_manager.play()    # Play all emitters
sound_manager.pause()   # Pause all emitters
sound_manager.stop()    # Stop all emitters
sound_manager.rewind()  # Rewind all emitters

9.2. Listener Position

The listener represents the player’s "ears" in the 3D audio space.

9.2.1. Setting Listener Position

# Set listener position (follows the camera)
listener_pos = fife.AudioSpaceCoordinate(10.0, 10.0, 0.0)
sound_manager.setListenerPosition(listener_pos)

# Set listener orientation
orientation = fife.AudioSpaceCoordinate(0.0, 1.0, 0.0)
sound_manager.setListenerOrientation(orientation)

# Set listener velocity (for Doppler effect)
velocity = fife.AudioSpaceCoordinate(0.0, 0.0, 0.0)
sound_manager.setListenerVelocity(velocity)

9.2.2. Distance Model

Configure how sound attenuates with distance:

# Distance models
sound_manager.setDistanceModel(fife.SD_DISTANCE_NONE)
sound_manager.setDistanceModel(fife.SD_DISTANCE_INVERSE)
sound_manager.setDistanceModel(fife.SD_DISTANCE_INVERSE_CLAMPED)
sound_manager.setDistanceModel(fife.SD_DISTANCE_LINEAR)
sound_manager.setDistanceModel(fife.SD_DISTANCE_LINEAR_CLAMPED)
sound_manager.setDistanceModel(fife.SD_DISTANCE_EXPONENT)
sound_manager.setDistanceModel(fife.SD_DISTANCE_EXPONENT_CLAMPED)

9.2.3. Doppler Effect

Configure the Doppler effect for moving sources:

sound_manager.setDopplerFactor(1.0)

9.3. Sound Emitters

Sound emitters are individual sound sources that can play audio clips.

9.3.1. Creating Emitters

# Create an empty emitter
emitter = sound_manager.createEmitter()

# Create emitter with a sound clip
emitter = sound_manager.createEmitter("mysound.ogg")

9.3.2. Emitter Listener

Listen for sound completion events:

class MySoundListener(fife.SoundEmitterListener):
    def onSoundFinished(self, emitterId, soundClipId):
        print("Sound finished:", emitterId)

listener = MySoundListener()
emitter.addListener(listener)
emitter.removeListener(listener)

9.3.3. Loading Sound Clips

# Load a sound clip
emitter.setSoundClip("sounds/mysound.ogg")

9.3.4. Playing Sounds

# Play once
emitter.play()

# Play with fade in/out (time in seconds)
emitter.play(0.5, 0.5)  # 0.5s fade in, 0.5s fade out

# Play in a loop
emitter.setLooping(True)
emitter.play()

# Stop playback
emitter.stop()

# Stop with fade out
emitter.stop(0.5)  # 0.5s fade out

# Pause/resume
emitter.pause()
emitter.play()
emitter.rewind()

9.3.5. Volume Control

# Set gain (0.0 = silence, 1.0 = normal)
emitter.setGain(0.8)
gain = emitter.getGain()

# Set max/min gain limits
emitter.setMaxGain(1.0)
emitter.setMinGain(0.0)

9.3.6. Pitch Control

emitter.setPitch(1.0)   # Normal speed
emitter.setPitch(2.0)   # Double speed (higher pitch)
emitter.setPitch(0.5)   # Half speed (lower pitch)

9.3.7. Positional Audio

Place emitters in 3D space for positional audio:

# Set position
pos = fife.AudioSpaceCoordinate(5.0, 3.0, 0.0)
emitter.setPosition(pos)

# Set direction (for directional sounds)
direction = fife.AudioSpaceCoordinate(1.0, 0.0, 0.0)
emitter.setDirection(direction)

# Set velocity (for Doppler effect)
velocity = fife.AudioSpaceCoordinate(0.0, 0.0, 0.0)
emitter.setVelocity(velocity)

9.3.8. Sound Cone

Configure directional sound cones:

emitter.setConeInnerAngle(90)   # Full volume within 90 degrees
emitter.setConeOuterAngle(180)  # Reduced volume outside 90 degrees
emitter.setConeOuterGain(0.5)   # Volume outside cone (0-1)

9.3.9. Distance Attenuation

Configure how sound fades with distance:

emitter.setReferenceDistance(1.0)  # Distance for half volume
emitter.setMaxDistance(100.0)      # Maximum audible distance
emitter.setRolloff(1.0)            # Attenuation factor

9.3.10. Relative Positioning

Use relative positioning for ambient sounds:

emitter.setRelativePositioning(True)  # Position relative to listener

9.3.11. Emitter State

Check emitter status:

state = emitter.getState()
if state == fife.SD_PLAYING_STATE:
    print("Playing")
elif state == fife.SD_PAUSED_STATE:
    print("Paused")
elif state == fife.SD_STOPPED_STATE:
    print("Stopped")
elif state == fife.SD_INITIAL_STATE:
    print("Initial (not yet played)")

if emitter.isFinished():
    print("Sound finished playing")

9.3.12. Emitter Activity

Check if emitter has an active OpenAL source:

if emitter.isActive():
    print("Emitter has an OpenAL source")

9.3.13. Cursor Position

Get and set playback cursor position:

# Set cursor by sample offset
emitter.setCursor(fife.SD_SAMPLE_POS, 44100.0)

# Set cursor by time (seconds)
emitter.setCursor(fife.SD_TIME_POS, 1.5)

# Set cursor by byte offset
emitter.setCursor(fife.SD_BYTE_POS, 1024.0)

# Get cursor position
pos = emitter.getCursor(fife.SD_TIME_POS)

The SoundPositionType enum values:

SD_SAMPLE_POS

Position by sample offset

SD_TIME_POS

Position in seconds

SD_BYTE_POS

Position by byte offset

9.3.14. Emitter Reset

Reset emitter to default state:

emitter.reset(defaultall=True)  # Reset position, velocity, gain, etc.
emitter.reset(defaultall=False)  # Reset only internal buffers

9.3.15. Sound Information

Get details about the loaded sound:

is_stereo = emitter.isStereo()
bit_depth = emitter.getBitResolution()
sample_rate = emitter.getSampleRate()
decoded_length = emitter.getDecodedLength()  # In bytes
duration = emitter.getDuration()  # In milliseconds
play_time = emitter.getPlayTimestamp()  # Last play start in ms

9.3.16. Relative Positioning

Use relative positioning for ambient sounds:

emitter.setRelativePositioning(True)  # Position relative to listener
is_rel = emitter.isRelativePositioning()

9.3.17. Emitter ID and SoundClip

Get the emitter’s unique ID and current sound clip:

emitter_id = emitter.getId()
sound_clip = emitter.getSoundClip()  # Returns SoundClipPtr

9.3.18. Emitter Reset

Reset emitter to default state:

emitter.reset(defaultall=True)  # Reset position, velocity, gain, etc.
emitter.reset(defaultall=False)  # Reset only internal buffers

9.4. Sound Groups

Group emitters for batch operations:

# Create a group
emitter.setGroup("music")

# Play/pause/stop entire group
sound_manager.play("music")
sound_manager.pause("music")
sound_manager.stop("music")
sound_manager.rewind("music")

# Set volume for entire group
sound_manager.setGain("music", 0.7)

# Remove group
sound_manager.removeGroup("music")

9.5. Sound Effects

Add effects like reverb to sounds:

# Create an effect by type
effect = sound_manager.createSoundEffect(fife.SF_EAXREVERB)

# Create effect with preset
effect = sound_manager.createSoundEffectPreset(fife.SEP_GENERIC)

# Add emitter to effect
sound_manager.addEmitterToSoundEffect(effect, emitter)

# Enable effect
sound_manager.enableSoundEffect(effect)

# Later, disable effect
sound_manager.disableSoundEffect(effect)

# Remove emitter from effect
sound_manager.removeEmitterFromSoundEffect(effect, emitter)

# Delete effect
sound_manager.deleteSoundEffect(effect)

9.6. Sound Filters

Apply direct filters to individual emitters:

# Create a filter
sound_filter = sound_manager.createSoundFilter(fife.SFT_LOWPASS)

# Add emitter to direct filter
sound_manager.addEmitterToDirectSoundFilter(sound_filter, emitter)

# Enable/disable direct filter
sound_manager.enableDirectSoundFilter(sound_filter)
sound_manager.disableDirectSoundFilter(sound_filter)

# Remove emitter from filter
sound_manager.removeEmitterFromDirectSoundFilter(sound_filter, emitter)

# Delete filter
sound_manager.deleteSoundFilter(sound_filter)

9.7. Listener Max Distance

Set maximum distance for emitter activation. Emitters beyond this distance are automatically deactivated:

sound_manager.setListenerMaxDistance(500.0)
max_dist = sound_manager.getListenerMaxDistance()

9.8. Sound Manager Initialization

Explicitly initialize the audio system (called automatically by Engine):

sound_manager.init()
if sound_manager.isActive():
    print("Audio system is active")

# Access the OpenAL context (advanced usage)
context = sound_manager.getContext()

9.9. Group Management Extended

Additional group operations:

# Set gain limits for a group
sound_manager.setMaxGain("music", 0.9)
sound_manager.setMinGain("music", 0.1)

# Remove all groups
sound_manager.removeAllGroups()

9.10. SoundClip

SoundClip manages audio data buffers and supports streaming for large files.

9.10.1. Streaming Check

Determine if the clip requires streaming:

sound_clip = emitter.getSoundClip()
if sound_clip.isStream():
    print("Uses streaming (large file)")

9.10.2. Buffer Management

For non-streaming clips:

count = sound_clip.countBuffers()  # Number of OpenAL buffers
buffers = sound_clip.getBuffers()  # Buffer IDs for queuing

9.10.3. Streaming Control

For streaming audio, manage the stream lifecycle:

stream_id = sound_clip.beginStreaming()
sound_clip.acquireStream(stream_id)  # Fill initial buffers

# Set/get stream position
sound_clip.setStreamPos(stream_id, fife.SD_TIME_POS, 5.0)  # Seek to 5s
pos = sound_clip.getStreamPos(stream_id, fife.SD_TIME_POS)

# Refill buffer (returns True at EOF)
eof = sound_clip.getStream(stream_id, buffer_id)

sound_clip.quitStreaming(stream_id)
sound_clip.endStreaming(stream_id)

9.10.4. Decoder

Attach a custom sound decoder:

sound_clip.setDecoder(decoder)
decoder = sound_clip.getDecoder()

9.11. Cleanup

Release emitters when no longer needed:

emitter.release()  # Release the emitter
sound_manager.deleteEmitter(emitter)  # Delete the emitter

# Or release by ID
sound_manager.releaseEmitter(emitter_id)

# Release OpenAL source handle (advanced)
sound_manager.releaseSource(emitter)

9.12. Attaching to Instances

Attach sounds to game instances for automatic position tracking:

# Create emitter and attach to instance
emitter = sound_manager.createEmitter("walk.ogg")
emitter.setLooping(True)
emitter.play()

# Update position each frame
def update():
    location = instance.getLocation()
    coords = location.getExactLayerCoordinates()
    emitter.setPosition(fife.AudioSpaceCoordinate(coords.x, coords.y, coords.z))

9.13. Example: Ambient Music

# Create background music
music = sound_manager.createEmitter()
music.setSoundClip("music/theme.ogg")
music.setGain(0.6)
music.setLooping(True)
music.setRelativePositioning(True)
music.play()

# Create ambient sound effect
ambient = sound_manager.createEmitter()
ambient.setSoundClip("sounds/wind.ogg")
ambient.setGain(0.3)
ambient.setLooping(True)
ambient.setRelativePositioning(True)
ambient.play()

10. GUI

This chapter covers FifeChan integration, widgets, and GUI management.

FIFE supports multiple GUI backends. The primary backend is FifeChan (a fork of Guichan).

10.1. GUI Manager

The IGUIManager interface provides access to the GUI system. The FifechanManager implements this interface for FifeChan.

10.1.1. Getting the GUI Manager

gui_manager = engine.getGuiManager()

10.2. FifeChan

FifeChan is the recommended GUI library for FIFE games. It provides a widget-based UI with Python bindings.

10.2.1. Available Backends

FIFE supports two GUI backends:

FifeChan

Default, lightweight, Python bindings via PyChan

CEGUI

Full-featured, requires PyCEGUI

10.2.2. Adding Widgets

# Get the FifechanManager
fifechan_manager = gui_manager  # if using FifechanManager

# Add a widget
fifechan_manager.add(my_widget)

# Remove a widget
fifechan_manager.remove(my_widget)

# Get the top container
top_container = fifechan_manager.getTopContainer()

10.2.3. Initializing FifechanManager

The FifechanManager is initialized by the engine. You can also initialize manually:

fifechan_manager.init("SDL2", screen_width, screen_height)

10.2.4. Tabbing

Enable or disable tab key focus cycling:

fifechan_manager.setTabbingEnabled(True)
if fifechan_manager.isTabbingEnabled():
    print("Tab navigation enabled")

10.2.5. Console Access

Access the in-game console directly:

console = fifechan_manager.getConsole()

10.2.6. Frame Update

The turn() method is called each frame to process GUI logic and rendering. This is handled automatically by the engine.

10.2.7. Resizing

Resize the top container (e.g., after window resize):

gui_manager.resizeTopContainer(x, y, width, height)

10.3. Widgets

FifeChan provides several built-in widgets:

Window

Container window

Button

Clickable button

Label

Text label

TextField

Single-line text input

TextBox

Multi-line text area

Slider

Horizontal/vertical slider

CheckBox

Checkbox control

RadioButton

Radio button

ListBox

List of items

DropDown

Dropdown selector

TabbedArea

Tabbed container

ScrollArea

Scrollable container

Icon

Image display

10.3.1. Creating Widgets

import fife

# Create a button
button = fife.fifechan.Button("Click Me")
button.setWidth(100)
button.setHeight(30)
button.setPosition(10, 10)

# Create a label
label = fife.fifechan.Label("Hello World")
label.setPosition(10, 50)

# Create a text field
textfield = fife.fifechan.TextField()
textfield.setWidth(200)
textfield.setHeight(25)
textfield.setPosition(10, 80)

# Create a checkbox
checkbox = fife.fifechan.CheckBox("Enable feature")
checkbox.setPosition(10, 115)

# Create a window
window = fife.fifechan.Window("My Window")
window.setWidth(300)
window.setHeight(200)
window.setPosition(50, 50)

10.3.2. Widget Properties

# Position
widget.setPosition(x, y)

# Size
widget.setWidth(width)
widget.setHeight(height)

# Visibility
widget.setVisible(True)
widget.setVisible(False)

# Enabled/disabled
widget.setEnabled(True)
widget.setEnabled(False)

# Focus
widget.setFocusable(True)

10.4. Text Input

10.4.1. Default Font

Set the default font before initializing the GUI:

settings = engine.getSettings()
settings.setDefaultFontPath("fonts/FreeSans.ttf")
settings.setDefaultFontSize(12)
settings.setDefaultFontGlyphs("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

10.4.2. Font Creation

Create custom fonts for specific widgets:

font = gui_manager.createFont("fonts/FreeSans.ttf", 14, glyphs)
widget.setFont(font)

10.5. Widget Events

Listen for widget events:

class MyWidgetListener:
    def __init__(self, button):
        self.button = button

    def handleAction(self, event):
        if event.getSource() == self.button:
            print("Button clicked!")

# In Python, you typically subclass widgets or use callbacks

10.6. Console

The built-in console allows executing Python code in-game.

10.6.1. Enabling the Console

gui_manager.setConsoleEnabled(True)

10.6.2. Using the Console

Press the default hotkey (typically backtick or tilde) to open the console. Type Python expressions and press Enter to execute them.

10.7. Cursor

Configure the mouse cursor:

cursor = engine.getCursor()

# Set cursor image
cursor.setImage(cursor_image)

# Set cursor animation
cursor.setAnimation(cursor_animation)

# Enable native image cursor (system cursor)
cursor.setNativeImageCursorEnabled(True)

10.8. Layout Example

def create_main_menu():
    # Create main window
    window = fife.fifechan.Window("Main Menu")
    window.setWidth(300)
    window.setHeight(250)
    window.setPosition(100, 100)

    # Create buttons
    start_btn = fife.fifechan.Button("Start Game")
    start_btn.setWidth(200)
    start_btn.setHeight(40)
    start_btn.setPosition(50, 30)

    options_btn = fife.fifechan.Button("Options")
    options_btn.setWidth(200)
    options_btn.setHeight(40)
    options_btn.setPosition(50, 80)

    quit_btn = fife.fifechan.Button("Quit")
    quit_btn.setWidth(200)
    quit_btn.setHeight(40)
    quit_btn.setPosition(50, 130)

    # Add buttons to window
    window.add(start_btn)
    window.add(options_btn)
    window.add(quit_btn)

    # Add window to GUI
    gui_manager.add(window)

    return window

10.9. Tabbing

Control keyboard navigation between widgets:

# Enable/disable tab navigation
gui_manager.setTabbingEnabled(True)

10.10. GUI Rendering

The GUI is rendered automatically each frame by gui_manager.turn(). No manual rendering is required.

10.11. Top Container

Access the top-level container for layout:

container = gui_manager.getTopContainer()
container.setWidth(screen_width)
container.setHeight(screen_height)
The top container is resized automatically when the screen mode changes.

11. Pathfinding

This chapter explains pathfinding, routes, and instance movement.

FIFE includes a pathfinding system that allows game entities to navigate the map automatically.

11.1. Overview

The pathfinding system consists of:

Route

Holds the calculated path between two locations

Pather

Algorithms that compute routes (A* based)

Pathfinding integrates with the model system through the Layer and Location classes.

11.2. Pathfinding Prerequisites

For pathfinding to work, the layer must be marked as walkable:

layer.setWalkable(True)

Blocking instances will prevent paths from going through them:

instance.setBlocking(True)

11.3. Route

A Route represents a path from a start location to a destination.

11.3.1. Creating Routes

Routes are typically created when calling instance.move():

# The move method internally creates a route
instance.move("walk", target_location, 5.0)

You can also create routes manually:

route = fife.Route(start_location, end_location)

11.3.2. Route Status

Routes have a status indicating their current state:

ROUTE_CREATED

(0) Route created but not yet calculated

ROUTE_SEARCHING

(1) Path calculation in progress

ROUTE_SEARCHED

(2) Search complete, ready to solve

ROUTE_SOLVED

(3) Path found successfully

ROUTE_FAILED

(4) No path could be found

Query the status:

status = route.getRouteStatus()
route.setRouteStatus(fife.ROUTE_SOLVED)

11.3.3. Route Nodes

# Get start and end locations
start = route.getStartNode()
end = route.getEndNode()

# Set new start/end
route.setStartNode(new_start)
route.setEndNode(new_end)

# Get current position on path
current = route.getCurrentNode()

# Get next/previous positions
next_node = route.getNextNode()
prev_node = route.getPreviousNode()

11.3.4. Route Properties

route = instance.getRoute()
if route:
    status = route.getRouteStatus()
    path_length = route.getPathLength()
    walked = route.getWalkedLength()

    if route.getRouteStatus() == fife.ROUTE_SOLVED:
        path = route.getPath()
        for node in path:
            print(node.getLayerCoordinates())

11.3.5. Path Manipulation

# Set path manually
route.setPath(path_list)

# Cut path after certain length
route.cutPath(3)  # Keep only first 3 steps

# Check if at end of path
if route.reachedEnd():
    print("Destination reached")

# Walk to next node
if route.walkToNextNode(step=1):
    print("Moved forward")

11.3.6. Route Replanning

Routes can be marked for replanning if the environment changes:

route.setReplanned(True)
if route.isReplanned():
    print("Route will be recalculated")

11.3.7. Session and Rotation

# Set search session ID
route.setSessionId(42)
session = route.getSessionId()

# Set current rotation for multi-cell pathfinding
route.setRotation(90)
rotation = route.getRotation()

11.3.8. Cost Configuration

# Set cost identifier for pathfinding
route.setCostId("movement")
cost_id = route.getCostId()

11.3.9. Multi-Cell Pathfinding

For objects that occupy multiple cells:

# Set occupied area on the route
route.setOccupiedArea([fife.ModelCoordinate(0, 0), fife.ModelCoordinate(1, 0)])

# Get occupied cells based on rotation
cells = route.getOccupiedCells(rotation)

# Check if multi-cell route
if route.isMultiCell():
    area = route.getOccupiedArea()

11.3.10. Object Association

Associate route with an object for z-step range and multi-cell info:

route.setObject(my_object)
obj = route.getObject()
z_range = route.getZStepRange()

11.3.11. Area Limitations

if route.isAreaLimited():
    areas = route.getLimitedAreas()

11.3.12. Cost Modifiers

Configure how pathfinding works on a layer:

# 4-direction movement (edges only)
layer.setPathingStrategy(fife.CELL_EDGES_ONLY)

# 8-direction movement (edges and diagonals)
layer.setPathingStrategy(fife.CELL_EDGES_AND_DIAGONALS)

11.4. Instance Movement

The Instance.move() method handles pathfinding automatically:

11.4.1. Basic Movement

# Move with action animation at speed 5
instance.move("walk", target_location, 5.0)

The parameters are:

actionName

Action to play during movement (e.g., "walk")

target

Destination location

speed

Movement speed (layer units per second)

costId

Optional cost identifier for pathfinding

11.4.2. Movement Speed

Speed is measured in layer coordinate units per second:

# Walk at 5 units/second
instance.move("walk", target, 5.0)

# Run at 10 units/second
instance.move("run", target, 10.0)

# Slow walk at 2 units/second
instance.move("walk", target, 2.0)

11.4.3. Canceling Movement

Stop an instance mid-movement:

# Cancel movement (stops at next cell)
instance.cancelMovement()

# Cancel after a certain number of cells
instance.cancelMovement(2)  # Stop after 2 more cells

11.5. Following

Instances can follow other instances or routes:

11.5.1. Following an Instance

# Follow another instance
instance.follow("walk", leader_instance, 5.0)

11.5.2. Following a Route

# Follow a pre-calculated route
instance.follow("walk", route, 5.0)

11.6. Multi-Cell Pathfinding

For objects that occupy multiple cells:

# Set occupied area on the route
route.setOccupiedArea([fife.ModelCoordinate(0, 0), fife.ModelCoordinate(1, 0)])

# Get occupied cells based on rotation
cells = route.getOccupiedCells(rotation)

11.7. Cost Modifiers

Pathfinding can use custom costs for different terrain or obstacles:

11.7.1. Instance Costs

# Set custom cost on instance
instance.setCost("movement", 2.0)  # Costs 2x normal
instance.setCost("movement", 0.5)  # Costs 0.5x normal

11.7.2. Route Cost ID

Specify which cost type to use:

# Create movement with cost identifier
instance.move("walk", target, 5.0, "movement")

11.8. Blocking Behavior

11.8.1. Blocking Instances

Blocking instances prevent paths from going through them:

# Set instance as blocking
instance.setBlocking(True)

# Override blocking (instance can pass through)
instance.setOverrideBlocking(True)

11.8.2. Ignoring Blockers

Routes can optionally ignore dynamic blockers:

route.setDynamicBlockerIgnored(True)

# Get locations where blockers are ignored
blocking_locations = route.getBlockingPathLocations()

11.9. Pathfinding Algorithms

FIFE uses A* (A-star) for pathfinding. The algorithm considers:

  • Distance heuristic (Manhattan or Euclidean)

  • Cell costs (terrain modifiers)

  • Blocking instances

  • Multi-cell object sizes

11.10. Route Replanning

Routes can be marked for replanning if the environment changes:

route.setReplanned(True)

11.11. Movement Actions

Instance actions are played automatically during movement:

# The "walk" action animation plays while moving
instance.move("walk", target, 5.0)

# Different actions for different speeds
instance.move("run", target, 10.0)
instance.move("sneak", target, 2.0)

11.12. Path Navigation

Navigate through a path manually:

route = instance.getRoute()

# Get current position on path
current = route.getCurrentNode()

# Get next position
next_node = route.getNextNode()

# Get previous position
prev_node = route.getPreviousNode()

# Move to next node
if route.walkToNextNode():
    print("Moved to next node")

# Check if at end
if route.reachedEnd():
    print("Destination reached")

11.13. Distance Calculations

Calculate distances between locations:

location1 = fife.Location(layer)
location1.setLayerCoordinates(fife.ModelCoordinate(0, 0))

location2 = fife.Location(layer)
location2.setLayerCoordinates(fife.ModelCoordinate(10, 10))

# Map coordinate distance
map_dist = location1.getMapDistanceTo(location2)

# Layer coordinate distance
layer_dist = location1.getLayerDistanceTo(location2)

11.14. Example: Move to Click Location

class ClickToMove(fife.IMouseListener):
    def __init__(self, camera, instance):
        self.camera = camera
        self.instance = instance

    def mousePressed(self, event):
        if event.getButton() == fife.MouseEvent.LEFT:
            screen = fife.ScreenPoint(event.getX(), event.getY(), 0)
            map_point = self.camera.toMapCoordinates(screen)

            target = fife.Location(self.instance.getLocation().getLayer())
            target.setExactLayerCoordinates(map_point)

            self.instance.move("walk", target, 5.0)

11.15. Layer Configuration for Pathfinding

Configure layer behavior for pathfinding:

# Set pathing strategy
layer.setPathingStrategy(fife.CELL_EDGES_AND_DIAGONALS)

# Make layer walkable
layer.setWalkable(True)

# Create interact layer for additional blocking info
interact_layer.setInteract(True, "ground_layer")
layer.addInteractLayer(interact_layer)

12. Virtual Filesystem

This chapter covers the virtual file system (VFS), file loading, and archive support.

FIFE’s VFS provides transparent access to files regardless of their location (filesystem, ZIP archives, etc.).

12.1. Overview

The Virtual File System (VFS) abstracts file access from the actual storage. Files can come from:

  • Local filesystem directories

  • ZIP archives

  • Custom sources

12.1.1. Getting the VFS

vfs = engine.getVFS()

12.2. File Operations

12.2.1. Checking File Existence

if vfs.exists("maps/mymap.xml"):
    print("File exists")

12.2.2. Checking Directories

if vfs.isDirectory("maps/"):
    print("Is a directory")

12.2.3. Opening Files

# Open a file
raw_data = vfs.open("maps/mymap.xml")

# Read from the file
content = raw_data.read()

# Close when done
raw_data.close()

12.3. VFS Cleanup

Clean up VFS resources:

vfs.cleanup()  # Close all open sources and clean up

12.4. Custom VFS Providers

Register custom file source providers:

# Add a custom provider (C++ level)
vfs.addProvider(my_provider)

# Create a source from a file (tries all providers)
source = vfs.createSource("custom_archive.dat")

# Add a pre-created source
vfs.addSource(source)

12.5. Removing Sources

# Remove by path
vfs.removeSource("assets.zip")

# Remove by source object
vfs.removeSource(source_object)

12.6. Directory Listing

12.6.1. List Files

# List all files in a directory
files = vfs.listFiles("sprites/")

# List files matching a regex pattern
xml_files = vfs.listFiles("maps/", ".*\.xml")

12.6.2. List Directories

# List subdirectories
dirs = vfs.listDirectories("assets/")

# List directories matching pattern
dirs = vfs.listDirectories("assets/", "level_.*")

12.7. Checking Sources

if vfs.hasSource("assets.zip"):
    print("ZIP archive is mounted")

12.8. Archive Support

FIFE supports reading files from ZIP archives.

12.8.1. Adding ZIP Sources

# Add a ZIP file as a source
vfs.addNewSource("assets.zip")

# Files in the archive are now accessible
# e.g., vfs.open("assets.zip/sprites/hero.png")

12.8.2. Removing Sources

vfs.removeSource("assets.zip")

12.8.3. Checking Sources

if vfs.hasSource("assets.zip"):
    print("ZIP archive is mounted")

12.9. File Source Order

VFS searches sources in the order they were added. The filesystem provider is typically added first, meaning local files override archived files.

All filenames must be lowercase. The VFS converts them automatically and warns if uppercase is used.

12.10. Lazy Loading

FIFE implements lazy loading for resources. Files are loaded only when first accessed, improving startup time.

12.10.1. Resource Managers

Resource managers handle lazy loading for different file types:

# Image manager - lazy loads images
image_manager = engine.getImageManager()

# Animation manager - lazy loads animations
animation_manager = engine.getAnimationManager()

# Sound clip manager - lazy loads audio
sound_clip_manager = engine.getSoundClipManager()

12.11. Loading Maps

Maps are loaded through the model using the VFS:

# Create a map loader
map_loader = fife.MapLoader(model, vfs, image_manager)

# Load a map file
my_map = map_loader.load("maps/mymap.xml")

12.12. Loading Objects

Object definitions can be loaded from XML:

# Object loader loads objects from XML
object_loader = fife.ObjectLoader(model, vfs, image_manager)

# Load objects
object_loader.load("objects/trees.xml")

12.13. Loading Animations

Animation definitions are loaded from XML:

# Animation loader
animation_loader = fife.AnimationLoader(vfs, image_manager)

# Load animations from XML
animation_loader.load("animations/hero_walk.xml")

12.14. VFS Providers

FIFE supports different VFS source providers:

HostFileSystem

Local filesystem (added automatically)

ZipProvider

ZIP archive support

12.15. Adding Custom Providers

Custom providers can be added for alternative file sources:

# Custom providers are added at the C++ level
# The VFS handles provider registration internally

12.16. Best Practices

12.16.1. File Path Conventions

  • Use forward slashes for paths: sprites/hero.png

  • All paths must be lowercase

  • Use relative paths from game root

12.16.2. Archive Organization

Organize ZIP archives by type:

assets/
├── sprites.zip      # Contains sprites/
├── maps.zip         # Contains maps/
├── sounds.zip       # Contains sounds/
└── fonts.zip        # Contains fonts/

12.16.3. Loading Order

  1. Initialize engine

  2. Add archive sources

  3. Load maps (which loads dependencies)

12.16.4. Memory Management

The VFS and resource managers handle memory automatically:

  • Resources are cached after first load

  • Unused resources can be released

  • Lazy loading prevents loading unnecessary files

12.17. Example: Complete Asset Setup

def setup_assets(engine):
    vfs = engine.getVFS()

    # Add ZIP archives as sources
    vfs.addNewSource("assets/sprites.zip")
    vfs.addNewSource("assets/maps.zip")
    vfs.addNewSource("assets/sounds.zip")

    # Load the map
    model = engine.getModel()
    image_manager = engine.getImageManager()
    map_loader = fife.MapLoader(model, vfs, image_manager)

    return map_loader.load("maps/level1.xml")

12.18. Troubleshooting

12.18.1. File Not Found

Check:

  • File exists in filesystem or archive

  • Path is lowercase

  • Source is added before file is accessed

  • Correct relative path from game root

12.18.2. Case Sensitivity

FIFE requires lowercase paths:

# Correct
vfs.open("sprites/hero.png")

# Will warn and convert to lowercase
vfs.open("Sprites/Hero.png")

12.18.3. Source Priority

If files exist in both filesystem and archive:

  • Local filesystem files take priority (loaded first)

  • Archive files are fallbacks

  • Check source order if unexpected files are loaded