emacs.d/clones/lisp/stevelosh.com/blog/2012/07/caves-of-clojure-06/index.html

349 lines
30 KiB
HTML
Raw Normal View History

2022-10-07 15:47:14 +02:00
<!DOCTYPE html>
<html lang='en'><head><meta charset='utf-8' /><meta name='pinterest' content='nopin' /><link href='../../../../static/css/style.css' rel='stylesheet' type='text/css' /><link href='../../../../static/css/print.css' rel='stylesheet' type='text/css' media='print' /><title>The Caves of Clojure: Part 6 / Steve Losh</title></head><body><header><a id='logo' href='https://stevelosh.com/'>Steve Losh</a><nav><a href='../../../index.html'>Blog</a> - <a href='https://stevelosh.com/projects/'>Projects</a> - <a href='https://stevelosh.com/photography/'>Photography</a> - <a href='https://stevelosh.com/links/'>Links</a> - <a href='https://stevelosh.com/rss.xml'>Feed</a></nav></header><hr class='main-separator' /><main id='page-blog-entry'><article><h1><a href='index.html'>The Caves of Clojure: Part 6</a></h1><p class='date'>Posted on July 30th, 2012.</p><p>This post is part of an ongoing series. If you haven't already done so, you
should probably start at <a href="../caves-of-clojure-01/index.html">the beginning</a>.</p>
<p>This entry corresponds to <a href="http://trystans.blogspot.com/2011/09/roguelike-tutorial-06-hitpoints-combat.html">post six in Trystan's tutorial</a>.</p>
<p>If you want to follow along, the code for the series is <a href="http://bitbucket.org/sjl/caves/">on Bitbucket</a> and
<a href="http://github.com/sjl/caves/">on GitHub</a>. Update to the <code>entry-06</code> tag to see the code as it stands
after this post.</p>
<p>Sorry for the long wait for this entry. I've been working on lots of other
stuff and haven't had a lot of time to write. Hopefully I can get back to it
more often!</p>
<ol class="table-of-contents"><li><a href="index.html#s1-summary">Summary</a></li><li><a href="index.html#s2-refactoring">Refactoring</a></li><li><a href="index.html#s3-attacking-and-defending">Attacking and Defending</a></li><li><a href="index.html#s4-messaging">Messaging</a></li><li><a href="index.html#s5-results">Results</a></li></ol>
<h2 id="s1-summary"><a href="index.html#s1-summary">Summary</a></h2>
<p>In Trystan's sixth post he adds a combat system and messaging infrastructure.
Once again I'm following his lead and implementing the same things, but in the
entity/aspect way of doing things.</p>
<p>As usual I ended up refactoring a few things, which I'll briefly cover first.</p>
<h2 id="s2-refactoring"><a href="index.html#s2-refactoring">Refactoring</a></h2>
<p>So far, the functions entities implement to fulfill aspects have looked like
this:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defaspect</span></i> Digger
<span class="paren2">(<span class="code">dig <span class="paren3">[<span class="code">this world dest</span>]</span>
...</span>)</span>
<span class="paren2">(<span class="code">can-dig? <span class="paren3">[<span class="code">this world dest</span>]</span>
...</span>)</span></span>)</span></span></code></pre>
<p>The entity has to be the first argument, because that's how protocols work.
I don't have any flexibility there. I originally made the world always be the
second argument, but it turns out that it's more convenient to make the world
the <em>last</em> argument.</p>
<p>To see why, imagine we want to allow players to dig and move at the same time,
instead of forcing them to be separate actions. Updating the world might look
like this:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren2">[<span class="code">new-world <span class="paren3">(<span class="code">dig player world dest</span>)</span>
new-world <span class="paren3">(<span class="code">move player world dest</span>)</span></span>]</span>
new-world</span>)</span></span></code></pre>
<p>You could make this one specific case a bit prettier, but in general chaining
together world-modifying actions is going to be a pain. If I change the aspect
functions to take the player, then other args, and <em>then</em> the world, I can use
<code>-&gt;&gt;</code> to chain actions:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">-&gt;&gt; world
<span class="paren2">(<span class="code">dig player dest</span>)</span>
<span class="paren2">(<span class="code">move player dest</span>)</span></span>)</span></span></code></pre>
<p>Much cleaner! I went ahead and switched all the aspect functions to use this
new scheme.</p>
<p>I also did some other minor refactoring. You can look through the changesets if
you're really curious.</p>
<h2 id="s3-attacking-and-defending"><a href="index.html#s3-attacking-and-defending">Attacking and Defending</a></h2>
<p>Instead of simply killing everything in one hit, I'm now going to give some
creatures a bit of hp. I also added a <code>:max-hp</code> attribute, since I'll likely
need that in the future. Here's a sample of what the <code>Bunny</code> creation function
looks like:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> make-bunny <span class="paren2">[<span class="code">location</span>]</span>
<span class="paren2">(<span class="code">map-&gt;Bunny <span class="paren3">{<span class="code"><span class="keyword">:id</span> <span class="paren4">(<span class="code">get-id</span>)</span>
<span class="keyword">:name</span> <span class="string">&quot;bunny&quot;</span>
<span class="keyword">:glyph</span> <span class="string">&quot;v&quot;</span>
<span class="keyword">:color</span> <span class="keyword">:yellow</span>
<span class="keyword">:location</span> location
<span class="keyword">:hp</span> 4
<span class="keyword">:max-hp</span> 4}</span>)</span></span>)</span></span></span></span></code></pre>
<p>I started using the <code>map-&gt;Foo</code> record constructors because the <code>-&gt;Foo</code> versions
that relied on positional arguments started getting hard to read.</p>
<p>Bunnies have 4 hp. It'd be trivial to randomize this in the future, but for now
I'll stick with a simple number.</p>
<p>Trystan uses a simple attack and defense system. I toyed with the idea of using
a different one (like Brogue's) but figured I should stick to his tutorial when
there's no clear reason not to.</p>
<p>Trystan's system needs attack and defense values, so I added functions to the
<code>Attacker</code> and <code>Destructible</code> aspects to retrieve these:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defaspect</span></i> Attacker
<span class="paren2">(<span class="code">attack <span class="paren3">[<span class="code">this target world</span>]</span>
...</span>)</span>
<span class="paren2">(<span class="code">attack-value <span class="paren3">[<span class="code">this world</span>]</span>
<span class="paren3">(<span class="code">get this <span class="keyword">:attack</span> 1</span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defaspect</span></i> Destructible
<span class="paren2">(<span class="code">take-damage <span class="paren3">[<span class="code">this damage world</span>]</span>
...</span>)</span>
<span class="paren2">(<span class="code"><i><span class="symbol">defense-value</span></i> <span class="paren3">[<span class="code">this world</span>]</span>
<span class="paren3">(<span class="code">get this <span class="keyword">:defense</span> 0</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>The default attack value is <code>1</code>. If an entity has an <code>:attack</code> attribute that
will be used instead. Or the entity could provide a completely custom version
of <code>attack-value</code> (e.g.: werewolves could have a larger attack if there's a full
moon in the game or something). Defense values work the same way.</p>
<p>Now that I'm starting to actually use HP I'll display it in the bottom row of
info on the screen:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> draw-hud <span class="paren2">[<span class="code">screen game</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">[<span class="code">hud-row <span class="paren4">(<span class="code">dec <span class="paren5">(<span class="code">second <span class="paren6">(<span class="code">s/get-size screen</span>)</span></span>)</span></span>)</span>
player <span class="paren4">(<span class="code">get-in game <span class="paren5">[<span class="code"><span class="keyword">:world</span> <span class="keyword">:entities</span> <span class="keyword">:player</span></span>]</span></span>)</span>
<span class="paren4">{<span class="code"><span class="keyword">:keys</span> <span class="paren5">[<span class="code">location hp max-hp</span>]</span></span>}</span> player
<span class="paren4">[<span class="code">x y</span>]</span> location
info <span class="paren4">(<span class="code">str <span class="string">&quot;hp [&quot;</span> hp <span class="string">&quot;/&quot;</span> max-hp <span class="string">&quot;]&quot;</span></span>)</span>
info <span class="paren4">(<span class="code">str info <span class="string">&quot; loc: [&quot;</span> x <span class="string">&quot;-&quot;</span> y <span class="string">&quot;]&quot;</span></span>)</span></span>]</span>
<span class="paren3">(<span class="code">s/put-string screen 0 hud-row info</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>The bottom row of the screen now looks like: &quot;hp [20/20] loc: [82-103]&quot;. I'll
probably get rid of the loc soon, but for now it won't hurt to keep it onscreen.</p>
<p>Now for the damage calculation. I added a little helper function to take care
of this in <code>attacker.clj</code>:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> get-damage <span class="paren2">[<span class="code">attacker target world</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">[<span class="code">attack <span class="paren4">(<span class="code">attack-value attacker world</span>)</span>
<i><span class="symbol">defense</span></i> <span class="paren4">(<span class="code"><i><span class="symbol">defense-value</span></i> target world</span>)</span>
max-damage <span class="paren4">(<span class="code">max 0 <span class="paren5">(<span class="code">- attack <i><span class="symbol">defense</span></i></span>)</span></span>)</span>
damage <span class="paren4">(<span class="code">inc <span class="paren5">(<span class="code">rand-int max-damage</span>)</span></span>)</span></span>]</span>
damage</span>)</span></span>)</span></span></code></pre>
<p>This matches what Trystan does. In a nutshell, the damage done is: &quot;If defense
is higher than attack, then 1. Otherwise, a random number between 1 and (attack
- defense).&quot;</p>
<p>I kept it separate from the <code>attack</code> function so that an entity can override
attack without having to reimplement this logic.</p>
<p>The <code>attack</code> default implementation needs to use this new damage calculator:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defaspect</span></i> Attacker
<span class="paren2">(<span class="code">attack <span class="paren3">[<span class="code">this target world</span>]</span>
<span class="paren3">{<span class="code"><span class="keyword">:pre</span> <span class="paren4">[<span class="code"><span class="paren5">(<span class="code">satisfies? Destructible target</span>)</span></span>]</span></span>}</span>
<span class="paren3">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren4">[<span class="code">damage <span class="paren5">(<span class="code">get-damage this target world</span>)</span></span>]</span>
<span class="paren4">(<span class="code">take-damage target damage</span>)</span></span>)</span></span>)</span>
<span class="paren2">(<span class="code">attack-value <span class="paren3">[<span class="code">this world</span>]</span>
<span class="paren3">(<span class="code">get this <span class="keyword">:attack</span> 1</span>)</span></span>)</span></span>)</span></span></code></pre>
<p><code>Destructible</code> already handles reducing HP appropriately. The only thing left
is to give my entities some non-1 attack, defense, and/or hp values. For now
I used the following values:</p>
<ul>
<li>Bunnies have 4 HP, default defense.</li>
<li>Lichens have 6 HP, default defense.</li>
<li>Silverfish have 15 HP, default defense.</li>
<li>Players have 40 HP, 10 attack, default defense.</li>
</ul>
<p>These are really just placeholder numbers until I add the ability for monsters
to attack back. Once I do that I'll be able to play the game a bit and
determine if it's too easy or hard.</p>
<h2 id="s4-messaging"><a href="index.html#s4-messaging">Messaging</a></h2>
<p>Now the player can attack things and it may take a few swings to kill them. The
problem is that there's no feedback while this is going on, so it's hard to tell
that you're actually doing damage until the monster dies.</p>
<p>A messaging system will let me display informational messages to give the player
some feedback. I decided to implement this like everything else: as an aspect.</p>
<p>An entity that implements the <code>Receiver</code> protocol will be able to receive
messages. Here's <code>entities/aspects/receiver.clj</code>:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">ns caves.entities.aspects.receiver
<span class="paren2">(<span class="code"><span class="keyword">:use</span> <span class="paren3">[<span class="code">caves.entities.core <span class="keyword">:only</span> <span class="paren4">[<span class="code"><i><span class="symbol">defaspect</span></i></span>]</span></span>]</span>
<span class="paren3">[<span class="code">caves.world <span class="keyword">:only</span> <span class="paren4">[<span class="code">get-entities-around</span>]</span></span>]</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defaspect</span></i> Receiver
<span class="paren2">(<span class="code">receive-message <span class="paren3">[<span class="code">this message world</span>]</span>
<span class="paren3">(<span class="code">update-in world <span class="paren4">[<span class="code"><span class="keyword">:entities</span> <span class="paren5">(<span class="code"><span class="keyword">:id</span> this</span>)</span> <span class="keyword">:messages</span></span>]</span> conj message</span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> send-message <span class="paren2">[<span class="code">entity message args world</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">if</span></i> <span class="paren3">(<span class="code">satisfies? Receiver entity</span>)</span>
<span class="paren3">(<span class="code">receive-message entity <span class="paren4">(<span class="code">apply format message args</span>)</span> world</span>)</span>
world</span>)</span></span>)</span></span></code></pre>
<p>I've got a helper function <code>send-message</code> which is what entities will use to
send messages, instead of performing the <code>(satisfies? Receiver entity)</code> check
themselves every time. If the entity they're sending the message to isn't
a <code>Receiver</code> it will simply drop the message on the floor by returning the world
unchanged. It also handles formatting the message string for them.</p>
<p>The default <code>receive-message</code> simply appends the message to a <code>:messages</code>
attribute in the entity.</p>
<p>I have a lot of ideas about extending this system in the future, but for now
I'll just keep it simple to match Trystan's.</p>
<p>Now I need to send some messages. The most obvious place to do this is when
something attacks something else, so I updated the <code>Attacker</code> aspect once more:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defaspect</span></i> Attacker
<span class="paren2">(<span class="code">attack <span class="paren3">[<span class="code">this target world</span>]</span>
<span class="paren3">{<span class="code"><span class="keyword">:pre</span> <span class="paren4">[<span class="code"><span class="paren5">(<span class="code">satisfies? Destructible target</span>)</span></span>]</span></span>}</span>
<span class="paren3">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren4">[<span class="code">damage <span class="paren5">(<span class="code">get-damage this target world</span>)</span></span>]</span>
<span class="paren4">(<span class="code">-&gt;&gt; world
<span class="paren5">(<span class="code">take-damage target damage</span>)</span>
<span class="paren5">(<span class="code">send-message this <span class="string">&quot;You strike the %s for %d damage!&quot;</span>
<span class="paren6">[<span class="code"><span class="paren1">(<span class="code"><span class="keyword">:name</span> target</span>)</span> damage</span>]</span></span>)</span>
<span class="paren5">(<span class="code">send-message target <span class="string">&quot;The %s strikes you for %d damage!&quot;</span>
<span class="paren6">[<span class="code"><span class="paren1">(<span class="code"><span class="keyword">:name</span> this</span>)</span> damage</span>]</span></span>)</span></span>)</span></span>)</span></span>)</span>
<span class="paren2">(<span class="code">attack-value <span class="paren3">[<span class="code">this world</span>]</span>
<span class="paren3">(<span class="code">get this <span class="keyword">:attack</span> 1</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>This is starting to get a little crowded. If I need to do much more in here
I'll refactor some stuff out into helper functions. But for now it's still
readable.</p>
<p>Here you can see how making world-altering functions take the world as the last
argument pays off by letting me use <code>-&gt;&gt;</code> to chain together actions.</p>
<p>I also added a <code>:name</code> attribute to entities so I can say &quot;You strike the bunny&quot;
instead of &quot;You strike the v&quot;. Nothing too special there.</p>
<p>Finally, I need a way to notify nearby entities when something happens. Here's
what I came up with:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> send-message-nearby <span class="paren2">[<span class="code">coord message world</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">[<span class="code">entities <span class="paren4">(<span class="code">get-entities-around world coord 7</span>)</span>
sm <span class="paren4">(<span class="code">fn <span class="paren5">[<span class="code">world entity</span>]</span>
<span class="paren5">(<span class="code">send-message entity message <span class="paren6">[<span class="code"></span>]</span> world</span>)</span></span>)</span></span>]</span>
<span class="paren3">(<span class="code">reduce sm world entities</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>First I grab all the entities within 7 squares of the message coordinate. Then
I create a little helper function called <code>sm</code> that wraps <code>send-message</code>. It
will take a world and an entity and return the modified world. I use <code>reduce</code>
here to iterate over the entities and send the message to each one. It's
a pretty way of handling that looping.</p>
<p>The <code>get-entities-around</code> function is new:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> get-entities-around
<span class="paren2">(<span class="code"><span class="paren3">[<span class="code">world coord</span>]</span> <span class="paren3">(<span class="code">get-entities-around world coord 1</span>)</span></span>)</span>
<span class="paren2">(<span class="code"><span class="paren3">[<span class="code">world coord radius</span>]</span>
<span class="paren3">(<span class="code">filter #<span class="paren4">(<span class="code">&lt;= <span class="paren5">(<span class="code">radial-distance coord <span class="paren6">(<span class="code"><span class="keyword">:location</span> %</span>)</span></span>)</span>
radius</span>)</span>
<span class="paren4">(<span class="code">vals <span class="paren5">(<span class="code"><span class="keyword">:entities</span> world</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
<p>It looks through all the entities in the world and returns a sequence of those
whose &quot;radial&quot; distance is less than or equal to the given radius. The &quot;radial
distance&quot; (also called the &quot;king's move&quot; distance by some) looks like this:</p>
<pre><code>3333333
3222223
3211123
3210123
3211123
3222223
3333333</code></pre>
<p>And the function:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> radial-distance
<span class="string">&quot;Return the radial distance between two points.&quot;</span>
<span class="paren2">[<span class="code"><span class="paren3">[<span class="code">x1 y1</span>]</span> <span class="paren3">[<span class="code">x2 y2</span>]</span></span>]</span>
<span class="paren2">(<span class="code">max <span class="paren3">(<span class="code">abs <span class="paren4">(<span class="code">- x1 x2</span>)</span></span>)</span>
<span class="paren3">(<span class="code">abs <span class="paren4">(<span class="code">- y1 y2</span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
<p>I may end up needing to modify this <code>send-message-nearby</code> function to be a bit
more powerful in the future. Trystan's version modifies the verbs and such.
For now this is good enough for me.</p>
<p>Now I can make lichens notify nearby creatures when they grow:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> grow <span class="paren2">[<span class="code"><span class="paren3">{<span class="code"><span class="keyword">:keys</span> <span class="paren4">[<span class="code">location</span>]</span></span>}</span> world</span>]</span>
<span class="paren2">(<span class="code">if-let <span class="paren3">[<span class="code">target <span class="paren4">(<span class="code">find-empty-neighbor world location</span>)</span></span>]</span>
<span class="paren3">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren4">[<span class="code">new-lichen <span class="paren5">(<span class="code">make-lichen target</span>)</span>
world <span class="paren5">(<span class="code">assoc-in world <span class="paren6">[<span class="code"><span class="keyword">:entities</span> <span class="paren1">(<span class="code"><span class="keyword">:id</span> new-lichen</span>)</span></span>]</span> new-lichen</span>)</span>
world <span class="paren5">(<span class="code">send-message-nearby location <span class="string">&quot;The lichen grows.&quot;</span> world</span>)</span></span>]</span>
world</span>)</span>
world</span>)</span></span>)</span></span></code></pre>
<p>The last step is to actually display these messages to the player. I added
a <code>draw-messages</code> function to the <code>ui/drawing.clj</code> file:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> draw-messages <span class="paren2">[<span class="code">screen messages</span>]</span>
<span class="paren2">(<span class="code">doseq <span class="paren3">[<span class="code"><span class="paren4">[<span class="code">i msg</span>]</span> <span class="paren4">(<span class="code">enumerate messages</span>)</span></span>]</span>
<span class="paren3">(<span class="code">s/put-string screen 0 i msg <span class="paren4">{<span class="code"><span class="keyword">:fg</span> <span class="keyword">:black</span> <span class="keyword">:bg</span> <span class="keyword">:white}</span></span>)</span></span>)</span></span>)</span></span></span></span></code></pre>
<p>And then I modified the main <code>draw-ui</code> function for <code>:play</code> UIs to draw the
messages on top of the map:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defmethod</span></i> draw-ui <span class="keyword">:play</span> <span class="paren2">[<span class="code">ui game screen</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">[<span class="code">world <span class="paren4">(<span class="code"><span class="keyword">:world</span> game</span>)</span>
<span class="paren4">{<span class="code"><span class="keyword">:keys</span> <span class="paren5">[<span class="code">tiles entities</span>]</span></span>}</span> world
player <span class="paren4">(<span class="code"><span class="keyword">:player</span> entities</span>)</span>
<span class="paren4">[<span class="code">cols rows</span>]</span> <span class="paren4">(<span class="code">s/get-size screen</span>)</span>
vcols cols
vrows <span class="paren4">(<span class="code">dec rows</span>)</span>
origin <span class="paren4">(<span class="code">get-viewport-coords game <span class="paren5">(<span class="code"><span class="keyword">:location</span> player</span>)</span> vcols vrows</span>)</span></span>]</span>
<span class="paren3">(<span class="code">draw-world screen vrows vcols origin tiles</span>)</span>
<span class="paren3">(<span class="code">doseq <span class="paren4">[<span class="code">entity <span class="paren5">(<span class="code">vals entities</span>)</span></span>]</span>
<span class="paren4">(<span class="code">draw-entity screen origin vrows vcols entity</span>)</span></span>)</span>
<span class="paren3">(<span class="code">draw-hud screen game</span>)</span>
<span class="paren3">(<span class="code">draw-messages screen <span class="paren4">(<span class="code"><span class="keyword">:messages</span> player</span>)</span></span>)</span>
<span class="paren3">(<span class="code">highlight-player screen origin player</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>This does mean that messages will cover a bit of the screen. For now I'll live
with that, but in the future it's something to fix.</p>
<p>Finally we need to clear the message queue out periodically, otherwise it'll
grow until it covers the entire screen! I modified the main game loop in
<code>core.clj</code>:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> clear-messages <span class="paren2">[<span class="code">game</span>]</span>
<span class="paren2">(<span class="code">assoc-in game <span class="paren3">[<span class="code"><span class="keyword">:world</span> <span class="keyword">:entities</span> <span class="keyword">:player</span> <span class="keyword">:messages</span></span>]</span> nil</span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> run-game <span class="paren2">[<span class="code">game screen</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">loop</span></i> <span class="paren3">[<span class="code"><span class="paren4">{<span class="code"><span class="keyword">:keys</span> <span class="paren5">[<span class="code">input uis</span>]</span> <span class="keyword">:as</span> game} game</span>]</span>
<span class="paren4">(<span class="code">when <span class="paren5">(<span class="code">seq uis</span>)</span>
<span class="paren5">(<span class="code"><i><span class="symbol">if</span></i> <span class="paren6">(<span class="code">nil? input</span>)</span>
<span class="paren6">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren1">[<span class="code">game <span class="paren2">(<span class="code">update-in game <span class="paren3">[<span class="code"><span class="keyword">:world</span></span>]</span> tick-all</span>)</span>
_ <span class="paren2">(<span class="code">draw-game game screen</span>)</span>
game <span class="paren2">(<span class="code">clear-messages game</span>)</span></span>]</span>
<span class="paren1">(<span class="code">recur <span class="paren2">(<span class="code">get-input game screen</span>)</span></span>)</span></span>)</span>
<span class="paren6">(<span class="code">recur <span class="paren1">(<span class="code">process-input <span class="paren2">(<span class="code">dissoc game <span class="keyword">:input</span></span>)</span> input</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></span></span></code></pre>
<h2 id="s5-results"><a href="index.html#s5-results">Results</a></h2>
<p>After fixing a few other bugs (you can read the changelog if you're interested)
I've now got a working combat system, and a messaging system so I can tell
what's going on:</p>
<p><img src="../../../../static/images/blog/2012/07/caves-06-01.png" alt="Screenshot"></p>
<p>It's actually starting to feel like a real game now, instead of just a sandbox
where you can break things.</p>
<p>You can view the code <a href="https://github.com/sjl/caves/tree/entry-06/src/caves">on GitHub</a> if you want to see the end
result.</p>
<p>The next article will move on to Trystan's seventh post, which adds multiple
z-levels to the caves.</p>
</article></main><hr class='main-separator' /><footer><nav><a href='https://github.com/sjl/'>GitHub</a><a href='https://twitter.com/stevelosh/'>Twitter</a><a href='https://instagram.com/thirtytwobirds/'>Instagram</a><a href='https://hg.stevelosh.com/.plan/'>.plan</a></nav></footer></body></html>