??, an emoji adventure
October 7, 2019 6:13 PM Subscribe
??, an emoji adventure
π is a tiny emoji-based adventure game I made for the latest Ludum Dare game jam, built out of Javascript, HTML, CSS, and the game's internal emoji-based scripting language.
The game is built around a simple rewriting system for emoji, each operation replacing some collection of emoji with others, with the player and the game together making choices of which legal operations to perform based on the symbols available. As such systems are Turing-complete, I was able to implement a game engine out of these rewrite rules, featuring exploration, turn-based combat, and leveling up. I made things easier for myself by creating shorthand codes for commonly useful operations (for example, "π₯ππ£" means "replace π₯ with π£ as many times as possible"). I also made something like an event system whereby rules can be grouped with a tag: "βΆπ" means "perform all currently-legal operations tagged with π". As an example, the "damage" emoji is π, and it's associated with the rule "π:πβ€β‘π€", meaning "immediately after creating a π, perform the substitution 'replace πβ€ with π€' once."
π is a tiny emoji-based adventure game I made for the latest Ludum Dare game jam, built out of Javascript, HTML, CSS, and the game's internal emoji-based scripting language.
The game is built around a simple rewriting system for emoji, each operation replacing some collection of emoji with others, with the player and the game together making choices of which legal operations to perform based on the symbols available. As such systems are Turing-complete, I was able to implement a game engine out of these rewrite rules, featuring exploration, turn-based combat, and leveling up. I made things easier for myself by creating shorthand codes for commonly useful operations (for example, "π₯ππ£" means "replace π₯ with π£ as many times as possible"). I also made something like an event system whereby rules can be grouped with a tag: "βΆπ" means "perform all currently-legal operations tagged with π". As an example, the "damage" emoji is π, and it's associated with the rule "π:πβ€β‘π€", meaning "immediately after creating a π, perform the substitution 'replace πβ€ with π€' once."
Role: designer and programmer
...at least I managed to fall in love (also: π) with a π. That's a kind of winning, right? ;-)
This was a lot of fun. I love the concepts in the scripting language too.
posted by avapoet at 4:33 AM on October 8, 2019
This was a lot of fun. I love the concepts in the scripting language too.
posted by avapoet at 4:33 AM on October 8, 2019
Congrats on the Good Ending. β» is, right now, the level cap; "π§:πππππ°π" is just the thing that opens all the locations when you click on the map (triggering the π§ event).
posted by NMcCoy at 4:55 PM on October 8, 2019
posted by NMcCoy at 4:55 PM on October 8, 2019
(Also I just noticed that the game's name got digested into question marks in the post title; the perils of being a pretentious indie game dev, I suppose.)
posted by NMcCoy at 4:58 PM on October 8, 2019 [1 favorite]
posted by NMcCoy at 4:58 PM on October 8, 2019 [1 favorite]
Crossposted from my comment on the game's Ludum Dare page, in response to someone being curious as to how it was implemented:
All the source code is embedded in the html file, though not really documented or anything (I was kinda making it up as I went so there's some questionable design decisions), so to summarize:
The core of the game consists of a dictionary serving as the "emoji pool", tracking the count of each emoji in play, and a dictionary mapping emoji->the "rules" associated with the emoji. Each rule is structured as "event:operation", the operation usually being a substitution effect (indicated with β‘) but sometimes a shorthand (e.g., bare emoji without one of my specially handled "verb" emoji is a shorthand for "replace nothing with these"). Hovering over an emoji lists all the rules that could currently be executed if the appropriate event were triggered (or all the rules if hints are enabled); clicking on an emoji fires the π event on that emoji, executing all the π rules on it. π and π are the only events explicitly invoked by the Javascript framework, all others are triggered "in-engine" by the βΆ verb-emoji. When this happens, the framework iterates over all non-zero-count emoji in the pool and executes the appropriate rules on each of them, a number of times equal to the count for that emoji.
In terms of presentation, I set it up so that there's a couple divs in the page that "request" specific emojis from the pool in a given order using a data-value in their tag; the "render" function first checks for the requests made by the page, grabbing those emoji from a temporary copy of the pool and allocating them, then puts all the remaining non-requested emoji in the main centered div. (There were a lot of visual bugs in testing, like when I forgot to add π₯Ύ to the inventory requests so they hung around in the main pool div instead.) Usable buttons (those with at least one π event that could legally be executed) are given an extra class when being written to the html of the page, which the CSS then gives an eye-catching rainbow-animated background to.
Figuring out how to have the emoji game engine track if the player was in combat/had defeated all foes was tricky; I didn't implement a categorization system, so all the combatant emoji had to have a rule where they responded to an event to indicate that they were still alive, and a rule that removed them when the player fled combat. Halfway through development, I made a helper function that I could give a list of "these emoji are combatants" and it would append those two rules to the dictionary's rules for that emoji, saving me a lot of further repetitive emoji-typing. The β emoji signifies that the player is in combat; the β :β rule that the combatants all have means "when the β event fires, add a β emoji to the pool" as an indicator of "hey, I'm still alive". The defeat rule for each combatant looks like "π:π₯π₯π₯π¦β‘βββ" -- when clicked, trade 3 damage emojis and a shark for 2 experience points and a check mark, if such a trade is possible. The β has a π event that removes all β , fires the β event to ask if anyone's still around, makes a πΊ if there aren't any β in the pool after that, then replaces itself with nothing. In hindsight there may have been a cleaner way to do it, but I think that it's a good solution for not exposing too much unintelligible "system guts" to the player, since *all* of the game state is stored/represented by visible emoji! In the end, I was able to disguise most of the mechanical state as UI - unlike in most games where the health bar on the HUD is a visual depiction of the internal number that tracks the player's HP, in this game the health bar literally IS the player's HP! Kind of a wild concept, if you're used to the model/view/controller paradigm of game architecture; here the model and view are one and the same, and the controller mostly consists of things taped onto the modelview elements.
posted by NMcCoy at 3:47 AM on October 9, 2019
All the source code is embedded in the html file, though not really documented or anything (I was kinda making it up as I went so there's some questionable design decisions), so to summarize:
The core of the game consists of a dictionary serving as the "emoji pool", tracking the count of each emoji in play, and a dictionary mapping emoji->the "rules" associated with the emoji. Each rule is structured as "event:operation", the operation usually being a substitution effect (indicated with β‘) but sometimes a shorthand (e.g., bare emoji without one of my specially handled "verb" emoji is a shorthand for "replace nothing with these"). Hovering over an emoji lists all the rules that could currently be executed if the appropriate event were triggered (or all the rules if hints are enabled); clicking on an emoji fires the π event on that emoji, executing all the π rules on it. π and π are the only events explicitly invoked by the Javascript framework, all others are triggered "in-engine" by the βΆ verb-emoji. When this happens, the framework iterates over all non-zero-count emoji in the pool and executes the appropriate rules on each of them, a number of times equal to the count for that emoji.
In terms of presentation, I set it up so that there's a couple divs in the page that "request" specific emojis from the pool in a given order using a data-value in their tag; the "render" function first checks for the requests made by the page, grabbing those emoji from a temporary copy of the pool and allocating them, then puts all the remaining non-requested emoji in the main centered div. (There were a lot of visual bugs in testing, like when I forgot to add π₯Ύ to the inventory requests so they hung around in the main pool div instead.) Usable buttons (those with at least one π event that could legally be executed) are given an extra class when being written to the html of the page, which the CSS then gives an eye-catching rainbow-animated background to.
Figuring out how to have the emoji game engine track if the player was in combat/had defeated all foes was tricky; I didn't implement a categorization system, so all the combatant emoji had to have a rule where they responded to an event to indicate that they were still alive, and a rule that removed them when the player fled combat. Halfway through development, I made a helper function that I could give a list of "these emoji are combatants" and it would append those two rules to the dictionary's rules for that emoji, saving me a lot of further repetitive emoji-typing. The β emoji signifies that the player is in combat; the β :β rule that the combatants all have means "when the β event fires, add a β emoji to the pool" as an indicator of "hey, I'm still alive". The defeat rule for each combatant looks like "π:π₯π₯π₯π¦β‘βββ" -- when clicked, trade 3 damage emojis and a shark for 2 experience points and a check mark, if such a trade is possible. The β has a π event that removes all β , fires the β event to ask if anyone's still around, makes a πΊ if there aren't any β in the pool after that, then replaces itself with nothing. In hindsight there may have been a cleaner way to do it, but I think that it's a good solution for not exposing too much unintelligible "system guts" to the player, since *all* of the game state is stored/represented by visible emoji! In the end, I was able to disguise most of the mechanical state as UI - unlike in most games where the health bar on the HUD is a visual depiction of the internal number that tracks the player's HP, in this game the health bar literally IS the player's HP! Kind of a wild concept, if you're used to the model/view/controller paradigm of game architecture; here the model and view are one and the same, and the controller mostly consists of things taped onto the modelview elements.
posted by NMcCoy at 3:47 AM on October 9, 2019
Took me a while for the concept to click but what a clever little game.
posted by AndrewStephens at 5:48 AM on October 9, 2019
posted by AndrewStephens at 5:48 AM on October 9, 2019
« Older Peer Learning is...... | All the Buddha Boxes: 70 Songs... Newer »
posted by avapoet at 4:09 AM on October 8, 2019