349 lines
No EOL
30 KiB
HTML
349 lines
No EOL
30 KiB
HTML
<!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>->></code> to chain actions:</p>
|
|
|
|
<pre><code><span class="code"><span class="paren1">(<span class="code">->> 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->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">"bunny"</span>
|
|
<span class="keyword">:glyph</span> <span class="string">"v"</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->Foo</code> record constructors because the <code>->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">"hp ["</span> hp <span class="string">"/"</span> max-hp <span class="string">"]"</span></span>)</span>
|
|
info <span class="paren4">(<span class="code">str info <span class="string">" loc: ["</span> x <span class="string">"-"</span> y <span class="string">"]"</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: "hp [20/20] loc: [82-103]". 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: "If defense
|
|
is higher than attack, then 1. Otherwise, a random number between 1 and (attack
|
|
- defense)."</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">->> 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">"You strike the %s for %d damage!"</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">"The %s strikes you for %d damage!"</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>->></code> to chain together actions.</p>
|
|
|
|
<p>I also added a <code>:name</code> attribute to entities so I can say "You strike the bunny"
|
|
instead of "You strike the v". 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"><= <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 "radial" distance is less than or equal to the given radius. The "radial
|
|
distance" (also called the "king's move" 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">"Return the radial distance between two points."</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">"The lichen grows."</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> |