<!--210218,210107-->
<h1>designing an achievement system (part 2) </h1>
april 2, 2021<br>
#gamedev #gamedesign #achievements <br>
<br>
<p>Designing an achievement system without any octopus tangles. <br></p>
<img src="/static/img/ent/rpgachievement_octopus.png" alt="(illustration: an octopus tangling up UI, Dialog, and other game systems.)"> <br>
<h2>a trusty mailman </h2>
<p>Octopus-tangling is a major design concern for a system that is so interconnected with every other system. I could scatter achievement code through every other system, but that would be a problem if I ever need to make a fundamental change to the achievement system. Also, tacking achievement code everywhere will make the other systems cluttered. <br></p>
<br>
<p>Instead, Blessfrey's achievement system is broken into three main, self-contained pieces: event handlers, the Knowledge Base, and the MessageBus. <br></p>
<br>
<p>Let's define the terms: <br></p>
<br>
<ul>
<li><b>knowledge:</b> each granular action or world event that contributes to earning an achievement </li>
<li><b>key:</b> an id number that identifies a piece of knowledge </li>
<li><b>topic:</b> just arrays of keys; used to categorize knowledge into groups </li>
<li><b>event handlers:</b> they subscribe to topics, waiting for news that a specific piece of knowledge has been encountered </li>
<li><b>Knowledge Base:</b> a singleton that stores all knowledge and facilitates the learning and forgetting of pieces of knowledge. <li><b>MessageBus:</b> a singleton that acts as a mailman; receives information about encountered knowledge and passes it off to all event handlers subscribed to that topic. </li>
</ul><br>
<p>Essentially, there is a database that stores all the achievements in the game, alongside a boolean value for locked or unlocked and some contextual information such as when they were unlocked. There are event handlers that wait for events to happen to unlock achievements and event handlers that wait for unlocked achievements to pay out rewards. Every event is filtered through the MessageBus and sent out to the relevant entities. This way, the only achievement code scattered everywhere are single MessageBus.subscribe("topic") lines. <br></p>
<br>
<h2>an example</h2>
<p>Let's say you get an achievement for finding the Nurse's Office. The moment the player loads into the Nurse's Office, data will zip back and forth between the MessageBus and the nurse's office object, different event handlers and the Knowledge Base. <br></p>
<a target="_blank" href="/static/img/ent/rpgachievement_KnowledgeBaseDiagram.png">
    <img src="/static/img/ent/rpgachievement_KnowledgeBaseDiagram.png" alt="(diagram: a zigzagging depiction of the list below.)">
</a><br>
<ol>
    <li>(Event Handler) At ready, event handlers call the MessageBus and subscribe to topics. </li>
    <li>(Nurse's Office) The player enters the Nurse's Office. The room object sends itself to the MessageBus. </li>
    <li>(MessageBus) Receives room object + sends it to all event handlers subscribed to the "place_entered" topic. </li>
    <li>(Event Handler) NursesOfficeEntered receives room object. If the room is the Nurse's Office, send its corresponding knowledge key to the MessageBus. It can also verify pre-requisites and gather additional data for the Knowledge Base. This way, the system supports anything I'd like to track about when or how knowledge was learned. </li>
    <li>(MessageBus) Receives the knowledge key + sends it to the Knowledge Base. </li>
    <li>(Knowledge Base) Finds the knowledge identified by the incoming key. "Learns" by setting that knowledge to true and filling in additional fields if extra data was sent. Sends the knowledge key to the MessageBus. </li>
    <li>(MessageBus) Receives the knowledge key + sends it to all "learned" event handlers. </li>
    <li>(Event Handler) KnowledgeLearned receives the knowledge key + calls code for any changes resulting from learning this knowledge. Maybe you'll get a Steam achievement, but if the Knowledge Base was being to facilitate game progression, a quest could update, the dialog system could unlock the option to ask about the Nurse's Office, or you could gain a Codex entry about the new location. The changes can be conditional, too, so the handler can track whether all necessary keys have been received before enacting the change. </li>
</ol><br>
<br>
<p>To use the achievement system for cyclical world events, you could trigger knowledge to be "forgotten" or ultimately set back to false in the Knowledge Base. This way, the phases of an event could begin anew. <br></p>
<br>
<br>
<h2>summary</h2>
<p>Achievements can come from any combination of in-game actions, so an achievement system should be designed separately from the rest of the game. I achieve this through a couple of separate objects. <br></p>
<br>
<ul>
    <li>Event Handlers: The tracking, verifying, and reward payout should be contained within event handlers, which can be generated and freed as needed. They subscribe to general topics and wait for their specific event to occur. </li>
    <li>The Knowledge Base tracks the status of all knowledge in the game and can be used to understand how far the player and world have progressed. </li>
    <li>The MessageBus is very light and only allows event handlers to subscribe to topics and for incoming message to be transmitted through that topic. It has absolutely no unique checks or code to execute, impartially delivering mail to the address on the envelope. </li>
    <li>Another set of event handlers is concerned about the outcome of encountering and learning knowledge and can prompt changes or directly impact other systems, depending on pre-requisites met.  </li>
<br>
<br>
Last updated July 28, 2022 <br>
<br>
