$ a murder mystery game built in 72 hours for Winter MelonJam 2025
Massaquerade is the third game built with a partner, for the Winter MelonJam 2025. The theme was "Mask." The player attends a masquerade ball as the murderer: talk to guests, collect clues, and identify the traitor. Each new piece of information can open up branches in conversations you've already had. Once you've decided who the target is, press M to strike.
The game ranked 36th overall out of 200 submissions, with 17th place in the Art category and 39th in Gameplay.
Dialogue is driven entirely by JSON files. One file per character per mask. Each dialogue node
defines the display text, response options, conditions required to show the option, flags to set
when the option is chosen, and the next node to advance to. Conditions are evaluated against two
scopes: conversationState, a dictionary local to the active conversation that
accumulates flags set during the current exchange, and Data.player.actionFlags, the
global persistent flag store that persists across all conversations.
The result is that choosing a response in one conversation can set a global flag that unlocks a new branch in an entirely different character's dialogue tree. The clue chain the player follows to identify the target.
Building a scalable dialogue system was the core technical challenge. A naive approach, writing dialogue state directly into scripts, would have made branching and authoring impossible to manage. The JSON tree format let dialogue content be written outside the code entirely.
The two-scope flag system (local conversation vs. global) required careful design: local flags track what has been said in the current exchange (useful for preventing repeat questions), while global flags track what the player has learned across all interactions (useful for unlocking new branches after the fact). Getting the evaluation order right (check conditions, display options, then apply mutations) meant no flag changes could leak backward into the conditions that gated the current step.
The honest limitation of this jam was throughput. The main bottleneck was not systems but content: writing enough unique, coherent dialogue for multiple characters across multiple mask variants consumed most of the 72 hours. The game systems were solid; the game was smaller than planned because writing is slow.
By far the biggest thing learned through this jam was how to build a dialogue system. We did a JSON-based system where you could define flags to set, flags to check, the next node to go to, and many other options. Not only was building this scalable system a challenge, but it was also quite difficult to try and write all the dialogue itself.
One of the main differences between this game and the previous ones is that it felt like the main limitation was how fast we could write dialogue instead of adding cooler systems and interactions. The game systems were solid; the game was smaller than planned because writing is slow. Regardless, it was a great time working on it and the result is something to be proud of.
The biggest learning was how much time narrative content takes relative to code. Systems that feel small to implement become enormous when multiplied by the number of characters, masks, and dialogue branches needed to make the mystery feel real. Some key takeaways: