- C++ 99%
- C 0.7%
- CMake 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| core | ||
| docs | ||
| gui | ||
| media | ||
| model | ||
| render | ||
| states | ||
| tests | ||
| .gitignore | ||
| CHANGELOG.txt | ||
| CMakeLists.txt | ||
| Definitions.h | ||
| LICENSE | ||
| main.cpp | ||
| README.md | ||
| ResourceIdentifiers.h | ||
| THIRD-PARTY-NOTICES.md | ||
VARN Engine
A C++/SFML game engine aiming to deliver a fully playable remake of Might and Magic Book One: The Secret of the Inner Sanctum, using the assets from the original MS-DOS release.
Forked from filasfilas/F-MM1.
Goal
Produce a complete, playable game that loads and uses the data files from the original MM1 DOS release (wall textures, map data, sprites, music). No asset recreation — original files are loaded at runtime.
Status
- Raycasting renderer with DDA, boundary walls, fog of war minimap
- 5 towns (Sorpigal, Dusk, etc.) and full outdoor world grid navigable
- Party system: view characters, manage inventory, equip/remove items
- Map scripts partially implemented (town events, encounters)
- Wandering monsters: the region's own encounter constants, the original's
group builder, and the monster picture out of
MONPIX.DTA. The fight itself is not written yet, so the party can simply walk away - Town services: blacksmith, market, temple, training hall and tavern, all priced and stocked out of MM.EXE; the inn is a stub
- Combat and spells — in progress
What's changed from F-MM1
F-MM1 was abandoned; VARN Engine picks it up, but little of it is untouched. The two projects differ in kind, not just in features.
Nothing from the original game is in the repository
This is the change everything else is arranged around. F-MM1 committed data
extracted from the game (data/items.dat, data/monsters.dat), art traced from
or lifted out of it (media/images/walls/wall01–18.png, the ten intro/screen*
frames, MM1-book.png, MM1-map.png), the original soundtrack as
media/music/MM1-main.ogg, and even a save/ROSTER.DTA. Might and Magic is
Ubisoft's; none of that is redistributable.
VARN Engine ships none of it and reads everything from the copy of the game you already own, at startup:
MM.EXE— spell tables, the 255-entry item table, the 195-entry monster table, class/race/alignment/condition names, wall strings, and the CGA bitmap font, which is decoded into an in-memory BDF so SFML can use it as a fontWALLPIX.DTA— wall textures, RLE- and CGA-decoded at runtimeMONPIX.DTA— the 76 monster pictures, decoded the same way, with their colours read out of MM.EXEMAZEDATA.DTAand the*.OVRoverlays — maze layouts, per-region constants and scriptsManual.pdfandMight and Magic - Cluebook.pdf— the spell and region names, which the game never stored in a data file (see "Asset setup")
The media/ tree that remains is original work under its own licences, listed
in THIRD-PARTY-NOTICES.md.
Hardcoded data replaced by runtime extraction
F-MM1 hardcoded what it could not load. Roughly 2500 lines of it are gone:
model/maps/maze00.cpp–maze25.cpp and mazelist.h, one hand-written subclass
per maze, are replaced by data-driven map loading plus OVR script
classification; string tables scattered through the sources are consolidated
behind ExeLoader; the spell system is driven off the EXE's own tables.
F-MM1's two remaining hand-entered map tables are gone as well. The outdoor
adjacency grid now comes from each region's own OVR exit triples, and a region's
three wall sprites from the wall ids in its maze constants, looked up in the
20-word table MM.EXE keeps for that purpose. model/hardcoded.h no longer exists.
Dead code removed
states/shops/Market.*, model/shops/market.* and states/TitleState.* were
non-functional stubs and are gone — shops will be written against the real data
rather than grown out of them. So are the stale duplicates render/MapSprite.h,
render/MapSprite (копия).h and render/oldVer/, and a set of character.h
members that were declared but never defined. The engine is about 6.5k lines to
F-MM1's 7.8k while doing considerably more.
Ported to SFML 3 and C++17
F-MM1 targets SFML 2. The port covers the event system (pollEvent returning
std::optional, getIf<>/is<> instead of the union), sf::Sprite and
sf::Text no longer being default-constructible, the sf::Vector2-based
geometry API, and scoped enums throughout.
Renderer and performance
Wall quads are batched into three sf::VertexArray by texture instead of one
draw call per column, the finished frame is cached in an sf::RenderTexture and
only redrawn when the view changes, the DDA loop is bounded at 32 steps rather
than 100, and the frame rate is capped at 60.
Memory safety
The virtual destructor State never had — every state change was deleting a
derived object through a base pointer and handing operator delete the wrong
size — plus out-of-range roster and inventory indices, unchecked optional
access and the crash on leaving the menu. tests/state_smoke.cpp now drives
every state through its full lifecycle under AddressSanitizer and UBSan as a
CTest target; it found the destructor bug on its first run.
New in VARN
- Fog-of-war minimap
- Widescreen support: F11 fullscreen and a maze that widens its field of view rather than stretching (see "Display")
- A
graphicReworktoggle switching between the original CGA look and higher-resolution art with character portraits - Spell system, and OVR scripts read straight out of the region overlays: portal prompts, message/sign handlers — including signs that read differently depending on which way the party faces — and Sorpigal's statues, whose handlers carry only a number and leave the plaque to MM.EXE's own tables
- Every string the game files hold is addressed, not numbered: MM.EXE's data segment and each overlay's data section are kept as images and read by the same addresses the original code uses, so a translation file can override any of them one at a time and fall back to the original for the rest (see media/localization/README.md)
- One translation file for every string the game can show, the interface's own text included (see "Localization"); F-MM1's Russian strings were dropped rather than left to rot half-translated
Requirements
- SFML 3.x
- zlib (PDF content streams in the manual and cluebook are Flate-compressed)
- CMake 3.10+
- C++17 compiler
- Original MM1 DOS game files placed under
assets/mm1/(see "Asset setup")
Build
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
./build/VARN-Engine
Tests
state-smoke drives every state through init/update/draw plus a full key sweep,
then exercises the state stack, the widescreen layout maths and window recreation,
all under AddressSanitizer and UBSan. It runs
against the game files in assets/mm1/ and skips itself if they (or a display)
are missing.
cmake -S . -B build -DVARN_BUILD_TESTS=ON
cmake --build build -j$(nproc)
ctest --test-dir build --output-on-failure
Display
The game is laid out in a 1024x768 (4:3) space, but the maze is raycast, so a widescreen window widens the horizontal field of view instead of stretching or letterboxing the picture: walls keep their size and you see more of the corridor at the sides. The minimap and party portraits follow the new edges. Aspects narrower than 4:3, or wider than 2.4:1, letterbox the remainder.
F11 toggles fullscreen; the window is also freely resizable. Set FULL_SCREEN
in Definitions.h to start fullscreen.
Localization
Every string the game can show — the text in MM.EXE, the text in each region
overlay, the spell and region names read out of the GOG manual and cluebook,
and the interface's own lines — is reachable through one table and can be
overridden from a single file, media/localization/game_<LANG>.txt. Only what
the file names is replaced; everything else falls back to the game's own text,
so a half-finished translation runs. --dump-strings writes a template with
all 2178 of them in it. Format and workflow:
media/localization/README.md.
Limitations
The engine is ready for other languages; the fonts are not. Both are ASCII-only in practice, so anything outside it — extended Latin, let alone a non-Latin script — will not render:
- Old-school mode draws with the original CGA font lifted out of MM.EXE.
That font is 65 glyphs,
0x20–0x60, and nothing else exists to draw with: lowercase is aliased to uppercase, and an accented letter has no glyph at all. Supporting more would mean drawing the extra characters by hand in the same 8x8 cell the originals use (doubled to 16x16 in the BDF the engine builds at startup) and extending that font. - Graphic rework mode uses Nodesto Caps Condensed, which maps 114 codepoints: 9 of them in Latin-1 Supplement, none in Latin Extended-A, none outside Latin. Anything past basic ASCII needs a different font — this one would have to be replaced, not patched.
Translating into a language that fits in ASCII works today. Anything else waits on the fonts.
Controls
| Key | Action |
|---|---|
| Arrow keys | Move / turn |
| F11 | Toggle fullscreen |
| M | Toggle minimap |
| Q | Party screen |
| 1–6 | Character detail (from party screen) |
| R | Rest (asks first) |
| B | Bash the barrier ahead |
| U | Unlock the barrier ahead (picks the lock) |
| S | Search the square |
| O | Reorder the party |
| P | Protections screen |
| F12 | Cheat console |
Cheat console
F12 opens a line to type into; F12 or Esc closes it again. Might and
Magic never had a console — Heroes III did, and its codes were all film
references, so these are too, and they share a three-letter prefix the way the
originals did. A code that is not recognised answers as well, so a typo does
not look like a dead key.
| Code | Effect |
|---|---|
jvcbeammeup [region] |
The party goes to that region, or home to Sorpigal |
jvcharrymudd [gold] |
Pays the first character, 65535 by default |
jvcmirrormirror [id] |
Turns a character's alignment into its opposite |
jvcredshirt [id] |
Leaves a character on one hit point |
jvcgorn |
Switches interface without leaving the maze |
jvcquadrotriticale [n] |
Food for the first character |
jvcdilithiumcrystals [n] |
Gems for the first character |
[id] is a party slot, 1 to 6, and the first character is meant when a code
does not say. The two stores fill to the brim when no number is given, and the
brim is what a character record holds — food is one byte of it and gems are
two — so nothing is handed out that the roster would lose on the way to disk.
mirrormirror leaves the alignment a character was made with alone, so a
temple will sell it back; a neutral character has no opposite and stays as
they are.
Asset setup
Place your original MM1 DOS data files under assets/mm1/. Nothing from the
original game is distributed with this engine — see
THIRD-PARTY-NOTICES.md.
VARN Engine is developed and tested against the GOG.com release of Might and Magic: Secret of the Inner Sanctum. Other copies of the DOS version should work — the engine only reads the original data files — but the GOG one is what everything here is verified against.
Copy the contents of the GOG installation's game directory into assets/mm1/:
MM.EXE— spell tables, item and monster stat tables, region overlay names and the CGA bitmap fontMAZEDATA.DTA,*.OVR— maze layouts and per-region scriptsWALLPIX.DTA— wall textures (used when the graphics rework is off)MONPIX.DTA— monster picturesROSTER.DTA— character roster
Two PDFs are required as well. They carry text the game itself never stored: the original referred players to the printed documentation, so spell and region names only ever existed on paper. The engine reads them at startup rather than carrying a transcription in source.
Manual.pdf— the 94 spell names. Ships with the GOG release and is already in the game directory, so it comes across with everything else.Might and Magic - Cluebook.pdf— the 55 region names. Not bundled with the game: download it from the extras/goodies section of the GOG product page and drop it intoassets/mm1/alongside the rest.
Without them the engine still starts, but spells and regions are nameless and it says so on stdout. Music files, by contrast, are genuinely optional — missing files are silently skipped.