Releases: kefiiiiir/VNEngine
Release list
v0.2.2
VNEngine update
For writers
- Typo fixes no longer nuke saves. Editing dialogue text (fixing a
typo, rewording a line) used to silently invalidate every player's save,
because the old text was baked into the save's "did the script change?"
check. That's fixed — only structural stuff (who's speaking, jumps,
labels, choices, etc.) is checked now, not the words themselves.- One-time heads up: because of how this fix works internally, saves
made before this update will show the normal "Save may be out of
date" prompt one last time on load, then behave correctly from then
on.
- One-time heads up: because of how this fix works internally, saves
- Players can no longer bold/color/italicize their own name. If
someone enters[b]Bobas their name, it now shows literally as
[b]Bobinstead of rendering as Bob. Markup tags ([b],[i],
[c=...]) written by you instory.jsstill work exactly as before —
this only strips them out of player-typed names andset()variables. - Docs fixed: removed two keyboard shortcuts from
VNEngine.md
(auto-advance'sAkey, skip'sCtrl/Tab) that were documented but
never actually existed in the engine — the engine has always been
buttons + Space/Enter/number-keys only. - Docs added:
VNEngine.mdnow says plainly that rollback history (the
"back one line" buffer) and the backlog/history overlay are memory
only — they reset on reload. Only checkpoints/auto-save/seen-tracking
survive a reload. - Topbar buttons are now screen-reader friendly (aria-labels added,
visuals unchanged). - Renamed to "VNEngine" (capital E) in the visible/branded spots:
page title, title-screen logo, README heading, and the manual — which is
nowtemplate/VNEngine.md(wasVNengine.md).
For coders
engine.js→save-resolve.jsextraction. Pulled the DOM-free save
logic (digestOps,hashStr,buildLabels,segmentNameAt,
posFromPtr,resolvePos) out intotemplate/js/save-resolve.js— a
small UMD module (window.VNSaveResolvein the browser,module.exports
in Node).engine.jsnow calls into it via a localSRalias. Loaded in
index.htmlright beforeengine.js.- The fix itself:
digestOps'ssaycase droppedo.text, kept
o.who. Save format bumpedv: 2 → v: 3;applySnapshotnow checks
s.v === 3. Anything else (v1's rawptr, or v2 — whose hash can't be
trusted anymore since it included text) falls into the legacy/mismatch
path, but still resolvesposwhen the save has one instead of just
resetting to line 0. segmentNameAtis now binary search, notObject.keys().forEach.
buildLabels()returns a pre-sorted[index, name]array once at boot;
lookup is O(log labels) instead of O(labels), called on every line of
dialogue.- Tests:
template/tools/tests/save-resolve.test.js— plain Node, no
framework, 14 cases coveringhashStr,digestOps(incl. the "text
change → same digest" regression lock),buildLabels/segmentNameAt,
andposFromPtr/resolvePosround-trips (including "insert lines in an
earlier label → save still resolves correctly," and a documented edge
case for inserting within the same segment before the saved line).
Run:node template/tools/tests/save-resolve.test.js.- Location note: lives under
template/tools/tests/, not a
repo-roottests/— repo root is just the setup scaffolder,template/
is the actual engine project, andtools/is where the other
dev-only/never-shipped tooling already lives (packager,devlog.js).
projectpackager-windows.py'sARCHIVE_DIRS = (css, js, src)confirms
tools/never ships in a built game.
- Location note: lives under
- CI added:
.github/workflows/ci.yml, on push/PR —py_compiles
setup.py,template/playtest.py,template/tools/*.py, and runs the
test file above. setup.pyrmtree hardened (~line 471, nowmain()'s overwrite
block + new_looks_like_vnengine_project()helper): refuses to touch a
non-empty destination unless it contains aproject.jsonwith
"engine": "VNengine"; shows file count in the confirmation prompt;
renames the old folder to<name>.bak-<timestamp>instead of
shutil.rmtree-ing it.fmt()inengine.js: addedstripMarkup(), applied to
player.first,player.last, and each interpolatedstate.vars[k]
before substitution (not to the final string — writer-authored markup
elsewhere in the line is untouched).- Rename:
template/VNengine.md→template/VNEngine.mdvia a
two-commit-safegit mv(staged as a rename, not yet committed — Windows
git needs the two-step dance for a pure case change).
v0.2.1
VNengine v0.2.1
VNengine now builds and ships as a desktop app, not a browser tab — and the
diagnostics you used to have to dig for in F12 come straight to your terminal.
Highlights
Native window
playtest and every packaged build open the game in its own window via
pywebview (WebView2 on Windows) — no browser
chrome, no address bar, no tab. The HTTP server runs on a background thread and
shuts down when you close the window; there's no separate Ctrl+C step.
VN_NO_BROWSER=1 still serves headlessly, and if pywebview isn't installed it
falls back to opening your default browser.
Boot diagnostics in the playtest terminal
A ~60-line dev shim (tools/devlog.js) is injected into index.html only when
playtest.py has a console (source runs, or a development build). It forwards
console.*, uncaught errors, and failed image/audio loads to the terminal, which
renders them grouped and colour-coded — errors red, warnings amber, one green
"no problems found" on a clean boot. Never referenced by index.html, never in a
shipping build; the real F12 console still works.
Two build profiles
The packager now asks for a profile:
| profile | runtime | for |
|---|---|---|
| development | --console |
testing — console window streams diagnostics next to the game |
| shipping | --windowed |
release — no console, no dev shim, index.html untouched |
Each profile has its own cached frozen runtime (keyed on a hash of
playtest.py), so switching profiles doesn't force a rebuild of the other.
index.html moved into the .pak
Packaged output is now just <Project>.exe + <Project>.pak — the HTML is
inside the archive, so there's no loose file for a player to edit.
project.json rides along too, and the window is titled from it (falling back to
the <title> in index.html). Old builds with a loose index.html still run
(disk fallback preserved).
In-world confirmation dialogs
"Erase save?", "Return to menu?", and checkpoint-jump prompts are now themed
in-engine modals — no more browser confirm() boxes reading "localhost:8000
says". Escape or a backdrop click cancels; destructive actions get a red button
with focus defaulting to Cancel; respects prefers-reduced-motion.
setup installs the toolchain
After scaffolding, setup offers to pip install the two packages a project
needs: pywebview (play/ship window) and pyinstaller (packager).
New dependencies
pywebview— required for the native window (graceful browser fallback if absent)pyinstaller— required only for packaging
setup installs both on request; otherwise: pip install pywebview pyinstaller.
Upgrading an existing project
Projects are copies of the engine, so they don't auto-update. Either re-scaffold
with the new setup, or copy these into your project and reload:
playtest.pytools/devlog.js(new file)tools/projectpackager-windows.pytools/projectpackager-tools/build-runtime.py- merge the
js/engine.js+css/style.csschanges (in-world dialogs)
Existing packaged .exe builds keep working unchanged.
Assets
VNEngine-Dist.zip— extract, runsetup.exe(Windows)- Source:
git clone, thenpython setup.py
Known rough edges
- First frozen build per machine can take ~a minute (PyInstaller cold start).
- A mis-bundled
pywebviewfalls back to the default browser rather than erroring. - Only a single
.pakbeside the.exeis supported. - Tooling is Windows-first; running from source on macOS/Linux is untested.
v0.2
VNEngine v0.2
For coders
The big fix: saves no longer break when you edit the script
- Every save now stores a hash of the script plus its position as
(nearest label, offset)instead of a raw op index. Insert a line in chapter 1 and existing saves in chapter 3 still resume in the right place. - If the hash doesn't match on load, the player gets a "Save may be out of date" dialog (Resume / Restart chapter / Back to title) instead of silently landing mid-sentence in the wrong scene.
Read-tracking cleaned up
seen("have I read this line") moved to its own localStorage key,vnengine_seen, keyed bylabel+offset. It's no longer cloned into all 30 checkpoints, so it can't blow the storage quota.lsSetnow reports failures (quota full) with a toast + console error instead of swallowing them.
Boot-time validator
- On load,
engine.jschecks the script and logs a grouped console report:- Errors: duplicate labels, unknown jump/choice targets
- Warnings: unknown characters, unknown sfx names, unreachable code
- Each entry includes the op index. It never stops the game.
jump/ifto a missing label is now guarded (was a hard crash).
Rendering / effects
renderSprites()diffs bydata-idand swapssrcin place instead of rebuilding. Fixes sprite pop-in and the sprite "jump" on every expression change (the bob animation no longer restarts).- The flash effect is removed on
animationend, so it fades fully instead of being cut at 260ms. - Character color now actually tints the name box (with an auto light/dark text-contrast pick).
New engine subsystems
- Rollback (50-entry ring buffer; wheel-up / PageUp / button)
- A backlog overlay
- Skip / auto-advance modes
Tooling
playtest.py: dev mode sendsCache-Control: no-store(so the?v=cache-busting trick is gone), packaged-archive assets get a long immutable cache, and it scans for a free port instead of assuming port 8000 is the same game.- Fonts are bundled as
woff2insrc/fonts/and@font-face'd — no Google Fonts request, works offline. setup.pynow rewrites the title-screen logo and credit line fromproject.json, so a fresh project doesn't show the engine's branding.audio.js: the deadMARKSmap is wired intomusic()offsets;SFX_NAMESis exported for the validator.
Touched: engine.js (most of it), story.js, audio.js, style.css, index.html, playtest.py, setup.py, VNengine.md, README.md, new src/fonts/. Nothing committed.
For writers
Your saves won't break when you edit your script. Add, move, or rewrite lines freely. Old saves resume in the right spot because they're anchored to the nearest label, not a line count. If a save is from an older draft, the player sees a small "this save may be out of date" prompt instead of getting dumped into the wrong scene.
The console now tells you when you make a mistake. Open the browser console (F12) and the engine lists: typo'd jump/choice targets, duplicate label names, characters or sound names you referenced but didn't define, and unreachable lines — each with its position. It won't stop your game, it just points at the problem.
New things you can write
- Variables in text.
set({ gold: 5 })thensay('ari', 'You have {gold} coins'). Any variable name in{curly braces}gets substituted. - Text styling.
[b]bold[/b],[i]italic[/i],[c=#ff8ec4]coloured[/c]inside any line. - Conditional choices. Give a choice option a
showfunction and it only appears when that's true:{ text: 'Hug them', to: 'hug', show: function (s) { return s.vars.warmth > 2; } }
- Set a number exactly. Normally
set({ gold: 5 })adds 5. Useabs()to assign:set({ gold: abs(0) })sets it to 0. - Voice lines.
say('ari', 'Hello.', 'talk', 'ari_hello')— the 4th argument plays a sound file (registered inSFX_FILES) for that line.
New things your players get, for free
- Rollback — scroll the mouse wheel up, press PageUp, or click the ↶ button to go back a line.
- Backlog — press
Lor the ▤ button to see everything said so far. - Skip — hold Ctrl (or press Tab) to fast-forward; it stops at every choice.
- Auto-advance — press
A.
Other
- Character color now tints the name box, not just the placeholder card.
- Sprites fade in smoothly and no longer jump when they change expression.
- The flash effect fades instead of cutting off.
- Fonts are built in, so your game works with no internet.
- Editing while playtesting: just reload — no more bumping
?v=numbers.
v0.1.1
Better UI & UX for all CLI scripts
Full Changelog: v0.1...v0.1.1
v0.1
First release. enjoy
Full Changelog: https://github.com/kefiiiiir/VNEngine/commits/v0.1