Python API reference

This page is the API reference for Python users.

Sample Scripts

The engine ships with small Python samples under Engine/Scripts/, each subclassing the matching Python base interface. They are the quickest way to see the API in use:

The base classes these subclass (trigger_interface.py, actor_interface.py, camera_extension_interface.py, player_extension_interface.py) document each method’s contract in their docstrings. Helper types live in vec3.py and generic_parameter.py.

Core Types

Vec3

3D vector class with comprehensive mathematical operations.

# Create Vec3 objects
vec = Vec3(x=1.0, y=2.0, z=3.0)
vec = Vec3()  # Defaults to (0, 0, 0)

# Access components
x, y, z = vec.x, vec.y, vec.z

# Mathematical operations
vec1 = Vec3(1, 2, 3)
vec2 = Vec3(4, 5, 6)

# Addition
result = vec1 + vec2  # Vec3(5, 7, 9)
vec1 += vec2          # In-place addition

# Subtraction
result = vec1 - vec2  # Vec3(-3, -3, -3)

# Scalar multiplication
result = vec1 * 2.0   # Vec3(2, 4, 6)

# Vector operations
dot_product = vec1.dot(vec2)
cross_product = vec1.cross(vec2)

# Length and normalization
length = vec1.length()
normalized = vec1.normalized()
vec1.normalize()  # In-place normalization

# Utility methods
tuple_vec = vec1.to_tuple()
list_vec = vec1.to_list()

# Class methods for common vectors
zero = Vec3.zero()      # (0, 0, 0)
one = Vec3.one()        # (1, 1, 1)
up = Vec3.up()          # (0, 1, 0)
right = Vec3.right()    # (1, 0, 0)
forward = Vec3.forward() # (0, 0, 1)

# Create from GenericParameter
vec3 = Vec3.from_generic_parameter(param)

Enums

ValueType and RequestParameterType are now defined as Python enums in the generic_parameter module:

from generic_parameter import RequestParameterType, ValueType, GenericParameter

# ValueType enum
class ValueType(Enum):
    STRING = 0
    DOUBLE = 1
    LONG = 2
    LONG_ARRAY = 3
    FLOAT_ARRAY = 4
    BOOLEAN = 5
    VEC4 = 6
    MAT4 = 7

# RequestParameterType enum
class RequestParameterType(Enum):
    MODEL = 0
    ANIMATION = 1
    SWITCH = 2
    FREE_TEXT = 3
    TRIGGER = 4
    GUI_TEXT = 5
    FREE_NUMBER = 6
    COORDINATE = 7
    TRANSFORM = 8
    MULTI_SELECT = 9
    LIGHT = 10
    SOUND = 11
    CAMERA_RIG = 12

LogSubsystem

Identifies which engine subsystem a log message belongs to. Used as the first argument to api.log().

import limon

limon.LogSubsystem.RENDER    # graphics/rendering
limon.LogSubsystem.MODEL     # model loading
limon.LogSubsystem.INPUT     # input handling
limon.LogSubsystem.SETTINGS  # options/settings
limon.LogSubsystem.AI        # actor/AI system
limon.LogSubsystem.LOAD_SAVE # world load/save
limon.LogSubsystem.EDITOR    # editor
limon.LogSubsystem.ANIMATION # animation system

LogLevel

Severity level for a log message. Used as the second argument to api.log().

import limon

limon.LogLevel.TRACE  # very verbose, internal tracing
limon.LogLevel.DEBUG  # debug information
limon.LogLevel.INFO   # general information
limon.LogLevel.WARN   # warnings
limon.LogLevel.ERROR  # errors

GenericParameter

A flexible parameter type that can hold different types of values.

from generic_parameter import RequestParameterType, ValueType, GenericParameter

# Create with enum values
param = GenericParameter(
    request_type=RequestParameterType.FREE_TEXT,
    description="Parameter description",
    value_type=ValueType.STRING,
    value="Default value",
    is_set=True
)

# Create with integer values (backward compatibility)
param = GenericParameter(
    request_type=3,  # RequestParameterType.FREE_TEXT
    description="Parameter description",
    value_type=0,    # ValueType.STRING
    value="Default value",
    is_set=True
)

# Type checking methods
if param.is_string():
    text = param.get_string()
elif param.is_vec4():
    vec4 = param.get_vec4()
elif param.is_double():
    number = param.get_double()
elif param.is_boolean():
    flag = param.get_boolean()
elif param.is_long():
    integer = param.get_long()
elif param.is_mat4():
    matrix = param.get_mat4()
elif param.is_long_array():
    array = param.get_long_array()
elif param.is_float_array():
    array = param.get_float_array()

Vec4

4-component vector class.

vec = limon.Vec4(x=1.0, y=2.0, z=3.0, w=4.0)
# Or using default values (0,0,0,0)
vec = limon.Vec4()

# Access components
x, y, z, w = vec.x, vec.y, vec.z, vec.w

LimonAPI

GUI Methods

add_gui_text

def add_gui_text(font_file_path: str, font_size: int, name: str, text: str,
                color: tuple = None, position: tuple = None, rotation: float = 0.0) -> int:
    """
    Add a text element to the GUI.

    Args:
        font_file_path: Path to the font file
        font_size: Size of the font
        name: Name of the text element
        text: The text to display
        color: RGB color as (r, g, b). Defaults to white (1.0, 1.0, 1.0).
        position: Position as (x, y). Defaults to (0.0, 0.0).
        rotation: Rotation in radians, clockwise. Defaults to 0.0.

    Returns:
        int: ID of the created GUI element
    """

add_gui_image

def add_gui_image(image_file_path: str, name: str, position: tuple = None,
                 scale: tuple = None, rotation: float = 0.0) -> int:
    """
    Add an image to the GUI.

    Args:
        image_file_path: Path to the image file
        name: Name of the image element
        position: Position as (x, y). Defaults to (0.0, 0.0).
        scale: Scale as (x, y). Defaults to (1.0, 1.0).
        rotation: Rotation in radians, clockwise. Defaults to 0.0.

    Returns:
        int: ID of the created GUI element
    """

update_gui_text

def update_gui_text(gui_text_id: int, new_text: str) -> bool:
    """
    Update the text of a GUI text element.

    Args:
        gui_text_id: ID of the GUI text element
        new_text: New text to display

    Returns:
        bool: True if the element was found and updated, False if ID is invalid
    """

remove_gui_element

def remove_gui_element(gui_element_id: int) -> bool:
    """
    Remove a GUI element.

    Args:
        gui_element_id: ID of the GUI element to remove

    Returns:
        bool: True if the element was found and removed, False if ID is invalid
    """

get_gui_element_position

def get_gui_element_position(gui_element_id: int) -> Vec4:
    """
    Returns the screen position of a GUI element as Vec4 (x, y, 0, 1).

    Args:
        gui_element_id: ID of the GUI element

    Returns:
        Vec4: Screen position with w=1, or zero Vec4 if not found
    """

set_gui_element_position

def set_gui_element_position(gui_element_id: int, position: Vec4) -> bool:
    """
    Set the screen position of a GUI element. x and y are used; z and w are ignored.

    Args:
        gui_element_id: ID of the GUI element
        position: Target screen-space position

    Returns:
        bool: True if the element was found and updated
    """

set_gui_element_visible

def set_gui_element_visible(gui_element_id: int, visible: bool) -> bool:
    """
    Show or hide a GUI element. Hidden elements are not rendered but remain
    in the world and can be made visible again.

    Args:
        gui_element_id: ID of the GUI element
        visible: True to show, False to hide

    Returns:
        bool: True if the element was found and updated
    """

Object Manipulation

set_object_temporary

def set_object_temporary(object_id: int, temporary: bool) -> bool:
    """
    Set if an object is temporary (will be removed when world changes).

    Args:
        object_id: ID of the object
        temporary: True if object should be temporary

    Returns:
        bool: True if the object was found and updated
    """

attach_object_to_object

def attach_object_to_object(object_id: int, object_to_attach_to_id: int) -> bool:
    """
    Attach one object to another.

    Args:
        object_id: ID of the object to attach
        object_to_attach_to_id: ID of the object to attach to

    Returns:
        bool: True if attachment succeeded
    """

attach_object_to_object_at_world_position

def attach_object_to_object_at_world_position(object_id: int, object_to_attach_to_id: int) -> bool:
    """
    Attach one object to another using the child's current world-space position as the offset.

    Unlike attach_object_to_object, the attachment offset is computed from the child's
    current world position at the moment of attachment rather than using a pre-set
    relative transform.

    Args:
        object_id: ID of the object to attach
        object_to_attach_to_id: ID of the object to attach to

    Returns:
        bool: True if attachment succeeded
    """

remove_trigger_object

def remove_trigger_object(trigger_object_id: int) -> bool:
    """
    Remove a trigger object.

    Args:
        trigger_object_id: ID of the trigger object

    Returns:
        bool: True if the object was found and removed
    """

is_inside_trigger

def is_inside_trigger(trigger_id: int) -> bool:
    """
    Returns True if a player is currently inside the trigger volume.

    Args:
        trigger_id: ID of the trigger object

    Returns:
        bool: True if the player is inside the volume, False if not or trigger not found
    """

get_object_by_name

def get_object_by_name(name: str) -> int:
    """
    Find an object by name. Searches models, GUI elements, and triggers.

    Args:
        name: The object name (for models: ``modelName_ID`` as shown in the editor)

    Returns:
        int: World object ID if found, 0 if not found
    """

get_object_parent

def get_object_parent(object_id: int) -> int:
    """
    Returns the parent object ID of the given object.

    Args:
        object_id: ID of the object

    Returns:
        int: Parent object ID, or 0 if no parent or object not found
    """

is_object_physics_connected

def is_object_physics_connected(object_id: int) -> bool:
    """
    Returns True if the object is active in the physics simulation.

    Args:
        object_id: ID of the object

    Returns:
        bool: True if physics-connected, False if disconnected or not found
    """

get_object_linear_velocity

def get_object_linear_velocity(object_id: int) -> Vec4:
    """
    Returns the linear velocity of an object's rigid body as Vec4 (w=0).
    Returns zero Vec4 if the object is not found or has no rigid body.

    Args:
        object_id: ID of the object

    Returns:
        Vec4: Linear velocity in world space, w=0
    """

set_object_linear_velocity

def set_object_linear_velocity(object_id: int, velocity: Vec4) -> bool:
    """
    Set the linear velocity of an object's rigid body. Wakes the body if sleeping.

    Args:
        object_id: ID of the object
        velocity: Desired velocity in world space (w is ignored)

    Returns:
        bool: True if the object was found and velocity set
    """

get_object_mass

def get_object_mass(object_id: int) -> float:
    """
    Returns the mass of an object in kilograms. Returns 0.0 for static objects or if not found.

    Args:
        object_id: ID of the object

    Returns:
        float: Mass in kg, or 0.0 for static objects / not found
    """

set_object_mass

def set_object_mass(object_id: int, mass: float) -> bool:
    """
    Change a model's mass, switching it between static and dynamic physics at runtime.
    A mass of 0 makes the object static (exact triangle-mesh collider); a mass greater
    than 0 makes it a dynamic rigid body (simplified convex-hull collider). The collision
    shape is reloaded and the body re-registered with the physics world accordingly.

    Has no effect on animated models: they are kinematic and always use the convex-hull
    collider regardless of mass.

    Args:
        object_id: ID of the object
        mass: New mass in kilograms. 0 = static, > 0 = dynamic

    Returns:
        bool: True on success, False if the object is not found or is not a model
    """

disconnect_object_from_physics

def disconnect_object_from_physics(object_id: int) -> bool:
    """
    Disable physics for an object.

    Args:
        object_id: ID of the object

    Returns:
        bool: True if the object was found and disconnected
    """

reconnect_object_to_physics

def reconnect_object_to_physics(object_id: int) -> bool:
    """
    Re-enable physics for an object.

    Args:
        object_id: ID of the object

    Returns:
        bool: True if the object was found and reconnected
    """

apply_force

def apply_force(object_id: int, force_position: Vec4, force_amount: Vec4) -> bool:
    """
    Apply force to an object.

    Args:
        object_id: ID of the object
        force_position: Position where force is applied
        force_amount: Force vector and magnitude

    Returns:
        bool: True if force was applied successfully
    """

apply_force_to_player

def apply_force_to_player(force_amount: Vec4) -> bool:
    """
    Apply force to the player.

    Args:
        force_amount: Force vector and magnitude

    Returns:
        bool: True if force was applied successfully
    """

add_object_translate

def add_object_translate(object_id: int, translation: Vec4) -> bool:
    """
    Add to an object's position.

    Args:
        object_id: ID of the object
        translation: Translation vector to add

    Returns:
        bool: True if translation was applied successfully
    """

add_object_scale

def add_object_scale(object_id: int, scale: Vec4) -> bool:
    """
    Add to an object's scale.

    Args:
        object_id: ID of the object
        scale: Scale vector to add

    Returns:
        bool: True if scale was applied successfully
    """

add_object_orientation

def add_object_orientation(object_id: int, orientation: Vec4) -> bool:
    """
    Add to an object's orientation (quaternion).

    Args:
        object_id: ID of the object
        orientation: Quaternion orientation to add

    Returns:
        bool: True if orientation was applied successfully
    """

get_object_transformation

def get_object_transformation(object_id: int) -> list:
    """
    Get an object's transformation as three Vec4 parameters.

    Args:
        object_id: ID of the object

    Returns:
        list: [translate (Vec4), scale (Vec4), orientation quaternion (Vec4 x,y,z,w)],
              or empty list if object not found.

    Note:
        Prefer get_object_transformation_matrix() when you need the final world matrix.
        Physical objects can define collision-shape offsets that are baked into the matrix
        but are not reflected in the decomposed TRS values returned here. Use this method
        only when you specifically need the individual translate/scale/orientation components.
    """

set_object_translate

def set_object_translate(object_id: int, position: Vec4) -> bool:
    """
    Set an object's world position.

    Args:
        object_id: ID of the object
        position: New world position (w component ignored)

    Returns:
        bool: True if successful, False if object not found
    """

set_object_scale

def set_object_scale(object_id: int, scale: Vec4) -> bool:
    """
    Set an object's scale.

    Args:
        object_id: ID of the object
        scale: New scale (w component ignored)

    Returns:
        bool: True if successful, False if object not found
    """

set_object_orientation

def set_object_orientation(object_id: int, orientation: Vec4) -> bool:
    """
    Set an object's orientation as a quaternion.

    Args:
        object_id: ID of the object
        orientation: Quaternion as Vec4 (x, y, z, w)

    Returns:
        bool: True if successful, False if object not found
    """

get_object_transformation_matrix

def get_object_transformation_matrix(object_id: int) -> list:
    """
    Get an object's transformation matrix.

    Args:
        object_id: ID of the object

    Returns:
        list: List of GenericParameter objects containing the 4x4 transformation matrix
    """

get_model_children

def get_model_children(model_id: int) -> list:
    """
    Get children of a model.

    Args:
        model_id: ID of the model

    Returns:
        list: List of child object IDs
    """

set_model_animation

def set_model_animation(model_id: int, animation_name: str, looped: bool = True) -> bool:
    """
    Set an animation for a model.

    Args:
        model_id: ID of the model
        animation_name: Name of the animation to play
        looped: Whether the animation should loop

    Returns:
        bool: True if the model was found and animation applied
    """

set_model_animation_with_blend

def set_model_animation_with_blend(model_id: int, animation_name: str, looped: bool = True, blend_time: int = 100) -> bool:
    """
    Set an animation for a model with blending.

    Args:
        model_id: ID of the model
        animation_name: Name of the animation to play
        looped: Whether the animation should loop
        blend_time: Time in milliseconds to blend from the previous animation

    Returns:
        bool: True if the model was found and animation applied
    """

set_model_animation_speed

def set_model_animation_speed(model_id: int, speed: float) -> bool:
    """
    Set the animation speed for a model.

    Args:
        model_id: ID of the model
        speed: Animation speed multiplier (1.0 = normal speed)

    Returns:
        bool: True if the model was found and speed applied
    """

get_model_animation_name

def get_model_animation_name(model_id: int) -> str:
    """
    Get the name of the current animation for a model.

    Args:
        model_id: ID of the model

    Returns:
        str: Name of the current animation
    """

get_model_animation_finished

def get_model_animation_finished(model_id: int) -> bool:
    """
    Check if model's animation has finished.

    Args:
        model_id: ID of the model

    Returns:
        bool: True if animation has finished
    """

get_model_animation_progress

def get_model_animation_progress(model_id: int) -> float:
    """
    Returns the normalized progress [0.0, 1.0] of the custom animation currently
    running on the model (started via animate_model). For looped animations the
    value wraps back to 0.0 at the end of each cycle.

    Args:
        model_id: ID of the model

    Returns:
        float: Progress in [0.0, 1.0], or 0.0 if no active custom animation
    """

list_model_animations

def list_model_animations(model_id: int) -> list:
    """
    Returns the names of all animations embedded in the model's asset file.
    These names can be passed to set_model_animation.

    Args:
        model_id: ID of the model

    Returns:
        list[str]: Animation names, or an empty list if the model is not found
    """

animate_model

def animate_model(model_id: int, animation_id: int, looped: bool = False, sound_path: str = "") -> int:
    """
    [DEPRECATED] Animate a model with a specific animation ID (numeric, not by name).

    The animation_id is a positional index into the world's loaded animation
    list and depends on load order. Prefer animate_model_by_name to address
    animations by their stable name instead. This function is kept only as a
    fallback.

    Args:
        model_id: ID of the model
        animation_id: Index of the animation in the world's loaded animation list
        looped: Whether the animation should loop
        sound_path: Path to a sound file to play with the animation. Pass "" for no sound.

    Returns:
        int: Animation status ID, or 0 on failure
    """

animate_model_by_name

def animate_model_by_name(model_id: int, animation_name: str, looped: bool = False, sound_path: str = "") -> int:
    """
    Animate a model using the loaded animation's name instead of its index.

    This is the recommended way to start a custom animation, since names are
    stable while indices depend on load order. Use list_loaded_animations to
    discover the available names.

    Args:
        model_id: ID of the model
        animation_name: Name of the loaded custom animation to apply
        looped: Whether the animation should loop
        sound_path: Path to a sound file to play with the animation. Pass "" for no sound.

    Returns:
        int: The model ID on success, or 0 if no loaded animation matches the name
    """

list_loaded_animations

def list_loaded_animations() -> list[str]:
    """
    Return the names of all custom (transformation) animations loaded in the world.

    These names can be passed to animate_model_by_name. The index of a name in
    the returned list is the same value accepted by animate_model.

    Returns:
        list[str]: Loaded custom animation names
    """

add_object

def add_object(model_file_path: str, model_weight: float = 1.0, physical: bool = True,
               position: tuple = (0, 0, 0), scale: tuple = (1, 1, 1),
               orientation: tuple = (1.0, 0.0, 0.0, 0.0)) -> int:
    """
    Add a new object to the scene.

    Args:
        model_file_path: Path to the model file
        model_weight: Physics mass of the object
        physical: Whether the object participates in physics simulation
        position: Initial position as (x, y, z)
        scale: Initial scale as (x, y, z)
        orientation: Initial orientation as quaternion (w, x, y, z)

    Returns:
        int: ID of the created object, or 0 on failure
    """

remove_object

def remove_object(object_id: int, remove_children: bool = True) -> bool:
    """
    Remove an object from the scene.

    Args:
        object_id: ID of the object to remove
        remove_children: Whether to also remove child objects

    Returns:
        bool: True if the object was found and removed
    """

get_result_of_trigger

def get_result_of_trigger(trigger_object_id: int, trigger_code_id: int) -> list:
    """
    Get the result parameters of a trigger.

    Args:
        trigger_object_id: ID of the trigger object
        trigger_code_id: Code ID selecting which result set to retrieve

    Returns:
        list: List of GenericParameter objects with the trigger's results
    """

Sound

attach_sound_to_object_and_play

Warning

Removed. Sound is now a first-class game object with full attachment support. Use play_sound to create the sound and obtain its ID, then use attach_object_to_object to attach it to the target model. Stop and remove it with stop_sound.

# Before (no longer available)
limon.attach_sound_to_object_and_play(model_id, "sound.wav", looped=True)

# After
sound_id = limon.play_sound("sound.wav", Vec4(0, 0, 0, 1), looped=True)
limon.attach_object_to_object(sound_id, model_id)

detach_sound_from_object

Warning

Removed. Now that attach_sound_to_object_and_play has been replaced with the play_sound + attach_object_to_object flow, the sound ID is available and stop_sound serves as the direct replacement.

play_sound

def play_sound(sound_path: str, position: Vec4, position_relative: bool = False,
               looped: bool = False, reference_distance: float = 2.0,
               max_distance: float = 50.0,
               channel: limon.AudioChannel = limon.AudioChannel.SFX) -> int:
    """
    Play a non-attached sound at a world position.

    Args:
        sound_path: Path to the sound file
        position: World-space position to play the sound at
        position_relative: If True, position is relative to the listener. Defaults to False.
        looped: Whether the sound should loop. Defaults to False.
        reference_distance: Distance at which the sound plays at full volume.
            Below this distance gain is clamped to maximum. Defaults to 2.0.
        max_distance: Distance at which the sound is fully attenuated and inaudible.
            Defaults to 50.0.
        channel: Audio channel (bus) to route the sound to. Defaults to
            limon.AudioChannel.SFX. Valid values: SFX, SPEECH, AMBIENT. Passing
            MASTER or MUSIC returns 0 — MASTER is a global volume multiplier, not
            an assignable channel; MUSIC is controlled exclusively through set_music /
            stop_music. See Audio Channels below for how channel volumes affect the
            effective gain.

    Returns:
        int: Sound ID for use with stop_sound, pause_sound, etc., or 0 on failure

    Note:
        Only mono audio files are spatialized. Stereo files play at a fixed volume.
    """

stop_sound

def stop_sound(sound_id: int) -> bool:
    """
    Stop a playing sound and remove it from the world.

    Args:
        sound_id: Sound ID returned by play_sound

    Returns:
        bool: True if the sound was found and stopped
    """

pause_sound

def pause_sound(sound_id: int) -> bool:
    """
    Pause a playing sound without stopping it.
    The sound can be resumed with resume_sound.

    Args:
        sound_id: Sound ID returned by play_sound

    Returns:
        bool: True if the sound was found and paused
    """

resume_sound

def resume_sound(sound_id: int) -> bool:
    """
    Resume a paused sound from where it left off.

    Args:
        sound_id: Sound ID returned by play_sound

    Returns:
        bool: True if the sound was found and resumed
    """

set_sound_volume

def set_sound_volume(sound_id: int, volume: float) -> bool:
    """
    Set the per-sound volume (gain).

    Args:
        sound_id: Sound ID returned by play_sound
        volume: New gain, normalized 0.0..1.0 (1.0 = full volume). This is the
            per-sound gain; the effective output is this value multiplied by the
            sound's channel volume and the master volume (see Audio Channels below).

    Returns:
        bool: True if the sound was found and volume updated. False if not found or sound not yet started
    """

set_sound_looped

def set_sound_looped(sound_id: int, looped: bool) -> bool:
    """
    Change whether a sound loops after the current play-through.
    Useful for transitioning a looping sound into a one-shot that
    stops after the current cycle completes.

    Args:
        sound_id: Sound ID returned by play_sound
        looped: True to loop, False to stop after the current play-through

    Returns:
        bool: True if the sound was found and updated
    """

is_sound_playing

def is_sound_playing(sound_id: int) -> bool:
    """
    Returns True if the sound is currently playing.

    Args:
        sound_id: Sound ID returned by play_sound

    Returns:
        bool: True if playing, False if stopped or not found
    """

Music

Each level has a single dedicated music track that plays on the MUSIC audio channel, loops by default, and is positioned at the listener (2D). Previously music could only be set in the editor; it can now be switched at runtime, optionally crossfading between tracks.

set_music

def set_music(music_path: str, fade_seconds: float = 0.0, looped: bool = True) -> bool:
    """
    Set (switch) the level music track.

    With fade_seconds == 0 the current track stops immediately and the new one
    starts at full volume. With fade_seconds > 0 the outgoing track fades out
    while the new track fades in over the same duration (crossfade).

    Pass an empty music_path to clear the level music.

    Args:
        music_path: Path to the music asset, or "" to clear the music
        fade_seconds: Crossfade duration in seconds. 0 swaps instantly.
        looped: Whether the music loops. Defaults to True.

    Returns:
        bool: True on success
    """

stop_music

def stop_music(fade_seconds: float = 0.0) -> bool:
    """
    Stop the level music, optionally fading it out.

    Args:
        fade_seconds: Fade-out duration in seconds. 0 stops instantly.

    Returns:
        bool: True if music was playing, False if no music was set
    """

get_music_name

def get_music_name() -> str:
    """
    Get the current music asset path.

    Returns:
        str: The current music path, or an empty string if no music is set
    """

is_music_playing

def is_music_playing() -> bool:
    """
    Returns:
        bool: True if level music is currently playing
    """

Audio Channels

Every sound is mixed on one of five channels (buses). The effective output gain of a sound is per-sound gain × channel volume × master volume.

Channel

Option name

play_sound

Description

MASTER

soundVolumeMaster

invalid

Global volume multiplier applied to all channels. Not an assignable channel — passing it to play_sound returns 0.

MUSIC

soundVolumeMusic

invalid

Dedicated music channel. Managed exclusively by set_music / stop_music. Passing it to play_sound returns 0.

SFX

soundVolumeSFX

default

Sound effects. Default channel for sounds played via play_sound.

SPEECH

soundVolumeSpeech

valid

Speech and voice-over.

AMBIENT

soundVolumeAmbient

valid

Environmental / ambient sounds.

Channel volumes are not set through dedicated API calls — they are global, persisted options. Change them through the options API (get_options / save_options) using the option names in the table above (each normalized 0.0..1.0, default 1.0). The engine reads these options and applies any change to the audio mixer automatically. This keeps player audio settings in one place and persisted across levels and sessions.

AI Interaction

interact_with_ai

def interact_with_ai(ai_id: int, interaction_information: list) -> bool:
    """
    Send interaction data to an AI actor.

    Args:
        ai_id: ID of the AI actor
        interaction_information: List of GenericParameter objects with interaction data

    Returns:
        bool: True if the actor was found and interaction delivered
    """

interact_with_player

def interact_with_player(interaction_information: list) -> None:
    """
    Send interaction data to the active player extension.

    Args:
        interaction_information: List of GenericParameter objects with interaction data
    """

Particle Systems

disable_particle_emitter

def disable_particle_emitter(emitter_id: int) -> bool:
    """
    Disable a particle emitter, stopping new particle emission.

    Args:
        emitter_id: ID of the particle emitter

    Returns:
        bool: True if the emitter was found and disabled
    """

enable_particle_emitter

def enable_particle_emitter(emitter_id: int) -> bool:
    """
    Enable a particle emitter, resuming particle emission.

    Args:
        emitter_id: ID of the particle emitter

    Returns:
        bool: True if the emitter was found and enabled
    """

add_particle_emitter

def add_particle_emitter(name: str, texture_file: str, start_position: Vec4,
                         max_start_distances: Vec4, size: Vec2, count: int,
                         life_time: int, particles_per_ms: float,
                         continuously_emit: bool) -> int:
    """
    Add a new particle emitter to the scene.

    Args:
        name: Name of the emitter
        texture_file: Path to the particle texture file
        start_position: World-space origin of the emitter
        max_start_distances: Maximum random spawn offsets per axis (Vec4)
        size: Width and height of each particle billboard (Vec2)
        count: Maximum number of live particles
        life_time: Lifetime of each particle in milliseconds
        particles_per_ms: Emission rate in particles per millisecond
        continuously_emit: If True, emitter loops indefinitely

    Returns:
        int: Emitter ID, or 0 on failure
    """

remove_particle_emitter

def remove_particle_emitter(emitter_id: int) -> bool:
    """
    Remove a particle emitter from the scene.

    Args:
        emitter_id: ID of the particle emitter

    Returns:
        bool: True if the emitter was found and removed
    """

set_emitter_particle_speed

def set_emitter_particle_speed(emitter_id: int, speed_multiplier: Vec4, speed_offset: Vec4) -> bool:
    """
    Set the speed parameters for particles emitted by an emitter.

    Args:
        emitter_id: ID of the particle emitter
        speed_multiplier: Per-axis speed multiplier (Vec4)
        speed_offset: Per-axis constant speed offset (Vec4)

    Returns:
        bool: True if the emitter was found and updated
    """

set_emitter_particle_gravity

def set_emitter_particle_gravity(emitter_id: int, gravity: Vec4) -> bool:
    """
    Set the per-axis gravity applied to particles from an emitter.

    Args:
        emitter_id: ID of the particle emitter
        gravity: Per-axis gravity vector (Vec4)

    Returns:
        bool: True if the emitter was found and updated
    """

Ray Casting

ray_cast_to_cursor

def ray_cast_to_cursor() -> list:
    """
    Cast a ray from camera to cursor position.

    Returns:
        list: List of GenericParameter objects containing hit details such as:
              - hit coordinates (VEC4)
              - hit object ID (LONG)
              - hit normal vector (VEC4)
              - distance to hit (DOUBLE)
    """

ray_cast_first_hit

def ray_cast_first_hit(start: Vec4, direction: Vec4) -> list:
    """
    Cast a ray from start point in direction and return the first hit.

    Args:
        start: Starting position as Vec4 (w component ignored)
        direction: Direction vector as Vec4 (w component ignored, need not be normalized)

    Returns:
        list: List of GenericParameter objects containing hit details:
              - hit coordinates (VEC4)
              - hit object ID (LONG)
              - hit normal vector (VEC4)
              - distance to hit (DOUBLE)
              Empty list if nothing was hit.
    """

Lighting

add_light

def add_light(light_type: int, position: Vec4, color: Vec4) -> int:
    """
    Add a new light to the scene.

    Args:
        light_type: Light type - 1 for directional, 2 for point
        position: World-space position of the light
        color: RGB color of the light (Vec4; w is ignored)

    Returns:
        int: Light ID, or 0 on failure
    """

remove_light

def remove_light(light_id: int) -> bool:
    """
    Remove a light from the scene.

    Args:
        light_id: Handle ID of the light

    Returns:
        bool: True if the light was found and removed
    """

add_light_translate

def add_light_translate(light_id: int, translation: Vec4) -> bool:
    """
    Translate a light by adding to its current position.

    Args:
        light_id: Handle ID of the light
        translation: Translation to add (w component ignored)

    Returns:
        bool: True if the light was found and moved
    """

set_light_color

def set_light_color(light_id: int, color: Vec4) -> bool:
    """
    Set a light's color.

    Args:
        light_id: Handle ID of the light
        color: RGB color with components in [0, 1] (w component ignored)

    Returns:
        bool: True if the light was found and updated
    """

get_light_position

def get_light_position(light_id: int) -> Vec4:
    """
    Returns the world-space position of a light as Vec4 (w=1).

    Args:
        light_id: Handle ID of the light

    Returns:
        Vec4: World-space position with w=1, or zero Vec4 if not found
    """

get_light_color

def get_light_color(light_id: int) -> Vec4:
    """
    Returns the color of a light as Vec4 (w=1).

    Args:
        light_id: Handle ID of the light

    Returns:
        Vec4: RGB color with w=1, or zero Vec4 if not found
    """

set_light_translate

def set_light_translate(light_id: int, position: Vec4) -> bool:
    """
    Set the absolute world-space position of a light. Unlike add_light_translate,
    this replaces the current position rather than adding to it.

    Args:
        light_id: Handle ID of the light
        position: Target world-space position (w component ignored)

    Returns:
        bool: True if the light was found and updated
    """

World Management

load_and_switch_world

def load_and_switch_world(world_file_name: str) -> bool:
    """
    Load a world and switch to it. If already loaded, resets it.

    Args:
        world_file_name: File name of the world to load

    Returns:
        bool: False if load fails or if the caller is part of the current world
              (a world cannot remove itself)
    """

return_to_world

def return_to_world(world_file_name: str) -> bool:
    """
    Load a world if not already loaded, then switch to it.

    Args:
        world_file_name: File name of the world to switch to

    Returns:
        bool: False if load fails
    """

load_and_remove

def load_and_remove(world_file_name: str) -> bool:
    """
    Load a new world and remove the current one.

    Args:
        world_file_name: File name of the world to load

    Returns:
        bool: False if load fails
    """

return_previous_world

def return_previous_world() -> None:
    """
    Return to the previously loaded world. No-op if no previous world exists.
    """

quit_game

def quit_game() -> None:
    """
    Quit the game.
    """

change_render_pipeline

def change_render_pipeline(pipeline_file_name: str) -> bool:
    """
    Change the active render pipeline.

    Args:
        pipeline_file_name: File name of the pipeline to load

    Returns:
        bool: False if the pipeline file could not be loaded
    """

get_options

def get_options() -> any:
    """
    Get the engine options object.

    Options are loaded from ./Engine/Options.xml (engine defaults) first,
    then ./Data/Options.xml (user options) is loaded on top, overriding any
    matching engine defaults. The user options file is optional.

    Returns:
        Options object with current engine configuration
    """

save_options

def save_options() -> bool:
    """
    Save current options to the user options file (./Data/Options.xml).

    Any changes made to the options at runtime are persisted, and will
    override the engine defaults on the next startup.

    Returns:
        bool: True on success, False otherwise
    """

Timed Events

add_timed_event

def add_timed_event(wait_time: int, use_wall_time: bool, callback: callable,
                  parameters: list = None) -> int:
    """
    Schedule a function to be called after a specified delay.

    Args:
        wait_time: Time to wait before triggering the event, in milliseconds
        use_wall_time: If True, uses real wall-clock time and is unaffected by
                     game pause or world transitions. If False, uses in-game time
                     which stops when the game is paused or a menu world is loaded.
        callback: Python function to call when the timer expires.
                 The function should accept a single parameter which will be the
                 list of parameters provided.
        parameters: Optional list of GenericParameter objects to pass to the callback.
                   Pass None or omit to use no parameters.

    Returns:
        int: ID of the created timer event, which can be used to cancel it.
    """

cancel_timed_event

def cancel_timed_event(timer_id: int) -> bool:
    """
    Cancel a previously scheduled timed event.

    Args:
        timer_id: ID of the timer event to cancel (returned by add_timed_event)

    Returns:
        bool: True if the event was successfully cancelled, False if not found
    """

Profiling

profile_scope

def profile_scope(name: str):
    """
    Open a named Tracy profiler zone. Returns a context manager; the zone
    closes when the ``with`` block exits.

    Args:
        name: Label shown in the Tracy profiler timeline.

    Example:
        with api.profile_scope("MyActor::play"):
            # ... work being profiled ...
            pass
    """

Variable Management

Note

Variables are not saved with the world. They exist only for the lifetime of the current world session and are lost on world reload or world switch. Use them for transient in-session state only.

Note

Any parameter passed to the editor must have is_set = True. The editor will refuse to save a trigger if any parameter is not set.

get_variable

def get_variable(variable_name: str) -> GenericParameter:
    """
    Get (or create) a script variable by name. Returns a copy of the current value.
    Use set_variable to write a new value back.

    Args:
        variable_name: Name of the variable

    Returns:
        GenericParameter: Current value of the variable (0-initialized if never set)
    """

set_variable

def set_variable(variable_name: str, value: GenericParameter) -> None:
    """
    Write a value to the named variable in the global variable store.

    Unlike the C++ API (where getVariable returns a mutable reference that acts as the
    setter), the Python binding provides separate get_variable and set_variable methods.

    Args:
        variable_name: Name of the variable to set
        value: GenericParameter value to store

    Note:
        Variables are not saved with the world. They exist only for the lifetime of the
        current world session and are reset on every world load or world switch.
    """

Logging

log

def log(subsystem: limon.LogSubsystem, level: limon.LogLevel, text: str) -> None:
    """
    Write a message to the engine log.

    Args:
        subsystem: Which subsystem the message belongs to (limon.LogSubsystem.*)
        level:     Severity of the message (limon.LogLevel.*)
        text:      The message text

    Example:
        api.log(limon.LogSubsystem.AI,    limon.LogLevel.INFO,  "actor reached waypoint")
        api.log(limon.LogSubsystem.INPUT, limon.LogLevel.WARN,  "unexpected input state")
    """

Debug Line Drawing

Lines are stored in numbered buffers managed by the engine. Each buffer persists across frames until explicitly removed. The typical per-frame pattern is clear → draw.

draw_debug_line

def draw_debug_line(from_pos: Vec4, to_pos: Vec4,
                    from_color: Vec4, to_color: Vec4,
                    require_camera_transform: bool = True) -> int:
    """
    Create a new debug line buffer containing one line segment.

    Args:
        from_pos:                 World-space start point (w ignored)
        to_pos:                   World-space end point (w ignored)
        from_color:               RGB color at the start vertex (w ignored)
        to_color:                 RGB color at the end vertex (w ignored)
        require_camera_transform: True for 3D world-space lines; False for 2D screen-space

    Returns:
        int: Buffer ID - pass to add_to_debug_line() or clear_debug_lines()
    """

add_to_debug_line

def add_to_debug_line(buffer_id: int,
                      from_pos: Vec4, to_pos: Vec4,
                      from_color: Vec4, to_color: Vec4,
                      require_camera_transform: bool = True) -> bool:
    """
    Append a line segment to an existing debug line buffer.

    Args:
        buffer_id:                ID returned by draw_debug_line()
        from_pos:                 World-space start point (w ignored)
        to_pos:                   World-space end point (w ignored)
        from_color:               RGB color at the start vertex (w ignored)
        to_color:                 RGB color at the end vertex (w ignored)
        require_camera_transform: Should match the value used when the buffer was created

    Returns:
        bool: True on success; False if buffer_id is not found
    """

clear_debug_lines

def clear_debug_lines(buffer_id: int) -> bool:
    """
    Remove a debug line buffer, hiding all its segments immediately.

    Args:
        buffer_id: ID returned by draw_debug_line()

    Returns:
        bool: True on success; False if buffer_id is not found

    Example (per-frame pattern inside an actor's play() or camera's get_camera_variables()):
        if self._line_buf != 0:
            api.clear_debug_lines(self._line_buf)
        self._line_buf = api.draw_debug_line(
            limon.Vec4(x1, y1, z1, 1), limon.Vec4(x2, y2, z2, 1),
            limon.Vec4(1, 0, 0, 1),    limon.Vec4(1, 0, 0, 1))
    """

Input System

Input Methods

simulate_input

def simulate_input(input_states: InputStates) -> None:
    """
    Simulate input events.

    Args:
        input_states: InputStates object with the events to inject
    """

get_mouse_change

def get_mouse_change() -> tuple:
    """
    Get mouse change information.

    Returns:
        tuple: (changed, x_pos, y_pos, x_change, y_change)
    """

set_mouse_change

def set_mouse_change(x_pos: float, y_pos: float, x_change: float, y_change: float) -> None:
    """
    Set mouse change on an InputStates object.

    Args:
        x_pos: Absolute X position
        y_pos: Absolute Y position
        x_change: Delta X since last frame
        y_change: Delta Y since last frame
    """

get_text

def get_text() -> str:
    """
    Get the current text input from an InputStates object.

    Returns:
        str: Current text input string
    """

set_text

def set_text(text: str) -> None:
    """
    Set text input on an InputStates object.

    Args:
        text: Text to inject
    """

reset_all_events

def reset_all_events() -> None:
    """
    Reset all input events on an InputStates object.
    """

get_input_events

def get_input_events(input_code: int) -> bool:
    """
    Get input events for a specific input code.

    Args:
        input_code: Input code to query (see Inputs class)

    Returns:
        bool: True if the input is currently active
    """

Input States

class Inputs:
    QUIT = 0
    MOUSE_MOVE = 1
    MOUSE_BUTTON_LEFT = 2
    MOUSE_BUTTON_MIDDLE = 3
    MOUSE_BUTTON_RIGHT = 4
    MOUSE_WHEEL_UP = 5
    MOUSE_WHEEL_DOWN = 6
    MOVE_FORWARD = 7
    MOVE_BACKWARD = 8
    MOVE_LEFT = 9
    MOVE_RIGHT = 10
    JUMP = 11
    RUN = 12
    DEBUG = 13
    EDITOR = 14
    KEY_SHIFT = 15
    KEY_CTRL = 16
    KEY_ALT = 17
    KEY_SUPER = 18
    TEXT_INPUT = 19
    NUMBER_1 = 20
    NUMBER_2 = 21
    F4 = 22

# Example usage:
if input_states.get_input_status(limon.Inputs.MOVE_FORWARD):
    # Move forward
    pass

Camera System

CameraExtensionInterface

Base class for a registered camera attachment (see How to implement a camera attachment). Subclass it and the engine auto-discovers it by class name, like Triggers and Actors; the engine wraps it in a CameraRig scene object that appears in the editor’s Cameras tree, is configurable, and may be perspective or orthographic.

import limon
from camera_extension_interface import CameraExtensionInterface

class MyCameraRig(CameraExtensionInterface):
    def __init__(self, limon_api):
        super().__init__(limon_api)

    def get_name(self) -> str:
        return "MyCameraRig"

    def is_dirty(self) -> bool:
        return self._dirty

    def clear_dirty(self) -> None:
        self._dirty = False

    def get_camera_variables(self, position: Vec3, center: Vec3, up: Vec3, right: Vec3) -> None:
        """
        Fill the camera pose in place. The four Vec3 arguments are modified directly:
        position, center (look-at direction), up, and right.
        """

    def get_projection(self) -> "limon.ProjectionParameters":
        # Default perspective; set type = limon.ProjectionType.ORTHOGRAPHIC for orthographic.
        return limon.ProjectionParameters()

Trigger Interface

Base class for creating custom triggers.

from trigger_interface import TriggerInterface

class MyTrigger(TriggerInterface):
    def __init__(self, limon_api):
        super().__init__(limon_api)
        self._limon_api = limon_api

    def get_parameters(self) -> list:
        """
        Return a list of parameters this trigger requires.

        Returns:
            list: List of GenericParameter objects
        """
        from generic_parameter import RequestParameterType, ValueType, GenericParameter
        param = GenericParameter()
        param.request_type = RequestParameterType.FREE_TEXT
        param.description = "Message to display"
        param.value = "Hello from Python!"
        return [param]

    def run(self, parameters: list) -> bool:
        """
        Called when the trigger is activated.

        Args:
            parameters: List of GenericParameter objects with user-provided values

        Returns:
            bool: True if successful, False otherwise
        """
        if parameters and parameters[0].is_set:
            message = parameters[0].value
            print(f"Trigger activated: {message}")
        return True

    def get_results(self) -> list:
        """
        Return any results from the trigger execution.

        Returns:
            list: List of GenericParameter objects with results
        """
        return []

    def get_name(self) -> str:
        """
        Get the name of this trigger.

        Returns:
            str: The trigger's name
        """
        return "My Custom Trigger"

Player Extension Interface

Base class for creating player extensions.

from player_extension_interface import PlayerExtensionInterface

class MyPlayerExtension(PlayerExtensionInterface):
    def __init__(self, limon_api):
        super().__init__(limon_api)
        self._limon_api = limon_api

    def process_input(self, input_states, player_info, time):
        """
        Process input for the player.

        Args:
            input_states: InputStates object containing current input state
            player_info: PlayerInformation object with player state
            time: Current game time
        """
        if input_states.get_input_status(limon.Inputs.KEY_SPACE):
            print("Jump!")

    def interact(self, interaction_data):
        """
        Handle interaction with the player.

        Args:
            interaction_data: List of GenericParameter objects with interaction data
        """
        print(f"Player interaction: {interaction_data}")

    def get_parameters(self) -> list:
        """
        Return the configurable parameters of this extension.

        Player extensions follow the same unified parameter contract as
        Actors: the returned list - each entry carrying both its descriptor
        and its value - is rendered by the editor and the values are
        persisted with the map. Return an empty list for no configuration.

        Returns:
            list: List of GenericParameter objects
        """
        from generic_parameter import RequestParameterType, ValueType, GenericParameter
        param = GenericParameter()
        param.request_type = RequestParameterType.FREE_NUMBER
        param.description = "Starting ammo"
        param.value = 30
        return [param]

    def set_parameters(self, parameters):
        """
        Store the configured parameter values on this extension.

        Called when the map designer edits values and when a map is loaded.

        Args:
            parameters: List of GenericParameter objects to set
        """
        if parameters and parameters[0].is_set:
            self._starting_ammo = parameters[0].value

    def get_name(self) -> str:
        """
        Get the name of this extension.

        Returns:
            str: The extension's name
        """
        return "My Player Extension"

Actor Interface

Base class for creating AI actors.

from actor_interface import ActorInterface

class MyActor(ActorInterface):
    def __init__(self, limon_api):
        super().__init__(limon_api)
        self._limon_api = limon_api

    def get_name(self) -> str:
        """
        Get the name of this actor.

        Returns:
            str: The actor's name
        """
        return "My Custom Actor"

    def play(self, time: int, actor_information):
        """
        Called each frame to update the actor's behavior.

        Args:
            time: Current game time
            actor_information: ActorInformation object with environment state
        """
        if actor_information.can_see_player_directly:
            print("Player spotted!")
            # React to player

    def interaction(self, interaction_data):
        """
        Handle interaction with this actor.

        Args:
            interaction_data: List of GenericParameter objects with interaction data

        Returns:
            bool: True if interaction was successful
        """
        print(f"Actor interaction: {interaction_data}")
        return True

    def get_parameters(self):
        """
        Get the current parameters of this actor.

        Returns:
            list: List of GenericParameter objects
        """
        from generic_parameter import RequestParameterType, ValueType, GenericParameter
        param = GenericParameter()
        param.request_type = RequestParameterType.FREE_TEXT
        param.description = "Actor behavior"
        param.value = "friendly"
        return [param]

    def set_parameters(self, parameters):
        """
        Set the parameters of this actor.

        Args:
            parameters: List of GenericParameter objects to set
        """
        if parameters and parameters[0].is_set:
            behavior = parameters[0].value
            print(f"Setting actor behavior to: {behavior}")

ActorInformation

Contains information about the actor’s environment and player state, passed to the play() method of ActorInterface.

Player Visibility and Position Detection

can_see_player_directlybool

True if the actor has direct line of sight to the player.

is_player_leftbool

True if the player is to the left of the actor.

is_player_rightbool

True if the player is to the right of the actor.

is_player_upbool

True if the player is above the actor.

is_player_downbool

True if the player is below the actor.

is_player_frontbool

True if the player is in front of the actor.

is_player_backbool

True if the player is behind the actor.

Player Relationship and Spatial Information

cosine_between_playerfloat

Cosine of the angle between actor’s forward vector and direction to player. Range: -1.0 (opposite direction) to 1.0 (same direction).

player_directiontuple

Vector from actor to player position as (x, y, z).

player_distancefloat

Distance from actor to player.

cosine_between_player_for_sidefloat

Cosine of the angle for side detection (left/right).

player_deadbool

True if the player is dead.

Pathfinding Information

route_to_requestlist

List of Vec3 waypoints for pathfinding route to requested destination.

maximum_route_distanceint

Maximum route distance in node count (default: 128).

route_foundbool

True if a valid route was found.

route_readybool

True if the route data is ready for use.

Usage Example

class MyActor(ActorInterface):  # from actor_interface import ActorInterface
    def play(self, time: int, actor_information):
        # Check if player is visible
        if actor_information.can_see_player_directly:
            print(f"Player spotted at distance {actor_information.player_distance}")

            # Check player position relative to actor
            if actor_information.is_player_front:
                print("Player is in front")
            elif actor_information.is_player_back:
                print("Player is behind")

            # Use cosine values for precise angle calculations
            if abs(actor_information.cosine_between_player) > 0.9:
                print("Player is directly facing or opposite to actor")

        # Check pathfinding status
        if actor_information.route_ready and actor_information.route_found:
            print(f"Route found with {len(actor_information.route_to_request)} waypoints")

        # Check if player is dead
        if actor_information.player_dead:
            print("Player is dead")

Utility Functions

# Create a new Vec4
vec = limon.create_vec4(x=1.0, y=2.0, z=3.0, w=4.0)

# Register custom trigger, extension and actor types
limon.register_trigger_type(name: str, factory_function)
limon.register_extension_type(name: str, factory_function)
limon.register_actor_type(name: str, factory_function)

# Get available trigger, extension and actor names
trigger_names = limon.get_trigger_names()
extension_names = limon.get_extension_names()
actor_names = limon.get_actor_names()

# Create player extension and actor dynamically
extension = limon.PlayerExtensionInterface.create_extension(name: str, limon_api)
actor = limon.ActorInterface.create_actor(name: str, id: int, limon_api)

Best Practices

  1. Error Handling: Always wrap API calls in try-except blocks.

  2. Resource Management: Clean up resources when they’re no longer needed.

  3. Performance: Cache frequently accessed values and avoid expensive operations in tight loops.

  4. Thread Safety: The Python API is not thread-safe. Call API methods from the main thread.

  5. Memory Management: Be careful with object references to prevent memory leaks.

  6. Error Reporting: Provide meaningful error messages for debugging.

  7. Compatibility: Test your scripts with different versions of the engine.

  8. Documentation: Document your code and provide usage examples.