Skip to content

Latest commit

 

History

History
113 lines (84 loc) · 9.04 KB

File metadata and controls

113 lines (84 loc) · 9.04 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

This is a boiler simulator originally written in summer 2003 using Borland C++ with BGI (Borland Graphics Interface) for DOS. The project is being ported to modern C++17 with SDL2.

The simulator is an interactive educational tool for coal/fire boiler operations: users control fire intensity, air/water valves, and monitor temperature, pressure, and water level through animated gauges. The UI is entirely in Chinese (GB2312 encoding), rendered via HZK16 bitmap fonts.

Build Commands

make            # Build boiler_sdl (requires SDL2)
make clean      # Remove binary and .o files

Requires: g++, SDL2 development libraries (sdl2-config must be in PATH).

Architecture

SDL2 Port (source files at repo root)

The port is split into 8 modules with a strictly acyclic dependency graph:

boiler_globals.h ← bgi_compat.h ← drawing.h ← physics.h ← handlers.h
                                                                 ↑
                                                              main.cpp

drawing.h is implemented across 4 source files (drawing.cpp, drawing_boiler.cpp, drawing_controls.cpp, drawing_dialogs.cpp).

File Lines Role
boiler_globals.h ~96 Shared constants, types, extern declarations for all globals
bgi_compat.h/.cpp ~695 BGI compatibility layer: drawing primitives, fonts, text, value display, screen save/restore
drawing.h ~58 Declares all draw_* functions
drawing.cpp ~62 Simulation state variable definitions + mian_interface_initialize()
drawing_boiler.cpp ~433 Boiler components: background, thermograph, stove (static), graduation marks, pipes (in/out/steam), boiler tank, barometer
drawing_controls.cpp ~409 Controls: valves (water/air open/close), buttons (+/- and stove), control panel, pressed-state variants, stove door animation
drawing_dialogs.cpp ~368 Dialogs: alarm indicators, ESC/HELP/MANUAL buttons, menus, warning windows, ABOUT, HELP window, ask buttons
physics.h/.cpp ~288 Animation (thermograph, pointer, water level), temperature/pressure physics, fire effect
handlers.h/.cpp ~228 Hit detection (in_rect, is_actionable_region) and all button click handlers
main.cpp ~395 SDL init, event loop, main(). Defines SDL/cursor/app-state globals.

Globals strategy

All globals are declared extern in boiler_globals.h and defined in one .cpp file:

  • SDL/cursor/app-state vars → main.cpp
  • BGI drawing state vars → bgi_compat.cpp
  • Simulation state vars → drawing.cpp

File-local data (static): font8x8[], lookup tables in bgi_compat.cpp; fire buffer, needle state in physics.cpp.

Original Code (project/)

10 CPP files forming the legacy DOS application. _EXTERN.CPP serves as a shared header with all extern function declarations.

File Role
_PROJECT.CPP Main entry point and event loop. Mouse input, button hit-testing, simulation state management. Heavy use of goto for menu/dialog flow.
_DRAWING.CPP All static UI rendering: boiler tank, pipes, valves, barometer, thermometer, stove, control panel, alarm indicators. 40+ drawing functions using hardcoded pixel coordinates.
_CARTOON.CPP Physics engine and animation. Temperature/pressure calculations with two modes (with/without water). Thermometer bar, pressure gauge needle rotation (trig-based), water level with sine-wave ripple.
_PUT_OUT.CPP Numeric display formatting for gauges. Giant switch statements mapping internal values (0-69 temp, 0-48 pressure, 0-22 fire/air %) to display strings.
_OUTPUTHZ.CPP Chinese character rendering from hzk16 bitmap font (GB2312). Supports scaling, bold, and horizontal/vertical text.
_WINDOWS.CPP Modal dialogs: warning windows, control panel layout, ABOUT screen, HELP documentation.
_VGA12H.CPP Low-level VGA: direct memory pixel plotting, fire animation (random red/yellow pattern).
_SOUND.CPP PC speaker control via direct I/O ports (0x43, 0x42, 0x61).
MYPOINTER.CPP Mouse driver wrapper using INT 0x33. Custom cursor shapes (arrow, hand, hourglass).
_EXTERN.CPP Extern declarations for all cross-module functions.

Supporting Resources

  • hzk16 — 16x16 bitmap font file for GB2312 Chinese characters (required at runtime, must be in working directory)
  • project/resources/ — Standalone reference implementations of individual components (fire effects, barometer, thermometer, control panel, mouse tests, color utilities).
  • project/resources/chinese/ — Header-based 256-color graphics library with Chinese font rendering, BMP loading, and mouse support.
  • project/flowcharts/ — Visio diagrams of the boiler, barometer, thermometer, and project flow.

Key Simulation Details

  • Fire intensity: 0-22% in integer steps, mapped to 5 heating tiers
  • Temperature: 0-69 internal units mapped to 0-160 degrees C display; caps at 43 when water present (steam point)
  • Pressure: 0-48 internal units mapped to 0.10-1.00 mPa; gauge needle sweeps 150-340 degrees
  • Water level: sine-wave distortion for ripple effect; boiler tank modeled as curved top/bottom (r=38) + straight middle
  • Alarms: water level, pressure, and temperature each have independent alarm states

Porting Notes

When porting from BGI to SDL2:

  • BGI line(), bar(), arc(), circle(), pieslice(), floodfill() need SDL2 equivalents (SDL_RenderDrawLine, SDL_RenderFillRect, custom arc/circle functions)
  • BGI uses a 16-color palette (0-15); map to RGB values
  • Mouse handling replaces INT 0x33 with SDL_Event polling
  • Sound replaces direct port I/O with SDL_Audio or can be stubbed out
  • Chinese text rendering reads from hzk16 file — the bitmap-to-pixel logic ports directly, just swap putpixel for SDL pixel drawing
  • All coordinates are hardcoded to 640x480 resolution

Known BGI-to-SDL2 pitfalls

  • floodfill leaks: SDL2 midpoint-circle/arc algorithms produce pixel gaps that BGI's implementations don't. floodfill() leaks through these gaps and paints the entire screen. Replace dangerous floodfills with direct scanline fills or bar() calls wherever possible. Remaining floodfills (alarm indicators, gauge center) are safe because they fill small enclosed regions.
  • Font scaling: BGI font 2 (SMALL_FONT) has smaller glyphs than our 8x8 bitmap font. outtextxy() applies a conditional base scale of 0.6 when g_text_font == 2 and setusercharsize multiplier >= 1.0; multipliers < 1.0 use no base reduction. This keeps boxed numeric displays small enough to fit while keeping gauge labels readable.
  • Pipe water indicators: The original uses floodfill() to color pipe indication windows between ellipse pairs. The SDL2 port uses fillellipse() + bar() to fill the same regions without leak risk.
  • Pressure gauge colors: The gauge face is filled with scanline loops using screen-coordinate atan2 angles. Green (121-211°), yellow (211-331°), red (331-61° wrapping), gray dead zone (61-121°). The annulus between r=40-50 is light gray; colored zones are in the ring r=34-40.
  • Alarm functions: draw_alarm_of_water/air/temperature take an rg parameter: 4=red alarm (flashing indicator + sound), 10=alarm cleared (reset to green), 0/default=normal green state.
  • Fire animation replacement: The original Fire() in _VGA12H.CPP uses direct VGA memory writes (0xA0000000) to scatter random red (4) / yellow (14) dots across 4 offset columns with different step sizes. Each pixel has a 1-in-8 chance of color change; no temporal coherence between frames. The SDL2 port in physics.cpp:248-301 replaces this with a cellular automata "doom fire" algorithm: a 48x50 heat buffer (fire_buf) seeded with high values at the bottom row, propagated upward with random horizontal drift (+-1 pixel) and random decay (0-3 per cell per frame). Heat maps to color via fire_heat_to_color(): >30=yellow (14), >24=brown (6), >16=red (4), else invisible. noFire() clears the buffer and fills the region with bar(). The fire region is positioned at (120, 355) to align with the stove opening.

Localization (CN/EN)

  • g_language (LANG_CN / LANG_EN) controls UI language; toggled via button at (575,216).
  • All translatable strings go through get_string(StringID) in strings.h/strings.cpp.
  • ASCII advance in English mode is compact: outhzxy3() and out_en16() advance ASCII characters by size*8 + space*2 (vs size*16 + space*8 for CJK). The 16x16 font glyphs only occupy ~8 columns, so the advance matches actual glyph width. When adding new English strings, calculate max chars as: available_width / (size*8 + space*2).
  • The outtextxy() 8x8 font is never localized — it handles numeric displays, keyboard shortcuts, and ASCII labels that are language-independent.
  • mian_interface_initialize() redraws the entire UI but resets dynamic visual state (valves, stove door) to default. After calling it mid-session, restore dynamic state explicitly.