← developer tools

texastoast

Python RPG engine with I2C hardware abstraction for magmacrunch game systems

v0.5.0 Apache-2.0 Python 3.10+

Playground

Run the real engine in your browser under Pyodide. Four examples, playable with the keyboard, no install required.

📖

API Reference

Game, entities, tile maps, camera, input, and the UI widgets, plus the magmascript domain table.

features

Game Loop Frame-rate independent, dt-clamped, gives up on a broken frame instead of spinning
Tile Maps JSON save/load, per-tile solidity, sub-stepped AABB collision that slides along walls
Scenes Push a pause scene and the world freezes by construction, no paused flag, no dispatch chain
Camera Smooth follow, clamped to map bounds, framerate-independent smoothing
Entities Pixels-per-second speed with normalized diagonals, plus a group that drives updates and sorts by feet line
UI Widgets Typewriter dialogue, menus, HUD stat bars, all frame-driven, all themeable from one object
Input Abstraction NES-style buttons behind one InputState snapshot; keyboard or hardware
Player Seats Join by button press, hotplug handling, and a returning controller reclaims its own seat
Audio Degrades from a real mixer to the platform player to silence; a machine with no audio runs the same game
I2C Hardware Magma Hub controllers over I2C on Raspberry Pi, with keyboard fallback
Hardware Dev Kit A bus-level simulator, a live controller bench, and session record/replay, build for the hardware without owning it
magmascript Binding Registers as the tt domain, so .mgs scripts drive the engine directly
Tools A tkinter map editor ships with the repo; the controller bench installs as texastoast-bench

about

A tkinter-based 2D game engine by magmacrunch media, inspired by adenosine and built for the same kind of small RPGs, but in Python, and able to read real controllers over I2C when it runs on a Raspberry Pi. Game logic never depends on tkinter, and hardware degrades gracefully to the keyboard when it is absent.

It also ships a hardware dev kit: a bus-level simulator that runs the real I2C stack against imaginary controllers, a live test bench for bringing real ones up, and session record/replay that turns a captured session into a regression test. You can build for the Magma Hub before the Magma Hub exists.

install

pip:

pip install texastoast

optional extras: [sprites] for Pillow sprite sheets, [hardware] for I2C, [audio] for a real sound mixer

quick start

from texastoast import CanvasRenderer, Entity, Game, KeyboardInput, TileMap

game = Game(title="my game", width=320, height=240, fps=30)
renderer = CanvasRenderer(game.canvas, 320, 240)
keyboard = KeyboardInput(game.root)

tilemap = TileMap([[1, 1, 1], [1, 0, 1], [1, 1, 1]], tile_size=16, solid_tiles={1})
player = Entity(x=20, y=20, width=12, height=12, speed=100)

def update(dt):
    state = keyboard.poll()
    player.move(state.dx, state.dy, dt, tilemap)

def render():
    renderer.clear()
    renderer.draw_tilemap(tilemap, {0: "#7cb342", 1: "#5d4037"})
    renderer.draw_rect(player.x, player.y, player.width, player.height, "#e94560")

game.set_update(update)
game.set_render(render)
game.start()

scenes

Once a game has more than one screen, hand the loop to a scene stack. Pushing a scene freezes the ones below it, so a pause menu needs no paused flag and no early-return chain, the modality is the stack.

from texastoast import SceneStack

stack = SceneStack()

class PauseScene:
    render_below = True          # the frozen world stays visible underneath

    def on_enter(self):  menu.show(["Resume", "Quit"], on_cancel=stack.pop)
    def on_exit(self):   menu.hide()
    def update(self, dt): pass
    def render(self):    menu.render()

class WorldScene:
    def update(self, dt): ...
    def render(self):     ...
    def handle_key(self, event):
        if event.keysym == "Escape":
            stack.push(PauseScene())    # world freezes; no flag exists

stack.push(WorldScene())
game.set_update(stack.update)
game.set_render(stack.render)
game.bind_key("<Key>", stack.dispatch_key)

A scene is anything with update(dt) and render(), there is no base class to subclass.

magmascript binding

texastoast registers itself as the texastoast domain (aliased tt), so a .mgs script can drive the engine with no glue code. Needs magmascript 3.2+; neither package depends on the other.

g = tt.game({"title": "hello", "width": 400, "height": 300, "fps": 30})
r = tt.renderer(g, 400, 300)
kb = tt.keyboard(g)
world = tt.tilemap([[1,1,1,1,1],[1,0,0,0,1],[1,1,1,1,1]], 20, [1])
player = tt.entity({"x": 40, "y": 30, "width": 14, "height": 14, "speed": 100})

update = fn(dt) {
    s = kb.poll()
    player.move(s.dx, s.dy, dt, world)
    if s.a { player.speed = 220 } else { player.speed = 100 }
}

Apache-2.0 · Copyright 2026 magmacrunch media