This repository has no description
Python 79%
Java 11%
HTML 9%
Batchfile <1%

README.md

jev-facade #

Façade (2005) with its text parser replaced by Jev, a decision model that returns a typed choice with a probability instead of text.

Façade never tried to understand sentences. It sorts whatever you type into one of about 35 social moves (agree, criticize, flirt, take a side, bring up the wedding photo...) and the whole drama runs on those. The sorting was done by a few thousand hand-written pattern rules, and it is the part of the game that aged worst: famously, it cannot tell what you mean most of the time.

This project swaps only that step. Your line goes to Jev, Jev picks the move, and the game's own reaction logic, story beats and drama manager carry on exactly as they were written, now reacting to what you actually said.

The live monitor: each line the player types, the moves sent to the game, and Jev's distribution for every question

Quick start #

You need three things, all free to get:

Façade 1.1 for Windows Free from its authors: playablstudios.com/facade. Install it to the default folder.
Python 3.9+ python.org/downloads. Tick "Add python.exe to PATH" in the installer. Nothing else to install: the sidecar is standard library only.
An OpenRouter API key Make one at openrouter.ai/keys and add a little credit at openrouter.ai/credits. Each line you type costs a fraction of a cent.

Then:

  1. Get this repo with git clone.
  2. Double-click install.bat. It asks for administrator rights (Façade lives in Program Files) and patches the game. Every file it replaces is kept beside it as <name>.orig.
  3. Double-click play.bat. The first time, it creates a .env file and opens it: paste your key after OPENROUTER_API_KEY=, save, and run play.bat again.

From then on play.bat is all you run. It starts the sidecar, opens the monitor in your browser and launches Façade. Leave its window open while you play, and type to Trip and Grace as usual.

To put the original game back: install.bat --uninstall. If Façade is not in C:\Program Files (x86)\Facade, give both scripts the folder (install.bat "D:\Games\Facade") or set a FACADE_HOME environment variable.

How it fits together #

Façade (bundled Java 1.4)  --plain HTTP, loopback-->  sidecar (Python)  --HTTPS-->  OpenRouter / Jev

The sidecar exists because the Java runtime the game ships with is too old to make a modern TLS connection, and because it keeps your API key out of the game folder. If the sidecar is not running, or Jev cannot be reached, the patched game falls back to its original parser line by line, so it is always playable. The sidecar always listens on 127.0.0.1:8765; that address is compiled into the game patch, so it is not a setting.

This repository contains no game files. You need your own copy of Façade, which its authors, Michael Mateas and Andrew Stern, released as freeware. This project is not affiliated with them or with TypeSafe. It was written against Façade 1.1 (the 2006 Windows installer: util/j2re1.4.2_06, loose class files in util/classes) and is Windows only, like the game.

The pieces play.bat and install.bat wrap, if you would rather run them yourself:

python -m sidecar.repl                  # type lines, see the moves; no game needed
python -m sidecar.server                # just the sidecar; monitor at http://127.0.0.1:8765/
python tools/install_patch.py --status  # no flag installs, --uninstall removes; needs an admin terminal

The monitor #

Every line the game sends shows up live: what you typed, the moves sent back (orange outline means said to Grace, blue to Trip), the round-trip time, and an amber "deflect" when Jev did not find a move. Click a line for Jev's top options on every question, what Trip and Grace had just said, which situation the game reported, and the exact integers handed to the game. The box at the top sends a line through the same path without the game.

How a line becomes moves #

The game's internal name for a move is a discourse act: five integers, an act type, who it was said to, and up to three parameters. sidecar/da_schema.py is that vocabulary as data, with a description of each option written for Jev.

One request asks Jev several questions about the same state. The state is your line (with trip/grace capitalised, because lowercase "trip" reads as a noun), a sentence describing the scene, what Trip and Grace just said, and what the game says is happening (its active parser contexts, translated to plain English by sidecar/contexts.py). The questions:

  • act: which move, or other
  • addressee: Grace, Trip or both
  • reference: which topic or object the line brings up, or none
  • one question per parameter family: emotion, question word, trait or advice, and who/relation/whom for the big accusations

The reference is its own question because it co-occurs with any act: "I love your couch" is praise and a reference to the couch.

A choice is accepted on the probability of the winner, not on Jev's confidence score. Confidence measures how peaked the distribution is, so two near-synonyms splitting the vote (0.51 oppose, 0.33 pacify) look like low confidence when they are really two good readings. The sidecar takes the winner, and deflects only when other wins or the winner is a weak plurality.

Output follows the game's own rules, read from its rule files: a reference is never addressed to anyone and its question word defaults to why; the big accusation is (relation, who, whom); and a SystemDoesntUnderstand act rides along with every line, as it does in the original, so a beat with no reaction to the real act deflects instead of going silent.

What the patch changes in the game #

The game starts its Java side as a separate java.exe on a bundled Java 1.4.2, with its classes as loose files, so single classes can be swapped. The patch replaces three and adds three (tools/patch_manifest.py is the full list):

  • TemplateCompiler.Preprocessor turned a typed line into word facts for the rule engine. The replacement asks the sidecar instead and asserts the resulting (DA type char p1 p2 p3) facts itself; the game's own listener turns those into the objects its story logic reads. The original class is kept, renamed inside its bytecode to PreprocessorRules, and handles any line the sidecar cannot answer. That renamed class is the game's own code, so it is not in this repo: the installer makes it from your copy.
  • facade.primact.PushTripDialogAnimation and PushGraceDialogAnimation are the acts every spoken line goes through. The replacements do what the originals did and also note the line, so Jev can be told what you are replying to. The game has no subtitle text, but it names every recorded line after its words (..._FASKDRINKT1NTPA_how1_does_a_martini_sound), and sidecar/dialogue.py turns that into TRIP: "how does a martini sound", keeping stage directions such as FURIOUS as the tone.

The five classes of ours ship compiled in patch/prebuilt/, which is why installing needs no JDK. The sources are in patch/src/.

Development #

python -m unittest discover -s tests     # no key needed: runs against a fake OpenRouter
python -m sidecar.evaluate --misses      # live: scores Jev on eval/cases.tsv, a request per line
python tools/extract_rules.py            # decompress the game's rule files into vendor/ (gitignored)
python tools/show_rules.py MapToDA       # read them

Changing patch/src is the one thing that needs a JDK (9 to 21, for example Adoptium) on PATH:

python tools/build_patch.py              # compile, retarget to Java 1.4, refresh patch/prebuilt/
python tools/test_patch.py               # run the result on the game's own JVM (start the sidecar first)

Nobody ships a Java 1.4 compiler any more, so the build compiles with a modern javac --release 8, refuses classes that reference post-1.4 APIs, and rewrites the class file version to 48. The sources therefore stay inside Java 1.4: no string + (it compiles to StringBuilder), no autoboxing, no class literals, no for-each over collections, no inner classes. test_patch.py is the proof: it uses the game's JVM, working directory and launch flags, against the sidecar and then against a dead port to show the fallback. Commit patch/prebuilt/ with the source change; a unit test fails if the two drift apart.

eval/cases.tsv is a set of labelled player lines, some from real play sessions. The option descriptions in da_schema.py were tuned against it, so it is a regression check, not a benchmark. Jev currently gets all 84, at about 200 ms median per request; the full round trip as the game sees it is typically 130 to 500 ms, in the same range as the game's own 300 ms input polling.

When a line is misread, the monitor shows which question went wrong; the fix is usually a clearer option description. When it is unclear where a phrase belongs, the game's grammar is the authority: tools/show_rules.py will show, for instance, that "stop it" is a disagree idiom there, not a pacify.

Not done #

  • The Misc act and its beat-specific parameters, and drink orders, are not mapped. Lines that would have used them deflect or land on a nearby act.
  • Gestures (hug, comfort, kiss, picking things up) never went through the text parser and are untouched.

Credits #

Façade is by Michael Mateas and Andrew Stern (Procedural Arts). Reading the game's back end was only practical thanks to the FacadeResearch decompilation.

Code in this repository is MIT licensed; see LICENSE.