texastoast

Python RPG engine with I2C hardware abstraction for magmacrunch game systems

v0.12.1 PolyForm Noncommercial 1.0.0 Python 3.10+

A tkinter-based 2D game engine by magmacrunch media, inspired by adenosine and built for the same kind of small RPGs, but in Python, with an input layer that can read controllers over I2C on a Raspberry Pi. Game logic never depends on tkinter, and hardware degrades gracefully to the keyboard when it's 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. It was written to build for the Magma Hub before the Magma Hub existed.

Status: texastoast is feature-complete and in maintenance. It is a working reference engine rather than the starting point for new magmacrunch games, which are built on the C engines such as magnolia and daffodil.

A note on the hub. The Magma Hub now exists, and its register map is not the one texastoast was written against: the real hub answers at 0x17 and reports each controller as a 4-byte word of buttons, stick angle and magnitude, where texastoast expects two bytes per controller at 0x08 to 0x0B. So its I2C input does not read the shipped hub yet, though the simulator, keyboard play and recordings all work as described. For quick experiments with the real hub, a one-file Python reader now ships with the hub firmware.

▶

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 Controllers over I2C on Raspberry Pi, with keyboard fallback; written to the pre-hardware protocol, not yet the shipped hub's
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

getting started

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's 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 }
}

PolyForm Noncommercial 1.0.0 · Copyright 2026 magmacrunch media. Apache-2.0 until 2026-10-15; versions published to PyPI before then stay available under it.

// source access //

texastoast installs from PyPI above, and the playground runs the real engine in your browser. The engine source is not public.

For source access, licensing, the hardware dev kit, or anything else, email info@magmacrunch.com.