Blog › ICP guides

BlitzMax developer on retainer: Type inheritance, Method override, game loop programming, and TList/TMap on monthly retainer

September 26, 2026 · ~15 min read

A BlitzMax game was being extended with a new sprite type. The developer created a EnemySprite Type that extended a base GameSprite Type and defined an Update Method intended to override the parent’s Update(speed:Float) Method. In BlitzMax, polymorphic Method dispatch is governed by the vtable, and the vtable is populated based on the exact match between the derived Type’s Method signature and the parent Type’s Method signature. The developer wrote Method Update(speed:Int) in EnemySprite — changing the parameter type from Float to Int — expecting BlitzMax to treat this as an override. BlitzMax’s object model requires that Method override signatures match exactly: parameter types and return type must be identical to the parent Method signature for the derived Method to be registered as an override in the vtable. The Update(speed:Int) Method in EnemySprite was treated as a new, independent Method on that Type rather than an override of the parent’s Update(speed:Float). When the game loop called sprite.Update(speed) through a GameSprite-typed reference, the vtable dispatch resolved to the parent’s Update(speed:Float) rather than the derived Type’s Update(speed:Int). Wrong dispatches: 3 call sites. The BlitzMax developer on retainer diagnosed the Method override signature constraint: BlitzMax does not warn when a derived Type Method has a similar name to a parent Method with a different signature; the compiler silently treats the derived Method as a new overload on the derived Type rather than an override of the parent. The developer matched the parameter type exactly to the parent signature — Method Update(speed:Float) — which registered the derived Method in the vtable as an override. Wrong dispatches: 3 → 0.

The work log entry read “fixed EnemySprite dispatch, 6h.” It names the result and duration. It cannot explain why a parameter type mismatch silently creates an overload rather than an override — in BlitzMax, the object model populates the vtable based on exact signature match between the base Type’s Method declaration and the derived Type’s Method declaration; a Method on a derived Type that differs in any parameter type or return type from the parent Method of the same name is, from the vtable’s perspective, a completely different Method that happens to share a name; there is no compiler warning because BlitzMax does not enforce intent — the developer never declared that EnemySprite.Update was intended to override GameSprite.Update; the compiler only knows that EnemySprite Extends GameSprite and that both Types have a Method named Update with different signatures. It cannot explain the distinction between a BlitzMax Method and a Function — Methods in BlitzMax are dispatched through the vtable when called on an object reference, supporting polymorphism; Functions associated with a Type are static and always resolve to the specific Type’s Function at the call site, never through the vtable; a Function declared in a derived Type with the same name as a Function in a base Type does not override anything because Functions are not part of the vtable. It cannot explain the use of Final Methods — a Method declared Final is marked as non-overridable and allows the compiler to generate a direct call instead of a vtable lookup, which is a measurable performance gain on hot paths in game loops where the overhead of vtable indirection accumulates across thousands of objects per frame. The 6 hours of signature audit, vtable verification, and inheritance hierarchy restructuring are invisible in the diff.

BlitzMax Type inheritance: Extends, Method vtable dispatch, override signature rules, and Final optimization

BlitzMax Types are declared with the Type keyword: Type EnemySprite Extends GameSprite. The Extends keyword establishes the inheritance relationship and is required for the derived Type’s Methods to be registered as overrides in the vtable. A Method declared in a derived Type overrides the corresponding Method in the parent Type if and only if the Method name and complete signature (parameter count, parameter types in order, return type) match exactly. The vtable is built at compile time based on this exact-match rule; at runtime, when a Method is called on an object reference typed as a base Type, the vtable dispatches to the derived Type’s implementation if one was registered. The New Method serves as the constructor: Method New() is called automatically when an object is created with the New keyword; derived Type constructors can call Super.New() to invoke the parent chain. Final Method marks a Method as non-overridable: derived Types cannot override a Final Method, and the compiler can devirtualize the call at the call site, replacing the vtable lookup with a direct function call. Strict mode enforces type-safe operations throughout the program: without Strict, BlitzMax performs implicit numeric conversions that can mask parameter type mismatches; with Strict, parameter type mismatches that would silently create overloads in relaxed mode produce type errors at compile time.

The Super keyword provides access to the parent Type’s implementation: inside an overriding Method, Super.MethodName(args) calls the parent Type’s implementation before or after the derived Type’s logic. This is the BlitzMax pattern for extending rather than replacing parent behavior. The common BlitzMax inheritance pitfall — the one the game developer above encountered — arises from BlitzMax’s permissive approach to Method names: the language does not require an explicit override declaration (as Kotlin’s override fun or C#’s override keyword do), so the developer has no compile-time signal that their derived Method was not registered as an override. The BlitzMax developer on retainer’s first diagnostic step is to enumerate all Methods in the derived Type and compare their complete signatures against the parent Type’s Methods of the same names; any mismatch in parameter type or return type indicates a potential unintentional overload. BlitzMax was developed by Blitz Research Ltd and is the successor to Blitz3D, adding object-oriented features and multi-platform compilation. Its closest retainer neighbors are Haxe developer retainers (both are multi-platform compiled languages with class/Type inheritance models) and Smalltalk developer retainers (both have message-passing object models where method dispatch is the core execution mechanism), but BlitzMax’s game-loop-centric API, its Graphics/Cls/Flip rendering pipeline, and the exact-signature-match rule for Method override registration make the retainer work distinct in game loop architecture, sprite hierarchy dispatch, and vtable-based polymorphism diagnosis.

BlitzMax graphics and game loop: Graphics, Cls/Flip frame loop, DrawImage, PollEvent, and TList/TMap collections

A BlitzMax game loop follows the Graphics → frame loop → Cls/Flip structure. Graphics(width, height, depth, hertz) initializes the display; depth=0 requests windowed mode and depth>0 fullscreen. The main game loop calls Cls at the start of each frame to clear the back buffer and Flip at the end to present the back buffer to the display; calling Flip 1 waits for vsync. Asset loading uses LoadImage("path/to/sprite.png") to produce a TImage object; DrawImage(image, x, y) draws the image at the given pixel coordinates; FreeImage(image) releases the asset when no longer needed. Text rendering uses DrawText(string, x, y) with the current font set via LoadFont/SetFont. SetBlend controls compositing: ALPHABLEND for transparency, SOLIDBLEND for opaque, LIGHTBLEND for additive lighting effects; SetAlpha(0.0 to 1.0) sets global draw opacity.

Event handling uses PollEvent() for non-blocking event polling (returns 0 when the event queue is empty) and WaitEvent() for blocking wait. The event type is retrieved with EventID(); common types are EVENT_KEYDOWN, EVENT_MOUSEDOWN, and EVENT_APPTERMINATE. Input state is queried with KeyDown(key_constant) for held state and KeyHit(key_constant) for edge-triggered press detection; mouse coordinates come from MouseX() and MouseY(). TList is BlitzMax’s doubly-linked list: list.AddLast(value), list.Remove(value), and For obj = EachIn list for iteration. TMap is the key-value store: map.Insert(key, value), map.ValueForKey(key), and For value = EachIn map for value iteration. Both are used extensively in game state management — TList for entity collections where objects are added and removed frequently, TMap for asset caches keyed by filename or identifier. The bmk makeapp source.bmx command compiles a BlitzMax application from source; Import "brl.module" declarations at the top of source files specify module dependencies, and the bmk build tool resolves module paths from the .mod directory structure.

How HourTab tracks BlitzMax developer retainer hours

BlitzMax retainer work carries the invisible-hours problem specific to game engine development: the mismatch between the perceived simplicity of “fixing a dispatch bug” and the actual investigative work required to identify which of dozens of Methods in a Type hierarchy has a silent signature mismatch. The EnemySprite example described above — where Update(speed:Int) was silently treated as a new overload rather than an override of Update(speed:Float) — is the most common correctness issue in BlitzMax code written by developers migrating from dynamically-typed languages like Python or JavaScript, where method override is determined by name match alone without regard to parameter types. In a statically-typed object-oriented language, parameter type mismatch at override is either a compile-time error (languages with explicit override declaration) or a silent semantic trap (languages like BlitzMax without it). Diagnosing a dispatch bug in a BlitzMax game requires more than reading the failing Method: the developer must enumerate all Method signatures in the inheritance chain, compare them pairwise for exact-match compliance, and test each potential override by calling it through a base Type reference to observe whether the vtable resolves to the derived or base implementation. In a game with 10–20 Types and 5–15 Methods per Type, this audit is a multi-hour investigation with no visible artifact. A retainer engagement typically involves a complete Type hierarchy audit (all derived Type Methods compared against parent Method signatures), a Final Method review (performance-critical hot-loop Methods that should be devirtualized), and a Strict mode compliance pass (enabling Strict to surface implicit type conversions that mask parameter type mismatches in the codebase).

HourTab gives BlitzMax developers a public retainer-hours URL they send to clients — typically indie game studios using BlitzMax for 2D game development, developers maintaining legacy BlitzMax codebases, and game developers extending Blitz3D projects to BlitzMax’s object-oriented feature set. For BlitzMax retainers, each work log entry should name the mechanism (dispatch category: Method override signature audit, Extends vtable registration, Final devirtualization, Super.Method() parent chain; graphics category: Graphics/Cls/Flip frame loop, LoadImage/DrawImage/FreeImage asset lifecycle, SetBlend/SetAlpha compositing; collection category: TList AddLast/Remove/EachIn, TMap Insert/ValueForKey; specific Type name and Method name and before/after wrong-dispatch count). BlitzMax retainers are often compared to Haxe developer retainers for the game development overlap, but BlitzMax’s exact-signature override model where a derived Method with a mismatched parameter type creates a silent overload (rather than Haxe’s explicit override keyword that makes override intent clear at the call site), the Graphics/Cls/Flip direct rendering pipeline, and the bmk module system make the retainer work distinct in vtable dispatch diagnosis, game loop frame-loop architecture, and Type hierarchy signature audit. HourTab’s work log makes the Method signature audit, inheritance hierarchy restructuring, and game loop performance analysis visible to clients who would otherwise see only the symptom — enemies moving with the wrong behavior or parent sprite logic executing instead of derived logic — and not understand why the fix required knowing that BlitzMax’s vtable is populated by exact signature match, not by name match, and that adding Strict to the source file would have turned the silent overload into a compile-time type error before the game shipped.

Track BlitzMax developer retainer hours without the status emails

HourTab gives BlitzMax developers a public URL per client retainer. One link, no login, live burn-down. Your clients stop asking “how many hours do I have left?” and your Type hierarchy audit log — override signature diagnosis, vtable registration, game loop frame optimization — becomes the proof of value that gets the retainer renewed.

See HourTab pricing →

FAQ: BlitzMax developer retainers

What does a BlitzMax developer on retainer typically do?

A BlitzMax developer on monthly retainer covers BlitzMax Type inheritance (Type Name Extends Base declaration; Method polymorphic vtable dispatch; exact parameter signature match required for override; Function static non-dispatch; Super.Method() parent chain; New constructor; Final Method for devirtualization; Strict type safety mode), BlitzMax graphics and game loop (Graphics display initialization; Cls/Flip frame loop; DrawImage/DrawRect/DrawText; LoadImage/FreeImage asset lifecycle; PollEvent/WaitEvent event loop; KeyDown/KeyHit input; MouseX/MouseY; SetBlend/SetAlpha compositing), and BlitzMax collections and build (TList with AddLast/Remove/EachIn; TMap with Insert/ValueForKey; bmk makeapp compilation; Import module dependencies; .mod directory structure).

What BlitzMax work is most commonly underlogged in a retainer?

Method override signature diagnosis (developer created Extends Type hierarchy; derived Method with slightly different parameter type treated as new overload not override; parent Method called through base-typed reference; wrong dispatches: 3/call site; matched parameter types exactly to parent signature; wrong dispatches: 3/call → 0; 5–9 hrs invisible); Type hierarchy design (identifying which Methods require vtable dispatch vs static Function; deciding Final vs non-Final for hot-loop performance; 4–8 hrs invisible); game loop event handling (PollEvent non-blocking vs WaitEvent blocking loop structure; input state accumulation pattern; frame delta timing with MilliSecs; 4–7 hrs invisible); TList/TMap collection refactoring (replacing array-based code with TList for dynamic collections; EachIn traversal; key type selection for TMap; 3–6 hrs invisible).

What are typical BlitzMax developer retainer rates?

Entry-level BlitzMax developers (1–2 years, basic Type declarations, Method dispatch, bmk compiler workflow) bill at $50–$90/hr. Mid-level BlitzMax programmers (2–4 years, Type inheritance hierarchies, Method override signature rules, graphics pipeline, TList/TMap) bill at $80–$145/hr. Senior BlitzMax game developers (4–8 years, complex Type hierarchies, game architecture, performance optimization, module system design) bill at $120–$215/hr. Monthly retainer ranges: $1,800–$3,600/mo advisory (15–25 hrs), $4,800–$12,000/mo for full BlitzMax game development.

What should a BlitzMax developer retainer agreement include?

A BlitzMax developer retainer agreement should specify: Type system scope (Type/Extends inheritance; Method vtable dispatch; exact override signature requirement; Function static; Super.Method() parent chain; Final Method; New constructor; Strict mode); graphics scope (Graphics display; Cls/Flip frame loop; DrawImage/DrawRect/DrawText; LoadImage/FreeImage; SetBlend/SetAlpha); event/input scope (PollEvent/WaitEvent; KeyDown/KeyHit; MouseX/MouseY); collections scope (TList with AddLast/Remove/EachIn; TMap with Insert/ValueForKey); build scope (bmk makeapp; Import module resolution); and hour logging format (dispatch: override signature audit, vtable registration, Final optimization; graphics: frame loop, asset lifecycle, blend mode; specific Method name and before/after wrong-dispatch count).

How should BlitzMax developer retainer hours be logged?

Log each BlitzMax retainer session with: dispatch category (override: parameter type mismatch turned overload, Extends declaration, vtable population, Super.Method() chain; Function vs Method: static dispatch vs polymorphic; Final: devirtualization for hot-loop Methods; hierarchy: base Type Method signature audit, derived Type override coverage); graphics category (frame loop: Cls/Flip timing, MilliSecs delta, Graphics mode; asset: LoadImage/FreeImage lifecycle, TImage management; blend: SetBlend ALPHABLEND/SOLIDBLEND/LIGHTBLEND, SetAlpha); the specific Type name and Method name and before/after wrong-dispatch count (Type: EnemySprite; parent Method: Update(speed:Float); derived Method: Update(speed:Int); parameter type mismatch: new overload not override; parent Update called through GameSprite reference; wrong dispatches: 3/call; matched to Update(speed:Float); wrong dispatches: 3/call → 0); and the before/after metric.