<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-9121642240025958832</id><updated>2012-03-06T19:59:26.161-08:00</updated><category term='C#'/><category term='domain events'/><category term='roguelike tutorial'/><category term='ddd'/><category term='Flixel'/><category term='roguelike'/><category term='java'/><category term='haskell'/><category term='play'/><category term='unit tests'/><category term='cqrs'/><category term='latin'/><category term='code'/><category term='project'/><category term='metroidvania'/><category term='FlxSkeleton'/><category term='writing'/><title type='text'>Trystan's blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>74</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-171003571605247260</id><published>2012-03-05T16:31:00.000-08:00</published><updated>2012-03-05T16:31:00.225-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike'/><title type='text'>My plan for the 2012 7DRL</title><content type='html'>I've been thinking a lot about what I want to do with this year's 7DRL. For 2011 I wanted to see what it was like to not be the hero but have to cooperate with other heroes and to try a survival goal rather than the tried-and-true "descend and return". Even though it wan't very fun, I'm happy that I did that and I consider it a success. I've posted &lt;a href="http://trystans.blogspot.com/2011/07/some-roguelike-ideas.html"&gt;some of my ideas&lt;/a&gt; before, but as March approaches, I've been narrowing down what I want to focus on.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Event based architecture.&lt;/b&gt;&amp;nbsp;I've had a lot of fun and learned much while delving into &lt;a href="http://trystans.blogspot.com/2011/11/from-collaborator-hell-to-dependency.html"&gt;simple event based programming&lt;/a&gt; and I think this will be a good test of how to apply it to an interesting application. Roguelikes tend to have very messy dependancies and have an unholy number of exceptions to the rules and complex interactions so this will be a good test of how well I can make &lt;a href="http://trystans.blogspot.com/2012/01/my-summary-of-cqrs-and-ddd.html"&gt;events, commands, and sagas&lt;/a&gt; work.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Interesting overworld.&lt;/b&gt;&amp;nbsp;I've gotten tired of playing games that have too much uninteresting space. Long corridors, huge outdoor fields, empty room after empty room after empty room. I want small and unique spaces. Moving is so boring that some roguelikes have it automated for you. Despite all the hours I played the original Zelda as a kid I never got tired of it. Each screen had its own unique charm and character,&amp;nbsp;each fit within its own little neighborhood, and&amp;nbsp;each area had it's own secrets to reveal. &lt;a href="http://trystans.blogspot.com/2012/02/possible-algorithm-for-zeldalike.html"&gt;I want that&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Random monsters.&lt;/b&gt; Other than the&amp;nbsp;&lt;a href="http://df.magmawiki.com/index.php/DF2010:Forgotten_beast"&gt;Forgotten Beasts of Dwarf Fortress&lt;/a&gt;, I haven't seen this implemented. I don't think I'll have DF's giant vomit monsters, just a few species with three or four random advantages each game. This should add variety like identifying potions does, except perhaps with deadlier consequences.&lt;br /&gt;&lt;br /&gt;&lt;div style="margin: 0px;"&gt;&lt;b&gt;Dynamic quests.&lt;/b&gt;&amp;nbsp;The more I think about it the less intimidating this seems - but that may just be my own hubris. Since it's a 7DRL I will probably only have a few quests. Getting other people to follow commands and attempt the same quests would be interesting - and it should be possible if I'm right about the &lt;a href="http://trystans.blogspot.com/2012/02/followers-spoken-commands-and-quests.html"&gt;connection between commands and quests&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Focusing on these means I would de-emphasize other things. There will probably be&amp;nbsp;just a few simple armors, weapons, and other items. I&amp;nbsp;will most likely skip magic alltogether.&amp;nbsp;The game itself may not even be fun or difficult. I'm not sure what the story will be either but it should involve quests and followers. I should probably think of something soon otherwise I'll end up with just a proof of concept: a bunch of neat ideas with no theme to tie them together.&lt;br /&gt;&lt;br /&gt;My 7 day plan:&lt;br /&gt;&lt;br /&gt;Day 1. Hyrulian&amp;nbsp;Overworld: worldgen, player controlled @&lt;br /&gt;Day 2. Basic gameplay: basic creatures, basic combat, basic stats, basic items&lt;br /&gt;Day 3. Monsters: randomized monsters, better creature AI, a few status effects&lt;br /&gt;Day 4. Society: people, victory, basic commands&lt;br /&gt;Day 5. Dynamic quests&lt;br /&gt;Day 6. More content and polish....&lt;br /&gt;Day 7. More content and polish....&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-171003571605247260?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/171003571605247260/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/03/my-plan-for-2012-7drl.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/171003571605247260'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/171003571605247260'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/03/my-plan-for-2012-7drl.html' title='My plan for the 2012 7DRL'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-1982241331711825698</id><published>2012-02-28T08:24:00.000-08:00</published><updated>2012-02-29T23:57:35.430-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike'/><title type='text'>Followers, spoken commands, and quests</title><content type='html'>&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;I think I've found something that hasn't been done that could add a lot to roguelikes: spoken commands. You type what you want your followers to do and they do it. Instead of having a complex AI that handles the entire english language, you could focus on a few things that could be combined in different ways. If you have many people following these commands then you and your rag-tag team of adventurers could really change the world.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Here's a simple command syntax that I have in mind:&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp; &amp;lt;target&amp;gt; &amp;lt;verb&amp;gt; [&amp;lt;objects&amp;gt;] [&amp;lt;preposition clauses&amp;gt;]&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;target is an optional person or group you are talking to: healer, fighters, Bob, Joe, etc.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;verb is one of a dozen or so supported verbs: attack, defend, go, pickup, drop, help, etc.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;objects are the people, places, or things directly involved with the verb: a sword, goblins, me, the desert, home, etc.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;preposition clauses are optional clauses that constrain the verb: in &amp;lt;location&amp;gt;, with &amp;lt;item&amp;gt;, etc.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;The prepositions are where the real flexibility and power come from since you can specify where something should happen, what should be used, or whatever else makes sense.&amp;nbsp;The verbs, people, places, things, and prepositions would be whatever is appropriate to the game; resource gathering, squad maneuvers, mixing potions, social intrigue, whatever you can think of.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Turning the spoken words into commands would be three steps:&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;Normalize the sentence, removing fluff words and changing synonyms (protect -&amp;gt; defend)&lt;/li&gt;&lt;li&gt;Parse the sentence&lt;/li&gt;&lt;li&gt;Correct the prepositions and objects if necessary&lt;/li&gt;&lt;/ol&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;As an example,&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Miners, give the blacksmith ore from the mines&lt;/span&gt;&amp;nbsp;would be normalized into&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;miners give blacksmith ore from mines&lt;/span&gt;&amp;nbsp;which would be parsed into this:&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;target = miners&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;verb = give&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;objects = blacksmith, ore&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;prepositions = from mines&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;The correction phase would be necessary in this example since the "give" verb requires the verb's object to be a thing and requires a "to &amp;lt;person&amp;gt;" prepositional clause. "blacksmith" is a person and "ore" is a thing so "blacksmith" should be the "to" prepositional clause and "ore" should remain as the verb's object. The corrected interpretation is:&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;target = miners&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;verb = give&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;objects = ore&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;prepositions = from mines, to blacksmith&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;or&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;miners give ore from mines to blacksmith&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;So the miners who hear this would get ore from the mines and give it to the blacksmith.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Some other example commands:&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;healer, help the villagers at home&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;kill the goblins in the dark forest with spears&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;Urist, craft a sword with steel at the master's forge&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;guards, escort the prince from the castle to the fort&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;There seems to be a deep similarity between commands and quests. Even my example &lt;a href="http://trystans.blogspot.com/2012/02/procedural-quests-for-dynamic-world.html"&gt;Lost Heirloom Quest&lt;/a&gt; could be seen as a command:&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;player, give the McUrist family watch from the dark forest to Urist McUrist&lt;/span&gt;.&amp;nbsp;Since the NPCs can follow commands, and commands and quests are the same, then they should be able to follow quests. Also; since the player can give commands, the player is also giving quests. Imagine a world with a few hundred NPCs running around trying to fulfill quests and giving quests to others when appropriate.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-1982241331711825698?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/1982241331711825698/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/02/followers-spoken-commands-and-quests.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1982241331711825698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1982241331711825698'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/02/followers-spoken-commands-and-quests.html' title='Followers, spoken commands, and quests'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-7386342823371312826</id><published>2012-02-22T20:38:00.000-08:00</published><updated>2012-02-22T20:38:00.557-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='domain events'/><category scheme='http://www.blogger.com/atom/ns#' term='roguelike'/><title type='text'>Procedural quests for a dynamic world</title><content type='html'>Interesting and procedurally generated quests are something of a holy grail for many roguelike developers. I've also given it some thought and here's a simple idea I came up with.&lt;br /&gt;&lt;br /&gt;Basically, if you have a catalogue of possible quests and you have an event based architecture, then each quest type would have it's own &lt;a href="http://trystans.blogspot.com/2012/01/my-summary-of-cqrs-and-ddd.html"&gt;saga&lt;/a&gt;. That saga would listen to domain events and when the preconditions were right, it would add the quest to a quest giver. By talking to a quest giver the player, or any other actor, could accept the quest. When the saga receives the right domain event, the quest is over and everyone undertaking the event is rewarded or notified as appropriate.&lt;br /&gt;&lt;br /&gt;Here's a concrete example I'll call a&amp;nbsp;&lt;b&gt;Lost Heirloom&lt;/b&gt; quest.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LostHeirloomSaga&lt;/span&gt; subscribes to certain events like &lt;span style="font-family: 'Courier New'; font-size: x-small;"&gt;PersonMoved&lt;/span&gt; (while fleeing or confused) or &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ItemStolenFromPerson&lt;/span&gt;. As a response to those events, it may create a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LoseItem&lt;/span&gt; command with a reference to the person and item.&lt;/li&gt;&lt;li&gt;Some command handler causes the target of the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LoseItem&lt;/span&gt; command to drop the item (if it wasn't already stolen) and create an &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ItemLost&lt;/span&gt; event with references to the person, the location, the time, the item, and the reason why.&lt;/li&gt;&lt;li&gt;The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LostHeirloomSaga&lt;/span&gt; listens to &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ItemLost&lt;/span&gt; events and when it handles the event, the person who dropped the item is given a &lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LostHeirloomQuest&lt;/span&gt; and can ask others to accept it.&lt;/li&gt;&lt;li&gt;When the player talks to that person they can accept that quest. The details from the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LostItem&lt;/span&gt; event can be used to create the quest specific rewards and text. Imagine talking to URIST McURIST the WOODCUTTER when he says "TWO WEEKS ago I was CUTTING WOOD in the DARK FOREST and was attacked by a GIANT BEETLE. While fleeing for my life, I dropped my McURIST FAMLIY WATCH. If you find it and return it, I'll give you my spare WOODCUTTER'S AXE and a PASS TO THE WOODCUTTER'S GUILD."&lt;/li&gt;&lt;li&gt;The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LostHeirloomSaga&lt;/span&gt; also listens to &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ItemGiven&lt;/span&gt; events. When the item that was given matches a lost item and the person it was given to matches the person who lost it, the associated quest is completed and the person who gave the item is rewarded.&lt;/li&gt;&lt;li&gt;If a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;CreatureDied&lt;/span&gt;&amp;nbsp;event for the quest giver happened, an &lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ItemDestroyed&lt;/span&gt; event for the lost item, or some&amp;nbsp;other quest-canceling event happens, then the&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LostHeirloomSaga&lt;/span&gt; cancels that&amp;nbsp;creature's quest and everything else is cleaned up appropriately.&lt;/li&gt;&lt;li&gt;For extra awesomeness,&amp;nbsp;when the saga notices other events related to the item (like &lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ItemSold&lt;/span&gt; or &lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ItemPickedUp&lt;/span&gt;), it could somehow add details to the world about it. Maybe the&amp;nbsp;town's gossipmonger would tell you rumors about how the local blacksmith found a family watch in the dark forest. You could then talk to the blacksmith to find out what happened to it or you could sneak through his stuff when he's not looking.&lt;/li&gt;&lt;/ol&gt;&lt;span class="Apple-style-span"&gt;Implementing this quest means adding a few classes: a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LoseItem&lt;/span&gt; command, a method to handle that command, a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ItemLost&lt;/span&gt; event, a &lt;span style="font-family: 'Courier New'; font-size: x-small;"&gt;LostHeirloomQuest&lt;/span&gt;&lt;/span&gt;, and &lt;span class="Apple-style-span"&gt;a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LostHeirloomSaga&lt;/span&gt;. The neat thing is that no existing classes need to be updated except for making sure the quest saga subscribes to the right events - all the logic is contained in the command handler and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LostHeirloomSaga&lt;/span&gt;. The&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LostHeirloomSaga&lt;/span&gt;&lt;span class="Apple-style-span"&gt;&amp;nbsp;can use details of the quest giver to create dynamic and relevant details about what was lost, how it was lost, and when and where it was lost as well as a reward&amp;nbsp;appropriate for the quest giver and the location and value of the lost item. &lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;The real emergent behavior comes from quests that play off of events generated by other quests. If each quest can be ended two or three different ways, each quest directly or indirectly involves affecting the world, and there are multiple actors giving or accepting quests, then a complex chain of events could cause gathering firewood to lead to the downfall of a civilization.&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Perhaps&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;LostHeirloomQuestCompleted&lt;/span&gt; events please the god of compassion who may give the quest completer a Help The Townfolk quest.&lt;/li&gt;&lt;li&gt;Perhaps when a Rescue Princess&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;quest is ended by the princess dying, her family creates a Kill Person quest targeting the killer and a Revenge quest against every group the killer was a member of. When a Rescue Princess quest is ended by the princess returning home, her family rewards whoever escorted her and every group the escort is a member of.&lt;/li&gt;&lt;li&gt;Perhaps 10 Expand Influence quests must be completed before a size 3 town can become size 4. Whenever an Expand Influence quest is completed, nearby paranoid town mayors become nervous and may create an Attack Town quest or Assassinate Ruler quest targeting their rival neighbors. When an Attack Town quest is completed, the targeted town's mayor can give Strengthen Defenses and retaliatory Attack Town quests. Undertaking either requires buying resources from the market and if a market trader's goods are running low then he could create Gather Resource quests. Of course gathering resources in your neighbor's territory will cause them to hand out quests to get you to stop; by bribery, trade, or sword and shield.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;This, right now, is just idle speculation but I think this could be done. By relying on domain events and sagas, new quests can be implemented without muddying up the existing code since it doesn't even need to be touched. By relying on specific details of the people and places creating the events, compelling and believable quests can be generated. And by relying on quests that build on domain events raised as part of attempting other quests,&amp;nbsp;an interdependent and dynamic history following simple laws of cause and effect can be created. I'll have to create a proof of concept some time.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-7386342823371312826?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/7386342823371312826/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/02/procedural-quests-for-dynamic-world.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/7386342823371312826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/7386342823371312826'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/02/procedural-quests-for-dynamic-world.html' title='Procedural quests for a dynamic world'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-7146749087104058309</id><published>2012-02-16T10:33:00.000-08:00</published><updated>2012-02-16T10:33:00.254-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike'/><title type='text'>Procedurally generated puzzles</title><content type='html'>Just jotting down some informal thoughts on types of puzzles and how to proceduraly generate them. Each puzzle is really a barrier that you must overcome to get to the other side.&amp;nbsp;Place a puzzle when you need a barrier to seperate zones, protect treasure, or just to keep things interesting.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Minigames.&lt;/b&gt; Reminds me of the Wii party games where whichever team gets the best overall score after 5 events wins the tournament.&amp;nbsp;Slider puzzles, tower of hanoi, checkers, gambling, etc. It would take time to code each one but it would also break up the monotony. You could also make the real game a metagame of the minigames. Imagine everyone in the game is obsessed with a collectable card game. By finding new cards in the game world you improve your chances of winning the card minigame, which could lead to rewards that improve your chances of the real game. Done right it would be interesting and fun diversion; done wrongly it would be a tedious and required waste of time.&lt;br /&gt;&lt;br /&gt;(days later) I just remembered that Star Wars: Knights Of The Old Republic had this with &lt;a href="http://starwars.wikia.com/wiki/Pazaak"&gt;Pazaak&lt;/a&gt;! I was thinking more along the lines of a Magic The Gathering type minigame, but a poker-blackjack hybrid is a good idea too. Neat! Proof that my idea works!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;b&gt;Logic mazes.&amp;nbsp;&lt;/b&gt;Reminds me of many games where you have to move like a knight on a giant chess board or slide across the ice in just the right pattern to make it to the other side. For these puzzles you must go from&amp;nbsp;&lt;i&gt;here&lt;/i&gt;&amp;nbsp;to&amp;nbsp;&lt;i&gt;there&lt;/i&gt;&amp;nbsp;but you must follow a certain way of moving. I think these would be easier to make in reverse. For example, start with an impassible expanse of ice. Start at the end point. Pick a direction and place a block in that neighboring cell. Move the opposite direction a few spaces. Repeat several times, making sure not to place blocks in areas the player will need to later pass through, and you end up with an end point, a start point, and at least one way to move from the start to the end. There are many more examples of logic mazes at&amp;nbsp;&lt;a href="http://www.mazelog.com/"&gt;http://www.mazelog.com/&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;b&gt;Inventory based. &lt;/b&gt;Reminds me of Metroidvania games. You must have a certain item or ability in order to pass. A locked door requires a key, a hall of spikes requires the spike armor, cracked walls require bombs. Basically there's some sort of "lock"&amp;nbsp;and some sort of inventory "key" is required to pass.&amp;nbsp;To place an inventory based puzzle, simply pick something from a preset table of a "lock" and item "key" and place the key somewhere before the lock. Or pick a random item from the world as the key: "The burly guard won't let you pass until you give him A HAM SANDWICH."&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Knowledge based. &lt;/b&gt;Reminds me of Myst. You must answer a question or enter the correct sequence to pass. A combination lock where the randomized&amp;nbsp;combination is written down elsewhere or&amp;nbsp;a guard who asks you questions that you would only know from reading books in the game or talking to people in the game. Like the inventory puzzles, simply pick something from a preset table of a "lock" and an information "key" and place the &lt;i&gt;randomly generated&lt;/i&gt; key somewhere before the lock.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Environment based. &lt;/b&gt;Reminds me of many sub puzzles in modern Zelda games. You must change the environment to a certain state before you can pass. Pull the lever to open a door, push all 4 buttons to lower the bridge, set all 6 torches on fire to open the treasure chest, redirect the laser to destroy the statue. This could be a superset of sokoban puzzles which are really just "push the blocks into the marked places". If you look at it a certain way, the requirements or triggers could even involve other people or groups: the king must be dead, the mages guild must be at war with the fighters guild, the price of milk must be low, the senate must be mostly of the Merchant's Alliance, etc.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;These puzzles can be combined to form larger and more intricate puzzles:&lt;br /&gt;&lt;br /&gt;To get the treasure chest you must push all four buttons&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;to push button 1 you just walk over and push it&lt;/li&gt;&lt;li&gt;to push button 2 you must cross a bottomless pit&lt;/li&gt;&lt;ul&gt;&lt;li&gt;to cross the pit you must&amp;nbsp;purchace Flying Boots or&amp;nbsp;know how to teleport&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;to push button 3 you must open the giant door&lt;/li&gt;&lt;ul&gt;&lt;li&gt;to open the door you must find and reassemble the&amp;nbsp;2 gears to the door machinery&lt;/li&gt;&lt;ul&gt;&lt;li&gt;gear&amp;nbsp;1 is on the other side of a giant randomized sokoban puzzle&lt;/li&gt;&lt;li&gt;gear&amp;nbsp;2 is buried at a random location&lt;/li&gt;&lt;ul&gt;&lt;li&gt;shovels can be purchaced at stores&lt;/li&gt;&lt;li&gt;the location is on a map you find in the dungeon&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;reassembling the gears is a slider puzzle&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;li&gt;to push button 4 you must defeat the guardian with&amp;nbsp;a special sword and a special shield&amp;nbsp;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;to get the special sword you must help the dying king&lt;/li&gt;&lt;ul&gt;&lt;li&gt;the king will give you the sword if you give him an elixer of life that no one knows how to make&lt;/li&gt;&lt;ul&gt;&lt;li&gt;the recipe is randomly generated but written in a recipie book - or you could just start mixing things and get lucky&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;li&gt;to get the special shield you must play the specific melody on the piano&lt;/li&gt;&lt;ul&gt;&lt;li&gt;the notes of the randomized melody are written on the wall of another room&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/ul&gt;You could create a puzzle like this at each dungeon or important location or create one giant puzzle that includes all the major locations in the game world.&lt;br /&gt;&lt;br /&gt;Ideas like this aren't worth much unless actually implemented though. I'll have to create some proof of concept or even a roguelike that has puzzles like this in it. One of these days.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-7146749087104058309?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/7146749087104058309/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/02/procedurally-generated-puzzles.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/7146749087104058309'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/7146749087104058309'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/02/procedurally-generated-puzzles.html' title='Procedurally generated puzzles'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-4587422229666799934</id><published>2012-02-11T10:32:00.000-08:00</published><updated>2012-02-11T10:32:11.039-08:00</updated><title type='text'>Problems, solutions, and sandwiches</title><content type='html'>Let's say you have a screwdriver and you happen upon a loose screw that needs to be tightened. Using the screwdriver to fix the loose screw seems like a pretty good idea - and it probably is.&lt;br /&gt;&lt;br /&gt;Now you happen to have a loose nail that needs to be fixed. You know you should use a hammer, but if you're really lazy, don't have enough time to fetch a hammer, or if someone else insists on it, then you could probably bang the nail in with your screwdriver. Yes, it won't work as well as a hammer, you may damage the screwdriver, and you may hurt yourself, but at the end of the day you will have fixed the loose nail. You can then go home with a banged up screwdriver and sore hand and wish you could do it the right way. Not ideal, but it happens from time to time.&lt;br /&gt;&lt;br /&gt;Now you happen to have an empty stomach. You really should eat a sandwich but instead you reach for your screwdriver. Why? Maybe everyone keeps talking about using a screwdriver so you go along. Maybe someone doesn't realize that even though &lt;i&gt;sandwich&lt;/i&gt; and &lt;i&gt;screwdriver&lt;/i&gt; have many letters in common, they're not the same. Or maybe you happen to have the screwdriver in your hand and really think it can help. For whatever reason, you, or someone else, decides that eating your screwdriver is a perfectly acceptable way to fix your empty stomach. Despite your best efforts and after several days or weeks, the best you can hope for is a broken screwdriver and several broken teeth. And you still have an empty stomach.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;This may or may not be a thinly disguised allegory for certain events that may or may not happen when programming on projects that involve several other people and hard deadlines. This has been one of the most difficult things for me to deal with.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-4587422229666799934?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/4587422229666799934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/02/problems-solutions-and-sandwiches.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/4587422229666799934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/4587422229666799934'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/02/problems-solutions-and-sandwiches.html' title='Problems, solutions, and sandwiches'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-867711296586061334</id><published>2012-02-10T03:18:00.000-08:00</published><updated>2012-02-21T17:59:22.786-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike'/><title type='text'>A possible algorithm for Zeldalike overworlds</title><content type='html'>The 2012 7DRL challenge is one month away!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I've been thinking about how to procedurally create a Zeldalike map for a roguelike. By Zeldalike I mean the important parts that I remember from playing the original Zelda game: it was broken into screen-sized chunks, everywhere was reachable (although sometimes you had to get an item first), there were different themed areas, it wasn't too dense or too sparse - it was interesting, charming, and fun.&lt;br /&gt;&lt;br /&gt;&lt;style&gt;table tr td { vertical-align:top; }pre { display:inline-block; color:#322; background:#d8d0d0; padding:0.25em; }&lt;/style&gt;&lt;br /&gt;Here's an algorithm that should come up with something interesting&amp;nbsp;like Zelda's overworld Hyrule. It creates one array of tiles and one array with details about each screen-sized chunk of the world. &lt;br /&gt;&lt;br /&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Since the world will be broken into screen sized chunks, we can start by determining how those screens will be connected. Let's start with a 16x5 array of screens that have no connections to any neighbors. &lt;br /&gt;&lt;br /&gt;This is a high-level view of the world. If you were to print it out, it may look something like this where each screen is represented as a blank space and the walls between them are solid spaces. &lt;/td&gt;&lt;td&gt;&lt;pre&gt;#################################&lt;br /&gt;# # # # # # # # # # # # # # # # #&lt;br /&gt;#################################&lt;br /&gt;# # # # # # # # # # # # # # # # #&lt;br /&gt;#################################&lt;br /&gt;# # # # # # # # # # # # # # # # #&lt;br /&gt;#################################&lt;br /&gt;# # # # # # # # # # # # # # # # #&lt;br /&gt;#################################&lt;br /&gt;# # # # # # # # # # # # # # # # #&lt;br /&gt;#################################&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;With the array of screens, create a simple perfect maze.&lt;br /&gt;&lt;br /&gt;When printing out how the screens are connected to make a big maze, it would look something like this. We can see that this time, the top left screen has an open path to the screen to the east. After traveling east a few screens, you can go south. &lt;/td&gt;&lt;td&gt;&lt;pre&gt;#################################&lt;br /&gt;#       #                       #&lt;br /&gt;####### ### ##### ###############&lt;br /&gt;#       # #     #               #&lt;br /&gt;# # ### # # ######### # ### ### #&lt;br /&gt;# # #           #   # #   #   # #&lt;br /&gt;# ### # # ####### ### # ##### # #&lt;br /&gt;# #   # #           # #     # # #&lt;br /&gt;# # # ##### # # ############# # #&lt;br /&gt;# # #   #   # #             # # #&lt;br /&gt;#################################&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Remove some of the walls to connect screens. There are now loops and many ways to move around. Now when traveling east from the top left screen you can go south on the second screen or continue to the fourth screen and go south. &lt;/td&gt;&lt;td&gt;&lt;pre&gt;#################################&lt;br /&gt;#       #                       #&lt;br /&gt;### ### ### ##### # # ###########&lt;br /&gt;#       #                       #&lt;br /&gt;# # ### # # ######### # ### ### #&lt;br /&gt;# # #           #   # #       # #&lt;br /&gt;# ### # # ####### ### # ##### # #&lt;br /&gt;# #   # #           # #     # # #&lt;br /&gt;# # # ##### # # ############# # #&lt;br /&gt;# # #   #   # #             # # #&lt;br /&gt;#################################&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Assign themes to some of the screens. I'm just using 1 through 5 as arbitrary themes. They could be like the original zelda: brown rock, green rock, brown forest, green forest, cemetary, etc or they could be your own themes. &lt;/td&gt;&lt;td&gt;&lt;pre&gt;#################################&lt;br /&gt;#  1    #  2 3                  #&lt;br /&gt;### ### ### ##### # # ###########&lt;br /&gt;#      4#    4                  #&lt;br /&gt;# # ### # # ######### # ### ### #&lt;br /&gt;# # #           #  1# #       # #&lt;br /&gt;# ### # # ####### ### # ##### # #&lt;br /&gt;# #   #5#        3 5# #    2# # #&lt;br /&gt;# # # ##### # # ############# # #&lt;br /&gt;# # #   #   # #             # # #&lt;br /&gt;#################################&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Spread the themes to adjacent screens that are connected. &lt;/td&gt;&lt;td&gt;&lt;pre&gt;#################################&lt;br /&gt;#1 1 1 4#2 2 3 3                #&lt;br /&gt;### ### ### ##### # # ###########&lt;br /&gt;#  1 4 4#  2 4 4                #&lt;br /&gt;# # ### # # ######### # ### ### #&lt;br /&gt;# # #  5        #3 1# #       # #&lt;br /&gt;# ### # # ####### ### # ##### # #&lt;br /&gt;# #   #5#      3 3 5# #  2 2# # #&lt;br /&gt;# # # ##### # # ############# # #&lt;br /&gt;# # #   #   # #             # # #&lt;br /&gt;#################################&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Continue spreading them until all screens have a theme. This ensures that the themes match the path that the player has to walk through and the user should feel like they spend some time in each theme before entering a different one. &lt;/td&gt;&lt;td&gt;&lt;pre&gt;#################################&lt;br /&gt;#1 1 1 4#2 2 3 3 3 3 3 3 3 3 3 3#&lt;br /&gt;### ### ### ##### # # ###########&lt;br /&gt;#1 1 4 4#2 2 4 4 4 4 4 2 2 2 2 2#&lt;br /&gt;# # ### # # ######### # ### ### #&lt;br /&gt;#1#1#5 5 5 2 2 2#3 1#4#2 2 2 2#2#&lt;br /&gt;# ### # # ####### ### # ##### # #&lt;br /&gt;#1#5 5#5#5 3 3 3 3 5#4#2 2 2#2#2#&lt;br /&gt;# # # ##### # # ############# # #&lt;br /&gt;#1#5#5 5#3 3#3#3 3 3 3 3 3 3#2#2#&lt;br /&gt;#################################&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;#####################&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;#     ##     ##     #&amp;amp;     &amp;amp;*     ** &lt;br /&gt;#  1  ##  1  ##  1  #&amp;amp;  4  &amp;amp;*  2  ** &lt;br /&gt;#     ##     ##     #&amp;amp;     &amp;amp;*     ** &lt;br /&gt;#####################&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;##############&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;#     ##     #&amp;amp;     &amp;amp;&amp;amp;     &amp;amp;*     ** &lt;br /&gt;#  1  ##  1  #&amp;amp;  4  &amp;amp;&amp;amp;  4  &amp;amp;*  2  ** &lt;br /&gt;#     ##     #&amp;amp;     &amp;amp;&amp;amp;     &amp;amp;*     ** &lt;br /&gt;##############&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;##############%%%%%%%%%%%%%%%%%%%%%%%&lt;br /&gt;#     ##     #%     %%     %%     %% &lt;br /&gt;#  1  ##  1  #%  5  %%  5  %%  5  %% &lt;br /&gt;#     ##     #%     %%     %%     %% &lt;br /&gt;##############%%%%%%%%%%%%%%%%%%%%%%%&lt;br /&gt;#######%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&lt;br /&gt;#     #%     %%     %%     %%     %% &lt;/pre&gt;&lt;/td&gt;&lt;td&gt;Now we can build the real world map based on the array of screens&amp;nbsp;created in the earlier part. For each cell in the array of screens, plop down a screen sized chunk in the world array. For this example I'll make each screen 7x5 tiles large and just show the top left few screens but a real game would have much larger screens. I will also put the theme number and use different&amp;nbsp;border tiles for each theme. &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;#####################&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;#     ##     ##     #&amp;amp;     &amp;amp;*     ** &lt;br /&gt;#  1      1      1      4  &amp;amp;*  2     &lt;br /&gt;#     ##     ##     #&amp;amp;     &amp;amp;*     ** &lt;br /&gt;########## ##########&amp;amp;&amp;amp;&amp;amp; &amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;########## ###&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; &amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;#     ##     #&amp;amp;     &amp;amp;&amp;amp;     &amp;amp;*     ** &lt;br /&gt;#  1      1      4      4  &amp;amp;*  2     &lt;br /&gt;#     ##     #&amp;amp;     &amp;amp;&amp;amp;     &amp;amp;*     ** &lt;br /&gt;### ###### ###&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; &amp;amp;&amp;amp;&amp;amp;*** *****&lt;br /&gt;### ###### ###%%%%%%%%%% %%%%%% %%%%%&lt;br /&gt;#     ##     #%     %%     %%     %% &lt;br /&gt;#  1  ##  1  #%  5      5      5     &lt;br /&gt;#     ##     #%     %%     %%     %% &lt;br /&gt;### ##########%%% %%%%%% %%%%%% %%%%%&lt;br /&gt;### ###%%%%%%%%%% %%%%%% %%%%%% %%%%%&lt;br /&gt;#     #%     %%     %%     %%     %% &lt;/pre&gt;&lt;/td&gt;&lt;td&gt;Then remove the walls for screens that should be connected. It would be easiest to always remove the one or two center blocks from a wall that separates connected screens but you could also make a wider path, a path at a different spot, or multiple paths between two screens. That would add variety but the screens are too small to do that here. After this, we have a themed maze to walk around in based on the array of screens. &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;#####################&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;#     ##     ###   ##&amp;amp; &amp;amp; &amp;amp; &amp;amp;* $ $ ** &lt;br /&gt;#  $     # #               &amp;amp;*        &lt;br /&gt;#     ##     ###   ##&amp;amp; &amp;amp; &amp;amp; &amp;amp;* $ $ ** &lt;br /&gt;########## ##########&amp;amp;&amp;amp;&amp;amp; &amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;########## ###&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; &amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;########     #&amp;amp;     &amp;amp;&amp;amp;#   #&amp;amp;*     ** &lt;br /&gt;###             &amp;amp;&amp;amp;&amp;amp;     ?  &amp;amp;*        &lt;br /&gt;### ####     #&amp;amp;     &amp;amp;&amp;amp;#   #&amp;amp;*     ** &lt;br /&gt;### ###### ###&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; &amp;amp;&amp;amp;&amp;amp;*** *****&lt;br /&gt;### ###### ###%%%%%%%%%% %%%%%% %%%%%&lt;br /&gt;##   ####   ##%%   %%%     %%     %% &lt;br /&gt;#  #  ###   ##%        ***     !     &lt;br /&gt;##   ####!$!##%%   %%%     %%     %% &lt;br /&gt;### ##########%%% %%%%%% %%%%%% %%%%%&lt;br /&gt;### ###%%%%%%%%%% %%%%%% %%%%%% %%%%%&lt;br /&gt;#%   %#%%%%%%%%     %%     %%     %% &lt;/pre&gt;&lt;/td&gt;&lt;td&gt;Randomly select different things to fill the screens with based on its theme: a center lake, small blocks of rock, rows of tree's, bordering trees, statues, etc. You could also have special things that only appear at dead ends: temples, towns, caves, treasure, bosses, puzzles, etc. Putting things at the dead ends makes it worth the time spent getting there - a good idea&amp;nbsp;since most players hate dead ends and mazes in general. Secret areas and bad guys could also be placed now.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;#####################&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;#     ##     ###   ##&amp;amp; &amp;amp; &amp;amp; &amp;amp;* $ $ ** &lt;br /&gt;#  $     # #               &amp;amp;*        &lt;br /&gt;#     ##     ###   ##&amp;amp; &amp;amp; ~~~~~~ $ ** &lt;br /&gt;########## ##########&amp;amp;&amp;amp;&amp;amp; ~~~~~~******&lt;br /&gt;########## ###&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; ~~~~~~******&lt;br /&gt;########     #&amp;amp;     &amp;amp;&amp;amp;#  ~~~~~~   ** &lt;br /&gt;~~#             &amp;amp;&amp;amp;&amp;amp;     ?  &amp;amp;*        &lt;br /&gt;~~# ####     #&amp;amp;     &amp;amp;&amp;amp;#   #&amp;amp;*     ** &lt;br /&gt;~~# ###### ###&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; &amp;amp;&amp;amp;&amp;amp;*** *****&lt;br /&gt;~~# ###### ###%%%%%%%%%% %%%%%% %%%%%&lt;br /&gt;~~   ####   ##%%   %%%     %%     %% &lt;br /&gt;~~ #  ###   ##%        ***     !     &lt;br /&gt;~~   ####!$!##%%   %%%     %%     %% &lt;br /&gt;~~# ##########%%% %%%%%% %%%%%% %%%%%&lt;br /&gt;~~# ###%%%%%%%%%% %%%%%% %%%%%% %%%%%&lt;br /&gt;~~   %#%%%%%%%%     %%     %%     %% &lt;/pre&gt;&lt;/td&gt;&lt;td&gt;Hyrule had shore lines along some the edges and small lakes at the corner of four screens. You can add those too. &lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;#####################&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;*********&lt;br /&gt;#     ##     ###   ##&amp;amp; &amp;amp; &amp;amp; &amp;amp;* $ $ ** &lt;br /&gt;#  $     # #               &amp;amp;*        &lt;br /&gt;#     ##     ###   ##&amp;amp; &amp;amp; ~~~~~~ $ ** &lt;br /&gt;########## ##########&amp;amp;&amp;amp;&amp;amp; ~~~~~~******&lt;br /&gt;########## ###&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; ~~~~~~******&lt;br /&gt;########     #&amp;amp;     &amp;amp;&amp;amp;#  ~~~~~~   ** &lt;br /&gt;~~#             &amp;amp;&amp;amp;&amp;amp;     ? ~&amp;amp;*        &lt;br /&gt;~~# ####     #&amp;amp;     &amp;amp;&amp;amp;#   ~&amp;amp;*     ** &lt;br /&gt;~~# ###### ###&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; &amp;amp;~&amp;amp;*** *****&lt;br /&gt;~~# ###### ###%%%%%%%%%% %~%%%% %%%%%&lt;br /&gt;~~~=~~~~~~=~~~~~~~~~~~~~=~~%%     %% &lt;br /&gt;~~    ###   ##%        * *     !     &lt;br /&gt;~~   ####!$!##%%   %%%     %%     %% &lt;br /&gt;~~# ##########%%% %%%%%% %%%%%% %%%%%&lt;br /&gt;~~# ###%%%%%%%%%% %%%%%% %%%%%% %%%%%&lt;br /&gt;~~   %#%%%%%%%%     %%     %%     %% &lt;/pre&gt;&lt;/td&gt;&lt;td&gt;Rivers could connect some of the lakes to the edges. It would be necessary to place bridges if the river is cutting off a path that used to be passable. &lt;br /&gt;&lt;br /&gt;A final step&amp;nbsp;could involve looking at each screen or tile in the world and placing more treasure, creatures, or structures at interesting points.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;I haven't implemented this yet so I'm not sure if this will really work or not. I'm also not sure how difficult this will be or how fun the results will be. It's something I haven't seen in any roguelike or non-Zelda game and if each screen has something interesting then it should avoid having long spans of time where all you do is walk around. I will probably use this for my entry into the 2012 7DRL challenge.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-867711296586061334?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/867711296586061334/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/02/possible-algorithm-for-zeldalike.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/867711296586061334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/867711296586061334'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/02/possible-algorithm-for-zeldalike.html' title='A possible algorithm for Zeldalike overworlds'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-299944889481290003</id><published>2012-02-01T12:28:00.000-08:00</published><updated>2012-02-01T12:28:52.634-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike'/><title type='text'>2011 7DRL re-retrospective</title><content type='html'>I'm really looking forward to the &lt;a href="http://7drl.org/2012/01/27/7drlc-2012-we-have-dates-march-10th-18th/"&gt;2012 7DRL next month&lt;/a&gt; and I've got a lot of ideas I want to try out. I rechecked the &lt;a href="http://www.roguetemple.com/7drl/2011/"&gt;scores I got for my entry last year&lt;/a&gt; to see when I should focus this time. I'm quite happy with the scores for my 2011 entry but I think I can do better this time.&lt;br /&gt;&lt;br /&gt;Here's what my 2011 entry Twelve Hours got:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Completeness: Bug free, polished game with no features that feel like they are missing.&lt;/b&gt;&lt;br /&gt;2.67 - My highest score. I've got a much better idea of what I want this time, some ways of reducing memory and CPU usage, and some time set aside to add polish so I expect to get a 2.67 or 3.00 this time.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;Aesthetics: Good looking, excellent controls and UI.&lt;/b&gt;&lt;br /&gt;2.33 - A pretty good score; probably from the clean UI, few commands, and standard ASCII visuals. Less garish colors and a few small animations should help improve the aesthetics.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Fun: If you try any 7DRLs, try this one.&lt;/b&gt;&lt;br /&gt;2.33 - One reviewer said: "Would be nice to have better control over your companions."Yup. I want to avoid having &lt;i&gt;total&lt;/i&gt; control but I agree that more influence and interaction with others could add much more fun. Adding some humor and a more heroic feel won't hurt either. A 3.00 would be nearly impossible but I hope I could get a 2.67.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Innovation: Brings something fundamentally new to roguelikes.&lt;/b&gt;&lt;br /&gt;2.00 - Reviewer: "Survive timer, goal other than finding the powerful artifact, inventory-less." My lowest score. I don't know about "fundamentally new" but I've got a few new ideas up my sleeve this year....&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Scope: Beyond what you think could have been done in seven days.&lt;/b&gt;&lt;br /&gt;2.33 - Fair enough.&amp;nbsp;I was unfocused for the first few days so it was less than 7 days of useful work. I've got a &lt;i&gt;long&lt;/i&gt; list of ideas in my head this time so I hope to have much more in the game this year.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Roguelike: 3 means Roguelike, 2 means Roguelike-like, 1 means Not Roguelike.&lt;/b&gt;&lt;br /&gt;2.67 -&amp;nbsp;"misses some points for tactical polish." Agreed. I've got some ideas and expect a 3.00 this time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Average&lt;/b&gt;&lt;/div&gt;&lt;div&gt;2.39 - Not a bad score - not bad at all. I did better than I thought I would but this year I'd like at least a 2.50. By looking at last year's entries, this seems to be a very difficult and rare thing. On the other hand, if one reviewer had given me one more point my average would have been 2.59 so I think 2.50 is reasonable for 2012.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;I think most of my posts for February and March will be about roguelikes. Ideas, implementations, and random musings and rants.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-299944889481290003?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/299944889481290003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/02/2011-7drl-re-retrospective.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/299944889481290003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/299944889481290003'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/02/2011-7drl-re-retrospective.html' title='2011 7DRL re-retrospective'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-2486415778644832769</id><published>2012-02-01T11:03:00.000-08:00</published><updated>2012-02-01T11:03:47.137-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='domain events'/><title type='text'>Java multimethods for a simple command handler</title><content type='html'>I came up with a simple way to have an event handler easily handle multiple types. It's basically a way to say a class implements &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;Handle&amp;lt;DomainEvent1&amp;gt;, Handle&amp;lt;DomainEvent2&amp;gt;&lt;/span&gt;, which works fine in C# but doesn't work in java due to type erasure. My solution: reflection based &lt;a href="http://en.wikipedia.org/wiki/Multiple_dispatch"&gt;multiple dispatch&lt;/a&gt;. I'm sure others have done this before so it's nothing new.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;public abstract class Handler {&lt;br /&gt;&lt;br /&gt; public void handle(Object message) {&lt;br /&gt;  multipleDispatch(message);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void multipleDispatch(Object message) {&lt;br /&gt;  try {&lt;br /&gt;   this.getClass().getMethod("handle", message.getClass()).invoke(this, message);&lt;br /&gt;  } catch (IllegalArgumentException e) {&lt;br /&gt;   e.printStackTrace();&lt;br /&gt;  } catch (SecurityException e) {&lt;br /&gt;   e.printStackTrace();&lt;br /&gt;  } catch (IllegalAccessException e) {&lt;br /&gt;   e.printStackTrace();&lt;br /&gt;  } catch (InvocationTargetException e) {&lt;br /&gt;   e.printStackTrace();&lt;br /&gt;  } catch (NoSuchMethodException e) {&lt;br /&gt;   ;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Not a good idea for high-performance applications and possibly not a good idea for any app. When you send a message to a class that extends Handler then the correct method will get called if it is capable of handling it. If you use a specific type then the specific method is called like normal, otherwise this multiple dispatcher tries to call the correct method.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-2486415778644832769?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/2486415778644832769/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/02/java-multimethods-for-simple-command.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2486415778644832769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2486415778644832769'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/02/java-multimethods-for-simple-command.html' title='Java multimethods for a simple command handler'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8565397445869139246</id><published>2012-01-27T14:01:00.000-08:00</published><updated>2012-01-27T14:01:00.373-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>System.Collections.IEnumerable and collection initializer syntax</title><content type='html'>I'd like to make a post about something that I think is one of the least utilized aspects of C#: custom collection initializers. Implementing a custom collection initializer for your class can make your code much more declarative and concise.&lt;br /&gt;&lt;br /&gt;It's common to have a collection of things that you set up at design time: drop down list items, configurations, items in a game, a list of mappings, all kinds of things. These are often stored in external xml files or in a sql table. If you're like me then you'd rather have this in code and have it as concise and easy to get right as possible. Here's how.&lt;br /&gt;&lt;br /&gt;Look at this example of setting up a list of Armor types for a game:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public List&amp;lt;Armor&amp;gt; ArmorsWithListExample()&lt;br /&gt;{&lt;br /&gt; List&amp;lt;Armor&amp;gt; armors = new List&amp;lt;Armor&amp;gt;();&lt;br /&gt; armors.Add(new Armor("leather armor",  1,  250, 10));&lt;br /&gt; armors.Add(new Armor("chain mail",     4,  500, 14));&lt;br /&gt; armors.Add(new Armor("splint mail",    7,  750, 16));&lt;br /&gt; armors.Add(new Armor("plate armor",   10, 1000, 18));&lt;br /&gt; return armors;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here is the same thing using the collection initializer syntax:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public List&amp;lt;Armor&amp;gt; ArmorsWithInitializedListExample()&lt;br /&gt;{&lt;br /&gt; return new List&amp;lt;Armor&amp;gt;() {&lt;br /&gt;  new Armor("leather armor",  1,  250, 10),&lt;br /&gt;  new Armor("chain mail",     4,  500, 14),&lt;br /&gt;  new Armor("splint mail",    7,  750, 16),&lt;br /&gt;  new Armor("plate armor",   10, 1000, 18),&lt;br /&gt; };&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There are several advantages with the collection initializer syntax. You don't have to have the temporary variable, it's less code with fewer redundancies, and it's a nice declarative way to show that all you are doing is creating the collection. Small advantages, I admit, but they add up. But if you look at some C code, you see that the code can be even more concise by declaring the objects themselves inline. Here's an example from the game &lt;a href="http://sites.google.com/site/broguegame/"&gt;Brogue&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:c"&gt;const itemTable armorTable[NUMBER_ARMOR_KINDS] = {&lt;br /&gt; {"leather armor", "", "", 10, 250,  10, {30,30,0},  true, false, "This lightweight armor offers basic protection."},&lt;br /&gt; {"scale mail",  "", "", 10, 350,  12, {40,40,0},  true, false, "Bronze scales cover the surface of treated leather, offering greater protection than plain leather with minimal additional weight."},&lt;br /&gt; {"chain mail",  "", "", 10, 500,  13, {50,50,0},  true, false, "Interlocking metal links make for a tough but flexible suit of armor."},&lt;br /&gt; {"banded mail",  "", "", 10, 800,  15, {70,70,0},  true, false, "Overlapping strips of metal horizontally encircle a chain mail base, offering an additional layer of protection at the cost of greater weight."},&lt;br /&gt; {"splint mail",  "", "", 10, 1000,  17, {90,90,0},  true, false, "Thick plates of metal are embedded into a chain mail base, providing the wearer with substantial protection."},&lt;br /&gt; {"plate armor",  "", "", 10, 1300,  19, {120,120,0}, true, false, "Emormous plates of metal are joined together into a suit that provides unmatched protection to any adventurer strong enough to bear its staggering weight."}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's a small improvement but I want to do that in C# as well. With custom collection initializers, you can.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public List&amp;lt;Armor&amp;gt; ArmorsWithCustomInitializerExample()&lt;br /&gt;{&lt;br /&gt; return new ArmorCollection() {&lt;br /&gt;  { "leather armor",  1,  250, 10 },&lt;br /&gt;  { "chain mail",     4,  500, 14 },&lt;br /&gt;  { "splint mail",    7,  750, 16 },&lt;br /&gt;  { "plate armor",   10, 1000, 18 },&lt;br /&gt; }.List;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Look at that! Concise and easy to get right. Here's the code for the ArmorCollection; notice that it's just a wrapper around a List. The only additional functionality it has is the Add method.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;class ArmorCollection : IEnumerable&lt;br /&gt;{&lt;br /&gt; public List&amp;lt;Armor&amp;gt; List { get; set; }&lt;br /&gt; &lt;br /&gt; public ArmorCollection()&lt;br /&gt; {&lt;br /&gt;  List = new List&amp;lt;Armor&amp;gt;();&lt;br /&gt; }&lt;br /&gt;  &lt;br /&gt; IEnumerator IEnumerable.GetEnumerator(){&lt;br /&gt;  return List.GetEnumerator();&lt;br /&gt; }&lt;br /&gt;  &lt;br /&gt; public void Add(string name, int weight, int cost, int ac)&lt;br /&gt; {&lt;br /&gt;  List.Add(new Armor(name, weight, cost, ac));&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Add method is what makes it work. According to section&amp;nbsp;7.6.10.3&amp;nbsp;of &lt;a href="http://msdn.microsoft.com/en-us/library/ms228593.aspx"&gt;the C# specs&lt;/a&gt;, if a class implements the IEnumerable interface then the compiler converts the initializer syntax into a series of calls to the Add method, even though the Add method isn't part of the IEnumerable interface. The Add method can have any number of parameters and the compiler will make it work.&lt;br /&gt;&lt;br /&gt;So when you have a collection of things that isn't going to change, instead of using a built-in collection with a series of Add calls, loading from an external xml file, or loading a sql table, consider creating an in memory collection by using a&amp;nbsp;custom initializer: it may be easier, faster, and more concise than the alternatives.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8565397445869139246?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8565397445869139246/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/01/systemcollectionsienumerable-and.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8565397445869139246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8565397445869139246'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/01/systemcollectionsienumerable-and.html' title='System.Collections.IEnumerable and collection initializer syntax'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-3836327508209505270</id><published>2012-01-24T17:00:00.000-08:00</published><updated>2012-01-27T10:45:40.261-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='domain events'/><category scheme='http://www.blogger.com/atom/ns#' term='ddd'/><category scheme='http://www.blogger.com/atom/ns#' term='cqrs'/><title type='text'>My summary of CQRS and DDD</title><content type='html'>Recent epiphany: sagas are just ways to turn domain events into commands.&lt;br /&gt;&lt;br /&gt;Here's my as-of-now summary of the concepts I see in many EventSourcing, &lt;a href="http://www.cqrsinfo.com/"&gt;CQRS&lt;/a&gt;, and &lt;a href="http://www.infoq.com/interviews/domain-driven-design-eric-evans"&gt;DDD videos&lt;/a&gt; and blog posts.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Command&lt;/b&gt;: A simple message that represents a user's request to make something happen. Just data with no behavior and often named in the imperative tense.&lt;br /&gt;&lt;pre class="brush:java"&gt;class CloseAccount implements Command&lt;br /&gt;{&lt;br /&gt;  public AccountId accountId;&lt;br /&gt;  public String reason;&lt;br /&gt; &lt;br /&gt;  public CloseAccount(AccountId accountId, String reason){&lt;br /&gt;    this.accountId = accountId;&lt;br /&gt;    this.reason = reason;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;CommandHandler&lt;/b&gt;: A domain service that &lt;i&gt;turns a command into events&lt;/i&gt; by calling methods on an aggregate root, publishes the resulting events, and handles transactions and persistence. Stateless with just behavior; just a task based method and sort of procedural.&lt;br /&gt;&lt;pre class="brush:java"&gt;class AccountCommandHandler extends CommandHandler&lt;br /&gt;{&lt;br /&gt;  private AccountRepository _repository;&lt;br /&gt;  private MessageBus _messageBus;&lt;br /&gt; &lt;br /&gt;  public AccountCommandHandler(AccountRepository repository, MessageBus messageBus){&lt;br /&gt;    this._repository = repository;&lt;br /&gt;    this._messageBus = messageBus;&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  public void handle(CloseAccount command){&lt;br /&gt;    Account account = _repository.get(command.accountId);&lt;br /&gt;&lt;br /&gt;    account.close(command.reason);&lt;br /&gt;  &lt;br /&gt;    persistAndPublishEvents(account);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Aggregate Root&lt;/b&gt;: A persistent domain object that &lt;i&gt;turns method calls into domain events&lt;/i&gt;. May contain references to other domain objects as part of a parent/child relationship. Stateful and behaviorful and guaranteed to be internally consistent.&lt;br /&gt;&lt;pre class="brush:java"&gt;class Account extends Aggregate&lt;br /&gt;{&lt;br /&gt;  private AccountId id;&lt;br /&gt;&lt;br /&gt;  public Account(AccountId id){&lt;br /&gt;    this.id = id;&lt;br /&gt;    super.storeEvent(new AccountCreated(this.id));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void Close(CloseReason reason){&lt;br /&gt;    super.storeEvent(new AccountClosed(this.id, reason));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Domain Event&lt;/b&gt;: A simple message that represents a change to the state of an aggregate. Just data with no behavior and often named in the past tense. &lt;br /&gt;&lt;pre class="brush:java"&gt;class ClosedAccount implements DomainEvent&lt;br /&gt;{&lt;br /&gt;  public AccountId id;&lt;br /&gt; &lt;br /&gt;  public ClosedAccount(AccountId id){&lt;br /&gt;    this.id = id;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;MessageBus&lt;/b&gt;: A mechanism for publishing commands and events and subscribing handlers to commands and events. May be a separate EventBus and CommandBus. The only behavior is publishing events and subscribing event and command handlers to events; the only state is what's required to track which services have subscribed to which events.&lt;br /&gt;&lt;pre class="brush:java"&gt;public class SimplestEventBus implements EventBus&lt;br /&gt;{&lt;br /&gt;  private List&amp;lt;Handler&amp;gt; handlers = new ArrayList&amp;lt;Handler&amp;gt;();&lt;br /&gt;  &lt;br /&gt;  public void subscribe(Handler handler)&lt;br /&gt;  {&lt;br /&gt;    handlers.add(handler);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void publish(DomainEvent event)&lt;br /&gt;  {&lt;br /&gt;    for (Handler handler : handlers)&lt;br /&gt;      handler.handle(event);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;Saga&lt;/b&gt;: A persistent object that &lt;i&gt;turns domain events into commands&lt;/i&gt;. Stateful and behaviorful. The only state is what's required for knowing when to create the commands.&lt;br /&gt;&lt;pre class="brush:java"&gt;// Send a notice to the account's owner once the account is&lt;br /&gt;// both paid off and closed. (ignore thread safety since this is a simple example)&lt;br /&gt;class OwnerNotificationSaga implements Saga&lt;br /&gt;{&lt;br /&gt;  private AccountRepository _repository;&lt;br /&gt;  private MessageBus _messageBus;&lt;br /&gt; &lt;br /&gt;  private List&amp;lt;Account&amp;gt; closedAccounts = new ArrayList&amp;lt;Account&amp;gt;();&lt;br /&gt;  private List&amp;lt;Account&amp;gt; paidOffAccounts = new ArrayList&amp;lt;Account&amp;gt;();&lt;br /&gt; &lt;br /&gt;  public OwnerNotificationSaga(AccountRepository repository, MessageBus messageBus){&lt;br /&gt;    this._repository = repository;&lt;br /&gt;    this._messageBus = messageBus;&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  public void handle(ClosedAccount event){&lt;br /&gt;    Account account = _repository.get(event.accountId);&lt;br /&gt;  &lt;br /&gt;    if (closedAccounts.contains(account))&lt;br /&gt;      return;&lt;br /&gt;  &lt;br /&gt;    if (paidOffAccounts.contains(account)){&lt;br /&gt;      sendNotice(account);&lt;br /&gt;      paidOffAccounts.remove(account);&lt;br /&gt;    } else {&lt;br /&gt;      closedAccounts.add(account);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  public void handle(PaidOffAccount event){&lt;br /&gt;    Account account = _repository.get(event.accountId);&lt;br /&gt;  &lt;br /&gt;    if (paidOffAccounts.contains(account))&lt;br /&gt;      return;&lt;br /&gt;  &lt;br /&gt;    if (closedAccounts.contains(account)){&lt;br /&gt;      sendNotice(account);&lt;br /&gt;      closedAccounts.remove(account);&lt;br /&gt;    } else {&lt;br /&gt;      paidOffAccounts.add(account);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  private void sendNotice(Account account){&lt;br /&gt;    _messageBus.publish(new NotifyOwner(account.ownerId));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, for many of the examples I've seen, a user creates Commands that are handed to a MessageBus that dispatches it to a subscribed CommandHandler. The CommandHandler loads the relevant Aggregate and calls methods on it. The resulting events are gathered, persisted (if doing Event Sourcing), and passed to the MessageBus. The subscribers to that event will often update some read model (effectively &lt;i&gt;turning domain events into updates on report tables&lt;/i&gt;) but they may be Sagas that track events and create related commands when necessary.&lt;br /&gt;&lt;br /&gt;None of that is the &lt;a href="http://devlicio.us/blogs/casey/archive/2009/02/09/ddd-the-ubiquitous-language.aspx"&gt;essence of DDD&lt;/a&gt; or &lt;a href="http://www.codeproject.com/Articles/291411/Continuous-thinking-CQRS-explained-to-a-10-year-ol"&gt;the essence of CQRS&lt;/a&gt;, but it's so common to the discussions and examples that this little glossary helps me to keep things straight.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-3836327508209505270?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/3836327508209505270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/01/my-summary-of-cqrs-and-ddd.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3836327508209505270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3836327508209505270'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/01/my-summary-of-cqrs-and-ddd.html' title='My summary of CQRS and DDD'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-3274377464571767786</id><published>2012-01-17T17:51:00.000-08:00</published><updated>2012-01-17T17:51:13.262-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Typefull Dynamic API: a typesafe C# facade/adapter/monad with phantom types</title><content type='html'>One of the things I came up with at home and actually got to implement at work is so useful, and novel to the C# crowd, that I thought I'd share. I call it a&amp;nbsp;Typefull Dynamic API and it looks like &lt;i&gt;an object that changes its public api at design time&lt;/i&gt; based on what would be the state of another object at&amp;nbsp;runtime. Impossible? Nope. I'll even show you how you could have came up with it yourself.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The scenario&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Imagine the following fictitious method:&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public Account Example()&lt;br /&gt;{&lt;br /&gt;    var account = new Account();&lt;br /&gt;    account.State = UsaState.California;&lt;br /&gt;    account.Open();&lt;br /&gt;    PeopleManager.SetAccountPeople(account, 0);&lt;br /&gt;    MemberLevelServiceHelper.SetAccountHolderLevel(account, MemberLevel.Gold);&lt;br /&gt;    new DebtCalculator().CalculateDebt(account);&lt;br /&gt;    account.Complete();&lt;br /&gt;    Reports.GetCompletedAccountReport().AddAccount(account);&lt;br /&gt;    AccountUtil.Close(account);&lt;br /&gt;    NoteAddingSingleton.Instance.AddNote(account, "I'm an example!");&lt;br /&gt;    return account;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;It looks like it creates an account, does some setup, and returns it. It's calling a bunch of things from all over the place. I know from experience that I'd have a hard time remembering where everything is and what order to call it in. Some things are on the account, some are singletons, some are helpers and utils - exactly the kind of situation&amp;nbsp;that anyone who's joined a long term project with many other developers is familiar with. If only there was a unified interface for all these subsystems - oh wait - isn't that the definition of the classic &lt;a href="http://en.wikipedia.org/wiki/Facade_pattern"&gt;Facade pattern&lt;/a&gt;?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The patterns&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;class AccountLifecycleFacade&lt;br /&gt;{&lt;br /&gt;    public Account RealAccount { get; set; }&lt;br /&gt;&lt;br /&gt;    public AccountLifecycleFacade() &lt;br /&gt;    {&lt;br /&gt;        RealAccount = new Account();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void SetState(UsaState state)&lt;br /&gt;    {&lt;br /&gt;        RealAccount.State = state;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void Open()&lt;br /&gt;    {&lt;br /&gt;        RealAccount.Open();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void SetAccountPeople(int count)&lt;br /&gt;    {&lt;br /&gt;        PeopleManager.SetAccountPeople(RealAccount, count);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void MakeGoldLevel()&lt;br /&gt;    {&lt;br /&gt;        MemberLevelServiceHelper.SetAccountHolderLevel(RealAccount, MemberLevel.Gold);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /* ... etc ... */&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Having this facade lets us work through the lifecycle of an account without worrying about where that functionality is actually implemented; no more trying to remember which helper, manager, singleton, service, utility, or business class to use since we only need to look at one place. With one class for the hi-level functionality, we have a clear api and intellisense will show us what we can do with an account.&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public Account FacadeExample()&lt;br /&gt;{&lt;br /&gt;    var account = new AccountLifecycleFacade();&lt;br /&gt;    account.SetState(UsaState.California);&lt;br /&gt;    account.Open();&lt;br /&gt;    account.SetAccountPeople(0);&lt;br /&gt;    account.MakeGoldLevel();&lt;br /&gt;    account.CalculateDebt();&lt;br /&gt;    account.Complete();&lt;br /&gt;    account.AddToCompletedAccountReport();&lt;br /&gt;    account.Close();&lt;br /&gt;    account.AddNote("I'm an example!");&lt;br /&gt;    return account.RealAccount;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;And since it works with a single account at a time we can make it a wrapper. If we make the wrapper return itself at the end of each method then we'll be able to call it using a &lt;a href="http://en.wikipedia.org/wiki/Fluent_interface"&gt;fluent interface&lt;/a&gt;.&lt;br /&gt;&lt;pre class="brush: csharp"&gt;class AccountLifecycleAdapter&lt;br /&gt;{&lt;br /&gt;    public Account RealAccount { get; set; }&lt;br /&gt;&lt;br /&gt;    public AccountLifecycleAdapter(Account account) &lt;br /&gt;    {&lt;br /&gt;        RealAccount = account;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public AccountLifecycleAdapter SetState(UsaState state)&lt;br /&gt;    {&lt;br /&gt;        RealAccount.State = state;&lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public AccountLifecycleAdapter Open()&lt;br /&gt;    {&lt;br /&gt;        RealAccount.Open();&lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;    /* ... etc ... */&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;And now our example method is a little simpler.&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public Account Example()&lt;br /&gt;{&lt;br /&gt;    var account = new AccountLifecycleAdapter(new Account())&lt;br /&gt;        .SetState(UsaState.California)&lt;br /&gt;        .Open()&lt;br /&gt;        .SetAccountPeople(0)&lt;br /&gt;        .MakeGoldLevel()&lt;br /&gt;        .CalculateDebt()&lt;br /&gt;        .Complete()&lt;br /&gt;        .AddToCompletedAccountReport()&lt;br /&gt;        .Close()&lt;br /&gt;        .AddNote("I'm an example!");&lt;br /&gt;&lt;br /&gt;    return account.RealAccount;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;That's much easier for me to read, understand, and test. I'm sure this facade/adapter has been done a hundred times before. I see one potential problem though: it looks like some of the methods are only valid at certain times. I'd wager that you can't complete a non-open account and that you really shouldn't add an account to the Closed Account Report until it's closed. We could rely on runtime assertions to verify the preconditions and post conditions of our new Account api, &lt;a href="http://en.wikipedia.org/wiki/Assertion_(computing)#Assertions_for_run-time_checking"&gt;which is a fine idea&lt;/a&gt;, but wouldn't it be nice if we could catch this at compile time?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The magic&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;If the preconditions and postconditions of these methods were expressed in the type system then not only would the compiler stop us from running invalid code, but intellisence would only show the methods that are valid for the account we have. In order for that to work we need to change the type of our AccountLifecycleAdapter to expose the state of the account. For this example, certain things may only be valid when the account is new, opened, completed, or closed and certain things may only be valid for new, bronze, silver, or gold members. Not only do these methods have preconditions based on the status and member level of the account, but they also have postconditions that change the state of the account.&lt;br /&gt;&lt;br /&gt;&lt;table style="border-bottom: grey 1px dotted; border-left: grey 1px dotted; border-right: grey 1px dotted; border-top: grey 1px dotted; margin-left: 1em;"&gt;&lt;caption&gt;Some preconditions and post conditions of the domain methods.&lt;/caption&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Method&lt;/th&gt;&lt;th&gt;Precondition&lt;/th&gt;&lt;th&gt;Postcondition&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Open()&lt;/td&gt;&lt;td&gt;Status = New&lt;/td&gt;&lt;td&gt;Status = Open&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Complete()&lt;/td&gt;&lt;td&gt;Status = Open&lt;/td&gt;&lt;td&gt;Status = Complete&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Close()&lt;/td&gt;&lt;td&gt;Status = Open or Complete&lt;/td&gt;&lt;td&gt;Status = Close&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AddToCompletedAccountReport()&lt;/td&gt;&lt;td&gt;Status = Complete&lt;/td&gt;&lt;td&gt;Status is unchanged&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;Although we can change the state of an object, we can't change its type. Luckily we don't need to since we are returning an AccountLifecycleAdapter with each method: we can return a different type that represents the state of the account and only supports the methods that are valid for that account state.&lt;br /&gt;&lt;br /&gt;One possible way is to have a new class for each possible state.&lt;br /&gt;&lt;pre class="brush: csharp"&gt;class AccountLifecycleAdapter_NewStatus_NoLevel&lt;br /&gt;{&lt;br /&gt;    public Account RealAccount { get; set; }&lt;br /&gt;    public AccountLifecycleAdapter_NewStatus_NoLevel(Account account) &lt;br /&gt;    {&lt;br /&gt;        RealAccount = account;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public AccountLifecycleAdapter_NewStatus_NoLevel SetState(UsaState state)&lt;br /&gt;    {&lt;br /&gt;        RealAccount.State = state; &lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public AccountLifecycleAdapter_OpenStatus_NoLevel Open()&lt;br /&gt;    {&lt;br /&gt;        RealAccount.Open();&lt;br /&gt;        return new AccountLifecycleAdapter_OpenStatus_NoLevel(RealAccount);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /* ... etc ... */&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;This would make sure that the wrapper expresses the state of the account and that only valid methods are called for that account. But it could become a cartesian explosion; this example as 4 statuses and 4 member levels for a total of 16 classes you need to make. Since some things are applicable for several states (like CalculateDebt and Close from our earlier table), they'd need to be implemented in several different wrapper classes. &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;Messy&lt;/a&gt;. Fortunately for C# users, the language offers a convenient way out by way of generics and extension methods. Instead of having the class name express the state, we can use a generic type parameter for each run time variable we wish to track; status and member level in this case. A method that creates a copy with the type parameters we want would let us chain the wrapped object along while changing the type parameters.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;class AccountLifecycleAdapter&amp;lt;Status, Level&amp;gt;&lt;br /&gt;{&lt;br /&gt;    public Account RealAccount { get; set; }&lt;br /&gt;    public AccountLifecycleAdapter(Account account)&lt;br /&gt;    {&lt;br /&gt;        RealAccount = account;&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;  public AccountLifecycleAdapter&amp;lt;NewStatus, NewLevel&amp;gt; SetTypes&amp;lt;NewStatus, NewLevel&amp;gt;()&lt;br /&gt;  {&lt;br /&gt;    return new AccountLifecycleAdapter&amp;lt;NewStatus, NewLevel&amp;gt;(RealAccount);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And a non-generic version will make sure we create the adapter with the correct default type parameters.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public class AccountLifecycleAdapter&lt;br /&gt;{&lt;br /&gt;  public static AccountLifecycleAdapter&amp;lt;New, NoLevel&amp;gt; New(Account account)&lt;br /&gt;  {&lt;br /&gt;    return new AccountLifecycleAdapter&amp;lt;New, NoLevel&amp;gt;(account);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The methods that used to belong to the wrapper can now be moved into extension methods.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt; public static class AccountLifecycleAdapterExtensions &lt;br /&gt; {&lt;br /&gt;  public static AccountLifecycleAdapter&amp;lt;New,NoLevel&amp;gt; SetState(this AccountLifecycleAdapter&amp;lt;New,NoLevel&amp;gt; adapter, UsaState state)&lt;br /&gt;     {&lt;br /&gt;         adapter.RealAccount.State = state;&lt;br /&gt;         return adapter.SetTypes&amp;lt;New,NoLevel&amp;gt;();&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;     public static AccountLifecycleAdapter&amp;lt;Open,L&amp;gt; Open&amp;lt;L&amp;gt;(this AccountLifecycleAdapter&amp;lt;New,L&amp;gt; adapter)&lt;br /&gt;         where L : AccountLevel&lt;br /&gt;     {&lt;br /&gt;         adapter.RealAccount.Open();&lt;br /&gt;         return adapter.SetTypes&amp;lt;Open,L&amp;gt;();&lt;br /&gt;     }&lt;br /&gt;  &lt;br /&gt;  public static AccountLifecycleAdapter&amp;lt;Open,L&amp;gt; SetAccountPeople&amp;lt;L&amp;gt;(this AccountLifecycleAdapter&amp;lt;Open,L&amp;gt; adapter, int count)&lt;br /&gt;   where L : AccountLevel&lt;br /&gt;     {&lt;br /&gt;         PeopleManager.SetAccountPeople(adapter.RealAccount, count);&lt;br /&gt;         return adapter;&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;    /* ... etc ... */&lt;br /&gt;&lt;/pre&gt;If we look at our extension methods then we see something I think is very interesting. With this kind of typefull api, the input parameter type represents the precondition and the return type represents the post condition.&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public static AccountLifecycleAdapter&amp;lt;Open,L&amp;gt; Open&amp;lt;L&amp;gt;(this AccountLifecycleAdapter&amp;lt;New,L&amp;gt; adapter) &lt;br /&gt;    where L : AccountLevel&lt;br /&gt;&lt;/pre&gt;&lt;table style="border-bottom: grey 1px dotted; border-left: grey 1px dotted; border-right: grey 1px dotted; border-top: grey 1px dotted; margin-left: 1em;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Method&lt;/th&gt;&lt;th&gt;Precondition&lt;/th&gt;&lt;th&gt;Postcondition&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Open&lt;/td&gt;&lt;td&gt;Status = New&lt;/td&gt;&lt;td&gt;Status = Open&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;Since the valid status is expressed as a precondition and the what the method changes is expressed as the return type, the compiler and intelisense can make sure you only chain methods that make sense. If a method returns an AccountLifecycleAdapter&amp;lt;Open, Silver&amp;gt; then you can't call an extension method that expects a Closed account. Intellesence won't suggest&amp;nbsp;invalid methods&amp;nbsp;and the compiler won't allow them. As an added bonus, changing the preconditions or post conditions in a way that breaks code will prevent a compile instead of lying dormant until discovered, investegated, and fixed during manual or automated testing.&lt;br /&gt;&lt;br /&gt;All we need now are the types that represent the runtime state.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;The Phantom Types&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;These types are a little odd in that they are only used for compile time info and we never create instances of them. The Haskell community would call them &lt;a href="http://neilmitchell.blogspot.com/2007/04/phantom-types-for-real-problems.html"&gt;Phantom Types&lt;/a&gt;&amp;nbsp;so&amp;nbsp;we should too.&lt;br /&gt;&lt;pre class="brush: csharp"&gt;namespace PhantomTypes&lt;br /&gt;{&lt;br /&gt;    public abstract class PhantomType {}&lt;br /&gt;&lt;br /&gt;    public abstract class AccountStatus : PhantomType {}&lt;br /&gt;    public abstract class New : AccountStatus {}&lt;br /&gt;    public abstract class Open : AccountStatus {}&lt;br /&gt;    public abstract class Completed : AccountStatus {}&lt;br /&gt;    public abstract class Closed : AccountStatus {}&lt;br /&gt;&lt;br /&gt;    public abstract class AccountLevel : PhantomType {}&lt;br /&gt;    public abstract class NoLevel : AccountLevel {}&lt;br /&gt;    public abstract class Bronze : AccountLevel {}&lt;br /&gt;    public abstract class Silver : AccountLevel {}&lt;br /&gt;    public abstract class Gold : AccountLevel {}&lt;br /&gt;}&lt;/pre&gt;I like to make it double obvious that these are different than normal types with a separate PhantomType namespace and PhantomType base class.&lt;br /&gt;&lt;br /&gt;Since the type parameters are part of the method signature, you can have different overloaded methods for different states. Here we can see that an overdraft will close the account unless it's a gold level account; they just get a warning.&lt;br /&gt;&lt;pre class="brush: csharp"&gt;    public AccountLifecycleAdapter&amp;lt;S,GoldLevel&amp;gt; Overdraft&amp;lt;L&amp;gt;(this AccountLifecycleAdapter&amp;lt;S,GoldLevel&amp;gt; adapter)&lt;br /&gt;        where S : AccountStatus&lt;br /&gt;    {&lt;br /&gt;        NotificationService.GetInstance().NotifyOfOverdraft(adapter.Account);&lt;br /&gt;        return adapter.SetTypes&amp;lt;S,GoldLevel&amp;gt;()&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public AccountLifecycleAdapter&amp;lt;Closed,L&amp;gt; Overdraft&amp;lt;S,L&amp;gt;(this AccountLifecycleAdapter&amp;lt;S,L&amp;gt; adapter)&lt;br /&gt;        where S : AccountStatus&lt;br /&gt;        where L : AccountLevel&lt;br /&gt;    {&lt;br /&gt;        AccountUtil.CloseDueToOverdraft(adapter);&lt;br /&gt;        return adapter.SetTypes&amp;lt;Closed,L&amp;gt;()&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;Interfaces can be used if an extension method will work with multiple different phantom types. &lt;br /&gt;&lt;pre class="brush: csharp"&gt;  public abstract class AccountStatus : PhantomType {}&lt;br /&gt;  public interface NotNew { }&lt;br /&gt;  public abstract class New : AccountStatus {}&lt;br /&gt;  public abstract class Open : AccountStatus, NotNew {}&lt;br /&gt;  public abstract class Complete : AccountStatus, NotNew {}&lt;br /&gt;  public abstract class Closed : AccountStatus, NotNew {} &lt;br /&gt;&lt;/pre&gt;And the method that uses it would add that as a type parameter constraint. &lt;br /&gt;&lt;pre class="brush: csharp"&gt;     public static AccountLifecycleAdapter&amp;lt;S,L&amp;gt; AddNote&amp;lt;S,L&amp;gt;(this AccountLifecycleAdapter&amp;lt;S,L&amp;gt; adapter, string note)&lt;br /&gt;         where S : AccountStatus, NotNew&lt;br /&gt;   where L : AccountLevel&lt;br /&gt;  {&lt;br /&gt;   NoteAddingSingleton.Instance.AddNote(adapter.RealAccount, note);&lt;br /&gt;         return adapter;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;strong&gt;Where to go from here&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;A lot more functionality and safety can be added. One idea is to add a check to the adapter's constructor or SetTypes method that asserts the phantom types match the actual run time state. This would let you know if your expectations of what's going on are correct or not.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public void CheckTypes&amp;lt;NewStatus, NewLevel&amp;gt;()&lt;br /&gt;{&lt;br /&gt;    if (typeof(GoldLevel).IsAssignableFrom(typeof(NewLevel)) &amp;amp;&amp;amp; RealAccount.MemberLevel != MemberLevel.Gold)&lt;br /&gt;      throw new InvalidOperationException("expected account member level Gold; got " + RealAccount.MemberLevel);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Another thing the adapter could do is collect all the ancillary&amp;nbsp;objects that get created&amp;nbsp;along the way. That way, once you're done with it you not only have your account, or whatever you were making, but any other related objects and you won't need to query for them.&lt;br /&gt;&lt;br /&gt;One idea I haven't tried (yet...) is generating documentation about the system based on the adapter's extension methods. If you think about it, each of these methods is a transition from preconditions to postconditions. Each precondition and postcondition would be a state. You could use reflection, or old fashioned string manipulation, to capture all of the states and transitions within an adapter and create a state transition diagram. In a complex system this could be &lt;i&gt;very&lt;/i&gt; useful for explaining the system to new hires, managers, users, other developers, or testers. Speaking of testing, knowing all of the different states transitions would also help ensure you are adequately testing all code paths.&lt;br /&gt;&lt;br /&gt;I'm not entirely sure what to call this construct or "pattern". It's sort of the opposite of the State pattern; instead of an object changing behavior at run time, it's exposing a different api at design time. It's sort of like a Builder since you're building an object through a path that's more complex than a simple constructor. It can be a Facade and call other sub systems, but it doesn't have to be. It's also an Adapter since it exposes functionality related to the wrapped object. In my mind, the important thing is that it this is an adapter&amp;nbsp;that&amp;nbsp;only exposes methods that are valid for the run time state of the wrapped object. For now I'm calling this a&amp;nbsp;Typefull Dynamic API&amp;nbsp;but there may be a better name.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;The Summary&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Using the facade pattern can greatly aid in readability, understandability, and testability of a complex system with many subsystems - that's been known by many people for many years now. Using an object that returns itself in each method makes fluent interfaces possible - that's been known for a few years too. And using the signature of the method to express the preconditions and postconditions and making sure things are only called on object with the correct state has been around for decades, although I think the object oriented crowd has missed out on a lot of this since you can't change the type of an object when its state is changed. Extension methods give C# developers a chance to combine these in a useful way to get something new: a&amp;nbsp;Typefull Dynamic API&amp;nbsp;that&amp;nbsp;changes its public api at design time based on what would be the state of another object at&amp;nbsp;runtime.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-3274377464571767786?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/3274377464571767786/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/01/typefull-dynamic-api-typesafe-c.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3274377464571767786'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3274377464571767786'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/01/typefull-dynamic-api-typesafe-c.html' title='Typefull Dynamic API: a typesafe C# facade/adapter/monad with phantom types'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-3326054975544451701</id><published>2012-01-10T17:48:00.000-08:00</published><updated>2012-01-24T08:55:59.650-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='domain events'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>The simplest typesafe EventBus</title><content type='html'>I'm a big fan of typesafety so while exploring messaging and events, I've also tried my own typesafe EventBus:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public static class EventBus&lt;br /&gt;{&lt;br /&gt;  private static Dictionary&amp;lt;Type, List&amp;lt;Action&amp;lt;object&amp;gt;&amp;gt;&amp;gt; handlers&amp;nbsp;&lt;br /&gt;      = new Dictionary&amp;lt;Type, List&amp;lt;Action&amp;lt;object&amp;gt;&amp;gt;&amp;gt;();&lt;br /&gt;  &lt;br /&gt;  public static void Subscribe&amp;lt;T&amp;gt;(Action&amp;lt;T&amp;gt; handler){&lt;br /&gt;    if (!handlers.ContainsKey(typeof(T)))&lt;br /&gt;      handlers.Add(typeof(T), new List&amp;lt;Action&amp;lt;object&amp;gt;&amp;gt;());&lt;br /&gt;   &lt;br /&gt;    handlers[typeof(T)].Add(e =&amp;gt; handler((T)e));&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public static void Publish&amp;lt;T&amp;gt;(T e){&lt;br /&gt;    var eventType = typeof(T);&lt;br /&gt;   &lt;br /&gt;    foreach (Type handlerType in handlers.Keys)&lt;br /&gt;      TryPublishForType(handlerType, eventType, e);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  private static void TryPublishForType(Type handlerType, Type eventType, object e){&lt;br /&gt;    if (handlerType.IsAssignableFrom(eventType))&lt;br /&gt;      handlers[handlerType].ForEach(handler =&amp;gt; handler(e));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's very similar to the simplest event bus. I'm sure there are other ways of tracking what subscribers can handle each event but the Dictionary&amp;lt;Type, List&amp;lt;Action&amp;lt;object&amp;gt;&amp;gt;&amp;gt; works well enough. If you're not used to Haskell or C# type trickery then it may seem too complex either way but I think much of that is caused by how verbose C# tends to be.&lt;br /&gt;&lt;br /&gt;One neat thing about typesafe event busses is that, if designed for it like this one is, they can respect the inheritance tree. That means if an Action&amp;lt;object&amp;gt; is subscribed, then it will get everything that get's published since all events are assignable to object. This also means that it is backwards compatible with the simplest EventBus. A few subscribers may need to explicitly state the event type they are interested in but if they use object as the type parameter when subscribing, then the behavior should be the same.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-3326054975544451701?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/3326054975544451701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/01/simplest-typesafe-eventbus.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3326054975544451701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3326054975544451701'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/01/simplest-typesafe-eventbus.html' title='The simplest typesafe EventBus'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-3285004612953595980</id><published>2012-01-04T15:31:00.000-08:00</published><updated>2012-01-24T08:55:49.049-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='domain events'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>The simplest EventBus</title><content type='html'>I've been playing a lot with event buses, domain events, messaging, or whatever you want to call it. I've seen some open source event buses but they looked pretty complicated and often had a lot of extra baggage that clouded what I was interested in: exploring different ways to decouple collaborating classes. I had enough of that and set out to find the simplest thing that would get the job done and here's what I came up with:&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public static class EventBus&lt;br /&gt;{&lt;br /&gt;  private&amp;nbsp;static List&amp;lt;Action&amp;lt;object&amp;gt;&amp;gt; handlers = new List&amp;lt;Action&amp;lt;object&amp;gt;&amp;gt;();&lt;br /&gt;  &lt;br /&gt;  public&amp;nbsp;static void Subscribe(Action&amp;lt;object&amp;gt; handler)&lt;br /&gt;  {&lt;br /&gt;    handlers.Add(handler);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public&amp;nbsp;static void Publish(object e)&lt;br /&gt;  {&lt;br /&gt;    handlers.ForEach(handler =&amp;gt; handler(e));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's it. No unsubscribing, multithreading, xml configuration,&amp;nbsp;flexible dispatching strategies, subscription tokens, base classes, interfaces, or attributes. You don't have to use any specific interfaces to subscribe or publish. You don't even have to instantiate it - it's always ready. Anything can subscribe, anything can publish, from anywhere in your project. Anything can be published and every subscriber will get it. Want to publish an AccountClosed domain event? Go for it. Want to publish a naked string or int? Go ahead. How about publishing your entire Windows Form? I'm not sure why you would, but this won't stop you.&lt;br /&gt;&lt;br /&gt;Just one line to wire-up a subscription (often called from Main):&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public static void AddSubscriptions(ILogger logger, PopulationReport pop, MainApp app)&lt;br /&gt;{&lt;br /&gt;  EventBus.Subscribe(e =&amp;gt; logger.Log(e.GetType().Name + ":" + e.ToString()));&lt;br /&gt;&lt;br /&gt;  EventBus.Subscribe(app.HandleEvent);&lt;br /&gt;&lt;br /&gt;  EventBus.Subscribe(e =&amp;gt; {&lt;br /&gt;    if (e is CreatureCreated)&lt;br /&gt;      pop.CreatureCreated(((CreatureCreated)e).Creature);&lt;br /&gt;    else if (e is CreatureDied)&lt;br /&gt;      pop.CreatureDied(((CreatureCreated)e).Creature);&lt;br /&gt;  });&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And one line to publish (often called from domain objects):&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public void Die()&lt;br /&gt;{&lt;br /&gt;  this.health = 0;&lt;br /&gt;  EventBus.Publish(new CreatureDied(this));&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It can be a bit on the slutty side since it's globally accessible and subscribers will get everything that's published, but that comes with benefits too. This is a good starting point and I haven't had any problems with it: the type checking is minimal for subscribers, I've never tried to subscribe a null, and I don't mind having my domain objects explicitly depend on it. You could add a ClearSubscribers method or a SetInstance method for unit testing, but I just use a single subscriber that dumps all events into a list when a unit test needs to ensure that specific events were created. In the last few at home projects I've worked on this has been the only static class I've used since this is one of the very few times that I'm in favor of a static class with behavior and state.&lt;br /&gt;&lt;br /&gt;I find it much easier to learn about something when it's striped of all the unnecessary fluff. Event busses, domain events, messaging, whatever you want to call it is, in my mind at least, really just easy, ubiquitous, and loosely coupled communication.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-3285004612953595980?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/3285004612953595980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2012/01/simplest-eventbus.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3285004612953595980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3285004612953595980'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2012/01/simplest-eventbus.html' title='The simplest EventBus'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-6766600649491824466</id><published>2011-12-28T15:08:00.000-08:00</published><updated>2012-01-24T08:56:43.696-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='domain events'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>From collaborator hell to dependency-free bliss</title><content type='html'>Let's say you're a developer on a C# app and just got a batch of new requirements. Let's also say that it has a fairly simple requirement: &lt;i&gt;when a user closes his or her account, the account status should be set to closed&lt;/i&gt;. You, as a good Test Driven Developer, write your unit test, watch it fail, then add the following code to the Account class and watch your test pass:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public void Close() {&lt;br /&gt;  this.Status = AccountStatus.Closed;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Easy-peasy.&lt;br /&gt;&lt;br /&gt;Many weeks later you come across a requirement that says &lt;i&gt;when a user closes his or her account, the account owner's IsSpecial flag should be set&lt;/i&gt;. To make your latest unit test pass, you do the simplest thing that could possibly work.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public void Close() {&lt;br /&gt;  this.Status = Status.Closed;&lt;br /&gt;  this.Owner.IsSpecial = true;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Your test went from Red to Green and now it's time to Refactor. Having your Account mutate some value on another object &lt;a href="http://typicalprogrammer.com/?p=23"&gt;breaks encapsulation&lt;/a&gt; so you Control+R &amp;amp; E to Extract Method then Control+R &amp;amp; M to Move your method to the Owner class and give it a clear name.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public void Close() {&lt;br /&gt;  this.Status = Status.Closed;&lt;br /&gt;  this.Owner.ClosedAccount(this);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's a little better. In stead of just setting some property you're now telling the other object that something happened. It's free to handle that how it wants and the Account needn't know about it - a textbook case of encapsulation and &lt;a href="http://en.wikipedia.org/wiki/Hollywood_principle_(computer_programming)"&gt;The Hollywood Principle&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;You've worked in this domain long enough to know that all kinds of things are going to happen when an account is closed. Maybe the other things should be moved to a different method? Not entirely YAGNI, but modifying the account and telling others about it are two different things so you &lt;i&gt;could&lt;/i&gt; make the argument that they should be two separate methods.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public void Close() {&lt;br /&gt;  this.Status = Status.Closed;&lt;br /&gt;  this.OnClosed();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void OnClosed(){&lt;br /&gt;  this.Owner.ClosedAccount(this);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's a judgment call but this does clearly separate things. There's one method where the account modifies itself and another where all the other objects get to react to the account being updated. You know that if you have requirements that start with &lt;i&gt;when an account is closed&lt;/i&gt;, then the code that deals with the account should go in the Close method and code that deals with other classes goes in the OnClosed method. But wait a second - doesn't this sound familiar? There's even something built into the C# language for just this scenario: events and delegates.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public delegate void ClosedEventHandler(object sender, AccountClosedEventArgs e);&lt;br /&gt;&lt;br /&gt;public class Account {&lt;br /&gt;  public event ClosedEventHandler Closed;&lt;br /&gt;&lt;br /&gt;  public void Close() {&lt;br /&gt;    this.Status = Status.Closed;&lt;br /&gt;&lt;br /&gt;    if (this.Closed != null)&lt;br /&gt;      this.Closed(this, new AccountClosedEventArgs(this));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now the owner can register a callback with the account's Closed event and the account can just tell whoever's listening when it has closed. The Account class no longer needs to reference the Owner class and we can rejoice.&lt;br /&gt;&lt;br /&gt;But something about this still doesn't quite seem right. Now instead of the Account telling the Owner, the Owner has to listen to the Account. You still have one class directly referencing and depending on another - you just switched the dependency direction. A day or two later you remember you once read something about Messaging and Domain Events so you look it up and get that started.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public void Close() {&lt;br /&gt;  this.Status = Status.Closed;&lt;br /&gt;  EventBus.Publish(new AccountClosed(this));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ... elsewhere ... */&lt;br /&gt;public class AccountClosed{&lt;br /&gt;  public Account Account { get; set; }&lt;br /&gt;&lt;br /&gt;  public AccountClosed(Account account){&lt;br /&gt;    this.account = account;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ... elsewhere ... */&lt;br /&gt;class AccountEventsHandler {&lt;br /&gt;&lt;br /&gt;  private IRepository repository;&lt;br /&gt;&lt;br /&gt;  public AccountEventsHandler(IRepository repository){&lt;br /&gt;    this.repository = repository;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void Handle(AccountClosed e){&lt;br /&gt;    this.repository.GetOwner(e.Account).IsSpecial = true;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ... elsewhere ... */&lt;br /&gt;public void WireEverythingTogether(IRepository repository){&lt;br /&gt;  var accountHandler = new AccountEventsHandler(repository);&lt;br /&gt;  EventBus.Subscribe&amp;lt;AccountClosed&amp;gt;(accountHandler.Handle);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Much nicer. Not only is the dependency completely removed from the Account and Owner classes, but each class no longer exposes methods that are just used for reacting to each other. The Account only modifies itself and creates events and the Owner only modifies itself and creates events. Now you have the Account depend on the EventBus and somewhere else you subscribe other classes to the appropriate events. You have a simple AccountEventsHandler that deals with coordinating other objects when the account changes so the domain objects themselves don't need to. It can be little bit more work, but it does reduce coupling and allow much cleaner domain objects. But the Account is still referencing the EventBus directly. You could use a service locator:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public void Close() {&lt;br /&gt;  this.Status = Status.Closed;&lt;br /&gt;  Services.Get&amp;lt;IEventBus&amp;gt;().Publish(new AccountClosed(this));&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;But the service locator can cause problems - like if an IEventBus wasn't registered - and is &lt;a href="http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx"&gt;considered an anti-pattern&lt;/a&gt; by some people. You could use constructor injection to pass in the dependency instead.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public Account(IEventBus bus){&lt;br /&gt;  this.eventBus = bus;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void Close() {&lt;br /&gt;  this.Status = Status.Closed;&lt;br /&gt;  this.eventBus.Publish(new AccountClosed(this));&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's clean and a good use of the &lt;a href="http://www.oodesign.com/dependency-inversion-principle.html"&gt;Dependency Inversion Principle&lt;/a&gt;, but maybe overkill since the EventBus is so simple and ubiquitous. Maybe hard coding a such a simple dependency is ok. In my limited experience this is an acceptable time for bending the rules and relying on a statefull static class. You will probably never hear me say that again since I &lt;i&gt;hate&lt;/i&gt; working with static classes and singletons that have state or complex behavior. On the other hand, constructor injection is a great first step to using a Dependency Injection Container to wire up the EventBus and other dependencies.&lt;br /&gt;&lt;br /&gt;Maybe months from now you can switch to actual full-scale &lt;a href="http://elegantcode.com/2009/11/11/cqrs-la-greg-young/"&gt;event sourcing and CQRS&lt;/a&gt;. With command handlers, the Domain Objects create events and store them locally. Then the command handler would fetch all the events from the domain objects and pass them to the event bus; your domain objects don't even need to know about the event bus anymore since the command handler deals with that.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;This series of slightly different ways of dealing with different objects involved in the same action is something that I've had a lot of problems with. It's all the same basic result but the important difference is in how to deal with coordinating different objects when something happens. These different designs have very different benefits and costs. The earlier examples are easy for anyone to do, easy to see exactly what happens when debugging or looking at the code, and you know exactly where to make the change: right in the method itself. The downside is that it becomes collaborator hell and all your classes quickly become so bloated that most of the class's code is actually about other classes. The classes become so interdependent that you can't change one thing without having to change everything else. Having lots of dependencies like this can also make build times suffer too. The latter examples only rely on the Event Bus, Service Locator, or Inversion Of Control Container so they are as non-dependent as possible but I can't really tell what's going to happen when the program runs. If everything is wired together with configuration in xml files or sql tables then it can also become exceedingly tedious and error prone to change. This kind of configuration becomes especially difficult if the developer's environment, qa environments, and production environments can all be configured differently. Not everyone will be familiar with or even like the event-based designs either. The whole thing is made even more confusing if different sections of the project have different ways of doing things and people mostly just do whatever they copied and pasted from - not that I've ever done that of course....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-6766600649491824466?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/6766600649491824466/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/11/from-collaborator-hell-to-dependency.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6766600649491824466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6766600649491824466'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/11/from-collaborator-hell-to-dependency.html' title='From collaborator hell to dependency-free bliss'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-2729284459378599880</id><published>2011-12-04T22:38:00.001-08:00</published><updated>2011-12-05T00:02:12.463-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='metroidvania'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>Randomized metroidvania update: simple fix</title><content type='html'>I decided to take a break from skeletal animation and focus more on the room generation for the Metroidvania game. While implementing architecture and art for underground caves, I think I found out how to fix the problem where transitioning into a new room would "bump" you to an unexpected place. I say that I "found out" instead of "figured out" because it was really just a lucky observation and a lot of trial and error.&lt;br /&gt;&lt;br /&gt;Why did it take me so long to figure that out? Well, the problem seemed to only happen when transitioning from one room to another room, while on the floor (i.e. not jumping), where the new room's ceiling was lower on the world map than the room you were transitioning from. Confusing enough? Even then, it only happened some of the time. After a lot of logging, I noticed an odd pattern: the player's new y coordinate would be slightly higher than expected (by less than a single pixel). After all that, here's the fix:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;private function checkBounds():void&lt;br /&gt;{&lt;br /&gt;    /* code to figure out the new room and new player.x and player.y values */&lt;br /&gt;&lt;br /&gt;    if (toRoom.mapY &amp;gt; currentRoom.mapY)&lt;br /&gt;        player.y -= 1; // gravity causes weird things sometimes....   &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Although I'm glad to finally have this fixed, as far as I can tell at least, I'm still annoyed by it. What annoys me most is that I have no idea how I could have prevented this and no idea how to even figure out this fix. It was a lot of observation and trial and error. There were at least a dozen previous attempted fixes that didn't fix anything. I still don't fully understand why having a y coordinate of 144 is ok but one of 144.00115 causes you to fall to the room below and have a different x coordinate. I don't even know how to write a unit test for this: it involves Flixel physics, Flixel collision detection, and the constantly changing algorithms that I use to create rooms. Could TDD have prevented this? Is there a unit test that reliably shows what the bug was?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-2729284459378599880?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/2729284459378599880/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/12/randomized-metroidvania-update-simple.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2729284459378599880'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2729284459378599880'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/12/randomized-metroidvania-update-simple.html' title='Randomized metroidvania update: simple fix'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-9066640782321536687</id><published>2011-11-22T08:15:00.001-08:00</published><updated>2011-11-25T19:11:28.149-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FlxSkeleton'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><category scheme='http://www.blogger.com/atom/ns#' term='Flixel'/><title type='text'>Skeletal animation with actual images</title><content type='html'>It took quite a while because I was busy with other things and because I tried to do too much at once. Once I broke the problem down into smaller pieces, things quickly fell into place. I had to rewrite most of the underlying bones and joints but I eventually got the skeletal animation to work with actual graphics. Here's a half-sized Galamoth from C:SOTN.&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="400" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/flxskeletonexample03.swf" type="application/x-shockwave-flash" width="500"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I completely got rid of bones and just use FlxSprites now. Each FlxSkeletonJoint is responsible for how the parent and child are positioned relative to each other. Joints do this by attaching the "ball" point of one sprite to the "socket" point of another at a given angle. Each joint can still have it's own set of animations. The FlxSkeleton itself just holds the bones and joints and helps assemble and detatch them.&lt;br /&gt;&lt;br /&gt;Next is basic inverse kinematics - just enough to support things like "look at that" or "put your right hand here". That should be good enough for what I need.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-9066640782321536687?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/9066640782321536687/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/11/skeletal-animation-with-actual-images.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/9066640782321536687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/9066640782321536687'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/11/skeletal-animation-with-actual-images.html' title='Skeletal animation with actual images'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-1300311245953709098</id><published>2011-11-14T19:16:00.001-08:00</published><updated>2011-11-14T20:40:45.274-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FlxSkeleton'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><category scheme='http://www.blogger.com/atom/ns#' term='Flixel'/><title type='text'>Skeletal animation with basic physics</title><content type='html'>Skeletons are now FLxObjects and not FlxGroups. There's a few other minor changes but that's the big one. You can see the bounding box by toggling debug mode by pressing [~] then clicking the bounding box icon in the upper right.&lt;br /&gt;&lt;br /&gt;Press [w] to jump,&amp;nbsp;[a] or [d] to walk, hold [shift] to run, and [k] to kill yourself. There's not really anything to do here - just jump around and self-kill when bored.&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="400" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/flxskeletonexample02.swf" type="application/x-shockwave-flash" width="500"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Completed this time:&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Make sure skeletons play well with Flixel physics (collisions, velocity, acceleration, etc.)&lt;/li&gt;&lt;li&gt;Detaching bones (exploding skeletons!)&lt;/li&gt;&lt;li&gt;More animations for HumanoidSkeleton&lt;/li&gt;&lt;/ul&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Next up:&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Make sure regular sprites and animated sprites can be used as bones&lt;/li&gt;&lt;li&gt;Constraints for joints&lt;/li&gt;&lt;li&gt;Ragdoll physics&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-1300311245953709098?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/1300311245953709098/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/11/skeletal-animation-with-basic-physics.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1300311245953709098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1300311245953709098'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/11/skeletal-animation-with-basic-physics.html' title='Skeletal animation with basic physics'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-6868364984268491732</id><published>2011-11-11T16:34:00.000-08:00</published><updated>2011-11-11T16:35:23.443-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FlxSkeleton'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><category scheme='http://www.blogger.com/atom/ns#' term='Flixel'/><title type='text'>Skeletal animation with forward kinematics</title><content type='html'>I'm off to a good start with my little detour into skeletal animation: I've got bones and joints that can be animated. The joints use keyframes to hold the target angle of the joint and interpolate between the current angle and the target angle so switching animations should smoothly transition from one to the other. I had a bit of difficulty since rotating a FlxSprite rotates about the center and I need to rotate around the joint. I also wasted nearly an hour on a really stupid bug where animating a joint would sometimes make it freak out (hint: -45 as a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Number&lt;/span&gt; and -45 as a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;uint&lt;/span&gt; are two very different values). Once I'm done, I plan on submitting a pull request to the original Flixel project so maybe the next version of Flixel will support skeletal animations....&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="400" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/flxskeletonexample01.swf" type="application/x-shockwave-flash" width="500"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Done so far:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;FlxSkeletonBone - a FlxSprite with a start and end point&lt;/li&gt;&lt;li&gt;FlxSkeletonJoint - a class that connects bones at an angle&lt;/li&gt;&lt;li&gt;FlxSkeleton - a FlxGroup that wires everything together&lt;/li&gt;&lt;li&gt;HumanoidSkeleton - a pre-made human skeleton with some generic animations&lt;/li&gt;&lt;li&gt;Keyframe based animations per joint&lt;/li&gt;&lt;li&gt;Simple forward kinematics&lt;/li&gt;&lt;/ul&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;To do:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Make sure regular sprites and animated sprites can be used as bones&lt;/li&gt;&lt;li&gt;Make sure skeletons play well with Flixel physics (collisions, velocity, acceleration, etc.)&lt;/li&gt;&lt;li&gt;Constraints for joints&lt;/li&gt;&lt;li&gt;Ragdoll physics&lt;/li&gt;&lt;li&gt;Detaching bones (exploding skeletons!)&lt;/li&gt;&lt;li&gt;Add inverse kinematics&lt;/li&gt;&lt;li&gt;More animations and helpers for HumanoidSkeleton&lt;/li&gt;&lt;li&gt;Walking velocity determined by feet positions?&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-6868364984268491732?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/6868364984268491732/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/11/skeletal-animation-with-forward.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6868364984268491732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6868364984268491732'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/11/skeletal-animation-with-forward.html' title='Skeletal animation with forward kinematics'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-1296968995452789517</id><published>2011-11-07T14:38:00.000-08:00</published><updated>2011-11-09T12:46:49.619-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='metroidvania'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>Randomized metroidvania 09: slightly better architecture</title><content type='html'>It took a few days longer than I'd like but I was able to implement some new stuff:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Slopes and stairs based off of &lt;a href="https://github.com/krix/SlopesTest/blob/master/src/FlxTilemapExt.as"&gt;https://github.com/krix/SlopesTest/blob/master/src/FlxTilemapExt.as&lt;/a&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;Different background colors, patterns, and decorations (just columns for now)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Different art and architecture preferences for each region&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="336" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/metroidvania09.swf" type="application/x-shockwave-flash" width="512"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Each region gets a few architecture and art preferences. The architecture preferences are used to determine the room generation: what tiles are empty, solid, or stairs. The art preferences are used to determine the graphics: what do the background, stairs, walls, and floors look like. There's still not much content but I have a simple system set up and I can add more and better art or architecture styles later. Alpha content will suffice for now.&lt;br /&gt;&lt;br /&gt;There's &lt;i&gt;still&lt;/i&gt; a bug where moving into another room sometimes bumps you to a different space. I can't pinpoint where or why though. It seems to only bump you towards the bottom of the map and never the top. It also seems to bump to the left and never the right. So it bumps up the Y axis and down the X axis.... weird.&lt;br /&gt;&lt;br /&gt;I think it's time to focus on player graphics for a while. Since I'm better at programming than art, I think I'll try my hand at skeletal animation. Flixel doesn't have anything like this as far as I can tell but forward and inverse kinematics should be fun and make a lot of the art stuff easier too. I estimate about a week until I have something useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-1296968995452789517?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/1296968995452789517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/11/randomized-metroidvania-09-slightly.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1296968995452789517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1296968995452789517'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/11/randomized-metroidvania-09-slightly.html' title='Randomized metroidvania 09: slightly better architecture'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-5397398602040708660</id><published>2011-11-04T19:33:00.000-07:00</published><updated>2011-11-04T19:37:21.577-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='metroidvania'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>Randomized metroidvania 08: initial architecture</title><content type='html'>I've got some initial architecture. It should now be possible to move from any room to any room. It certainly ain't pretty and is sometimes frustrating to get the jumps but it does allow you to get from one room to the next. I also added restricted regions to the world generation so the resulting map won't be as dense.&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="336" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/metroidvania08.swf" type="application/x-shockwave-flash" width="512"&gt;&lt;/embed&gt;&lt;br /&gt;[it seems that transitioning from one room to the other sometimes "bumps" the player to a different spot... worked fine on my machine. I'll look a little closer into that code.]&lt;br /&gt;&lt;br /&gt;Part of automating the process of creating room internals was changing the screen size to 32x21 tiles. Next up is adding sloped tiles for stairs, more variety, and more randomness to the architecture.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-5397398602040708660?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/5397398602040708660/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/11/randomized-metroidvania-08-initial.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5397398602040708660'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5397398602040708660'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/11/randomized-metroidvania-08-initial.html' title='Randomized metroidvania 08: initial architecture'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-516604970410810754</id><published>2011-11-02T19:56:00.000-07:00</published><updated>2011-11-04T14:15:18.738-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='metroidvania'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>Randomized metroidvania 07: a brief study of C:SOTN and vertical movement</title><content type='html'>Next up is adding stairs, lifts, and all the architecture that should go in a room. It's already possible to move to rooms to the left and right, but moving up the map isn't possible yet. I decided to turn to &lt;a href="http://www.castlevaniacrypt.com/games/sotn/maps.htm"&gt;maps of Castevania: Symphony Of The Night&lt;/a&gt; to see how they did it.&lt;br /&gt;&lt;br /&gt;First of all, Alucard seems to be two tiles wide and three tall. When he jumps his feet are 4 tiles higher than they were when he started. This means that the shortest ceiling is 3 tiles from the floor and he can jump onto a tile that has a top that's 4 tiles off the floor.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Sometimes a flight of stairs or a hole in the floor is good enough to go up. The second image has three tiles from the top of the stairs to the bottom of the next floor - just short enough to jump up.&lt;br /&gt;&lt;div style="text-align: left;"&gt;&amp;nbsp;&lt;a href="http://3.bp.blogspot.com/-xE9Mnvb896o/TrHygzxoXVI/AAAAAAAAADQ/QJPkMU6Ck68/s1600/long+stairs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="197" src="http://3.bp.blogspot.com/-xE9Mnvb896o/TrHygzxoXVI/AAAAAAAAADQ/QJPkMU6Ck68/s200/long+stairs.png" width="200" /&gt;&lt;/a&gt;&lt;a href="http://2.bp.blogspot.com/-8ZqSfKRtJQ8/TrHyfq7dvCI/AAAAAAAAADE/KRR1d-aAWog/s1600/jump.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="113" src="http://2.bp.blogspot.com/-8ZqSfKRtJQ8/TrHyfq7dvCI/AAAAAAAAADE/KRR1d-aAWog/s200/jump.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;div style="margin: 0px;"&gt;Sometimes you have to make a series of jumps between sides and center blocks.&lt;/div&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; margin: 0px; text-align: left;"&gt;&lt;a href="http://4.bp.blogspot.com/-jY_CyYLgCbI/TrHyik1BJ6I/AAAAAAAAADo/tTBWE6nAdVE/s1600/floating+block.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="156" src="http://4.bp.blogspot.com/-jY_CyYLgCbI/TrHyik1BJ6I/AAAAAAAAADo/tTBWE6nAdVE/s200/floating+block.png" style="cursor: move;" width="200" /&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://3.bp.blogspot.com/-zrmR6aJfnb4/TrHyean78iI/AAAAAAAAACw/l7-Udo-YMNQ/s1600/side+and+center+ledges.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="161" src="http://3.bp.blogspot.com/-zrmR6aJfnb4/TrHyean78iI/AAAAAAAAACw/l7-Udo-YMNQ/s200/side+and+center+ledges.png" style="cursor: move;" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Jumping back and forth between side platforms is the most common. These pictures show that sometimes it's more obvious than other times.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://4.bp.blogspot.com/-qe0U1lAyydM/TrHyhnna1OI/AAAAAAAAADY/fmKvStHXgfQ/s1600/alternating+ledges2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-qe0U1lAyydM/TrHyhnna1OI/AAAAAAAAADY/fmKvStHXgfQ/s200/alternating+ledges2.png" width="111" /&gt;&lt;/a&gt;&lt;a href="http://3.bp.blogspot.com/-iiBY4q3nkeI/TrHyjlmKcOI/AAAAAAAAAD4/zrfDH1LZQMk/s1600/alternating+ledges.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/-iiBY4q3nkeI/TrHyjlmKcOI/AAAAAAAAAD4/zrfDH1LZQMk/s200/alternating+ledges.png" width="123" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-xmrRzYmEJ74/TrHygmUCykI/AAAAAAAAADI/l9Au03BYhto/s1600/alternating+platform+stairs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-xmrRzYmEJ74/TrHygmUCykI/AAAAAAAAADI/l9Au03BYhto/s200/alternating+platform+stairs.png" width="91" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-KpsTq08WsAY/TrHyfOoKqsI/AAAAAAAAAC4/KLF-DIfOokY/s1600/alternating+platform+stairs2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-KpsTq08WsAY/TrHyfOoKqsI/AAAAAAAAAC4/KLF-DIfOokY/s200/alternating+platform+stairs2.png" width="125" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-BoexPYXAePU/TrHykaaUFHI/AAAAAAAAAEA/upB2uVvvcEc/s1600/alternating+stairs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-BoexPYXAePU/TrHykaaUFHI/AAAAAAAAAEA/upB2uVvvcEc/s200/alternating+stairs.png" width="130" /&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://3.bp.blogspot.com/-mL7G4EoNBmg/TrHymDGX8II/AAAAAAAAAEU/-7p5IUTkI1c/s1600/alternating+center+stairs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/-mL7G4EoNBmg/TrHymDGX8II/AAAAAAAAAEU/-7p5IUTkI1c/s200/alternating+center+stairs.png" width="101" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-4mgCZqJB2CA/TrHyeC7SLPI/AAAAAAAAACo/eldP7PgveDQ/s1600/same+side+stairs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-4mgCZqJB2CA/TrHyeC7SLPI/AAAAAAAAACo/eldP7PgveDQ/s200/same+side+stairs.png" width="131" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; margin: 0px; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Here's a short series of jump-through blocks.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://3.bp.blogspot.com/-C0sgruT4pOc/TrHyiDHkKDI/AAAAAAAAADg/M2z8cnmVKnc/s1600/jump+through+blocks.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/-C0sgruT4pOc/TrHyiDHkKDI/AAAAAAAAADg/M2z8cnmVKnc/s200/jump+through+blocks.png" width="129" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&amp;nbsp;This could be either a series of jump-through blocks or jumping from side to side depending on how you look at it. This pattern shows up in a few places.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://1.bp.blogspot.com/-RY3cgxm15qU/TrHyk4DGYxI/AAAAAAAAAEM/Lb9X19F9_Ys/s1600/alternating+center+jump+through.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-RY3cgxm15qU/TrHyk4DGYxI/AAAAAAAAAEM/Lb9X19F9_Ys/s1600/alternating+center+jump+through.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;There are also a few places with moving platforms to bring you up or down.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://4.bp.blogspot.com/-Gx1xeDSvmRE/TrHyjJ7d8bI/AAAAAAAAADw/II3y9n4L9RI/s1600/moving+platforms.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-Gx1xeDSvmRE/TrHyjJ7d8bI/AAAAAAAAADw/II3y9n4L9RI/s200/moving+platforms.png" width="137" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;And this is my favorite example: a weird mix of side to side, stairs, and just jumping up through a hole in the floor. I don't remember if it's here or another place but I think you can't get to&amp;nbsp;some of the non-essential side platforms&amp;nbsp;until you have double-jump. The background graphics and variety make it much more interesting.&amp;nbsp;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://4.bp.blogspot.com/-qDjociytLbI/TrHydqewyuI/AAAAAAAAACk/ztRX6UjB_84/s1600/low+ceiling+mish+mash.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-qDjociytLbI/TrHydqewyuI/AAAAAAAAACk/ztRX6UjB_84/s1600/low+ceiling+mish+mash.png" /&gt;&lt;/a&gt;&lt;/div&gt;A simple algorithm for this might be: evenly place floors, add a flight of stairs going up each third floor,&amp;nbsp;&amp;nbsp;remove the floor from the top of the stairs to the opposite wall, remove the floor form the base of the stairs to the nearest wall, and add a gap in each remaining floor.&amp;nbsp;The background shadows, floors, stairs, and banisters at the top of the staircases are good examples of the attention to details found in C:SOTN.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;So it seems that sometimes you just walk up some stairs or do a single jump, at other times you have to jump from side to side, or sometimes from sides to center blocks, or even have to jump through one-way blocks. The best places (in my opinion) are a crazy mix of each. This is probably much easier to automate if your screen can be split into an even number of floors like the last image; that way you could place the floors and then pick random ways to move between them. It's also good to point out that each region has it's preferred method for going up. Don't forget to carefully plan the architecture to match the height and jump hight of the main character.&lt;br /&gt;&lt;br /&gt;This may have been pretty obvious to most of you, but I learned a lot: you can use the same action (jump to the upper left then the upper right then the upper left....) but provide slight differences in art and layout and it will still provide interesting architecture and opportunities for fun. I've also got some working code to create rooms with layouts like the last image. And lastly; after 15 years, C:SOTN is still a beautiful and intricate game.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-516604970410810754?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/516604970410810754/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/11/randomized-metrovania-07-brief-study-of.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/516604970410810754'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/516604970410810754'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/11/randomized-metrovania-07-brief-study-of.html' title='Randomized metroidvania 07: a brief study of C:SOTN and vertical movement'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-xE9Mnvb896o/TrHygzxoXVI/AAAAAAAAADQ/QJPkMU6Ck68/s72-c/long+stairs.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8807296423759938710</id><published>2011-11-01T19:19:00.000-07:00</published><updated>2011-11-02T19:36:05.615-07:00</updated><title type='text'>Randomized metroidvania 06: player in a room</title><content type='html'>I slept through my alarm this morning and woke up 4 hours after I should have been at work so I had a lot of time for this project today.&lt;br /&gt;&lt;br /&gt;Time for the first steps to actual gameplay: a rectangle that can walk and jump around empty rooms. Press [m] to view the map.&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="384" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/metroidvania06.swf" type="application/x-shockwave-flash" width="640"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Nothing exciting yet - just rooms with connections to the neighboring room. There's not even a way to go up stairs or anything so you eventually end up at a dead end. It was tricky getting the player to walk out one door and appear in the correct one in the correct room; it would have been easier to just create one huge map with the entire level but that would take a lot of memory and the camera would look into rooms you haven't been into yet. Next I'll add background graphics along with stairs and floors within each room; later the room sizes and shapes can be determined by the region. I'll also make sure the map only shows rooms you've been into and a few more tweaks. The really fun stuff will begin once I can generate some decent architecture. I should think of an overall theme I'd like to work with too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8807296423759938710?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8807296423759938710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/11/randomized-metroidvania-06-player-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8807296423759938710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8807296423759938710'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/11/randomized-metroidvania-06-player-in.html' title='Randomized metroidvania 06: player in a room'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8135890920244136175</id><published>2011-10-31T23:52:00.000-07:00</published><updated>2011-11-01T10:42:20.450-07:00</updated><title type='text'>Randomized metroidvania 05: map specials</title><content type='html'>I was going to create the player and rooms to move around in but I decided to add a little more to the map. The very crowded map now shows bonus items in dead ends and secret rooms. You can also mouseover a room to see details about it's region, name, and contents.&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="384" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/metroidvania05.swf" type="application/x-shockwave-flash" width="640"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;There's a limited amount of keys and locks so if you add enough regions then you'll see "key #33" or something like that.&lt;br /&gt;&lt;br /&gt;Here are some things I added:&lt;br /&gt;&amp;nbsp; &amp;nbsp; for each unlocked dead end,&amp;nbsp;there's&amp;nbsp;a chance of adding a random lock and bonus&lt;br /&gt;&amp;nbsp; &amp;nbsp; when placing a room, there's a chance of adding a&amp;nbsp;secret room&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;when placing a room,&amp;nbsp;there's&amp;nbsp;a chance of a name and or some item&lt;br /&gt;&lt;div&gt;&lt;br /&gt;I'm not sure what theme or items I want in the final game, the ones here are just examples. Once I get the mechanics figured out (like how to create an interesting map) then I can figure out what theme I want to have.&lt;br /&gt;&lt;br /&gt;I think I've crammed all I can into the map for now; time to start on actual gameplay.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8135890920244136175?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8135890920244136175/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-05-map-specials.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8135890920244136175'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8135890920244136175'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-05-map-specials.html' title='Randomized metroidvania 05: map specials'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-6925614660740870111</id><published>2011-10-30T12:55:00.000-07:00</published><updated>2011-10-30T12:55:03.055-07:00</updated><title type='text'>Randomized metroidvania bugs</title><content type='html'>My last post had a bug where new regions may not connect to a neighboring room. I thought about fixing it before posting but decided to post it anyway. Now I hate buggy software as much as anyone, and this bug makes the map unsolveable since there can be rooms you can't get to, so why did I post it anyway?&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;This is a work in progress and sometimes - most of the time - those are messy. Sometimes it's good to see how things are made, bugs and all.&lt;/li&gt;&lt;li&gt;I already fixed it when I changed that part of the code for my next iteration.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;So what caused the bug in the first place? I changed the code that connects a room when it is placed. I made it keep placing connections until the new room is connected to another room in the same region unless it was the first room in that region. Otherwise a room could be placed and it would connect to a neighboring room from a different region and not connect to a room in it's own region. If that was the room that had the key and it was locked with that corresponding lock, then there was no way to get the key. In fixing that bug I introduced a new one.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-6925614660740870111?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/6925614660740870111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-bugs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6925614660740870111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6925614660740870111'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-bugs.html' title='Randomized metroidvania bugs'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-2323217710318174534</id><published>2011-10-30T12:30:00.000-07:00</published><updated>2011-10-30T12:35:54.109-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='metroidvania'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>Randomized metroidvania 04: map keys and locks</title><content type='html'>It took a few hours and I had to rewrite the code for doors, but I've got keys and locks working. A major part of any metroidvania is exploring new areas and finding some kind of impassable barrier - &amp;nbsp;a wide pit, a wall of metal blocks, a force field, etc. It's only later when you find some new ability or item - double jump, rocket launcher, field disabler, etc - that you can go past the barrier to a new region. You can abstract this idea of obstacles and items as "locks" and matching "keys". In one game the "red locks" and "red key" might represent actual locked doors and a key, in another game they might be security cameras and a disguise, and in another game they might be tall walls and the ability to climb walls.&lt;br /&gt;&lt;br /&gt;You can see the locks and keys in my latest update. The icons are ugly and hard to distinguish, but it's just for example and will be redone many times before the project is over. Consider this the "alpha version" artwork.&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="384" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/metroidvania04.swf" type="application/x-shockwave-flash" width="640"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;[I just noticed that sometimes the first room in a region doesn't connect to the other rooms. A bug I just fixed when I redid that code for my latest work.]&lt;br /&gt;&lt;br /&gt;when visiting a region for locks and keys:&lt;br /&gt;&amp;nbsp; &amp;nbsp; place an unused key&lt;br /&gt;&amp;nbsp; &amp;nbsp; put the matching lock on one door to each unvisited adjacent region&lt;br /&gt;&amp;nbsp; &amp;nbsp; any unlocked doors to adjacent regions are added to a "to do" list&lt;br /&gt;&amp;nbsp; &amp;nbsp; visit each unvisited adjacent region&lt;br /&gt;&lt;br /&gt;after visiting all regions for locks and keys:&lt;br /&gt;&amp;nbsp; &amp;nbsp; for each unlocked door on the "to do" list, put a random used lock&lt;br /&gt;&lt;br /&gt;This ensures that each region has a key that you need to proceed to the neighboring regions. Although; since each region can connect with its neighboring regions multiple times, it's theoretically possible that getting the first key would be enough to explore all regions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-2323217710318174534?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/2323217710318174534/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-04-map-keys-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2323217710318174534'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2323217710318174534'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-04-map-keys-and.html' title='Randomized metroidvania 04: map keys and locks'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-7537364666212046588</id><published>2011-10-29T15:23:00.001-07:00</published><updated>2011-10-29T15:23:48.758-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='metroidvania'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>Randomized metroidvania 03: map regions</title><content type='html'>Different regions! It was easier than I thought it would be - of course it has been in the back of my head for a while so I didn't jump in with my first thought.&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="384" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/metroidvania03.swf" type="application/x-shockwave-flash" width="640"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;to start a new region:&lt;br /&gt;&amp;nbsp; &amp;nbsp; create a list of all frontiers for all rooms&lt;br /&gt;&amp;nbsp; &amp;nbsp; randomly pick one to start the new region&lt;br /&gt;&amp;nbsp; &amp;nbsp; clear all other frontiers&lt;br /&gt;&amp;nbsp; &amp;nbsp; place a room as normal&lt;br /&gt;&lt;br /&gt;The current region determines the room color and a randomly chosen maximum height and width and can easily be used to control more things like the artwork, items, bad guys, secrets, and other things.&amp;nbsp;Like placing doors, this algorithm is guaranteed to connect with existing regions and could even connect multiple other regions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-7537364666212046588?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/7537364666212046588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-03-map-regions.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/7537364666212046588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/7537364666212046588'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-03-map-regions.html' title='Randomized metroidvania 03: map regions'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-9116625068797323</id><published>2011-10-29T10:44:00.000-07:00</published><updated>2011-10-29T15:22:58.374-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='metroidvania'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>Randomized metroidvania 02: map connections</title><content type='html'>The rooms are now connected! I used a very simple method that's guaranteed to connect and will even create loops to join different "branches" that happen to be near by.&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="384" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/metroidvania02.swf" type="application/x-shockwave-flash" width="640"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;to connect a room:&lt;br /&gt;&amp;nbsp; &amp;nbsp; after placing a room&lt;br /&gt;&amp;nbsp; &amp;nbsp; get a list of all adjacent cells that are in a room&lt;br /&gt;&amp;nbsp; &amp;nbsp; randomly pick some cells to create doors&lt;br /&gt;&lt;br /&gt;This can create multiple opening between two rooms. That's not a problem yet but it could be tweaked later on.&amp;nbsp;This should be enough to create the gameplay portion but I'd like to work on defining different regions at the world-gen level before I move on to creating a player to run around.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-9116625068797323?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/9116625068797323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-02-map.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/9116625068797323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/9116625068797323'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-02-map.html' title='Randomized metroidvania 02: map connections'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-3597223911813733315</id><published>2011-10-28T22:56:00.000-07:00</published><updated>2011-10-29T15:23:12.065-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='metroidvania'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>Randomized metroidvania 01: map rooms</title><content type='html'>So I've decided to make a randomized metroidvania game; sort of a cross between a &lt;a href="http://roguebasin.roguelikedevelopment.org/index.php/Main_Page"&gt;roguelike&lt;/a&gt; and one of the greatest games ever, &lt;a href="http://en.wikipedia.org/wiki/Castlevania:_Symphony_of_the_Night"&gt;Castlevania: Symphony Of The Night&lt;/a&gt;. Since I want this to be a flash game, I'll use &lt;a href="http://www.eclipse.org/downloads/"&gt;Eclipse&lt;/a&gt; and &lt;a href="http://flixel.org/"&gt;Flixel&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I spent a few hours getting Eclipse to work with Actionscript and playing with Flixel a bit. For my game I decided to start with generating a random world map.&lt;br /&gt;&lt;br /&gt;&lt;embed align="middle" allowscriptaccess="always" height="384" pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="https://sites.google.com/site/trystansprojects/metroidvania01.swf" type="application/x-shockwave-flash" width="640"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Here's the basic algorithm, which took less time than setting up Eclipse and Actionscript:&lt;br /&gt;&lt;br /&gt;to start:&lt;br /&gt;&amp;nbsp; &amp;nbsp; add a 1x1 "frontier" space&lt;br /&gt;&amp;nbsp; &amp;nbsp; add a room&lt;br /&gt;&lt;br /&gt;to add a room:&lt;br /&gt;&amp;nbsp; &amp;nbsp; randomly pick a frontier space&lt;br /&gt;&amp;nbsp; &amp;nbsp; start a room there&lt;br /&gt;&amp;nbsp; &amp;nbsp; randomly grow it for a while&lt;br /&gt;&amp;nbsp; &amp;nbsp; remove frontiers under it&lt;br /&gt;&amp;nbsp; &amp;nbsp; add its empty neighboring cells to the list of frontiers&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-3597223911813733315?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/3597223911813733315/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-01-map-rooms.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3597223911813733315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3597223911813733315'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/randomized-metroidvania-01-map-rooms.html' title='Randomized metroidvania 01: map rooms'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8812685412160862412</id><published>2011-10-28T13:00:00.000-07:00</published><updated>2011-10-28T13:00:01.577-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial retrospective</title><content type='html'>Time for a retrospective! I think retrospectives are&amp;nbsp;probably the most important thing a developer could do since reflecting on what you've done is the difference between someone with 10 years of experience and someone with 1 year of experience repeated 10 times.&amp;nbsp;There are as many different styles of retrospectives as there are teams who do them but I've found that these few questions are usually good enough for me.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What does this retrospective cover?&lt;/b&gt; For this roguelike tutorial I tried a few new and different things; the most obvious one being that this was a series of blog posts. This is also the first time I had something of a magic system. There were also a few tweaks to how I normally code things. The roguelike itself is not meant to be a complete and fun game - it's just a basic example of a few different things that may help some roguelike developers - so I'm not going to review the game as a game.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What worked well?&lt;/b&gt; As far as code goes, the Screen interface worked really well. Having goblins pickup and use items is neat, as is learning the identity of things by watching others use them. As far as a tutorial goes, I think it can be useful for some people. Even though like there are, at most, a handful of people who read each post, I did get more feedback than I expected, including a reference to behavior trees - something I didn't know about. I'm also pleased that my posts were a mix of code, references to other sources, and some of my own thoughts about why I'm doing what I'm doing. It's not as authoritative as saying "THIS IS THE ONE TRUE AND RIGHT WAY TO DO THIS", and some of it was probably a bad idea, but I've always found that the most instructive guides show the uncertainties and mistakes we run into and how to deal with them.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What needs to work better?&lt;/b&gt; One thing I've learned about building something via blog posts is that refactoring is very difficult to show; it's easy to show new code to add, especially if you have small classes and methods, but I don't know of any easy way to show removing, rewriting, and refactoring. This means that the final code, although mostly small pieces, could use a lot of cleanup work and this isn't representative of my best code and may even be a bad example. Oops. Using no globals was a laudable goal but lead to some things I'm not to happy about, like the path finder using a collection of points rather than a fixed size 2D array since I didn't have an easy way to tell the path finder what the map size is. There are a few minor annoyances with the code, like the creature class becoming a behemoth, but most of that is from adding features without refactoring. The spell-related code is quite ugly and clunky too - mostly because I'm not familiar with writing magic systems and couldn't refractor so it was just charging ahead with little idea what I was doing.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What should be done differently next time?&lt;/b&gt; I still think a series of blog posts can make a good tutorial but it needs something that makes it easier to show refactoring - perhaps putting everything on github and discussing the changes each time. That would&amp;nbsp;be a slight improvement but still not quite good enough.&amp;nbsp;I tried to be very structured to make sure I followed through and didn't lose interest halfway through but it would have been better to break it into more than 20 parts. I've used Ninject to make it easier to wire things together and something like that would work well for this kind of project; I thought about it but didn't want to add another post about how to use a dependency injection framework.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;In summary&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Use something to counter the limits to show refactoring on a series of blog posts.&lt;/li&gt;&lt;li&gt;Don't limit yourself to a fixed number of posts.&lt;/li&gt;&lt;li&gt;The Screen interface was the best thing I did: keep doing that.&lt;/li&gt;&lt;li&gt;The AsciiPanel was useful but it needs work to avoid flickering and to support animations.&lt;/li&gt;&lt;li&gt;Each post should have code, narration, references, and details about the author's thought process.&lt;/li&gt;&lt;li&gt;I liked getting comments from readers even more than I thought I would.&lt;/li&gt;&lt;/ul&gt;Any feedback from you, readers? What did you like? What did you not like? What could be done differently?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8812685412160862412?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8812685412160862412/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-retrospective.html#comment-form' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8812685412160862412'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8812685412160862412'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-retrospective.html' title='roguelike tutorial retrospective'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8155690490067044787</id><published>2011-10-25T13:00:00.000-07:00</published><updated>2011-10-28T07:04:12.349-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 20: item appearance and identification</title><content type='html'>One of the things I enjoy the most about roguelikes is identifying items. Usually not by scroll of identify or funky unicorn tricks, but by boldly quaffing and reading &amp;mdash; yeah, I tend to die a lot. Identification is something I haven't seen in other games and something that I think any good roguelike needs. The basic idea is that if we've identified an item, or given it a name of our own, then we see the name, otherwise we see it's appearance.&lt;br /&gt;&lt;br /&gt;So let's start by adding an appearance to the Item class.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private String appearance;&lt;br /&gt;public String appearance() { &lt;br /&gt;    if (appearance == null)&lt;br /&gt;        return name;&lt;br /&gt;&lt;br /&gt;    return appearance;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then have it passed in to the constructor.&lt;br /&gt;&lt;br /&gt;Since the appearance has to be passed in, our code is broken in all the places that create items - this is a good thing because now we can see what areas we need to change. If appearance is null then the name is used since most things look like what they are; e.g. a rock looks like a rock and a sword looks like a sword. Because of this we can pass a null appearance to all items except the potions. Before we get to the potions we need to set up some colors and text to use in our factory.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private Map&amp;lt;String, Color&amp;gt; potionColors;&lt;br /&gt;    private List&amp;lt;String&amp;gt; potionAppearances;&lt;br /&gt; &lt;br /&gt;    public StuffFactory(World world){&lt;br /&gt;        this.world = world;&lt;br /&gt;  &lt;br /&gt;        setUpPotionAppearances();&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    private void setUpPotionAppearances(){&lt;br /&gt;        potionColors = new HashMap&amp;lt;String, Color&amp;gt;();&lt;br /&gt;        potionColors.put("red potion", AsciiPanel.brightRed);&lt;br /&gt;        potionColors.put("yellow potion", AsciiPanel.brightYellow);&lt;br /&gt;        potionColors.put("green potion", AsciiPanel.brightGreen);&lt;br /&gt;        potionColors.put("cyan potion", AsciiPanel.brightCyan);&lt;br /&gt;        potionColors.put("blue potion", AsciiPanel.brightBlue);&lt;br /&gt;        potionColors.put("magenta potion", AsciiPanel.brightMagenta);&lt;br /&gt;        potionColors.put("dark potion", AsciiPanel.brightBlack);&lt;br /&gt;        potionColors.put("grey potion", AsciiPanel.white);&lt;br /&gt;        potionColors.put("light potion", AsciiPanel.brightWhite);&lt;br /&gt;&lt;br /&gt;        potionAppearances = new ArrayList&amp;lt;String&amp;gt;(potionColors.keySet());&lt;br /&gt;        Collections.shuffle(potionAppearances);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This creates some text ("red potion", "green potion") and some corresponding colors (red, green), and shuffles the text so each time you create a new factory they will be in a different order. Since we create a new factory once per game, each game will have different text and colors.&lt;br /&gt;&lt;br /&gt;Now to set the potion color, just use the new data when creating potions.&lt;br /&gt;&lt;pre class="brush: java"&gt;String appearance = potionAppearances.get(0);&lt;br /&gt;  Item item = new Item('!', potionColors.get(appearance), "health potion", appearance);&lt;br /&gt;&lt;/pre&gt;...and...&lt;br /&gt;&lt;pre class="brush: java"&gt;String appearance = potionAppearances.get(1);&lt;br /&gt;  Item item = new Item('!', potionColors.get(appearance), "mana potion", appearance);&lt;br /&gt;&lt;/pre&gt;...etc.&lt;br /&gt;&lt;br /&gt;Not the best way but it works well enough and is easy to show.&lt;br /&gt;&lt;br /&gt;If you play the game the potions should be random colors now.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;Our CreatureAi class needs to record what items have been identified and what the names of identified or renamed items are. We could have this tracked by the items themselves or some global variables, but this way creatures can identify and converse about item appearances too. By modeling it as close to reality as possible, i.e. where creature's have their own mind and names for items, interesting possibilities and emergent behavior are more likely.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private Map&amp;lt;String, String&amp;gt; itemNames;&lt;br /&gt;&lt;br /&gt;    public String getName(Item item){&lt;br /&gt;        String name = itemNames.get(item.name());&lt;br /&gt;        return name == null ? item.appearance() : name;&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    public void setName(Item item, String name){&lt;br /&gt;        itemNames.put(item.name(), name);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And we need this to be available to the creatures.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public String nameOf(Item item){&lt;br /&gt;    return ai.getName(item);&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;public void learnName(Item item){&lt;br /&gt;    notify("The " + item.appearance() + " is a " + item.name() + "!");&lt;br /&gt;    ai.setName(item, item.name());&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Wherever we use an item's name we need to use nameOf instead. The easiest way I can think of doing this is to make the name method private and see where the code breaks. Those places need to use the creature's nameOf method instead. The only places we need the real name is in the PlayScreen where we check to see if the player has the Teddy Bear, or whatever the victory object is, and in the CreatureAi getName and setName methods. Once we make all the other changes we can make the name method public and everything should compile again.&lt;br /&gt;&lt;br /&gt;Run it and you should see that potions are now listed by color instead of the real name. I got an error when starting but that was fixed by setting up the GoblinAi before the Goblin is given a weapon and armor.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;Now we need to let the player identify things when used. In the StuffFactory, for each quaffEffect make the creature learn the name of the potion if any effect happens. Here's an example:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newPotionOfHealth(int depth){&lt;br /&gt;    String appearance = potionAppearances.get(0);&lt;br /&gt;    final Item item = new Item('!', potionColors.get(appearance), "health potion", appearance);&lt;br /&gt;    item.setQuaffEffect(new Effect(1){&lt;br /&gt;        public void start(Creature creature){&lt;br /&gt;           if (creature.hp() == creature.maxHp())&lt;br /&gt;               return;&lt;br /&gt;    &lt;br /&gt;           creature.modifyHp(15);&lt;br /&gt;           creature.doAction("look healthier");&lt;br /&gt;           creature.learnName(item);&lt;br /&gt;       }&lt;br /&gt;    });&lt;br /&gt;  &lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Creature's throwAttack should also let the thrower learn the name of anything that has a quaffEffect. Now when you play you can learn the identity of potions by quaffing or throwing them.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;It's still possible to learn the name of an Effect that you shouldn't learn. If you kill a bat with a yellow potion you shouldn't be able to see the effect it had or identify what the potion is since the bat died from being hit by a bottle and there wasn't anything left for the effect to apply to. Let's create a version of the doAction method that handles this logic. This version of doAction will take a message and an item. Anyone who can see the creature should see the message and learn the identity of the item, unless the creature is dead, then nothing happens. We can reuse some of the code in the current doAction.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void doAction(String message, Object ... params){&lt;br /&gt;    for (Creature other : getCreaturesWhoSeeMe()){&lt;br /&gt;        if (other == this){&lt;br /&gt;            other.notify("You " + message + ".", params);&lt;br /&gt;        } else {&lt;br /&gt;            other.notify(String.format("The %s %s.", name, makeSecondPerson(message)), params);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;public void doAction(Item item, String message, Object ... params){&lt;br /&gt;    if (hp &amp;lt; 1)&lt;br /&gt;        return;&lt;br /&gt;  &lt;br /&gt;    for (Creature other : getCreaturesWhoSeeMe()){&lt;br /&gt;        if (other == this){&lt;br /&gt;            other.notify("You " + message + ".", params);&lt;br /&gt;        } else {&lt;br /&gt;            other.notify(String.format("The %s %s.", name, makeSecondPerson(message)), params);&lt;br /&gt;        }&lt;br /&gt;        other.learnName(item);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;private List&amp;lt;Creature&amp;gt; getCreaturesWhoSeeMe(){&lt;br /&gt;    List&amp;lt;Creature&amp;gt; others = new ArrayList&amp;lt;Creature&amp;gt;();&lt;br /&gt;    int r = 9;&lt;br /&gt;    for (int ox = -r; ox &amp;lt; r+1; ox++){&lt;br /&gt;        for (int oy = -r; oy &amp;lt; r+1; oy++){&lt;br /&gt;            if (ox*ox + oy*oy &amp;gt; r*r)&lt;br /&gt;                continue;&lt;br /&gt;    &lt;br /&gt;            Creature other = world.creature(x+ox, y+oy, z);&lt;br /&gt;    &lt;br /&gt;            if (other == null)&lt;br /&gt;                continue;&lt;br /&gt;    &lt;br /&gt;            others.add(other);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return others;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style="border: 1px solid grey; float: right; margin: 0.5em; padding: 0.5em;"&gt;A possibility:&lt;br /&gt;&lt;pre&gt;The goblin quaffs a cyan potion.&lt;br /&gt;The goblin looks stronger.&lt;br /&gt;The cyan potion is a strength potion!&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;If the potions' quaffEffect use this new doAction then things will work a little better and we can remove the learnName from throwAttack since everyone who see's the creature will identify the potion based on the effect. This also means that if your creatures quaff potions everyone who watches can figure out what it is.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;And that's one way of doing item identification. We had to make a lot of little changes all over the place, like using the creature's nameOf instead of the item's name method, but it wasn't difficult or error-prone. Each creature has it's own idea of what things are named so you could even let intelligent creatures tell each other the names. Maybe each goblin should quaff one unidentified potion during it's life and then discuss with others.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void discussItemName(Item item, Creature other){&lt;br /&gt;    creature.doAction(item, "say \"%ss are %ss\"", item.appearance(), item.name());&lt;br /&gt;    other.learnName(item);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Or all goblins could share the same itemNames map &amp;mdash; although that's kind of cheating.&lt;br /&gt;&lt;br /&gt;You could also let the player give names to things. A RenameItemScreen that let's you rename a specific item or all items with a specific appearance. That way if the player deduces that red potions are healing potions then they can rename all red potions to healing potions or if they like their individual sword they can rename it Excalibur.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;One last addition: add a "cause of death" string to the creature class and pass one in to modifyHp.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private String causeOfDeath;&lt;br /&gt; public String causeOfDeath() { return causeOfDeath; }&lt;br /&gt; &lt;br /&gt; public void modifyHp(int amount, String causeOfDeath) { &lt;br /&gt;     hp += amount;&lt;br /&gt;     this.causeOfDeath = causeOfDeath;&lt;br /&gt;  &lt;br /&gt;     if (hp &gt; maxHp) {&lt;br /&gt;         hp = maxHp;&lt;br /&gt;     } else if (hp &lt; 1) {&lt;br /&gt;         doAction("die");&lt;br /&gt;         leaveCorpse();&lt;br /&gt;         world.remove(this);&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;Then update the places that modify hp. Here's an example:&lt;pre class="brush: java"&gt;public void modifyFood(int amount) { &lt;br /&gt;    food += amount;&lt;br /&gt;  &lt;br /&gt;    if (food &gt; maxFood) {&lt;br /&gt;        maxFood = (maxFood + food) / 2;&lt;br /&gt;        food = maxFood;&lt;br /&gt;        notify("You can't belive your stomach can hold that much!");&lt;br /&gt;        modifyHp(-1, "Killed by overeating.");&lt;br /&gt;    } else if (food &lt; 1 &amp;&amp; isPlayer()) {&lt;br /&gt;        modifyHp(-1000, "Starved to death.");&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;Then pass the player to the WinScreen and LoseScreen and you can tell the user details about how they died, what they were carrying, conduct, etc.&lt;a href='https://sites.google.com/site/trystansprojects/rltut20.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8155690490067044787?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8155690490067044787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-20-item-appearance.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8155690490067044787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8155690490067044787'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-20-item-appearance.html' title='roguelike tutorial 20: item appearance and identification'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-6276174296773529932</id><published>2011-10-21T13:00:00.000-07:00</published><updated>2011-10-21T13:00:05.786-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 19: mana, spells, and magic books</title><content type='html'>Time to add some magic to our game. This will take a lot of code and will take a long time to balance. To keep it simple, we'll just use mana and spellbooks. Scrolls are easy to add since they're basically potions that you read instead of quaff so I won't add scrolls - but I'm sure you can figure out how to do that by now or at least you will be able to after this tutorial.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;We're going to have spells and each will have it's own effect. But when we add that effect to a creature, we want each creature to get it's own effect, otherwise weird things will happen because the spell will have an effect being applied to many creatures and the shared state (like duration) will be wonky. Instead, a spell will have an effect and when applying it to something we can create a copy and apply the copy. That way each time you cast a spell the effect will have it's own state. Add a &lt;a href="http://msdn.microsoft.com/en-us/library/ms173116.aspx"&gt;copy constructor&lt;/a&gt; to the Effect class like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Effect(Effect other){&lt;br /&gt;    this.duration = other.duration; &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The first new class we'll need is a Spell class to tie together a spell name, cost, and effect.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public class Spell {&lt;br /&gt;&lt;br /&gt;    private String name;&lt;br /&gt;    public String name() { return name; }&lt;br /&gt;&lt;br /&gt;    private int manaCost;&lt;br /&gt;    public int manaCost() { return manaCost; }&lt;br /&gt;&lt;br /&gt;    private Effect effect;&lt;br /&gt;    public Effect effect() { return new Effect(effect); }&lt;br /&gt;&lt;br /&gt;    public Spell(String name, int manaCost, Effect effect){&lt;br /&gt;        this.name = name;&lt;br /&gt;        this.manaCost = manaCost;&lt;br /&gt;        this.effect = effect;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;See how we return a copy of the effect instead of the original?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Let's add the mana related stuff to the Creature class.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private int maxMana;&lt;br /&gt;    public int maxMana() { return maxMana; }&lt;br /&gt;    &lt;br /&gt;    private int mana;&lt;br /&gt;    public int mana() { return mana; }&lt;br /&gt;    public void modifyMana(int amount) { mana = Math.max(0, Math.min(mana+amount, maxMana)); &lt;br /&gt;    &lt;br /&gt;    private int regenManaCooldown;&lt;br /&gt;    private int regenManaPer1000;&lt;br /&gt;    public void modifyRegenManaPer1000(int amount) { regenManaPer1000 += amount; }&lt;br /&gt;&lt;br /&gt;    private void regenerateMana(){&lt;br /&gt;        regenManaCooldown -= regenManaPer1000;&lt;br /&gt;        if (regenManaCooldown &amp;lt; 0){&lt;br /&gt;            if (mana &amp;lt; maxMana) {&lt;br /&gt;                modifyMana(1);&lt;br /&gt;                modifyFood(-1);&lt;br /&gt;            }&lt;br /&gt;            regenManaCooldown += 1000;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Don't forget to initialize regenManaPer1000 in the constructor and to call regenerateMana during the update method. We also have something else we can gain during a level-up.   &lt;br /&gt;&lt;pre class="brush: java"&gt;public void gainMaxMana() {&lt;br /&gt;        maxMana += 5;&lt;br /&gt;        mana += 5;&lt;br /&gt;        doAction("look more magical");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void gainRegenMana(){&lt;br /&gt;        regenManaPer1000 += 5;&lt;br /&gt;        doAction("look a little less tired");&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And add the new options to the LevelUpController.   &lt;br /&gt;&lt;pre class="brush: java"&gt;new LevelUpOption("Increased mana"){&lt;br /&gt;            public void invoke(Creature creature) { creature.gainMaxMana(); }&lt;br /&gt;        },new LevelUpOption("Increased mana regeneration"){&lt;br /&gt;            public void invoke(Creature creature) { creature.gainRegenMana(); }&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It would also be nice to add our new stats to the displayOutput method of the PlayScreen class.   &lt;br /&gt;&lt;pre class="brush: java"&gt;String stats = String.format(" %3d/%3d hp  %d/%d mana  %8s", &lt;br /&gt;    player.hp(), player.maxHp(), player.mana(), player.maxMana(), hunger());  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Adding spell books could be done many different ways. I think we should stick with the Item class and extend it with what we need.  &lt;br /&gt;&lt;pre class="brush: java"&gt;private List&amp;lt;Spell&amp;gt; writtenSpells;&lt;br /&gt;    public List&amp;lt;Spell&amp;gt; writtenSpells() { return writtenSpells; }&lt;br /&gt;&lt;br /&gt;    public void addWrittenSpell(String name, int manaCost, Effect effect){&lt;br /&gt;        writtenSpells.add(new Spell(name, manaCost, effect));&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Don't forget to initialize writtenSpells in the Item constructor. This should allow us to create scrolls, spell books, notes, or even engraved items. Very simple and flexible. If we create an effect that just displays a note to the user then we could even let the player add his own non-magical engravings to items.&lt;br /&gt;&lt;br /&gt;Let's add some spell books. I'm going to create two simple ones but you should add more spells and more books. These are just examples of what can be done. The first is a healer type book: &lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newWhiteMagesSpellbook(int depth) {&lt;br /&gt;        Item item = new Item('+', AsciiPanel.brightWhite, "white mage's spellbook");&lt;br /&gt;        item.addWrittenSpell("minor heal", 4, new Effect(1){&lt;br /&gt;            public void start(Creature creature){&lt;br /&gt;                if (creature.hp() == creature.maxHp())&lt;br /&gt;                    return;&lt;br /&gt;                &lt;br /&gt;                creature.modifyHp(20);&lt;br /&gt;                creature.doAction("look healthier");&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;        &lt;br /&gt;        item.addWrittenSpell("major heal", 8, new Effect(1){&lt;br /&gt;            public void start(Creature creature){&lt;br /&gt;                if (creature.hp() == creature.maxHp())&lt;br /&gt;                    return;&lt;br /&gt;                &lt;br /&gt;                creature.modifyHp(50);&lt;br /&gt;                creature.doAction("look healthier");&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;        &lt;br /&gt;        item.addWrittenSpell("slow heal", 12, new Effect(50){&lt;br /&gt;            public void update(Creature creature){&lt;br /&gt;                super.update(creature);&lt;br /&gt;                creature.modifyHp(2);&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        item.addWrittenSpell("inner strength", 16, new Effect(50){&lt;br /&gt;            public void start(Creature creature){&lt;br /&gt;                creature.modifyAttackValue(2);&lt;br /&gt;                creature.modifyDefenseValue(2);&lt;br /&gt;                creature.modifyVisionRadius(1);&lt;br /&gt;                creature.modifyRegenHpPer1000(10);&lt;br /&gt;                creature.modifyRegenManaPer1000(-10);&lt;br /&gt;                creature.doAction("seem to glow with inner strength");&lt;br /&gt;            }&lt;br /&gt;            public void update(Creature creature){&lt;br /&gt;                super.update(creature);&lt;br /&gt;                if (Math.random() &amp;lt; 0.25)&lt;br /&gt;                    creature.modifyHp(1);&lt;br /&gt;            }&lt;br /&gt;            public void end(Creature creature){&lt;br /&gt;                creature.modifyAttackValue(-2);&lt;br /&gt;                creature.modifyDefenseValue(-2);&lt;br /&gt;                creature.modifyVisionRadius(-1);&lt;br /&gt;                creature.modifyRegenHpPer1000(-10);&lt;br /&gt;                creature.modifyRegenManaPer1000(10);&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;        &lt;br /&gt;        world.addAtEmptyLocation(item, depth);&lt;br /&gt;        return item;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the second has a hodgepodge of spells that do weird things, mostly to show what can be done within the effects: &lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newBlueMagesSpellbook(int depth) {&lt;br /&gt;        Item item = new Item('+', AsciiPanel.brightBlue, "blue mage's spellbook");&lt;br /&gt;&lt;br /&gt;        item.addWrittenSpell("blood to mana", 1, new Effect(1){&lt;br /&gt;            public void start(Creature creature){&lt;br /&gt;                int amount = Math.min(creature.hp() - 1, creature.maxMana() - creature.mana());&lt;br /&gt;                creature.modifyHp(-amount);&lt;br /&gt;                creature.modifyMana(amount);&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;        &lt;br /&gt;        item.addWrittenSpell("blink", 6, new Effect(1){&lt;br /&gt;            public void start(Creature creature){&lt;br /&gt;                creature.doAction("fade out");&lt;br /&gt;                &lt;br /&gt;                int mx = 0;&lt;br /&gt;                int my = 0;&lt;br /&gt;                &lt;br /&gt;                do&lt;br /&gt;                {&lt;br /&gt;                    mx = (int)(Math.random() * 11) - 5;&lt;br /&gt;                    my = (int)(Math.random() * 11) - 5;&lt;br /&gt;                }&lt;br /&gt;                while (!creature.canEnter(creature.x+mx, creature.y+my, creature.z)&lt;br /&gt;                        &amp;amp;&amp;amp; creature.canSee(creature.x+mx, creature.y+my, creature.z));&lt;br /&gt;                &lt;br /&gt;                creature.moveBy(mx, my, 0);&lt;br /&gt;                &lt;br /&gt;                creature.doAction("fade in");&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;        &lt;br /&gt;        item.addWrittenSpell("summon bats", 11, new Effect(1){&lt;br /&gt;            public void start(Creature creature){&lt;br /&gt;                for (int ox = -1; ox &amp;lt; 2; ox++){&lt;br /&gt;                    for (int oy = -1; oy &amp;lt; 2; oy++){&lt;br /&gt;                        int nx = creature.x + ox;&lt;br /&gt;                        int ny = creature.y + oy;&lt;br /&gt;                        if (ox == 0 &amp;amp;&amp;amp; oy == 0 &lt;br /&gt;                                || creature.creature(nx, ny, creature.z) != null)&lt;br /&gt;                            continue;&lt;br /&gt;                        &lt;br /&gt;                        Creature bat = newBat(0);&lt;br /&gt;                        &lt;br /&gt;                        if (!bat.canEnter(nx, ny, creature.z)){&lt;br /&gt;                            world.remove(bat);&lt;br /&gt;                            continue;&lt;br /&gt;                        }&lt;br /&gt;                        &lt;br /&gt;                        bat.x = nx;&lt;br /&gt;                        bat.y = ny;&lt;br /&gt;                        bat.z = creature.z;&lt;br /&gt;                        &lt;br /&gt;                        creature.summon(bat);&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;        &lt;br /&gt;        item.addWrittenSpell("detect creatures", 16, new Effect(75){&lt;br /&gt;            public void start(Creature creature){&lt;br /&gt;                creature.doAction("look far off into the distance");&lt;br /&gt;                creature.modifyDetectCreatures(1);&lt;br /&gt;            }&lt;br /&gt;            public void end(Creature creature){&lt;br /&gt;                creature.modifyDetectCreatures(-1);&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;        world.addAtEmptyLocation(item, depth);&lt;br /&gt;        return item;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I had to add a couple methods to the creature class to support these. &lt;br /&gt;&lt;pre class="brush: java"&gt;public void summon(Creature other) {&lt;br /&gt;        world.add(other);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    private int detectCreatures;&lt;br /&gt;    public void modifyDetectCreatures(int amount) { detectCreatures += amount; }&lt;br /&gt;    &lt;br /&gt;    public void castSpell(Spell spell, int x2, int y2) {&lt;br /&gt;        Creature other = creature(x2, y2, z);&lt;br /&gt;        &lt;br /&gt;        if (spell.manaCost() &amp;gt; mana){&lt;br /&gt;            doAction("point and mumble but nothing happens");&lt;br /&gt;            return;&lt;br /&gt;        } else if (other == null) {&lt;br /&gt;            doAction("point and mumble at nothing");&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        other.addEffect(spell.effect());&lt;br /&gt;        modifyMana(-spell.manaCost());&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And update one method: &lt;br /&gt;&lt;pre class="brush: java"&gt;public boolean canSee(int wx, int wy, int wz){&lt;br /&gt;        return (detectCreatures &amp;gt; 0 &amp;amp;&amp;amp; world.creature(wx, wy, wz) != null&lt;br /&gt;                || ai.canSee(wx, wy, wz));&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You should also add a potion to restore mana.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now we just need a ReadScreen to select an item to read, a ReadSpell screen to select a spell from the item, and a CastSpell screen to select a target for the spell. You should be able to figure those out but let's do them in the opposite order: &lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Spell;&lt;br /&gt;&lt;br /&gt;public class CastSpellScreen extends TargetBasedScreen {&lt;br /&gt;    private Spell spell;&lt;br /&gt;    &lt;br /&gt;    public CastSpellScreen(Creature player, String caption, int sx, int sy, Spell spell) {&lt;br /&gt;        super(player, caption, sx, sy);&lt;br /&gt;        this.spell = spell;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public void selectWorldCoordinate(int x, int y, int screenX, int screenY){&lt;br /&gt;        player.castSpell(spell, x, y);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The ReadSpellScreen is very similar to the InventoryBasedScreen. &lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;import rltut.Spell;&lt;br /&gt;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public class ReadSpellScreen implements Screen {&lt;br /&gt;&lt;br /&gt;    protected Creature player;&lt;br /&gt;    private String letters;&lt;br /&gt;    private Item item;&lt;br /&gt;    private int sx;&lt;br /&gt;    private int sy;&lt;br /&gt;    &lt;br /&gt;    public ReadSpellScreen(Creature player, int sx, int sy, Item item){&lt;br /&gt;        this.player = player;&lt;br /&gt;        this.letters = "abcdefghijklmnopqrstuvwxyz";&lt;br /&gt;        this.item = item;&lt;br /&gt;        this.sx = sx;&lt;br /&gt;        this.sy = sy;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public void displayOutput(AsciiPanel terminal) {&lt;br /&gt;        ArrayList&amp;lt;String&amp;gt; lines = getList();&lt;br /&gt;        &lt;br /&gt;        int y = 23 - lines.size();&lt;br /&gt;        int x = 4;&lt;br /&gt;&lt;br /&gt;        if (lines.size() &amp;gt; 0)&lt;br /&gt;            terminal.clear(' ', x, y, 20, lines.size());&lt;br /&gt;        &lt;br /&gt;        for (String line : lines){&lt;br /&gt;            terminal.write(line, x, y++);&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        terminal.clear(' ', 0, 23, 80, 1);&lt;br /&gt;        terminal.write("What would you like to read?", 2, 23);&lt;br /&gt;        &lt;br /&gt;        terminal.repaint();&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    private ArrayList&amp;lt;String&amp;gt; getList() {&lt;br /&gt;        ArrayList&amp;lt;String&amp;gt; lines = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;        &lt;br /&gt;        for (int i = 0; i &amp;lt; item.writtenSpells().size(); i++){&lt;br /&gt;            Spell spell = item.writtenSpells().get(i);&lt;br /&gt;            &lt;br /&gt;            String line = letters.charAt(i) + " - " + spell.name() + " (" + spell.manaCost() + " mana)";&lt;br /&gt;            &lt;br /&gt;            lines.add(line);&lt;br /&gt;        }&lt;br /&gt;        return lines;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Screen respondToUserInput(KeyEvent key) {&lt;br /&gt;        char c = key.getKeyChar();&lt;br /&gt;&lt;br /&gt;        Item[] items = player.inventory().getItems();&lt;br /&gt;        &lt;br /&gt;        if (letters.indexOf(c) &amp;gt; -1 &lt;br /&gt;                &amp;amp;&amp;amp; items.length &amp;gt; letters.indexOf(c)&lt;br /&gt;                &amp;amp;&amp;amp; items[letters.indexOf(c)] != null) {&lt;br /&gt;            return use(item.writtenSpells().get(letters.indexOf(c)));&lt;br /&gt;        } else if (key.getKeyCode() == KeyEvent.VK_ESCAPE) {&lt;br /&gt;            return null;&lt;br /&gt;        } else {&lt;br /&gt;            return this;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected Screen use(Spell spell){&lt;br /&gt;        return new CastSpellScreen(player, "", sx, sy, spell);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the ReadScreen is simple enough. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;&lt;br /&gt;public class ReadScreen extends InventoryBasedScreen {&lt;br /&gt;&lt;br /&gt;    private int sx;&lt;br /&gt;    private int sy;&lt;br /&gt;    &lt;br /&gt;    public ReadScreen(Creature player, int sx, int sy) {&lt;br /&gt;        super(player);&lt;br /&gt;        this.sx = sx;&lt;br /&gt;        this.sy = sy;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected String getVerb() {&lt;br /&gt;        return "read";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected boolean isAcceptable(Item item) {&lt;br /&gt;        return !item.writtenSpells().isEmpty();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected Screen use(Item item) {&lt;br /&gt;        return new ReadSpellScreen(player, sx, sy, item);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Update the PlayScreen to add spell books, map the r key to the new ReadScreen, and update the HelpScreen.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;I know this is already a very long tutorial, but there's one little thing that bugs me. The creature class has some special methods that are only called when gaining a level, the gain* methods. They're only called from one place and they're small enough that we can just inline them. Eclipse, and most IDEs, can do this for you, just select the method name, right click, Refactor, Inline. &lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;(days later....) I just realized a different, and almost certainly better, way of handling effects for each spell. Instead of each spell having a reference to an Effect and using the Effect's copy constructor, the spell class should act as an Effect factory and have an abstract newEffect method. Each individual spell would subclass Spell and implement newEffect. I'll have to do that with my next roguelike.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut19.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-6276174296773529932?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/6276174296773529932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-19-mana-spells-and.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6276174296773529932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6276174296773529932'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-19-mana-spells-and.html' title='roguelike tutorial 19: mana, spells, and magic books'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-1982175886735104595</id><published>2011-10-18T13:00:00.000-07:00</published><updated>2011-10-18T13:00:04.461-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 18: potions and effects</title><content type='html'>Let's add some potions now. When the player, or smart monster, quaffs a potion it's effect will be applied to the creature. What can we say about effects? Most effects will only last for a certain duration. Some will apply every turn or every few turns (like poison or a slow heal). Others will have a change that lasts for the duration (like confuse or resist cold). This can be done with a start method that that applies the change when first quaffed, an end method that unapplies the change when the duration has run out, and an update method that is called every turn in between. I think this will cover the basics so let's get started on them. We'll create a base Effect class and subclass it with specific effects.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public class Effect {&lt;br /&gt;        protected int duration;&lt;br /&gt;        &lt;br /&gt;        public boolean isDone() { return duration &amp;lt; 1; }&lt;br /&gt;        &lt;br /&gt;        public Effect(int duration){&lt;br /&gt;                this.duration = duration;&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        public void update(Creature creature){&lt;br /&gt;                duration--;&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        public void start(Creature creature){&lt;br /&gt;                &lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        public void end(Creature creature){&lt;br /&gt;                &lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And add a quaffEffect to Item class. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private Effect quaffEffect;&lt;br /&gt;public Effect quaffEffect() { return quaffEffect; }&lt;br /&gt;public void setQuaffEffect(Effect effect) { this.quaffEffect = effect; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now that we've got items that can have effects when quaffed it's time to add some potions to our StuffFactory. I'll start with three simple ones that show three ways of doing things. &lt;br /&gt;&lt;br /&gt;Here's a simple one-time potion where the work happens in the start method: &lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newPotionOfHealth(int depth){&lt;br /&gt;    Item item = new Item('!', AsciiPanel.white, "health potion");&lt;br /&gt;    item.setQuaffEffect(new Effect(1){&lt;br /&gt;        public void start(Creature creature){&lt;br /&gt;            if (creature.hp() == creature.maxHp())&lt;br /&gt;                return;&lt;br /&gt;                                &lt;br /&gt;            creature.modifyHp(15);&lt;br /&gt;            creature.doAction("look healthier");&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;                &lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here is a potion that affects the creature each turn. &lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newPotionOfPoison(int depth){&lt;br /&gt;    Item item = new Item('!', AsciiPanel.white, "poison potion");&lt;br /&gt;    item.setQuaffEffect(new Effect(20){&lt;br /&gt;        public void start(Creature creature){&lt;br /&gt;            creature.doAction("look sick");&lt;br /&gt;        }&lt;br /&gt;                        &lt;br /&gt;        public void update(Creature creature){&lt;br /&gt;            super.update(creature);&lt;br /&gt;            creature.modifyHp(-1);&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;                &lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here's one that will affect the creature at the start and restore it at the end. &lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newPotionOfWarrior(int depth){&lt;br /&gt;    Item item = new Item('!', AsciiPanel.white, "warrior's potion");&lt;br /&gt;    item.setQuaffEffect(new Effect(20){&lt;br /&gt;        public void start(Creature creature){&lt;br /&gt;            creature.modifyAttackValue(5);&lt;br /&gt;            creature.modifyDefenseValue(5);&lt;br /&gt;            creature.doAction("look stronger");&lt;br /&gt;        }&lt;br /&gt;        public void end(Creature creature){&lt;br /&gt;            creature.modifyAttackValue(-5);&lt;br /&gt;            creature.modifyDefenseValue(-5);&lt;br /&gt;            creature.doAction("look less strong");&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;                &lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And add a randomizer to help us add the new potions. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Item randomPotion(int depth){&lt;br /&gt;                switch ((int)(Math.random() * 3)){&lt;br /&gt;                case 0: return newPotionOfHealth(depth);&lt;br /&gt;                case 1: return newPotionOfPoison(depth);&lt;br /&gt;                default: return newPotionOfWarrior(depth);&lt;br /&gt;                }&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now let's go back to the creature class and make sure it's calling the right methods at the right times. It will need a list of effects that are currently applied to it. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private List&amp;lt;Effect&amp;gt; effects;&lt;br /&gt;public ListList&amp;lt;Effect&amp;gt; effects(){ return effects; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Don't forget to initialize it in the creature's constructor.&lt;br /&gt;&lt;br /&gt;Add quaff method to Creature class. Since they're so similar, the quaff and eat methods can share some code. &lt;br /&gt;&lt;pre class="brush: java"&gt;public void quaff(Item item){&lt;br /&gt;            doAction("quaff a " + item.name());&lt;br /&gt;            consume(item);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void eat(Item item){&lt;br /&gt;            doAction("eat a " + item.name());&lt;br /&gt;            consume(item);&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        private void consume(Item item){&lt;br /&gt;            if (item.foodValue() &amp;lt; 0)&lt;br /&gt;                notify("Gross!");&lt;br /&gt;                &lt;br /&gt;            addEffect(item.quaffEffect());&lt;br /&gt;                &lt;br /&gt;            modifyFood(item.foodValue());&lt;br /&gt;            getRidOf(item);&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        private void addEffect(Effect effect){&lt;br /&gt;            if (effect == null)&lt;br /&gt;                return;&lt;br /&gt;                &lt;br /&gt;            effect.start(this);&lt;br /&gt;            effects.add(effect);&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Create a new method to update the effects each turn and remove any that are done. Don't forget to call the end method before removing the effect. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void updateEffects(){&lt;br /&gt;            List&amp;ltEffect&amp;gt; done = new ArrayList&amp;ltEffect&amp;gt;();&lt;br /&gt;                &lt;br /&gt;            for (Effect effect : effects){&lt;br /&gt;                effect.update(this);&lt;br /&gt;                if (effect.isDone()) {&lt;br /&gt;                    effect.end(this);&lt;br /&gt;                    done.add(effect);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;                &lt;br /&gt;            effects.removeAll(done);&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Call this during the creature's update method. The player can't quaff anything until we create a QuaffScreen to go with our new behavior. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;&lt;br /&gt;public class QuaffScreen extends InventoryBasedScreen {&lt;br /&gt;&lt;br /&gt;        public QuaffScreen(Creature player) {&lt;br /&gt;                super(player);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        protected String getVerb() {&lt;br /&gt;                return "quaff";&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        protected boolean isAcceptable(Item item) {&lt;br /&gt;                return item.quaffEffect() != null;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        protected Screen use(Item item) {&lt;br /&gt;                player.quaff(item);&lt;br /&gt;                return null;&lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I think I've said this before but that InventoryBasedScreen is really paying off. Now bind that to the 'q' key in the PlayScreen class and update the createItems method, also in the PlayScreen class, to add some potions. Try 3 or 4 per level to start with. Don't forget to update the HelpScreen too. Play around and change the durations or strengths or abundance of potions. Try adding some new ones that change the vision radius, heal over time, stop regeneration, consume extra food, or fill someone's stomach. I'm sure you can think of even more potions and effects. There you go; potions and effects that are simple and flexible. We're going to add a few more effects in the next tutorial when we add magic. &lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;Wouldn't it be cool if throwing a potion at a creature caused the effect to apply to it? Easy-peasy. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void throwAttack(Item item, Creature other) {&lt;br /&gt;    commonAttack(other, attackValue / 2 + item.thrownAttackValue(), "throw a %s at the %s for %d damage", item.name(), other.name);&lt;br /&gt;    other.addEffect(item.quaffEffect());&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then make sure the throw method removes the item if it has a quaffEffect and the target was a creature, otherwise it should add to the world like it already does. Now you can sit back and chuck poison bottles at goblins.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut18.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-1982175886735104595?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/1982175886735104595/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-18-potions-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1982175886735104595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1982175886735104595'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-18-potions-and.html' title='roguelike tutorial 18: potions and effects'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-3419315811558411937</id><published>2011-10-14T13:00:00.000-07:00</published><updated>2011-10-28T09:56:19.495-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 17: smarter monsters</title><content type='html'>Now that we've got all these nifty items and abilities, wouldn't it be cool if our cave monsters could use them too? Let's create a new creature capable of using weapons.&lt;br /&gt;&lt;br /&gt;We can start with a GoblinAi that's almost exactly the same as the ZombieAi.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;public class GoblinAi extends CreatureAi {&lt;br /&gt;    private Creature player;&lt;br /&gt;&lt;br /&gt;    public GoblinAi(Creature creature, Creature player) {&lt;br /&gt;        super(creature);&lt;br /&gt;        this.player = player;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void onUpdate(){&lt;br /&gt;        if (creature.canSee(player.x, player.y, player.z))&lt;br /&gt;            hunt(player);&lt;br /&gt;        else&lt;br /&gt;            wander();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void hunt(Creature target){&lt;br /&gt;        List&amp;lt;Point&amp;gt; points = new Path(creature, target.x, target.y).points();&lt;br /&gt;    &lt;br /&gt;        int mx = points.get(0).x - creature.x;&lt;br /&gt;        int my = points.get(0).y - creature.y;&lt;br /&gt;    &lt;br /&gt;        creature.moveBy(mx, my, 0);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now let's add newGoblin to the StuffFactory. Goblins start with random weapons and armor.&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature newGoblin(int depth, Creature player){&lt;br /&gt;        Creature goblin = new Creature(world, 'g', AsciiPanel.brightGreen, "goblin", 66, 15, 5);&lt;br /&gt;        goblin.equip(randomWeapon(depth));&lt;br /&gt;        goblin.equip(randomArmor(depth));&lt;br /&gt;        world.addAtEmptyLocation(goblin, depth);&lt;br /&gt;        new GoblinAi(goblin, player);&lt;br /&gt;        return goblin;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now update creature's equip method to make sure anything equipped is added to the inventory if it isn't already there.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void equip(Item item){&lt;br /&gt;        if (!inventory.contains(item)) {&lt;br /&gt;            if (inventory.isFull()) {&lt;br /&gt;                notify("Can't equip %s since you're holding too much stuff.", item.name());&lt;br /&gt;                return;&lt;br /&gt;            } else {&lt;br /&gt;                world.remove(item);&lt;br /&gt;                inventory.add(item);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        if (item.attackValue() == 0 &amp;amp;&amp;amp; item.rangedAttackValue() == 0 &amp;amp;&amp;amp; item.defenseValue() == 0)&lt;br /&gt;            return;&lt;br /&gt;    &lt;br /&gt;        if (item.attackValue() + item.rangedAttackValue() &amp;gt;= item.defenseValue()){&lt;br /&gt;            unequip(weapon);&lt;br /&gt;            doAction("wield a " + item.name());&lt;br /&gt;            weapon = item;&lt;br /&gt;        } else {&lt;br /&gt;            unequip(armor);&lt;br /&gt;            doAction("put on a " + item.name());&lt;br /&gt;            armor = item;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And add the missing method to the World class. This allows us to remove an object even if we don't know where it is.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void remove(Item item) {&lt;br /&gt;        for (int x = 0; x &amp;lt; width; x++){&lt;br /&gt;            for (int y = 0; y &amp;lt; height; y++){&lt;br /&gt;                for (int z = 0; z &amp;lt; depth; z++){&lt;br /&gt;                if (items[x][y][z] == item) {&lt;br /&gt;                    items[x][y][z] = null;&lt;br /&gt;                    return;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We also need to make sure that when a creature dies it drops anything it was holding.&lt;br /&gt;&lt;pre class="brush: java"&gt;private void leaveCorpse(){&lt;br /&gt;        Item corpse = new Item('%', color, name + " corpse");&lt;br /&gt;        corpse.modifyFoodValue(maxHp);&lt;br /&gt;        world.addAtEmptySpace(corpse, x, y, z);&lt;br /&gt;        for (Item item : inventory.getItems()){&lt;br /&gt;            if (item != null)&lt;br /&gt;                drop(item);&lt;br /&gt;            }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;After adding goblins to the addCreatures method of the PlayScreen you should be able to play and fight some rather tough goblins with armor and weapons of their own. Don't forget to tweak each creatures hp, attack, defense, the food values of corpses, and the xp gained or how much xp is needed for each level. You should also change the number of items and creatures per level. I prefer few items on the ground. That way the player almost has to confront goblins to get better loot.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;So our goblins can use melee weapons and armor — that's nice — but they will run over and beat you with a bow instead of fire them. How about if they could throw things or fire from a distance? Time to work on the GoblinAi. My goblins will, in order of priority, try to: ranged attack, throw attack, melee attack, pickup stuff, and wander if they can't do anything else. Let's get to it.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void onUpdate(){&lt;br /&gt;        if (canRangedWeaponAttack(player))&lt;br /&gt;            creature.rangedWeaponAttack(player);&lt;br /&gt;        else if (canThrowAt(player))&lt;br /&gt;            creature.throwItem(getWeaponToThrow(), player.x, player.y, player.z);&lt;br /&gt;        else if (creature.canSee(player.x, player.y, player.z))&lt;br /&gt;            hunt(player);&lt;br /&gt;        else if (canPickup())&lt;br /&gt;            creature.pickup();&lt;br /&gt;        else&lt;br /&gt;            wander();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;You should make your goblins do things in whatever order you want; maybe you want them to pick things up first so you could slow them down by putting some rocks or something between you.&lt;br /&gt;&lt;br /&gt;The new helper methods are:&lt;br /&gt;&lt;pre class="brush: java"&gt;private boolean canRangedWeaponAttack(Creature other){&lt;br /&gt;        return creature.weapon() != null&lt;br /&gt;                &amp;amp;&amp;amp; creature.weapon().rangedAttackValue() &amp;gt; 0&lt;br /&gt;                &amp;amp;&amp;amp; creature.canSee(other.x, other.y, other.z);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private boolean canThrowAt(Creature other) {&lt;br /&gt;        return creature.canSee(other.x, other.y, other.z)&lt;br /&gt;          &amp;amp;&amp;amp; getWeaponToThrow() != null;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private Item getWeaponToThrow() {&lt;br /&gt;        Item toThrow = null;&lt;br /&gt;    &lt;br /&gt;        for (Item item : creature.inventory().getItems()){&lt;br /&gt;        if (item == null || creature.weapon() == item || creature.armor() == item)&lt;br /&gt;            continue;&lt;br /&gt;        &lt;br /&gt;        if (toThrow == null || item.thrownAttackValue() &amp;gt; toThrow.attackValue())&lt;br /&gt;            toThrow = item;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        return toThrow;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private boolean canPickup() {&lt;br /&gt;        return creature.item(creature.x, creature.y, creature.z) != null&lt;br /&gt;          &amp;amp;&amp;amp; !creature.inventory().isFull();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's a good idea to take these helper methods, and the hunt method, and move them up to the CreatureAi class. You can use the Pull Up refactoring in Eclipse. That way they can be used by any future creatures we add.&lt;br /&gt;&lt;br /&gt;After adding intelligent goblins things should be much more difficult. Play test many times and tweak all the values and behavior you can.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;Goblins should also see if they are holding better weapon or armor and switch to it if they are.&lt;br /&gt;&lt;br /&gt;Here's the helper methods I came up with:&lt;br /&gt;&lt;pre class="brush: java"&gt;protected boolean canUseBetterEquipment() {&lt;br /&gt;        int currentWeaponRating = creature.weapon() == null ? 0 : creature.weapon().attackValue() + creature.weapon().rangedAttackValue();&lt;br /&gt;        int currentArmorRating = creature.armor() == null ? 0 : creature.armor().defenseValue();&lt;br /&gt;    &lt;br /&gt;        for (Item item : creature.inventory().getItems()){&lt;br /&gt;            if (item == null)&lt;br /&gt;                continue;&lt;br /&gt;        &lt;br /&gt;            boolean isArmor = item.attackValue() + item.rangedAttackValue() &amp;lt; item.defenseValue();&lt;br /&gt;        &lt;br /&gt;            if (item.attackValue() + item.rangedAttackValue() &amp;gt; currentWeaponRating&lt;br /&gt;                || isArmor &amp;amp;&amp;amp; item.defenseValue() &amp;gt; currentArmorRating)&lt;br /&gt;                return true;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;protected void useBetterEquipment() {&lt;br /&gt;        int currentWeaponRating = creature.weapon() == null ? 0 : creature.weapon().attackValue() + creature.weapon().rangedAttackValue();&lt;br /&gt;        int currentArmorRating = creature.armor() == null ? 0 : creature.armor().defenseValue();&lt;br /&gt;    &lt;br /&gt;        for (Item item : creature.inventory().getItems()){&lt;br /&gt;            if (item == null)&lt;br /&gt;                continue;&lt;br /&gt;        &lt;br /&gt;            boolean isArmor = item.attackValue() + item.rangedAttackValue() &amp;lt; item.defenseValue();&lt;br /&gt;        &lt;br /&gt;            if (item.attackValue() + item.rangedAttackValue() &amp;gt; currentWeaponRating&lt;br /&gt;                || isArmor &amp;amp;&amp;amp; item.defenseValue() &amp;gt; currentArmorRating) {&lt;br /&gt;                creature.equip(item);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now you have goblins that will chase you when they can, attack from afar when they can, and switch to better equipment they find. That's a monster that's more intelegent than many rogulike denizens. You can make them even more intellegent by having them remember where the player is. If they can't see the player anymore then they go to that location. This way they won't lose interest in you just because you step out of view for one turn.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;These goblins are tough — sometimes too tough. It would be easier if we could regenerate some health. Add this to the creature class:&lt;br /&gt;&lt;pre class="brush: java"&gt;private int regenHpCooldown;&lt;br /&gt;    private int regenHpPer1000;&lt;br /&gt;    public void modifyRegenHpPer1000(int amount) { regenHpPer1000 += amount; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Set the regenHpPer1000 to something reasonable in the constructor. I use 10. You could also use constructor injection or call modifyRegenHpPer1000 in our factory to make some monsters regenerate very quickly.&lt;br /&gt;&lt;br /&gt;Then create a new method to regenerateHealth and call it as part of the update method.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void regenerateHealth(){&lt;br /&gt;        regenHpCooldown -= regenHpPer1000;&lt;br /&gt;        if (regenHpCooldown &amp;lt; 0){&lt;br /&gt;            modifyHp(1);&lt;br /&gt;            modifyFood(-1);&lt;br /&gt;            regenHpCooldown += 1000;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Since we have a new stat, we can add to our level up options too.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://sites.google.com/site/trystansprojects/rltut17.zip?attredirects=0&amp;amp;d=1"&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-3419315811558411937?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/3419315811558411937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-17-smarter-monsters.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3419315811558411937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3419315811558411937'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-17-smarter-monsters.html' title='roguelike tutorial 17: smarter monsters'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-1064628781365698908</id><published>2011-10-11T13:00:00.000-07:00</published><updated>2011-10-11T13:00:04.634-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 16: throwing and ranged weapons</title><content type='html'>Now that we've got our TargetBasedScreen let's make some more things with it. We've already got items so let's make a ThrowScreen. If we add ranged weapons then we could create a FireWeaponScreen.&lt;br /&gt;&lt;br /&gt;First let's add another value to represent how much damage is done when an item is thrown.&lt;br /&gt;&lt;pre class="brush: java"&gt;private int thrownAttackValue;&lt;br /&gt;    public int thrownAttackValue() { return thrownAttackValue; }&lt;br /&gt;    public void modifyThrownAttackValue(int amount) { thrownAttackValue += amount; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We can set the default to 1 in the constructor and update the StuffFactory to give a value to things that are good for throwing like certain weapons or rocks. Don't forget to update the item class's details method.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now let's add a couple methods to the Creature class. One throws an item to a location and one damages any creature there.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void throwItem(Item item, int wx, int wy, int wz) {&lt;br /&gt;        Point end = new Point(x, y, 0);&lt;br /&gt;    &lt;br /&gt;        for (Point p : new Line(x, y, wx, wy)){&lt;br /&gt;            if (!realTile(p.x, p.y, z).isGround())&lt;br /&gt;                break;&lt;br /&gt;            end = p;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        wx = end.x;&lt;br /&gt;        wy = end.y;&lt;br /&gt;    &lt;br /&gt;        Creature c = creature(wx, wy, wz);&lt;br /&gt;    &lt;br /&gt;        if (c != null)&lt;br /&gt;            throwAttack(item, c);&lt;br /&gt;        else&lt;br /&gt;            doAction("throw a %s", item.name());&lt;br /&gt;    &lt;br /&gt;        unequip(item);&lt;br /&gt;        inventory.remove(item);&lt;br /&gt;        world.addAtEmptySpace(item, wx, wy, wz);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now actual attacks with thrown weapons. I'll add half the base attack value of the thrower since thrown weapons should generally do less damage than mele weapons.&lt;br /&gt;&lt;pre class="brush: java"&gt;private void throwAttack(Item item, Creature other) {&lt;br /&gt;        modifyFood(-1);&lt;br /&gt;    &lt;br /&gt;        int amount = Math.max(0, attackValue / 2 + item.thrownAttackValue() - other.defenseValue());&lt;br /&gt;    &lt;br /&gt;        amount = (int)(Math.random() * amount) + 1;&lt;br /&gt;    &lt;br /&gt;        doAction("throw a %s at the %s for %d damage", item.name(), other.name, amount);&lt;br /&gt;    &lt;br /&gt;        other.modifyHp(-amount);&lt;br /&gt;    &lt;br /&gt;        if (other.hp &amp;lt; 1)&lt;br /&gt;            gainXp(other);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The throwing screen will be interesting because it's the first time we have a double screen scenario. We need a subclass of InventoryBasedScreen to select what we want to throw and then a subclass of a TargetBasedScreen to pick what to throw it at.&lt;br /&gt;&lt;br /&gt;The ThrowAtScreen is simple and self explanatory. We can throw at anything we can see that isn't blocked by walls.&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;import rltut.Line;&lt;br /&gt;import rltut.Point;&lt;br /&gt;&lt;br /&gt;public class ThrowAtScreen extends TargetBasedScreen {&lt;br /&gt;    private Item item;&lt;br /&gt;&lt;br /&gt;    public ThrowAtScreen(Creature player, int sx, int sy, Item item) {&lt;br /&gt;        super(player, "Throw " + item.name() + " at?", sx, sy);&lt;br /&gt;        this.item = item;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean isAcceptable(int x, int y) {&lt;br /&gt;        if (!player.canSee(x, y, player.z))&lt;br /&gt;            return false;&lt;br /&gt;    &lt;br /&gt;        for (Point p : new Line(player.x, player.y, x, y)){&lt;br /&gt;            if (!player.realTile(p.x, p.y, player.z).isGround())&lt;br /&gt;                return false;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void selectWorldCoordinate(int x, int y, int screenX, int screenY){&lt;br /&gt;        player.throwItem(item, x, y, player.z);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The ThrowScreen is also a simple InventoryBasedScreen except we need to pass some values that aren't needed by the ThrowScreen but are used by the ThrowAtScreen.&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;&lt;br /&gt;public class ThrowScreen extends InventoryBasedScreen {&lt;br /&gt;    private int sx;&lt;br /&gt;    private int sy;&lt;br /&gt;&lt;br /&gt;    public ThrowScreen(Creature player, int sx, int sy) {&lt;br /&gt;        super(player);&lt;br /&gt;        this.sx = sx;&lt;br /&gt;        this.sy = sy;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected String getVerb() {&lt;br /&gt;        return "throw";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected boolean isAcceptable(Item item) {&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected Screen use(Item item) {&lt;br /&gt;        return new ThrowAtScreen(player, sx, sy, item);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Just add throwing to the HelpScreen and the respondToUserInput method of the PlayScreen and then try it.&lt;br /&gt;&lt;pre class="brush: java"&gt;case KeyEvent.VK_T: subscreen = new ThrowScreen(player,&lt;br /&gt;        player.x - getScrollX(),&lt;br /&gt;        player.y - getScrollY()); break;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now you have a use for those rocks that are scattered around.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;It's great that we can throw but how about some ranged weapons?  Let's update the Item class.&lt;br /&gt;&lt;pre class="brush: java"&gt;private int rangedAttackValue;&lt;br /&gt;    public int rangedAttackValue() { return rangedAttackValue; }&lt;br /&gt;    public void modifyRangedAttackValue(int amount) { rangedAttackValue += amount; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Anything that has a non zero rangedAttackValue is a ranged weapon. This way a weapon can have separate attack values for melee, thrown, and ranged combat.  We'll keep it simple and say that ranged weapons can hit anything we can see that isn't blocked by terrain.&lt;br /&gt;&lt;br /&gt;Add new ranged weapons to the StuffFactory. Bows are good at ranged combat but not very good at hitting things up close.&lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newBow(int depth){&lt;br /&gt;        Item item = new Item(')', AsciiPanel.yellow, "bow");&lt;br /&gt;        item.modifyAttackValue(1);&lt;br /&gt;        item.modifyRangedAttackValue(5);&lt;br /&gt;        world.addAtEmptyLocation(item, depth);&lt;br /&gt;        return item;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;Make sure new weapons gan be created when asking for a randomWeapon.&lt;br /&gt;&lt;pre class="brush: java"&gt;public Item randomWeapon(int depth){&lt;br /&gt;        switch ((int)(Math.random() * 3)){&lt;br /&gt;        case 0: return newDagger(depth);&lt;br /&gt;        case 1: return newSword(depth);&lt;br /&gt;        case 2: return newBow(depth);&lt;br /&gt;        default: return newStaff(depth);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Creatures need a way to attack with ranged weapons. I'll add half the attack value like with thrown items.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void rangedWeaponAttack(Creature other){&lt;br /&gt;        modifyFood(-1);&lt;br /&gt;    &lt;br /&gt;        int amount = Math.max(0, attackValue / 2 + weapon.rangedAttackValue() - other.defenseValue());&lt;br /&gt;    &lt;br /&gt;        amount = (int)(Math.random() * amount) + 1;&lt;br /&gt;    &lt;br /&gt;        doAction("fire a %s at the %s for %d damage", weapon.name(), other.name, amount);&lt;br /&gt;    &lt;br /&gt;        other.modifyHp(-amount);&lt;br /&gt;    &lt;br /&gt;        if (other.hp &amp;lt; 1)&lt;br /&gt;            gainXp(other);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The FireWeaponScreen is similar to the ThrowAtScreen. We can fire our weapon at anything we can see that isn't blocked by walls.&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Line;&lt;br /&gt;import rltut.Point;&lt;br /&gt;&lt;br /&gt;public class FireWeaponScreen extends TargetBasedScreen {&lt;br /&gt;&lt;br /&gt;    public FireWeaponScreen(Creature player, int sx, int sy) {&lt;br /&gt;        super(player, "Fire " + player.weapon().name() + " at?", sx, sy);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean isAcceptable(int x, int y) {&lt;br /&gt;        if (!player.canSee(x, y, player.z))&lt;br /&gt;            return false;&lt;br /&gt;    &lt;br /&gt;        for (Point p : new Line(player.x, player.y, x, y)){&lt;br /&gt;            if (!player.realTile(p.x, p.y, player.z).isGround())&lt;br /&gt;                return false;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void selectWorldCoordinate(int x, int y, int screenX, int screenY){&lt;br /&gt;        Creature other = player.creature(x, y, player.z);&lt;br /&gt;    &lt;br /&gt;        if (other == null)&lt;br /&gt;            player.notify("There's no one there to fire at.");&lt;br /&gt;        else&lt;br /&gt;            player.rangedWeaponAttack(other);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Just add firing to the HelpScreen and the respondToUserInput method of the PlayScreen and then try it.&lt;br /&gt;&lt;pre class="brush: java"&gt;case KeyEvent.VK_F:&lt;br /&gt;        if (player.weapon() == null || player.weapon().rangedAttackValue() == 0)&lt;br /&gt;         player.notify("You don't have a ranged weapon equiped.");&lt;br /&gt;        else&lt;br /&gt;         subscreen = new FireWeaponScreen(player,&lt;br /&gt;             player.x - getScrollX(),&lt;br /&gt;             player.y - getScrollY()); break;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr/&gt;Creatures sometimes use up an item and it no longer exists. Other times they no longer have the item but it still exists in the world. Either way, we need to make sure they no longer have it equipped and that they no longer have it in their inventory. One way to do this is to create two helper methods and call these when possible.&lt;br /&gt;&lt;pre class="brush: java"&gt;private void getRidOf(Item item){&lt;br /&gt;        inventory.remove(item);&lt;br /&gt;        unequip(item);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void putAt(Item item, int wx, int wy, int wz){&lt;br /&gt;        inventory.remove(item);&lt;br /&gt;        unequip(item);&lt;br /&gt;        world.addAtEmptySpace(item, wx, wy, wz);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr/&gt;The attack weapons all have similar bodies so we could use an &lt;a href="http://www.refactoring.com/catalog/extractMethod.html"&gt;Extract Method &lt;/a&gt; refactoring to move the commonalities to a separate method and use &lt;a href="http://www.refactoring.com/catalog/parameterizeMethod.html"&gt;Extract Parameter or Parameterize Method&lt;/a&gt; to pass in the differences.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void meleeAttack(Creature other){&lt;br /&gt;        commonAttack(other, attackValue(), "attack the %s for %d damage", other.name);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void throwAttack(Item item, Creature other) {&lt;br /&gt;        commonAttack(other, attackValue / 2 + item.thrownAttackValue(), "throw a %s at the %s for %d damage", item.name(), other.name);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void rangedWeaponAttack(Creature other){&lt;br /&gt;        commonAttack(other, attackValue / 2 + weapon.rangedAttackValue(), "fire a %s at the %s for %d damage", weapon.name(), other.name);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void commonAttack(Creature other, int attack, String action, Object ... params) {&lt;br /&gt;        modifyFood(-2);&lt;br /&gt;    &lt;br /&gt;        int amount = Math.max(0, attack - other.defenseValue());&lt;br /&gt;    &lt;br /&gt;        amount = (int)(Math.random() * amount) + 1;&lt;br /&gt;    &lt;br /&gt;        Object[] params2 = new Object[params.length+1];&lt;br /&gt;        for (int i = 0; i &amp;lt; params.length; i++){&lt;br /&gt;         params2[i] = params[i];&lt;br /&gt;        }&lt;br /&gt;        params2[params2.length - 1] = amount;&lt;br /&gt;    &lt;br /&gt;        doAction(action, params2);&lt;br /&gt;    &lt;br /&gt;        other.modifyHp(-amount);&lt;br /&gt;    &lt;br /&gt;        if (other.hp &amp;lt; 1)&lt;br /&gt;            gainXp(other);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This particular implementation has the hidden side effect that the message that is passed in must end in a damage indicator since the commonAttack method will add that. It's not good to do things like this too often, but sometimes that happens.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut16.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-1064628781365698908?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/1064628781365698908/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-16-throwing-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1064628781365698908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1064628781365698908'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-16-throwing-and.html' title='roguelike tutorial 16: throwing and ranged weapons'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-4140331920090337495</id><published>2011-10-07T13:00:00.000-07:00</published><updated>2011-10-07T13:00:04.179-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 15: help, examine, and look screens</title><content type='html'>We've got the basics of a decent game so far. Let's take some time out to add some more screens.&lt;br /&gt;&lt;br /&gt;We'll start with the easiest, a HelpScreen.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public class HelpScreen implements Screen {&lt;br /&gt;&lt;br /&gt;    public void displayOutput(AsciiPanel terminal) {&lt;br /&gt;        terminal.clear();&lt;br /&gt;        terminal.writeCenter("roguelike help", 1);&lt;br /&gt;        terminal.write("Descend the Caves Of Slight Danger, find the lost Teddy Bear, and return to", 1, 3);&lt;br /&gt;        terminal.write("the surface to win. Use what you find to avoid dying.", 1, 4);&lt;br /&gt;    &lt;br /&gt;        int y = 6;&lt;br /&gt;        terminal.write("[g] or [,] to pick up", 2, y++);&lt;br /&gt;        terminal.write("[d] to drop", 2, y++);&lt;br /&gt;        terminal.write("[e] to eat", 2, y++);&lt;br /&gt;        terminal.write("[w] to wear or wield", 2, y++);&lt;br /&gt;        terminal.write("[?] for help", 2, y++);&lt;br /&gt;        terminal.write("[x] to examine your items", 2, y++);&lt;br /&gt;        terminal.write("[;] to look around", 2, y++);&lt;br /&gt;    &lt;br /&gt;        terminal.writeCenter("-- press any key to continue --", 22);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Screen respondToUserInput(KeyEvent key) {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's sufficient but kind of lame. I'm sure you could come up with a better story and maybe say something about what all the symbols are. Don't forget to add this new screen to the respondToUserInput method in the PlayScreen class.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;Now lets make a screen that tells us details about what's in our inventory. I'll call it the ExamineScreen and map it to the x key in the PlayScreen.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;&lt;br /&gt;public class ExamineScreen extends InventoryBasedScreen {&lt;br /&gt;&lt;br /&gt;    public ExamineScreen(Creature player) {&lt;br /&gt;        super(player);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected String getVerb() {&lt;br /&gt;        return "examine";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected boolean isAcceptable(Item item) {&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected Screen use(Item item) {&lt;br /&gt;        String article = "aeiou".contains(item.name().subSequence(0, 1)) ? "an " : "a ";&lt;br /&gt;        player.notify("It's " + article + item.name() + "." + item.details());&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And add a details method to the Item class. You can do whatever you like but here's what I came up with:&lt;br /&gt;&lt;pre class="brush: java"&gt;public String details() {&lt;br /&gt;    String details = "";&lt;br /&gt;&lt;br /&gt;    if (attackValue != 0)&lt;br /&gt;        details += "     attack:" + attackValue;&lt;br /&gt;&lt;br /&gt;    if (defenseValue != 0)&lt;br /&gt;        details += "     defense:" + defenseValue;&lt;br /&gt;&lt;br /&gt;    if (foodValue != 0)&lt;br /&gt;        details += "     food:" + foodValue;&lt;br /&gt;    &lt;br /&gt;    return details;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You could also display some extra description that gets passed into the item constructor.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;Let's start our screen to let us look around. We'll let the user pick a tile and then tell them what it is. If you think about it, this isn't the only time the user will pick a tile through. Throwing, firing bows, and aiming spells all involve picking a tile. Since the InventoryBasedScreen has payed off so well, I think we should create a TargetBasedScreen. Let's get to it.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Line;&lt;br /&gt;import rltut.Point;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public abstract class TargetBasedScreen implements Screen {&lt;br /&gt;&lt;br /&gt;    protected Creature player;&lt;br /&gt;    protected String caption;&lt;br /&gt;    private int sx;&lt;br /&gt;    private int sy;&lt;br /&gt;    private int x;&lt;br /&gt;    private int y;&lt;br /&gt;&lt;br /&gt;    public TargetBasedScreen(Creature player, String caption, int sx, int sy){&lt;br /&gt;        this.player = player;&lt;br /&gt;        this.caption = caption;&lt;br /&gt;        this.sx = sx;&lt;br /&gt;        this.sy = sy;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We'll keep track of the player, a caption representing what we're targeting, the screen coordinates where the player is looking from, and the s and y offset of where we're targeting. The player and caption are protected so our subclasses can use them. Don't worry, it will make sense.&lt;br /&gt;&lt;br /&gt;When it's time to display the output, we need to draw a line from the player to the target. I chose a line of magenta *s, but that's up to you. We also need to display the caption to the user.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void displayOutput(AsciiPanel terminal) {&lt;br /&gt;    for (Point p : new Line(sx, sy, sx + x, sy + y)){&lt;br /&gt;        if (p.x &amp;lt; 0 || p.x &amp;gt;= 80 || p.y &amp;lt; 0 || p.y &amp;gt;= 24)&lt;br /&gt;            continue;&lt;br /&gt;        &lt;br /&gt;        terminal.write('*', p.x, p.y, AsciiPanel.brightMagenta);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    terminal.clear(' ', 0, 23, 80, 1);&lt;br /&gt;    terminal.write(caption, 0, 23);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The user can change what's being targeted with the movement keys, select a target with Enter, or cancel with Escape. If the user tries to target something it can't, like firing out of range, then we go back to where we were targeting before.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Screen respondToUserInput(KeyEvent key) {&lt;br /&gt;        int px = x;&lt;br /&gt;        int py = y;&lt;br /&gt;&lt;br /&gt;        switch (key.getKeyCode()){&lt;br /&gt;        case KeyEvent.VK_LEFT:&lt;br /&gt;        case KeyEvent.VK_H: x--; break;&lt;br /&gt;        case KeyEvent.VK_RIGHT:&lt;br /&gt;        case KeyEvent.VK_L: x++; break;&lt;br /&gt;        case KeyEvent.VK_UP:&lt;br /&gt;        case KeyEvent.VK_J: y--; break;&lt;br /&gt;        case KeyEvent.VK_DOWN:&lt;br /&gt;        case KeyEvent.VK_K: y++; break;&lt;br /&gt;        case KeyEvent.VK_Y: x--; y--; break;&lt;br /&gt;        case KeyEvent.VK_U: x++; y--; break;&lt;br /&gt;        case KeyEvent.VK_B: x--; y++; break;&lt;br /&gt;        case KeyEvent.VK_N: x++; y++; break;&lt;br /&gt;        case KeyEvent.VK_ENTER: selectWorldCoordinate(player.x + x, player.y + y, sx + x, sy + y); return null;&lt;br /&gt;        case KeyEvent.VK_ESCAPE: return null;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        if (!isAcceptable(player.x + x, player.y + y)){&lt;br /&gt;            x = px;&lt;br /&gt;            y = py;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        enterWorldCoordinate(player.x + x, player.y + y, sx + x, sy + y);&lt;br /&gt;    &lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We'll provide a simple method to determine if a tile is an acceptable target. Subclasses can override this if they want something more specific.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public boolean isAcceptable(int x, int y) {&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After each time the target moves, we let subclasses do whatever they want, usually this will be to update the caption or do nothing.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void enterWorldCoordinate(int x, int y, int screenX, int screenY) {&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And we do the same once the user has selected a specific location.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void selectWorldCoordinate(int x, int y, int screenX, int screenY){&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This should provide a good base for any kind of targeting action.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The simplest targeting action is looking at surroundings; a LookScreen.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;import rltut.Tile;&lt;br /&gt;&lt;br /&gt;public class LookScreen extends TargetBasedScreen {&lt;br /&gt;&lt;br /&gt;    public LookScreen(Creature player, String caption, int sx, int sy) {&lt;br /&gt;        super(player, caption, sx, sy);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void enterWorldCoordinate(int x, int y, int screenX, int screenY) {&lt;br /&gt;        Creature creature = player.creature(x, y, player.z);&lt;br /&gt;        if (creature != null){&lt;br /&gt;            caption = creature.glyph() + " "     + creature.name() + creature.details();&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        Item item = player.item(x, y, player.z);&lt;br /&gt;        if (item != null){&lt;br /&gt;            caption = item.glyph() + " "     + item.name() + item.details();&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        Tile tile = player.tile(x, y, player.z);&lt;br /&gt;        caption = tile.glyph() + " " + tile.details();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will display details to the user about whatever they are targeting. If you use this code then you'll need to create a few methods to get details about creatures and tiles. Don't forget to map it to the ';' key, or whatever key you want, in the PlayScreen.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Add a details method to the Creature class.&lt;br /&gt;&lt;pre class="brush: java"&gt;public String details() {&lt;br /&gt;        return String.format("     level:%d     attack:%d     defense:%d     hp:%d", level, attackValue(), defenseValue(), hp);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And add details to the Tile class.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;FLOOR((char)250, AsciiPanel.yellow, "A dirt and rock cave floor."),&lt;br /&gt;    WALL((char)177, AsciiPanel.yellow, "A dirt and rock cave wall."),&lt;br /&gt;    BOUNDS('x', AsciiPanel.brightBlack, "Beyond the edge of the world."),&lt;br /&gt;    STAIRS_DOWN('&amp;gt;', AsciiPanel.white, "A stone staircase that goes down."),&lt;br /&gt;    STAIRS_UP('&amp;lt;', AsciiPanel.white, "A stone staircase that goes up."),&lt;br /&gt;    UNKNOWN(' ', AsciiPanel.white, "(unknown)");&lt;br /&gt;&lt;br /&gt;    private String details;&lt;br /&gt;    public String details(){ return details; }&lt;br /&gt;&lt;br /&gt;    Tile(char glyph, Color color, String details){&lt;br /&gt;        this.glyph = glyph;&lt;br /&gt;        this.color = color;&lt;br /&gt;        this. details = details;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr/&gt;You may have noticed a problem with the LookScreen in that you can get details about things the player can't see. Let's fix it by making some small changes to the Creature class. Basically, only return what the creature can see or remember.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Tile realTile(int wx, int wy, int wz) {&lt;br /&gt;        return world.tile(wx, wy, wz);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Tile tile(int wx, int wy, int wz) {&lt;br /&gt;        if (canSee(wx, wy, wz))&lt;br /&gt;            return world.tile(wx, wy, wz);&lt;br /&gt;        else&lt;br /&gt;            return ai.rememberedTile(wx, wy, wz);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature creature(int wx, int wy, int wz) {&lt;br /&gt;        if (canSee(wx, wy, wz))&lt;br /&gt;            return world.creature(wx, wy, wz);&lt;br /&gt;        else&lt;br /&gt;            return null;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Item item(int wx, int wy, int wz) {&lt;br /&gt;        if (canSee(wx, wy, wz))&lt;br /&gt;            return world.item(wx, wy, wz);&lt;br /&gt;        else&lt;br /&gt;            return null;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The CreatureAi canSee method needs to use the new realTile method to avoid getting caught in an infinite recursion loop.&lt;br /&gt;&lt;br /&gt;Here's a good-enough-for-now implementation of the CreatureAi rememberedTile method since they don't actually have a memory:&lt;br /&gt;&lt;pre class="brush: java"&gt;public Tile rememberedTile(int wx, int wy, int wz) {&lt;br /&gt;        return Tile.UNKNOWN;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the PlayerAi override:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Tile rememberedTile(int wx, int wy, int wz) {&lt;br /&gt;        return fov.tile(wx, wy, wz);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we've got a help screen, a way to see details about what's in our inventory, and our surroundings. Nothing very glamorous or game changing this time but it's all very helpful for the user. The TargetBasedScreen should also make future screens easier.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut15.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-4140331920090337495?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/4140331920090337495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-15-help-examine-and.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/4140331920090337495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/4140331920090337495'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-15-help-examine-and.html' title='roguelike tutorial 15: help, examine, and look screens'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8097149928279856061</id><published>2011-10-04T13:00:00.000-07:00</published><updated>2011-10-04T13:59:28.814-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 14: experience and leveling up</title><content type='html'>It's great that we've got some caves, equipment, and monsters but what about character development? Levels, feats, attributes, classes, and skill trees can be a complex subject worth studying but, and maybe you saw this coming, we'll do something simple to start with. For now, when a creature is killed, it's killer gains xp, when enough xp is gained, a new level is gained and some benefit is chosen. One thing I'd like to have is other creatures can gain levels too. Why? Just because I haven't seen it done before (at least not that I've noticed). It also means that positioning weak monsters between you and the big bad guy may just make the big bad guy gain levels. Maybe you should start rethinking the use of meat shields....&lt;br /&gt;&lt;br /&gt;Our creatures need xp, a level, and a way to gain xp and levels. When the xp passes the next level threshold, the level should be incremented, the ai should get notified, and the creature should heal a bit.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private int xp;&lt;br /&gt;  public int xp() { return xp; }&lt;br /&gt;  public void modifyXp(int amount) {&lt;br /&gt;      xp += amount;&lt;br /&gt;&lt;br /&gt;      notify("You %s %d xp.", amount &amp;lt; 0 ? "lose" : "gain", amount);&lt;br /&gt;&lt;br /&gt;      while (xp &amp;gt; (int)(Math.pow(level, 1.5) * 20)) {&lt;br /&gt;          level++;&lt;br /&gt;          doAction("advance to level %d", level);&lt;br /&gt;          ai.onGainLevel();&lt;br /&gt;          modifyHp(level * 2);&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private int level;&lt;br /&gt;  public int level() { return level; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The starting level is initialized to 1 in the constructor. I'm using a formula to determine how much experience is needed for the next level but you can use a lookup table or some other formula. Many interesting things have been said about leveling and &lt;a href="http://roguebasin.roguelikedevelopment.org/index.php/Power_Curve"&gt;power curves&lt;/a&gt; so read up, try different things, and do what works for you.&lt;br /&gt;&lt;br /&gt;We need to update the attack method to grant experience when a creature is killed. Add the following to the end of the attack method:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;if (other.hp &amp;lt; 1)&lt;br /&gt;      gainXp(other);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we can grant experience based on some experience value the creature has, or on it's level, or on the killers level, or by some combination. I'm using a simple formula for now.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void gainXp(Creature other){&lt;br /&gt;    int amount = other.maxHp&lt;br /&gt;      + other.attackValue()&lt;br /&gt;      + other.defenseValue()&lt;br /&gt;      - level * 2;&lt;br /&gt;&lt;br /&gt;    if (amount &amp;gt; 0)&lt;br /&gt;      modifyXp(amount);&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This ensures that tougher creatures are worth more and by subtracting the killer's level, easy creatures will soon be worth nothing. It's not perfect but it's simple to explain, understand, and code.&lt;br /&gt;&lt;br /&gt;If you play it now (after creating an empty onGainLevel in CreatureAi) you should see notices about gaining levels. That's a good sign. &lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;For this simple roguelike, when something gains a level it get's some stat bonus; increased hp, increased attack, etc. The player will be shown a list to chose from but other creatures will get one at random.&lt;br /&gt;&lt;br /&gt;Let's create a class to represent a level up option's name and actual effect.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public abstract class LevelUpOption {&lt;br /&gt;  private String name;&lt;br /&gt;  public String name() { return name; }&lt;br /&gt;&lt;br /&gt;  public LevelUpOption(String name){&lt;br /&gt;    this.name = name;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public abstract void invoke(Creature creature);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We need something to track all the possible options and enforce some of our level-up logic. We'll call it a LevelUpController &amp;mdash; even though classes with Manager or Controller in the name are usually vague and messy and not the best way to do things.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;public class LevelUpController {&lt;br /&gt;&lt;br /&gt;  private static LevelUpOption[] options = new LevelUpOption[]{&lt;br /&gt;    new LevelUpOption("Increased hit points"){&lt;br /&gt;      public void invoke(Creature creature) { creature.gainMaxHp(); }&lt;br /&gt;    },&lt;br /&gt;    new LevelUpOption("Increased attack value"){&lt;br /&gt;      public void invoke(Creature creature) { creature.gainAttackValue(); }&lt;br /&gt;    },&lt;br /&gt;    new LevelUpOption("Increased defense value"){&lt;br /&gt;      public void invoke(Creature creature) { creature.gainDefenseValue(); }&lt;br /&gt;    },&lt;br /&gt;    new LevelUpOption("Increased vision"){&lt;br /&gt;      public void invoke(Creature creature) { creature.gainVision(); }&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;I created a few simple options based on the stats we already have but I'm sure you can come up with more. These are anonymous classes - if you're not familiar with them you should check them out. Anonymous classes can make some things very clear and succinct - other things are best left to regular classes.&lt;br /&gt;&lt;br /&gt;This LevelUpController should be able to select one option at random and apply it to a given creature.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void autoLevelUp(Creature creature){&lt;br /&gt;    options[(int)(Math.random() * options.length)].invoke(creature);&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now the CreatureAi can call this to automatically gain some benefit when a creature gains a level.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void onGainLevel() {&lt;br /&gt;    new LevelUpController().autoLevelUp(creature);&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Of course the Creature class needs to support these new options too. Here's what I came up with:&lt;br /&gt;&lt;pre class="brush: java"&gt;public void gainMaxHp() {&lt;br /&gt;    maxHp += 10;&lt;br /&gt;    hp += 10;&lt;br /&gt;    doAction("look healthier");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void gainAttackValue() {&lt;br /&gt;    attackValue += 2;&lt;br /&gt;    doAction("look stronger");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void gainDefenseValue() {&lt;br /&gt;    attackValue += 2;&lt;br /&gt;    doAction("look tougher");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void gainVision() {&lt;br /&gt;    visionRadius += 1;&lt;br /&gt;    doAction("look more aware");&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you try it now you should see that when you gain a level you look tougher or stronger etc. You may even notice something else looking stronger or more aware. That's why I think it's so cool to have a method like doAction; you can, if you're lucky, see the rare and subtle events like these.&lt;br /&gt;&lt;br /&gt;Try it.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;What about when the player gains a level? Shouldn't we show a list of options for the user to choose from? Let's start by making sure the player doesn't automatically get free bonuses.&lt;br /&gt;&lt;br /&gt;Override the onGainLevel method in the PlayerAi class.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void onGainLevel(){&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we update the PlayScreen's respondToUserInput method. When we first enter the method, before the user does anything, we need to record the player's level.&lt;br /&gt;&lt;pre class="brush: java"&gt;int level = player.level();&lt;br /&gt;&lt;/pre&gt;After responding to the player's input, we need to see if that resulted in a level up. If so, we jump into a LevelUpScreen and tell it how many bonuses the player get's to pick.&lt;br /&gt;&lt;pre class="brush: java"&gt;if (player.level() &amp;gt; level)&lt;br /&gt;      subscreen = new LevelUpScreen(player, player.level() - level);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now all we need is a LevelUpScreen that uses a LevelUpController to show what can be picked and applies that choice.&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.LevelUpController;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public class LevelUpScreen implements Screen {&lt;br /&gt;  private LevelUpController controller;&lt;br /&gt;  private Creature player;&lt;br /&gt;  private int picks;&lt;br /&gt;&lt;br /&gt;  public LevelUpScreen(Creature player, int picks){&lt;br /&gt;    this.controller = new LevelUpController();&lt;br /&gt;    this.player = player;&lt;br /&gt;    this.picks = picks;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void displayOutput(AsciiPanel terminal) {&lt;br /&gt;    List&amp;lt;String&amp;gt; options = controller.getLevelUpOptions();&lt;br /&gt;&lt;br /&gt;    int y = 5;&lt;br /&gt;    terminal.clear(' ', 5, y, 30, options.size() + 2);&lt;br /&gt;    terminal.write("     Choose a level up bonus       ", 5, y++);&lt;br /&gt;    terminal.write("------------------------------", 5, y++);&lt;br /&gt;&lt;br /&gt;    for (int i = 0; i &amp;lt; options.size(); i++){&lt;br /&gt;      terminal.write(String.format("[%d] %s", i+1, options.get(i)), 5, y++);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public Screen respondToUserInput(KeyEvent key) {&lt;br /&gt;    List&amp;lt;String&amp;gt; options = controller.getLevelUpOptions();&lt;br /&gt;    String chars = "";&lt;br /&gt;&lt;br /&gt;    for (int i = 0; i &amp;lt; options.size(); i++){&lt;br /&gt;      chars = chars + Integer.toString(i+1);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    int i = chars.indexOf(key.getKeyChar());&lt;br /&gt;&lt;br /&gt;    if (i &amp;lt; 0)&lt;br /&gt;      return this;&lt;br /&gt;&lt;br /&gt;    controller.getLevelUpOption(options.get(i)).invoke(player);&lt;br /&gt;&lt;br /&gt;    if (--picks &amp;lt; 1)&lt;br /&gt;      return null;&lt;br /&gt;    else&lt;br /&gt;      return this;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And there you go; a simple leveling system that lets the player decide how they want to progress their character. You could add different bonuses like special moves, critical hits, extra xp per kill, or special abilities. You could even make it so the player could chose a new item after leveling up.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut14.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8097149928279856061?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8097149928279856061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-14-experience-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8097149928279856061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8097149928279856061'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/10/roguelike-tutorial-14-experience-and.html' title='roguelike tutorial 14: experience and leveling up'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-1175617102265488855</id><published>2011-09-30T13:00:00.000-07:00</published><updated>2011-09-30T13:00:02.155-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 13: aggressive monsters</title><content type='html'>Now that we have all these cool weapons and armor and food, the bat's and fungi aren't as troublesome as they used to be. We need something that charges straight for us, something that peruses us relentlessly, a simple minded foe that we don't want to run into. For that we need a way to find a path to the player.&lt;br /&gt;&lt;br /&gt;The monsters are only going to pathfind to the player if they see him so we could do the simpleist thing and move east if the player is east, north if the player is north, etc. That would almost always work well enough but let's go ahead and add real path finding. Entire tutorials are written about path finding but for this we can use the following code that implements the A Star algorithm and is specialized for our creatures:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.Collections;&lt;br /&gt;import java.util.HashMap;&lt;br /&gt;&lt;br /&gt;public class PathFinder {&lt;br /&gt;       private ArrayList&amp;lt;Point&amp;gt; open;&lt;br /&gt;       private ArrayList&amp;lt;Point&amp;gt; closed;&lt;br /&gt;       private HashMap&amp;lt;Point, Point&amp;gt; parents;&lt;br /&gt;       private HashMap&amp;lt;Point,Integer&amp;gt; totalCost;&lt;br /&gt;     &lt;br /&gt;       public PathFinder() {&lt;br /&gt;             this.open = new ArrayList&amp;lt;Point&amp;gt;();&lt;br /&gt;             this.closed = new ArrayList&amp;lt;Point&amp;gt;();&lt;br /&gt;             this.parents = new HashMap&amp;lt;Point, Point&amp;gt;();&lt;br /&gt;             this.totalCost = new HashMap&amp;lt;Point, Integer&amp;gt;();&lt;br /&gt;       }&lt;br /&gt;     &lt;br /&gt;       private int heuristicCost(Point from, Point to) {&lt;br /&gt;             return Math.max(Math.abs(from.x - to.x), Math.abs(from.y - to.y));&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       private int costToGetTo(Point from) {&lt;br /&gt;             return parents.get(from) == null ? 0 : (1 + costToGetTo(parents.get(from)));&lt;br /&gt;       }&lt;br /&gt;     &lt;br /&gt;       private int totalCost(Point from, Point to) {&lt;br /&gt;             if (totalCost.containsKey(from))&lt;br /&gt;                 return totalCost.get(from);&lt;br /&gt;           &lt;br /&gt;             int cost = costToGetTo(from) + heuristicCost(from, to);&lt;br /&gt;             totalCost.put(from, cost);&lt;br /&gt;             return cost;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       private void reParent(Point child, Point parent){&lt;br /&gt;             parents.put(child, parent);&lt;br /&gt;             totalCost.remove(child);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public ArrayList&amp;lt;Point&amp;gt; findPath(Creature creature, Point start, Point end, int maxTries) {&lt;br /&gt;             open.clear();&lt;br /&gt;             closed.clear();&lt;br /&gt;             parents.clear();&lt;br /&gt;             totalCost.clear();&lt;br /&gt;       &lt;br /&gt;             open.add(start);&lt;br /&gt;           &lt;br /&gt;             for (int tries = 0; tries &amp;lt; maxTries &amp;amp;&amp;amp; open.size() &amp;gt; 0; tries++){&lt;br /&gt;                   Point closest = getClosestPoint(end);&lt;br /&gt;                 &lt;br /&gt;                   open.remove(closest);&lt;br /&gt;                   closed.add(closest);&lt;br /&gt;&lt;br /&gt;                   if (closest.equals(end))&lt;br /&gt;                         return createPath(start, closest);&lt;br /&gt;                   else&lt;br /&gt;                         checkNeighbors(creature, end, closest);&lt;br /&gt;             }&lt;br /&gt;             return null;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;        private Point getClosestPoint(Point end) {&lt;br /&gt;            Point closest = open.get(0);&lt;br /&gt;            for (Point other : open){&lt;br /&gt;                if (totalCost(other, end) &amp;lt; totalCost(closest, end))&lt;br /&gt;                    closest = other;&lt;br /&gt;            }&lt;br /&gt;            return closest;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void checkNeighbors(Creature creature, Point end, Point closest) {&lt;br /&gt;            for (Point neighbor : closest.neighbors8()) {&lt;br /&gt;                if (closed.contains(neighbor)&lt;br /&gt;                 || !creature.canEnter(neighbor.x, neighbor.y, creature.z)&lt;br /&gt;                 &amp;amp;&amp;amp; !neighbor.equals(end))&lt;br /&gt;                     continue;&lt;br /&gt;    &lt;br /&gt;                if (open.contains(neighbor))&lt;br /&gt;                    reParentNeighborIfNecessary(closest, neighbor);&lt;br /&gt;                else&lt;br /&gt;                    reParentNeighbor(closest, neighbor);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void reParentNeighbor(Point closest, Point neighbor) {&lt;br /&gt;            reParent(neighbor, closest);&lt;br /&gt;            open.add(neighbor);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void reParentNeighborIfNecessary(Point closest, Point neighbor) {&lt;br /&gt;            Point originalParent = parents.get(neighbor);&lt;br /&gt;            double currentCost = costToGetTo(neighbor);&lt;br /&gt;            reParent(neighbor, closest);&lt;br /&gt;            double reparentCost = costToGetTo(neighbor);&lt;br /&gt;  &lt;br /&gt;            if (reparentCost &amp;lt; currentCost)&lt;br /&gt;                open.remove(neighbor);&lt;br /&gt;            else&lt;br /&gt;                reParent(neighbor, originalParent);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private ArrayList&amp;lt;Point&amp;gt; createPath(Point start, Point end) {&lt;br /&gt;            ArrayList&amp;lt;Point&amp;gt; path = new ArrayList&amp;lt;Point&amp;gt;();&lt;br /&gt;&lt;br /&gt;            while (!end.equals(start)) {&lt;br /&gt;                path.add(end);&lt;br /&gt;                end = parents.get(end);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            Collections.reverse(path);&lt;br /&gt;            return path;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So far I've liked having Points and Lines where all the work is done in the constructor and would like to extend this idea to Paths. So let's create a Path class that hides the details from us.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;public class Path {&lt;br /&gt;&lt;br /&gt;  private static PathFinder pf = new PathFinder();&lt;br /&gt;&lt;br /&gt;  private List&amp;lt;Point&amp;gt; points;&lt;br /&gt;  public List&amp;lt;Point&amp;gt; points() { return points; }&lt;br /&gt;&lt;br /&gt;  public Path(Creature creature, int x, int y){&lt;br /&gt;      points = pf.findPath(creature, &lt;br /&gt;                           new Point(creature.x, creature.y, creature.z), &lt;br /&gt;                           new Point(x, y, creature.z), &lt;br /&gt;                           300);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If having our Line path do all that work in the constructor was questionable then this is far more questionable. I may end up regretting this and making sure future employers never see this but for now I'll try it and we'll see if it becomes a problem.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Like with our other creatures we need a CreatureAi. I'll take the easy and uncreative way out and pick Zombies for our new monster. The ZombieAi will be a bit different than the others since it needs a reference to the player so it knows who to look for.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;public class ZombieAi extends CreatureAi {&lt;br /&gt;  private Creature player;&lt;br /&gt;&lt;br /&gt;  public ZombieAi(Creature creature, Creature player) {&lt;br /&gt;    super(creature);&lt;br /&gt;    this.player = player;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;During the zombie's turn it will move to the player if it can see him, otherwise it will wander around. Since zombies are a little slow, I gave them a chance of doing nothing during their turn for just a little bit of interest.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void onUpdate(){&lt;br /&gt;      if (Math.random() &amp;lt; 0.2)&lt;br /&gt;          return;&lt;br /&gt;  &lt;br /&gt;      if (creature.canSee(player.x, player.y, player.z))&lt;br /&gt;          hunt(player);&lt;br /&gt;      else&lt;br /&gt;          wander();&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Creating a new path each turn may not be the best idea but we'll only have a few zombies and rogulikes are turn based so it shouldn't be too much of a problem. If it does be come a performance problem we can fix it.&lt;br /&gt;&lt;br /&gt;The hunt method finds a path to the target and moves to it.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void hunt(Creature target){&lt;br /&gt;      List&amp;lt;Point&amp;gt; points = new Path(creature, target.x, target.y).points();&lt;br /&gt;  &lt;br /&gt;      int mx = points.get(0).x - creature.x;&lt;br /&gt;      int my = points.get(0).y - creature.y;&lt;br /&gt;  &lt;br /&gt;      creature.moveBy(mx, my, 0);&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we can add zombies to our factory. Since the Ai needs a reference to the player, we have to pass that in.&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature newZombie(int depth, Creature player){&lt;br /&gt;      Creature zombie = new Creature(world, 'z', AsciiPanel.white, "zombie", 50, 10, 10);&lt;br /&gt;      world.addAtEmptyLocation(zombie, depth);&lt;br /&gt;      new ZombieAi(zombie, player);&lt;br /&gt;      return zombie;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To add zombies to our world we need to update createCreatures in the PlayScreen.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;for (int i = 0; i &amp;lt; z + 3; i++){&lt;br /&gt;         factory.newZombie(z, player);&lt;br /&gt;     }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Adding pathfinding to a game is a big deal. The PathFinder we're using for now is good enough but has some major inefficiencies. I'm using a HashMap of points rather than an array so we don't have to worry about the world size or anything like that. This will take up less memory and handle aarbitrarily large maps but it will be much &lt;em&gt;much&lt;/em&gt; slower.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut13.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-1175617102265488855?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/1175617102265488855/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-13-aggressive.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1175617102265488855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1175617102265488855'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-13-aggressive.html' title='roguelike tutorial 13: aggressive monsters'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-2608806140856517271</id><published>2011-09-27T13:00:00.000-07:00</published><updated>2011-09-27T13:00:00.059-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 12: weapons and armor</title><content type='html'>Time for some weapons and armor.&lt;br /&gt;&lt;br /&gt;Since we have a very simple Attack value and Defense value for creatures, let's use that for our weapons and armor. Go ahead and add that to the Item class.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private int attackValue;&lt;br /&gt;  public int attackValue() { return attackValue; }&lt;br /&gt;  public void modifyAttackValue(int amount) { attackValue += amount; }&lt;br /&gt;&lt;br /&gt;  private int defenseValue;&lt;br /&gt;  public int defenseValue() { return defenseValue; }&lt;br /&gt;  public void modifyDefenseValue(int amount) { defenseValue += amount; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And create some new items in our factory class.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newDagger(int depth){&lt;br /&gt;    Item item = new Item(')', AsciiPanel.white, "dagger");&lt;br /&gt;    item.modifyAttackValue(5);&lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Item newSword(int depth){&lt;br /&gt;    Item item = new Item(')', AsciiPanel.brightWhite, "sword");&lt;br /&gt;    item.modifyAttackValue(10);&lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Item newStaff(int depth){&lt;br /&gt;    Item item = new Item(')', AsciiPanel.yellow, "staff");&lt;br /&gt;    item.modifyAttackValue(5);&lt;br /&gt;    item.modifyDefenseValue(3);&lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Item newLightArmor(int depth){&lt;br /&gt;    Item item = new Item('[', AsciiPanel.green, "tunic");&lt;br /&gt;    item.modifyDefenseValue(2);&lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Item newMediumArmor(int depth){&lt;br /&gt;    Item item = new Item('[', AsciiPanel.white, "chainmail");&lt;br /&gt;    item.modifyDefenseValue(4);&lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Item newHeavyArmor(int depth){&lt;br /&gt;    Item item = new Item('[', AsciiPanel.brightWhite, "platemail");&lt;br /&gt;    item.modifyDefenseValue(6);&lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Item randomWeapon(int depth){&lt;br /&gt;    switch ((int)(Math.random() * 3)){&lt;br /&gt;    case 0: return newDagger(depth);&lt;br /&gt;    case 1: return newSword(depth);&lt;br /&gt;    default: return newStaff(depth);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Item randomArmor(int depth){&lt;br /&gt;    switch ((int)(Math.random() * 3)){&lt;br /&gt;    case 0: return newLightArmor(depth);&lt;br /&gt;    case 1: return newMediumArmor(depth);&lt;br /&gt;    default: return newHeavyArmor(depth);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Don't forget to add them to the newly created game in the PlayScreen createItems method.&lt;br /&gt;&lt;br /&gt;If you play you should be able to see them and carry them around.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If we want to use them then we need to add some methods to the creature class to equip and unequip weapons and armor. For now, creatures can wield one weapon and wear one pice of armor at a time. If you want separate armor slots for helmet, rings, shoes, etc, you can do that too. I'm also going to use the same methods to deal with armor or weapons.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private Item weapon;&lt;br /&gt;  public Item weapon() { return weapon; }&lt;br /&gt;&lt;br /&gt;  private Item armor;&lt;br /&gt;  public Item armor() { return armor; }&lt;br /&gt;&lt;/pre&gt;&lt;pre class="brush: java"&gt;public void unequip(Item item){&lt;br /&gt;      if (item == null)&lt;br /&gt;         return;&lt;br /&gt;  &lt;br /&gt;      if (item == armor){&lt;br /&gt;          doAction("remove a " + item.name());&lt;br /&gt;          armor = null;&lt;br /&gt;      } else if (item == weapon) {&lt;br /&gt;          doAction("put away a " + item.name());&lt;br /&gt;          weapon = null;&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;pre class="brush: java"&gt;public void equip(Item item){&lt;br /&gt;      if (item.attackValue() == 0 &amp;amp;&amp;amp; item.defenseValue() == 0)&lt;br /&gt;          return;&lt;br /&gt;  &lt;br /&gt;      if (item.attackValue() &amp;gt;= item.defenseValue()){&lt;br /&gt;          unequip(weapon);&lt;br /&gt;          doAction("wield a " + item.name());&lt;br /&gt;          weapon = item;&lt;br /&gt;      } else {&lt;br /&gt;          unequip(armor);&lt;br /&gt;          doAction("put on a " + item.name());&lt;br /&gt;          weapon = item;&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And make sure that we unequip anything we eat or drop.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void eat(Item item){&lt;br /&gt;      if (item.foodValue() &amp;lt; 0)&lt;br /&gt;         notify("Gross!");&lt;br /&gt;  &lt;br /&gt;      modifyFood(item.foodValue());&lt;br /&gt;      inventory.remove(item);&lt;br /&gt;      unequip(item);&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void drop(Item item){&lt;br /&gt;    if (world.addAtEmptySpace(item, x, y, z)){&lt;br /&gt;        doAction("drop a " + item.name());&lt;br /&gt;        inventory.remove(item);&lt;br /&gt;        unequip(item);&lt;br /&gt;    } else {&lt;br /&gt;        notify("There's nowhere to drop the %s.", item.name());&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The easiest way to use our new equipment when calculating our overall attack and defense values is just to add them to the creature's getters.&lt;br /&gt;&lt;pre class="brush: java"&gt;public int attackValue() {&lt;br /&gt;    return attackValue&lt;br /&gt;     + (weapon == null ? 0 : weapon.attackValue())&lt;br /&gt;     + (armor == null ? 0 : armor.attackValue());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public int defenseValue() {&lt;br /&gt;    return defenseValue&lt;br /&gt;     + (weapon == null ? 0 : weapon.defenseValue())&lt;br /&gt;     + (armor == null ? 0 : armor.defenseValue());&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And now create an EquipScreen.&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;&lt;br /&gt;public class EquipScreen extends InventoryBasedScreen {&lt;br /&gt;&lt;br /&gt;  public EquipScreen(Creature player) {&lt;br /&gt;    super(player);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected String getVerb() {&lt;br /&gt;    return "wear or wield";&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected boolean isAcceptable(Item item) {&lt;br /&gt;    return item.attackValue() &amp;gt; 0 || item.defenseValue() &amp;gt; 0;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected Screen use(Item item) {&lt;br /&gt;    player.equip(item);&lt;br /&gt;    return null;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Wasn't that easy?&lt;br /&gt;&lt;br /&gt;All that's left is making the 'w' wear or wield items in the PlayScreen. I prefer having one key for both rather than one for armor and another for weapons. If you'd rather have different keys then you can do that.&lt;br /&gt;&lt;pre class="brush: java"&gt;case KeyEvent.VK_W: subscreen = new EquipScreen(player); break;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now you can find and use weapons and armor. Play around with different attackValues, defenseValues, and hit points. You can have 3 or 4 weapons or 300 weapons. Try changing how abundant weapons and armor are or maybe have some more common than others.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;One advantage of having all our items be the same class but have different values is that an item can be more than one thing, e.g. you could make an edible weapon and the player would be able to eat or wield it with no extra code or you could have have a weapon that increases attack and defense.&lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newEdibleWeapon(int depth){&lt;br /&gt;    Item item = new Item(')', AsciiPanel.yellow, "baguette");&lt;br /&gt;    item.modifyAttackValue(3);&lt;br /&gt;    item.modifyFoodValue(50);&lt;br /&gt;    world.addAtEmptyLocation(item, depth);&lt;br /&gt;    return item;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can't do that with Weapon, Food, and Armor subclasses.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;Wouldn't it also be nice if the inventory screens told us what we have equipped so we don't eat the armor we're wearing or try to wear something we're already wearing? Here's one possible update to the InventoryBasedScreen:&lt;br /&gt;&lt;pre class="brush: java"&gt;String line = letters.charAt(i) + " - " + item.glyph() + " " + item.name();&lt;br /&gt;    &lt;br /&gt;  if(item == player.weapon() || item == player.armor())&lt;br /&gt;      line += " (equipped)";&lt;br /&gt;    &lt;br /&gt;  lines.add(line);&lt;br /&gt;&lt;/pre&gt;Maybe the EquipScreen shouldn't let us equip what we're already using. Or maybe wearing or wielding what's already equipped should un-wear or un-weild it? That way the 'w' key can equip or unequip. It's your game so it's up to you. Implementing those is left as an exercise.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut12.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-2608806140856517271?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/2608806140856517271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-12-weapons-and-armor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2608806140856517271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2608806140856517271'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-12-weapons-and-armor.html' title='roguelike tutorial 12: weapons and armor'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8653347043273829567</id><published>2011-09-23T13:00:00.000-07:00</published><updated>2011-09-23T13:00:01.947-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 11: hunger and food</title><content type='html'>Now that we've got monsters to kill and the ability to pick up and use things, how about we add some corpses and the ability to eat them?&lt;br /&gt;&lt;br /&gt;We first need to update our Item class to support some nutritional value.&lt;br /&gt;&lt;pre class="brush: java"&gt;private int foodValue;&lt;br /&gt;public int foodValue() { return foodValue; }&lt;br /&gt;public void modifyFoodValue(int amount) { foodValue += amount; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And update our creature to leave corpses.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void modifyHp(int amount) {&lt;br /&gt;    hp += amount;&lt;br /&gt;    if (hp &amp;lt; 1) {&lt;br /&gt;        doAction("die");&lt;br /&gt;        leaveCorpse();&lt;br /&gt;        world.remove(this);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void leaveCorpse(){&lt;br /&gt;    Item corpse = new Item('%', color, name + " corpse");&lt;br /&gt;    corpse.modifyFoodValue(maxHp * 3);&lt;br /&gt;    world.addAtEmptySpace(corpse, x, y, z);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Update creatures to also have hunger.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private int maxFood;&lt;br /&gt;public int maxFood() { return maxFood; }&lt;br /&gt;&lt;br /&gt;private int food;&lt;br /&gt;public int food() { return food; }&lt;br /&gt;&lt;br /&gt;public void modifyFood(int amount) {&lt;br /&gt;    food += amount;&lt;br /&gt;    if (food &amp;gt; maxFood) {&lt;br /&gt;        food = maxFood;&lt;br /&gt;    } else if (food &amp;lt; 1 &amp;amp;&amp;amp; glyph == '@') {&lt;br /&gt;        modifyHp(-1000);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Do you see the terrible hack there? We only want the player to be able to die of starvation since it would be boring if every monster dropped dead of starvation and if they need to eat they'd have to go around killing each other. We could have an entire ecosystem of bats farming fungus, that would introduce some neat gameplay options, but that's quite a bit more complicated than I'd like to do right now. Anyway dying only if you look like a @ is still an ugly hack &amp;mdash; a hack so ugly our children's children will feel the shame. Let's fix it right now:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void modifyFood(int amount) {&lt;br /&gt;    food += amount;&lt;br /&gt;    if (food &amp;gt; maxFood) {&lt;br /&gt;        food = maxFood;&lt;br /&gt;    } else if (food &amp;lt; 1 &amp;amp;&amp;amp; isPlayer()) {&lt;br /&gt;        modifyHp(-1000);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public boolean isPlayer(){&lt;br /&gt;    return glyph == '@';&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The hack is still there but it's isolated for now. Later if we have other creatures with an @ glyph or if the player can assume other forms, we can update this one isolated place. One thing I've learned from real life software is that although ugly hacks are inevitable, you can always isolate them so the callers don't need to deal with it.&lt;br /&gt;&lt;br /&gt;But enough preaching, our Creatures also need a method to eat.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void eat(Item item){&lt;br /&gt;    modifyFood(item.foodValue());&lt;br /&gt;    inventory.remove(item);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Don't forget that creatures should start with decent belly full. Add this to the creature constructor:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;this.maxFood = 1000;&lt;br /&gt;this.food = maxFood / 3 * 2;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now add an EatScreen so we can eat something in our inventory.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;&lt;br /&gt;public class EatScreen extends InventoryBasedScreen {&lt;br /&gt;&lt;br /&gt;    public EatScreen(Creature player) {&lt;br /&gt;        super(player);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected String getVerb() {&lt;br /&gt;        return "eat";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected boolean isAcceptable(Item item) {&lt;br /&gt;        return item.foodValue() != 0;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected Screen use(Item item) {&lt;br /&gt;        player.eat(item);&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Wow, that was easy. InventoryBasedScreen is paying off already.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;For the PlayScreen we need to map the 'e' key to the EatScreen.&lt;br /&gt;&lt;pre class="brush: java"&gt;case KeyEvent.VK_E: subscreen = new EatScreen(player); break;&lt;br /&gt;&lt;/pre&gt;We should also let the player know how hungry he is. Change the stats in displayOutput to this:&lt;br /&gt;&lt;pre class="brush: java"&gt;String stats = String.format(" %3d/%3d hp %8s", player.hp(), player.maxHp(), hunger());&lt;br /&gt;&lt;/pre&gt;And add a helper method. You can use whatever text and amounts you want.&lt;br /&gt;&lt;pre class="brush: java"&gt;private String hunger(){&lt;br /&gt;    if (player.food() &amp;lt; player.maxFood() * 0.1)&lt;br /&gt;        return "Starving";&lt;br /&gt;    else if (player.food() &amp;lt; player.maxFood() * 0.2)&lt;br /&gt;        return "Hungry";&lt;br /&gt;    else if (player.food() &amp;gt; player.maxFood() * 0.9)&lt;br /&gt;        return "Stuffed";&lt;br /&gt;    else if (player.food() &amp;gt; player.maxFood() * 0.8)&lt;br /&gt;        return "Full";&lt;br /&gt;    else&lt;br /&gt;        return "";&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Of course none of this will do anything if we don't use up the food we've eaten. Go ahead and add a call to modifyFood in the relevant creature methods. Here's a couple examples:&lt;br /&gt;&lt;pre class="brush: java"&gt;public void dig(int wx, int wy, int wz) {&lt;br /&gt;    modifyFood(-10);&lt;br /&gt;    world.dig(wx, wy, wz);&lt;br /&gt;    doAction("dig");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;pre class="brush: java"&gt;public void update(){&lt;br /&gt;    modifyFood(-1);&lt;br /&gt;    ai.onUpdate();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Go ahead and use the food values you want. You should play around with it for a while to decide what feels right. Maybe you want starvation to be a serious problem and hunting bats is the only way to stay alive or maybe you want starvation to hardly ever happen. Maybe heroes start with an inventory full of supplies or maybe they start with an empty and growling belly &amp;mdash; as the designer it's up to you.&lt;br /&gt;&lt;br /&gt;While looking at the modifyFood method I noticed we don't have prevent hp from going higher than maxHp. Even though we don't have a way to do that yet you should add a check for that.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;If we eat more than the maxFood shouldn't our stomach stretch and increase our maxFood? Or maybe the user should explode from overeating? Here's my implementation:&lt;br /&gt;&lt;pre class="brush: java"&gt;public void modifyFood(int amount) {&lt;br /&gt;    food += amount;&lt;br /&gt;&lt;br /&gt;    if (food &amp;gt; maxFood) {&lt;br /&gt;        maxFood = (maxFood + food) / 2;&lt;br /&gt;        food = maxFood;&lt;br /&gt;        notify("You can't believe your stomach can hold that much!");&lt;br /&gt;        modifyHp(-1);&lt;br /&gt;    } else if (food &amp;lt; 1 &amp;amp;&amp;amp; isPlayer()) {&lt;br /&gt;        modifyHp(-1000);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's a subtle effect but it gives the player a decision to make when full and carrying a lot of food and under the right circumstances overeating may become a useful strategy.&lt;br /&gt;&lt;br /&gt;Now go ahead and add some food to your world. Bread, meat, apples, whatever.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut11.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8653347043273829567?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8653347043273829567/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-11-hunger-and-food.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8653347043273829567'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8653347043273829567'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-11-hunger-and-food.html' title='roguelike tutorial 11: hunger and food'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-483544190913518748</id><published>2011-09-20T13:00:00.000-07:00</published><updated>2011-09-20T13:00:00.800-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 10: items, inventory, inventory screens</title><content type='html'>Before we add potions, spellbooks, treasures, armor, weapons, food, and other roguelike goodies we need to think about inventory and start small. We'll need a new class for items, we'll need to update the world to track, add, and remove items, the creature class will need to be updated to pickup, use, and drop items, and the PlayScreen needs to be updated to display items and accept keystrokes to let the player actually pickup or drop items. Let's start with a new Item class.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.awt.Color;&lt;br /&gt;&lt;br /&gt;public class Item {&lt;br /&gt;&lt;br /&gt;    private char glyph;&lt;br /&gt;    public char glyph() { return glyph; }&lt;br /&gt;&lt;br /&gt;    private Color color;&lt;br /&gt;    public Color color() { return color; }&lt;br /&gt;&lt;br /&gt;    private String name;&lt;br /&gt;    public String name() { return name; }&lt;br /&gt;&lt;br /&gt;    public Item(char glyph, Color color, String name){&lt;br /&gt;        this.glyph = glyph;&lt;br /&gt;        this.color = color;&lt;br /&gt;        this.name = name;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pretty simple so far. I didn't give it an x, y, or z coordinate because items don't need to know where they are; it kind of makes since to have them when laying on the ground but what about when they're in a container or being carried around by a creature? &lt;div style='float:right; width:20em; padding:0.5em; margin:0.5em; border:1px solid grey;'&gt;I guess you could also make a Location interface. Then Point, Creature, and Item could implement it. That way an item's location could be a point in the world, a creature that's carrying it (or it's point in the world), or a container it's in. That would also be useful because an item would have a reference to wherever it is and whoever is carrying it. I'll have to try that on my next roguelike.&lt;/div&gt;I guess you could have the owner update their location and add another flag indicating if the item is on the floor, in a container, or being carried. Sounds cumbersome and unnecessary; best to do without for now.&lt;br /&gt;&lt;br /&gt;I'm happy with having a CreatureFactory to handle the details of creating a new creature so let's do the same for items. We could create an ItemFactory but I'd like to try something different: add items to the CreatureFactory. I haven't tried this before so I'm not sure if it's better to keep the two separate or not. I guess I'm going to find out.&lt;br /&gt;&lt;br /&gt;The first step is the most powerful refactoring of all, Rename (Martin Feathers agrees: TODO). We'll rename the CreatureFactory to something more general. I'm going to just call it StuffFactory. That's an atrociously non-descriptive name but I can rename it when I think of something better &amp;mdash; of course temporary things often stay that way so this will probably remain a StuffFactory for a while.&lt;br /&gt;&lt;br /&gt;And now we can add our first item.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newRock(int depth){&lt;br /&gt;        Item rock = new Item(',', AsciiPanel.yellow, "rock");&lt;br /&gt;        world.addAtEmptyLocation(rock, depth);&lt;br /&gt;        return rock;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now that we have an item to put in the world, we need to extend the world class to handle that. Instead of a list of all items I'm going to try something different &amp;mdash; I'm only going to allow one item per tile. Good idea or bad, let's go ahead with that for now.&lt;br /&gt;&lt;br /&gt;Our world needs one item per tile.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private Item[][][] items;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This should get initialized in the constructor.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;this.items = new Item[width][height][depth];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We need a way to determine what item is in a location.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Item item(int x, int y, int z){&lt;br /&gt;    return items[x][y][z];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And a way to add an item to a random spot similar to how we add creatures.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void addAtEmptyLocation(Item item, int depth) {&lt;br /&gt;    int x;&lt;br /&gt;    int y;&lt;br /&gt;    &lt;br /&gt;    do {&lt;br /&gt;        x = (int)(Math.random() * width);&lt;br /&gt;        y = (int)(Math.random() * height);&lt;br /&gt;    }&lt;br /&gt;    while (!tile(x,y,depth).isGround() || item(x,y,depth) != null);&lt;br /&gt;    &lt;br /&gt;    items[x][y][depth] = item;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And lastly, we need to update our methods that are used for displaying the world to also display items.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public char glyph(int x, int y, int z){&lt;br /&gt;    Creature creature = creature(x, y, z);&lt;br /&gt;    if (creature != null)&lt;br /&gt;        return creature.glyph();&lt;br /&gt;    &lt;br /&gt;    if (item(x,y,z) != null)&lt;br /&gt;        return item(x,y,z).glyph();&lt;br /&gt;    &lt;br /&gt;    return tile(x, y, z).glyph();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;pre class="brush: java"&gt;public Color color(int x, int y, int z){&lt;br /&gt;    Creature creature = creature(x, y, z);&lt;br /&gt;    if (creature != null)&lt;br /&gt;        return creature.color();&lt;br /&gt;    &lt;br /&gt;    if (item(x,y,z) != null)&lt;br /&gt;        return item(x,y,z).color();&lt;br /&gt;    &lt;br /&gt;    return tile(x, y, z).color();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the only change to the PlayScreen is to add our new rocks.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void createItems(StuffFactory factory) {&lt;br /&gt;    for (int z = 0; z &amp;lt; world.depth(); z++){&lt;br /&gt;        for (int i = 0; i &amp;lt; world.width() * world.height() / 20; i++){&lt;br /&gt;            factory.newRock(z);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Just call that during setup of a new game and play it.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;Now that we've got a world with items in it, we need to be able to pick them up and do stuff with them.&lt;br /&gt;&lt;br /&gt;A lot can happen with a creature's inventory so let's create another class for that. Instead of using a list I'm going to use an array so the items index doesn't change when we lose something before it. E.g. if we quaff the potion in our 'd' slot, whatever was in the 'e' slot should remain there and not slide into the 'd' slot. If you want that kind of behavior then you could use a List &amp;mdash; it's your choice.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public class Inventory {&lt;br /&gt;&lt;br /&gt;    private Item[] items;&lt;br /&gt;    public Item[] getItems() { return items; }&lt;br /&gt;    public Item get(int i) { return items[i]; }&lt;br /&gt;&lt;br /&gt;    public Inventory(int max){&lt;br /&gt;        items = new Item[max];&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We need a method to add an item to the first open slot in our inventory.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void add(Item item){&lt;br /&gt;    for (int i = 0; i &amp;lt; items.length; i++){&lt;br /&gt;        if (items[i] == null){&lt;br /&gt;             items[i] = item;&lt;br /&gt;             break;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And a way to remove an item from our inventory.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void remove(Item item){&lt;br /&gt;    for (int i = 0; i &amp;lt; items.length; i++){&lt;br /&gt;        if (items[i] == item){&lt;br /&gt;             items[i] = null;&lt;br /&gt;             return;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We also need to know if the inventory is full and we can't carry any more.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public boolean isFull(){&lt;br /&gt;    int size = 0;&lt;br /&gt;    for (int i = 0; i &amp;lt; items.length; i++){&lt;br /&gt;        if (items[i] != null)&lt;br /&gt;             size++;&lt;br /&gt;    }&lt;br /&gt;    return size == items.length;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now that we've got something to represent an inventory, we can add one to our Creature class. This means that potentially any creature can have an inventory (Spoiler alert!)&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private Inventory inventory;&lt;br /&gt;    public Inventory inventory() { return inventory; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We need to initialize it in the constructor. I prefer smaller inventories since that means the player can't carry half the world with them; having to chose which two swords to bring with you is more interesting than just carrying them all. I also tend to forget what I've got once it goes beyond a screenfull.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;this.inventory = new Inventory(20);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And our creatures need to be able to pickup and drop stuff, moving it from the world to the creatures inventory or back.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void pickup(){&lt;br /&gt;        Item item = world.item(x, y, z);&lt;br /&gt;    &lt;br /&gt;        if (inventory.isFull() || item == null){&lt;br /&gt;            doAction("grab at the ground");&lt;br /&gt;        } else {&lt;br /&gt;            doAction("pickup a %s", item.name());&lt;br /&gt;            world.remove(x, y, z);&lt;br /&gt;            inventory.add(item);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void drop(Item item){&lt;br /&gt;        doAction("drop a " + item.name());&lt;br /&gt;        inventory.remove(item);&lt;br /&gt;        world.addAtEmptySpace(item, x, y, z);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Your IDE has probably warned you that he world class doesn't support removing and adding items so let's take care of that. Removing an item is easy:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void remove(int x, int y, int z) {&lt;br /&gt;    items[x][y][z] = null;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Adding an item to a specific place is more complicated since we only allow one item per tile. Because of that, we need to check adjacent tiles for an open space and repeat until we find one or run out of open spaces.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void addAtEmptySpace(Item item, int x, int y, int z){&lt;br /&gt;    if (item == null)&lt;br /&gt;        return;&lt;br /&gt;    &lt;br /&gt;    List&amp;lt;Point&amp;gt; points = new ArrayList&amp;lt;Point&amp;gt;();&lt;br /&gt;    List&amp;lt;Point&amp;gt; checked = new ArrayList&amp;lt;Point&amp;gt;();&lt;br /&gt;    &lt;br /&gt;    points.add(new Point(x, y, z));&lt;br /&gt;    &lt;br /&gt;    while (!points.isEmpty()){&lt;br /&gt;        Point p = points.remove(0);&lt;br /&gt;        checked.add(p);&lt;br /&gt;        &lt;br /&gt;        if (!tile(p.x, p.y, p.z).isGround())&lt;br /&gt;            continue;&lt;br /&gt;         &lt;br /&gt;        if (items[p.x][p.y][p.z] == null){&lt;br /&gt;            items[p.x][p.y][p.z] = item;&lt;br /&gt;            Creature c = this.creature(p.x, p.y, p.z);&lt;br /&gt;            if (c != null)&lt;br /&gt;                c.notify("A %s lands between your feet.", item.name());&lt;br /&gt;            return;&lt;br /&gt;        } else {&lt;br /&gt;            List&amp;lt;Point&amp;gt; neighbors = p.neighbors8();&lt;br /&gt;            neighbors.removeAll(checked);&lt;br /&gt;            points.addAll(neighbors);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A funky side effect of this is that if there are no open spaces then the item won't be added but will no longer be in the creature's inventory - it will vanish from the game. You can either let that happen or somehow let the caller know that it hasn't been added and shouldn't be removed from the inventory. Or you could notify everyone in viewing distance that it has vanished. I'll leave that up to you. If you leave it as it is then there's no indication that the item vanished and that may be interpreted as a bug. If you tell users it happens they probably won't consider it a bug - just part of the game. This could also make a funny scenario: imagine being trapped in a room where the floor is covered in treasure but you can't pick any up since your inventory is full and there's no room to drop your useless rusty sword.&lt;br /&gt;&lt;br /&gt;Here's one possibility:&lt;br /&gt;&lt;pre class="brush: java" style='width:30em;'&gt;public void drop(Item item){&lt;br /&gt;    if (world.addAtEmptySpace(item, x, y, z)){&lt;br /&gt;         doAction("drop a " + item.name());&lt;br /&gt;         inventory.remove(item);&lt;br /&gt;    } else {&lt;br /&gt;         notify("There's nowhere to drop the %s.", item.name());&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The final step is to update the PlayScreen's respondToUserInput method so the user can actually pickup things. Some roguelikes use the 'g' key to get things, some use the ',' key, and some use either one.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;switch (key.getKeyChar()){&lt;br /&gt;         case 'g':&lt;br /&gt;         case ',': player.pickup(); break;&lt;br /&gt;         case '&amp;lt;': player.moveBy( 0, 0, -1); break;&lt;br /&gt;         case '&amp;gt;': player.moveBy( 0, 0, 1); break;&lt;br /&gt;         }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Try it out.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;We can pick up some rocks and the code is there to drop them, but we don't have a way to specify what to drop. Code that isn't being used gives me a bad feeling so let's wire up the GUI to that drop method soon. Ideally the user will press the 'd' key, the GUI will ask what to drop, the user types the letter of the thing to drop, the player drops it, and we go back to the game. Remember all that time we spent creating Screen interface and thinking about cases with different rules for user input and output? Time to make a new Screen.&lt;br /&gt;&lt;br /&gt;Actually, if we think about what we want to do with inventory, we can do better. Here's a few scenarios off the top of my head:&lt;br /&gt;press 'd', ask what to drop, the user selects something that can be dropped, drop it&lt;br /&gt;press 'q', ask what to quaff, the user selects something that can be quaffed, quaff it&lt;br /&gt;press 'r', ask what to read, the user selects something that can be read, read it&lt;br /&gt;press 't', ask what to throw, the user selects something that can be thrown, throw it&lt;br /&gt;press 'e', ask what to eat, the user selects something that can be eaten, eat it&lt;br /&gt;Notice a pattern? There's a key that get's pressed, some verb (drop, quaff, read), some check against the items (droppable, quaffable, readable), and some action (drop, quaff, read). The common behavior can be put in one class called InventoryBasedScreen and the specific details can be in subclasses. That way we can have a DropScreen, QuaffScreen, ReadScreen and others that all subclass the InventoryBasedScreen and just provide a few simple details.&lt;br /&gt;&lt;br /&gt;Let's start with a basic InventoryBasedScreen:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public abstract class InventoryBasedScreen implements Screen {&lt;br /&gt;&lt;br /&gt;    protected Creature player;&lt;br /&gt;    private String letters;&lt;br /&gt;&lt;br /&gt;    protected abstract String getVerb();&lt;br /&gt;    protected abstract boolean isAcceptable(Item item);&lt;br /&gt;    protected abstract Screen use(Item item);&lt;br /&gt;&lt;br /&gt;    public InventoryBasedScreen(Creature player){&lt;br /&gt;        this.player = player;&lt;br /&gt;        this.letters = "abcdefghijklmnopqrstuvwxyz";&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We need the reference to the player because that's the one who's going to do the work of dropping, quaffing, eating, etc. It's protected so that the subclasses can use it. The letters are so we can assign a letter to each inventory slot (If you allow the inventory to be larger then you need to add more characters). Maybe this should be part of the inventory class but I think this is the only place where we will use it so I'll put it here for now. We've also got abstract methods so our subclasses can specify the verb, what items are acceptable for the action, and a method to actually perform the action. Using an item returns a Screen since it may lead to a different screen, e.g. if we're going to throw something then we can transition into some sort of targeting screen.&lt;br /&gt;&lt;br /&gt;Since this is a screen it needs to actually display some output. We not only ask what they want to use but go ahead and show a list of acceptable items.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void displayOutput(AsciiPanel terminal) {&lt;br /&gt;        ArrayList&amp;lt;String&amp;gt; lines = getList();&lt;br /&gt;    &lt;br /&gt;        int y = 23 - lines.size();&lt;br /&gt;        int x = 4;&lt;br /&gt;&lt;br /&gt;        if (lines.size() &amp;gt; 0)&lt;br /&gt;            terminal.clear(' ', x, y, 20, lines.size());&lt;br /&gt;    &lt;br /&gt;        for (String line : lines){&lt;br /&gt;            terminal.write(line, x, y++);&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        terminal.clear(' ', 0, 23, 80, 1);&lt;br /&gt;        terminal.write("What would you like to " + getVerb() + "?", 2, 23);&lt;br /&gt;    &lt;br /&gt;        terminal.repaint();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That should be pretty clear: write the list in the lower left hand corner and ask the user what to do. If you allow a larger inventory then you'll have to show two columns or scroll the list or something.&lt;br /&gt;&lt;br /&gt;The getList method will make a list of all the acceptable items and the letter for each corresponding inventory slot.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private ArrayList&amp;lt;String&amp;gt; getList() {&lt;br /&gt;        ArrayList&amp;lt;String&amp;gt; lines = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;        Item[] inventory = player.inventory().getItems();&lt;br /&gt;    &lt;br /&gt;        for (int i = 0; i &amp;lt; inventory.length; i++){&lt;br /&gt;            Item item = inventory[i];&lt;br /&gt;        &lt;br /&gt;            if (item == null || !isAcceptable(item))&lt;br /&gt;                continue;&lt;br /&gt;        &lt;br /&gt;            String line = letters.charAt(i) + " - " + item.glyph() + " " + item.name();&lt;br /&gt;        &lt;br /&gt;            lines.add(line);&lt;br /&gt;        }&lt;br /&gt;        return lines;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now that we've got some output we need to respond to user input. The user can press escape to go back to playing the game, select a valid character to use, or some invalid key that will do nothing and keep them on the current screen.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Screen respondToUserInput(KeyEvent key) {&lt;br /&gt;        char c = key.getKeyChar();&lt;br /&gt;&lt;br /&gt;        Item[] items = player.inventory().getItems();&lt;br /&gt;    &lt;br /&gt;        if (letters.indexOf(c) &amp;gt; -1&lt;br /&gt;             &amp;amp;&amp;amp; items.length &amp;gt; letters.indexOf(c)&lt;br /&gt;             &amp;amp;&amp;amp; items[letters.indexOf(c)] != null&lt;br /&gt;             &amp;amp;&amp;amp; isAcceptable(items[letters.indexOf(c)]))&lt;br /&gt;            return use(items[letters.indexOf(c)]);&lt;br /&gt;        else if (key.getKeyCode() == KeyEvent.VK_ESCAPE)&lt;br /&gt;            return null;&lt;br /&gt;        else&lt;br /&gt;            return this;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I hope that little mess makes sense. Use it, exit, or ask again.&lt;br /&gt;&lt;br /&gt;This doesn't do anything yet, in fact it's an abstract class so it can't do anything until we create a subclass and use that. The first subclass will be a DropScreen.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import rltut.Creature;&lt;br /&gt;import rltut.Item;&lt;br /&gt;&lt;br /&gt;public class DropScreen extends InventoryBasedScreen {&lt;br /&gt;&lt;br /&gt;    public DropScreen(Creature player) {&lt;br /&gt;        super(player);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We just need to supply the methods that were abstract in the original. We're asking the use what they want to drop so the getVerb should return that.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;protected String getVerb() {&lt;br /&gt;        return "drop";&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Since anything can be dropped, all items are acceptable.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;protected boolean isAcceptable(Item item) {&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once the user selects what to drop we tell the player to do the work and return null since we are done with the DropScreen.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;protected Screen use(Item item) {&lt;br /&gt;        player.drop(item);&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we can update the PlayScreen to use our fancy new DropScreen. The DropScreen is a little different that the start, play, win, and lose screens since it needs to return to the PlayScreen once it's done. We could pass the current play screen into newly created DropScreen and have it return the PlayScreen when it's done, but I've tried that before and it became kind of messy. This time I'll try something different. We can have the PlayScreen know if we're working with another sub screen and delegate input and output to that screen screen. Once the subscreen is done, it get's set to null and the PlayScreen works as normal.&lt;br /&gt;&lt;br /&gt;First the PlayScreen needs to know what the subscreen is. If it's null then everything should work as it did before. There's no need to initialize this since we check for nulls when we use it.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private Screen subscreen;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After we displayOutput the subscreen should get a chance to display. This way the current game world will be a background to whatever the subscreen wants to show.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;if (subscreen != null)&lt;br /&gt;    subscreen.displayOutput(terminal);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And any user input needs to be sent to the subscreen if it exists. The subscreen will also tell the PlayScreen what the new subscreen is. We also need to handle the users pressing the 'd' key to drop items from inventory. Lastly, if we should update the world only if we don't have a subscreen.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Screen respondToUserInput(KeyEvent key) {&lt;br /&gt;     if (subscreen != null) {&lt;br /&gt;         subscreen = subscreen.respondToUserInput(key);&lt;br /&gt;     } else {&lt;br /&gt;         switch (key.getKeyCode()){&lt;br /&gt;         case KeyEvent.VK_ESCAPE: return new LoseScreen();&lt;br /&gt;         case KeyEvent.VK_ENTER: return new WinScreen();&lt;br /&gt;         case KeyEvent.VK_LEFT:&lt;br /&gt;         case KeyEvent.VK_H: player.moveBy(-1, 0, 0); break;&lt;br /&gt;         case KeyEvent.VK_RIGHT:&lt;br /&gt;         case KeyEvent.VK_L: player.moveBy( 1, 0, 0); break;&lt;br /&gt;         case KeyEvent.VK_UP:&lt;br /&gt;         case KeyEvent.VK_K: player.moveBy( 0,-1, 0); break;&lt;br /&gt;         case KeyEvent.VK_DOWN:&lt;br /&gt;         case KeyEvent.VK_J: player.moveBy( 0, 1, 0); break;&lt;br /&gt;         case KeyEvent.VK_Y: player.moveBy(-1,-1, 0); break;&lt;br /&gt;         case KeyEvent.VK_U: player.moveBy( 1,-1, 0); break;&lt;br /&gt;         case KeyEvent.VK_B: player.moveBy(-1, 1, 0); break;&lt;br /&gt;         case KeyEvent.VK_N: player.moveBy( 1, 1, 0); break;&lt;br /&gt;         case KeyEvent.VK_D: subscreen = new DropScreen(player); break;&lt;br /&gt;         }&lt;br /&gt;        &lt;br /&gt;         switch (key.getKeyChar()){&lt;br /&gt;         case 'g':&lt;br /&gt;         case ',': player.pickup(); break;&lt;br /&gt;         case '&amp;lt;': player.moveBy( 0, 0, -1); break;&lt;br /&gt;         case '&amp;gt;': player.moveBy( 0, 0, 1); break;&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt;    &lt;br /&gt;     if (subscreen == null)&lt;br /&gt;         world.update();&lt;br /&gt;    &lt;br /&gt;     if (player.hp() &amp;lt; 1)&lt;br /&gt;         return new LoseScreen();&lt;br /&gt;    &lt;br /&gt;     return this;&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That was a lot of little changes to a few different places but if the DropScreen is any indication, the InventoryBasedScreen should be a major win in terms of being able to implement new features with little effort. The PlayScreen is getting a little out of hand now that it creates a new world, displays the world, handles user commands and deals with subscreens. Maybe the part about setting up a new game should be moved somewhere else.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;Let's make this a proper roguelike with a special object to retrieve and return to the surface with. This will also give the player a legitimate victory condition other than pressing enter.&lt;br /&gt;&lt;br /&gt;Add our new item to the SuffFactory:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Item newVictoryItem(int depth){&lt;br /&gt;        Item item = new Item('*', AsciiPanel.brightWhite, "teddy bear");&lt;br /&gt;        world.addAtEmptyLocation(item, depth);&lt;br /&gt;        return item;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And make sure it get's created when we start a new game:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void createItems(StuffFactory factory) {&lt;br /&gt;    for (int z = 0; z &amp;lt; world.depth(); z++){&lt;br /&gt;        for (int i = 0; i &amp;lt; world.width() * world.height() / 20; i++){&lt;br /&gt;            factory.newRock(z);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    factory.newVictoryItem(world.depth() - 1);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And update the WorldBuilder to include some exist stairs.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private WorldBuilder addExitStairs() {&lt;br /&gt;        int x = -1;&lt;br /&gt;        int y = -1;&lt;br /&gt;    &lt;br /&gt;        do {&lt;br /&gt;            x = (int)(Math.random() * width);&lt;br /&gt;            y = (int)(Math.random() * height);&lt;br /&gt;        }&lt;br /&gt;        while (tiles[x][y][0] != Tile.FLOOR);&lt;br /&gt;    &lt;br /&gt;        tiles[x][y][0] = Tile.STAIRS_UP;&lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And make that part of creating caves.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public WorldBuilder makeCaves() {&lt;br /&gt;        return randomizeTiles()&lt;br /&gt;             .smooth(8)&lt;br /&gt;             .createRegions()&lt;br /&gt;             .connectRegions()&lt;br /&gt;             .addExitStairs();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Our normal stair handling won't work with up stairs on the uppermost layer of the world so let's handle that in the PlayScreen.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;switch (key.getKeyChar()){&lt;br /&gt;    case 'g':&lt;br /&gt;    case ',': player.pickup(); break;&lt;br /&gt;    case '&amp;lt;':&lt;br /&gt;        if (userIsTryingToExit())&lt;br /&gt;         return userExits();&lt;br /&gt;        else&lt;br /&gt;         player.moveBy( 0, 0, -1); &lt;br /&gt;        break;&lt;br /&gt;    case '&amp;gt;': player.moveBy( 0, 0, 1); break;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private boolean userIsTryingToExit(){&lt;br /&gt;    return player.z == 0 &amp;amp;&amp;amp; world.tile(player.x, player.y, player.z) == Tile.STAIRS_UP;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private Screen userExits(){&lt;br /&gt;    for (Item item : player.inventory().getItems()){&lt;br /&gt;        if (item != null &amp;amp;&amp;amp; item.name().equals("teddy bear"))&lt;br /&gt;            return new WinScreen();&lt;br /&gt;    }&lt;br /&gt;    return new LoseScreen();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now you can remove the cases for VK_ESCAPE and VK_ENTER. You can also remove the message about pressing escape or enter. It took half the tutorials but we finally have a victory condition.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut10.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-483544190913518748?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/483544190913518748/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-10-items-inventory.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/483544190913518748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/483544190913518748'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-10-items-inventory.html' title='roguelike tutorial 10: items, inventory, inventory screens'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-5114094157788595151</id><published>2011-09-16T13:00:00.000-07:00</published><updated>2011-09-16T13:00:00.126-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 09: wandering monsters</title><content type='html'>We need some monsters to wander around our caves. How about some fast moving bats?&lt;br /&gt;&lt;br /&gt;First let's add some default movement behavior to the CreatureAi.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void onEnter(int x, int y, int z, Tile tile){&lt;br /&gt;    if (tile.isGround()){&lt;br /&gt;         creature.x = x;&lt;br /&gt;         creature.y = y;&lt;br /&gt;         creature.z = z;&lt;br /&gt;    } else {&lt;br /&gt;         creature.doAction("bump into a wall");&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's also give it a method of moving randomly. This common behavior can be called by any subclass.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void wander(){&lt;br /&gt;    int mx = (int)(Math.random() * 3) - 1;&lt;br /&gt;    int my = (int)(Math.random() * 3) - 1;&lt;br /&gt;    creature.moveBy(mx, my, 0);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now the ai for our bats.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public class BatAi extends CreatureAi {&lt;br /&gt;&lt;br /&gt;    public BatAi(Creature creature) {&lt;br /&gt;        super(creature);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void onUpdate(){&lt;br /&gt;        wander();&lt;br /&gt;        wander();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We could set up a system for &lt;a href="http://roguebasin.roguelikedevelopment.org/index.php/Articles#Time_management"&gt;dealing with different monster speeds&lt;/a&gt; but this is simple enough: bats move twice for every one of your moves. Easy to implement, easy to understand.&lt;br /&gt;&lt;br /&gt;Now we add bats to our CreatureFactory. I picked some low hp and attack so they could nibble on you a bit but shouldn't be too much of a problem.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature newBat(int depth){&lt;br /&gt;    Creature bat = new Creature(world, 'b', AsciiPanel.yellow, 15, 5, 0);&lt;br /&gt;    world.addAtEmptyLocation(bat, depth);&lt;br /&gt;    new BatAi(bat);&lt;br /&gt;    return bat;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And in our PlayScreen we need to update createCreatures. How about 20 bats per level?&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;for (int i = 0; i &amp;lt; 20; i++){&lt;br /&gt;    creatureFactory.newBat(z);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now that our player is in some danger of being killed, let's add a check after the world get's updated.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;if (player.hp() &amp;lt; 1)&lt;br /&gt;    return new LoseScreen();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Try it out. You should see bats being flying about and being batty.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;If you run this you'll soon notice that the bats drop dead from attacking each other or even attacking themselves. Suicide bats are funny but they quickly go extinct.&lt;br /&gt;&lt;br /&gt;Let's add a check to the first line of the moveBy method in the Creature class. It should bail out early if we're not actually moving. This will take care of creatures killing themselves when all they want to do is stand in one place.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;if (mx==0 &amp;amp;&amp;amp; my==0 &amp;amp;&amp;amp; mz==0)&lt;br /&gt;    return;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Creatures should also be able to see what other creatures are in the world so the CreatureAi can know what's going on.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature creature(int wx, int wy, int wz) {&lt;br /&gt;    return world.creature(wx, wy, wz);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We can now improve the CreatureAi wander method to make sure creatures don't fight other's like them.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void wander(){&lt;br /&gt;    int mx = (int)(Math.random() * 3) - 1;&lt;br /&gt;    int my = (int)(Math.random() * 3) - 1;&lt;br /&gt;    &lt;br /&gt;    Creature other = creature.creature(creature.x + mx, creature.y + my, creature.z);&lt;br /&gt;    &lt;br /&gt;    if (other != null &amp;amp;&amp;amp; other.glyph() == creature.glyph())&lt;br /&gt;        return;&lt;br /&gt;    else&lt;br /&gt;        creature.moveBy(mx, my, 0);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You could also make it keep trying until mx != 0 &amp;amp;&amp;amp; my != 0, that way it would never stand in the same spot. You may want to make sure it doesn't try to move into a wall or make it able to go up or down stairs.&lt;br /&gt;&lt;br /&gt;And there you go. You now have some deep and bat-filled caves to explore.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;Using the glyph in messages is lame; Creatures should have a name.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private String name;&lt;br /&gt;    public String name() { return name; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Using constructor injection, update the creatureFactory to pass in the appropriate name. Finally, update any messages that used the creature's glyph to now use the creature's name.&lt;br /&gt;&lt;br /&gt;Much better.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut09.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-5114094157788595151?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/5114094157788595151/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-09-wandering.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5114094157788595151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5114094157788595151'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-09-wandering.html' title='roguelike tutorial 09: wandering monsters'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-2467533460848123808</id><published>2011-09-13T13:00:00.000-07:00</published><updated>2011-09-13T13:00:06.324-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 08: vision, line of sight, and field of view</title><content type='html'>It doesn't feel like we're exploring much since we see the whole level from the beginning. Ideally we can only see our immediate surroundings and remember what we've already seen. I think we can do that in one session.&lt;br /&gt;&lt;br /&gt;The first thing we need is a way to determine if something is in our line of sight. To do this we get all the points in between us and what we want to look at and see if any of them block our vision. For this, we can create a new Line class that uses &lt;a href="http://en.wikipedia.org/wiki/Bresenham's_line_algorithm"&gt;Bresenham's line algorithm&lt;/a&gt; to find all the points along the line.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.Iterator;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;public class Line {&lt;br /&gt;    private List&amp;lt;Point&amp;gt; points;&lt;br /&gt;    public List&amp;lt;Point&amp;gt; getPoints() { return points; }&lt;br /&gt;&lt;br /&gt;    public Line(int x0, int y0, int x1, int y1) {&lt;br /&gt;        points = new ArrayList&amp;lt;Point&amp;gt;();&lt;br /&gt;    &lt;br /&gt;        int dx = Math.abs(x1-x0);&lt;br /&gt;        int dy = Math.abs(y1-y0);&lt;br /&gt;    &lt;br /&gt;        int sx = x0 &amp;lt; x1 ? 1 : -1;&lt;br /&gt;        int sy = y0 &amp;lt; y1 ? 1 : -1;&lt;br /&gt;        int err = dx-dy;&lt;br /&gt;    &lt;br /&gt;        while (true){&lt;br /&gt;            points.add(new Point(x0, y0, 0));&lt;br /&gt;        &lt;br /&gt;            if (x0==x1 &amp;amp;&amp;amp; y0==y1)&lt;br /&gt;                break;&lt;br /&gt;        &lt;br /&gt;            int e2 = err * 2;&lt;br /&gt;            if (e2 &amp;gt; -dx) {&lt;br /&gt;                err -= dy;&lt;br /&gt;                x0 += sx;&lt;br /&gt;            }&lt;br /&gt;            if (e2 &amp;lt; dx){&lt;br /&gt;                err += dx;&lt;br /&gt;                y0 += sy;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you look this all the work is done in the constructor - that's a bad sign. So says &lt;a href="http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/"&gt;Misko Hevery&lt;/a&gt; of Google fame, Martian Feathers of Working Effectively With Legacy Code, and &lt;a href="http://c2.com/cgi/wiki?ConstructorDoesTheWork"&gt;anyone who's had to deal with this before&lt;/a&gt;. On the other hand, it doesn't do that much work; it just creates a list of points. The points are value objects and the line itself could be a value object. I'm certainly no fan of constructors that initialize their collaborators but this seems like a special case. Since it's just a personal project and no one's life or money are on the line, I'll try it and see if it becomes a problem.&lt;br /&gt;&lt;br /&gt;To make things a tiny bit more convenient to loop through the points in a line, we can make the class implement Iterable&amp;lt;Point&amp;gt;. All we have to do is declare that the Line implements Iterable&amp;lt;Point&amp;gt; and add the following method:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Iterator&amp;lt;Point&amp;gt; iterator() {&lt;br /&gt;        return points.iterator();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I have a feeling that we will use this Line class a lot as we add more features.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;Since creatures are the ones who are doing the seeing, it makes since to give creatures a new stat to say how far they can see and a couple methods for looking at the world.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private int visionRadius;&lt;br /&gt;    public int visionRadius() { return visionRadius; }&lt;br /&gt;&lt;br /&gt;    public boolean canSee(int wx, int wy, int wz){&lt;br /&gt;        return ai.canSee(wx, wy, wz);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Tile tile(int wx, int wy, int wz) {&lt;br /&gt;        return world.tile(wx, wy, wz);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I set the vision radius to 9 in the creature's constructor but you should use whatever value you prefer or even have it passed in from the creatureFactory. Since we delegate the task to seeing to the CreatureAi, that's where the work is done and what we'll add to next.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public boolean canSee(int wx, int wy, int wz) {&lt;br /&gt;        if (creature.z != wz)&lt;br /&gt;            return false;&lt;br /&gt;    &lt;br /&gt;        if ((creature.x-wx)*(creature.x-wx) + (creature.y-wy)*(creature.y-wy) &amp;gt; creature.visionRadius()*creature.visionRadius())&lt;br /&gt;            return false;&lt;br /&gt;    &lt;br /&gt;        for (Point p : new Line(creature.x, creature.y, wx, wy)){&lt;br /&gt;            if (creature.tile(p.x, p.y, wz).isGround() || p.x == wx &amp;amp;&amp;amp; p.y == wy)&lt;br /&gt;                continue;&lt;br /&gt;        &lt;br /&gt;            return false;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now that our player can see his immediate surroundings, we should update the PlayScreen to only show the monsters and tiles that can be seen. Tiles outside the line of sight are shown in dark grey.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void displayTiles(AsciiPanel terminal, int left, int top) {&lt;br /&gt;     for (int x = 0; x &amp;lt; screenWidth; x++){&lt;br /&gt;         for (int y = 0; y &amp;lt; screenHeight; y++){&lt;br /&gt;             int wx = x + left;&lt;br /&gt;             int wy = y + top;&lt;br /&gt;&lt;br /&gt;             if (player.canSee(wx, wy, player.z)){&lt;br /&gt;                 Creature creature = world.creature(wx, wy, player.z);&lt;br /&gt;                 if (creature != null)&lt;br /&gt;                     terminal.write(creature.glyph(), creature.x - left, creature.y - top, creature.color());&lt;br /&gt;                 else&lt;br /&gt;                     terminal.write(world.glyph(wx, wy, player.z), x, y, world.color(wx, wy, player.z));&lt;br /&gt;             } else {&lt;br /&gt;                 terminal.write(world.glyph(wx, wy, player.z), x, y, Color.darkGray);&lt;br /&gt;             }&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Try it. You should see tiles outside of the player's range in a dark grey color.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;We need a place to store what tiles the user has seen but who tracks what's been seen? It's part of the GUI so maybe the PlayScreen? But it's heavely based on the map so maybe the World class should flag tiles that have been seen &amp;mdash; many games do this so maybe we should too? But the Creature is the one seeing so maybe it should. But we want the player to record this, the other monsters don't need to so maybe the CreatureAi should track what the creature has seen? That last one seems right to me; the PlayerAi should track what the player has seen and the PlayScreen should use that info to determines what get's displayed. This also means that we can store what tile the player saw at each location so what the player remembers may be different than what is in the real world. So if there's a cave in, or tunnels are dug, or some other change in the world then the player will remember what was last seen and be quite surprised when returning to that area.  Neat posiblities.&lt;br /&gt;&lt;br /&gt;We need a new tile to indicate a place that has not been seen. This is similar to the out of bounds tile we have since it's not really part of the world but it makes things much easier.&lt;br /&gt;&lt;pre class="brush: java"&gt;UNKNOWN(' ', AsciiPanel.white)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There are several &lt;a href="http://roguebasin.roguelikedevelopment.org/index.php/Articles#Line_of_sight.2C_field_of_vision"&gt;different ways of determining what is in the player's field of view&lt;/a&gt; this but the simplest, and therefore what I prefer, is called raycasting. It's exactly what we're already doing: draw a line from the viewer to the tile in question to see if anything is blocking the vision. Raycasting is probably the slowest way, but it's quick enough and I think has the best overall look. Other methods perform differently when columns and doorways are involved. &lt;br /&gt;&lt;br /&gt;Let's create a new class for our field of view. We can slightly extend the common definition of ours to not only determine what is in view but to remember what has already been seen too. What's visible now and what was seen earlier are technically two different things and possibly should be implemented by two different classes, but they're close enough and we can change it later if necessary.&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public class FieldOfView {&lt;br /&gt;    private World world;&lt;br /&gt;    private int depth;&lt;br /&gt;&lt;br /&gt;    private boolean[][] visible;&lt;br /&gt;    public boolean isVisible(int x, int y, int z){&lt;br /&gt;        return z == depth &amp;amp;&amp;amp; x &amp;gt;= 0 &amp;amp;&amp;amp; y &amp;gt;= 0 &amp;amp;&amp;amp; x &amp;lt; visible.length &amp;amp;&amp;amp; y &amp;lt; visible[0].length &amp;amp;&amp;amp; visible[x][y];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private Tile[][][] tiles;&lt;br /&gt;    public Tile tile(int x, int y, int z){&lt;br /&gt;        return tiles[x][y][z];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public FieldOfView(World world){&lt;br /&gt;        this.world = world;&lt;br /&gt;        this.visible = new boolean[world.width()][world.height()];&lt;br /&gt;        this.tiles = new Tile[world.width()][world.height()][world.depth()];&lt;br /&gt;    &lt;br /&gt;        for (int x = 0; x &amp;lt; world.width(); x++){&lt;br /&gt;            for (int y = 0; y &amp;lt; world.height(); y++){&lt;br /&gt;                for (int z = 0; z &amp;lt; world.depth(); z++){&lt;br /&gt;                    tiles[x][y][z] = Tile.UNKNOWN;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;That seems like a good interface. We can ask if a tile is visible and we can ask what tile was last seen somewhere. We just need to add the method to update what's visible and has been seen.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void update(int wx, int wy, int wz, int r){&lt;br /&gt;        depth = wz;&lt;br /&gt;        visible = new boolean[world.width()][world.height()];&lt;br /&gt;    &lt;br /&gt;        for (int x = -r; x &amp;lt; r; x++){&lt;br /&gt;            for (int y = -r; y &amp;lt; r; y++){&lt;br /&gt;                if (x*x + y*y &amp;gt; r*r)&lt;br /&gt;                    continue;&lt;br /&gt;         &lt;br /&gt;                if (wx + x &amp;lt; 0 || wx + x &amp;gt;= world.width() &lt;br /&gt;                 || wy + y &amp;lt; 0 || wy + y &amp;gt;= world.height())&lt;br /&gt;                    continue;&lt;br /&gt;         &lt;br /&gt;                for (Point p : new Line(wx, wy, wx + x, wy + y)){&lt;br /&gt;                    Tile tile = world.tile(p.x, p.y, wz);&lt;br /&gt;                    visible[p.x][p.y] = true;&lt;br /&gt;                    tiles[p.x][p.y][wz] = tile;&lt;br /&gt;             &lt;br /&gt;                    if (!tile.isGround())&lt;br /&gt;                        break;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Only the player is going to use this advanced field of view, all other creatures can use the default line of sight code. Add a FieldOfView variable to the PlayerAi and override the canSee method.&lt;br /&gt;&lt;pre class="brush: java"&gt;public boolean canSee(int wx, int wy, int wz) {&lt;br /&gt;    return fov.isVisible(wx, wy, wz);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Since the FieldOfView requires a world to be passed in the constructor and we don't want the ai's to know about the world, we can build the FieldOfView elseware and rely on constructor injection to give it to the PlayerAi. This means it will have to be passed into the CreatureFactory from the PlayScreen too.&lt;br /&gt;&lt;br /&gt;The PlayScreen should construct a new field of view once the world has been made and pass it to the CreatureFactory. Since the PlayScreen is also responsible for displaying the world to the user, we should keep a reference to the field of view. Then we can update it before displaying the world and rely on it for tiles outside of the player's view. After that, we just need to modify the displayTiles method.&lt;br /&gt;&lt;pre class="brush: java"&gt;private void displayTiles(AsciiPanel terminal, int left, int top) {&lt;br /&gt;        fov.update(player.x, player.y, player.z, player.visionRadius());&lt;br /&gt;    &lt;br /&gt;        for (int x = 0; x &amp;lt; screenWidth; x++){&lt;br /&gt;         for (int y = 0; y &amp;lt; screenHeight; y++){&lt;br /&gt;             int wx = x + left;&lt;br /&gt;             int wy = y + top;&lt;br /&gt;&lt;br /&gt;             if (player.canSee(wx, wy, player.z)){&lt;br /&gt;                 Creature creature = world.creature(wx, wy, player.z);&lt;br /&gt;                 if (creature != null)&lt;br /&gt;                     terminal.write(creature.glyph(), creature.x - left, creature.y - top, creature.color());&lt;br /&gt;                 else&lt;br /&gt;                     terminal.write(world.glyph(wx, wy, player.z), x, y, world.color(wx, wy, player.z));&lt;br /&gt;                 else&lt;br /&gt;                     terminal.write(fov.tile(wx, wy, player.z).glyph(), x, y, Color.darkGray);&lt;br /&gt;             }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;And there you go, line of sight and field of view. These caves are starting to feel like real caves. If only they had some more monsters....&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;I just remembered something I wanted to do with the world once we added creatures. Replace the glyph and color methods with these:&lt;br /&gt;&lt;pre class="brush: java"&gt;public char glyph(int x, int y, int z){&lt;br /&gt;    Creature creature = creature(x, y, z);&lt;br /&gt;    return creature != null ? creature.glyph() : tile(x, y, z).glyph();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;pre class="brush: java"&gt;public Color color(int x, int y, int z){&lt;br /&gt;    Creature creature = creature(x, y, z);&lt;br /&gt;    return creature != null ? creature.color() : tile(x, y, z).color();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Since the world takes care of that for us, the PlayScreen becomes simpler.&lt;br /&gt;&lt;pre class="brush: java"&gt;private void displayTiles(AsciiPanel terminal, int left, int top) {&lt;br /&gt;    fov.update(player.x, player.y, player.z, player.visionRadius());&lt;br /&gt;    &lt;br /&gt;    for (int x = 0; x &amp;lt; screenWidth; x++){&lt;br /&gt;        for (int y = 0; y &amp;lt; screenHeight; y++){&lt;br /&gt;            int wx = x + left;&lt;br /&gt;            int wy = y + top;&lt;br /&gt;&lt;br /&gt;            if (player.canSee(wx, wy, player.z))&lt;br /&gt;                terminal.write(world.glyph(wx, wy, player.z), x, y, world.color(wx, wy, player.z));&lt;br /&gt;            else&lt;br /&gt;                terminal.write(fov.tile(wx, wy, player.z).glyph(), x, y, Color.darkGray);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;Instead of having doAction notify everyone nearby, it should only notify them if they can see the one doing the action.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void doAction(String message, Object ... params){&lt;br /&gt;    int r = 9;&lt;br /&gt;    for (int ox = -r; ox &amp;lt; r+1; ox++){&lt;br /&gt;        for (int oy = -r; oy &amp;lt; r+1; oy++){&lt;br /&gt;            if (ox*ox + oy*oy &amp;gt; r*r)&lt;br /&gt;                continue;&lt;br /&gt;         &lt;br /&gt;            Creature other = world.creature(x+ox, y+oy, z);&lt;br /&gt;         &lt;br /&gt;            if (other == null)&lt;br /&gt;                continue;&lt;br /&gt;         &lt;br /&gt;            if (other == this)&lt;br /&gt;                other.notify("You " + message + ".", params);&lt;br /&gt;            else if (other.canSee(x, y, z))&lt;br /&gt;                other.notify(String.format("The %s %s.", name, makeSecondPerson(message)), params);&lt;br /&gt;         }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now you have to actually see something happen in order to be notified about it.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut08.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-2467533460848123808?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/2467533460848123808/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-08-vision-line-of.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2467533460848123808'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2467533460848123808'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-08-vision-line-of.html' title='roguelike tutorial 08: vision, line of sight, and field of view'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8716619626580897105</id><published>2011-09-09T13:00:00.000-07:00</published><updated>2011-09-09T13:00:00.505-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 07: z levels and deeper caves</title><content type='html'>Our simple little roguelike is so flat and two dimensional. Let's change that. I'll try to write use code thats easy to read and understand, even if it's woefully inefficient. It's a good thing world gen only happens once.&lt;br /&gt;&lt;br /&gt;Most roguelikes have a 2d level and just simulate the current one. When you leave the current level either a new one is created or an old one is loaded. If loading an old level then the program usually simulates the passage of time and adds some items or creatures and makes it look like things happened even though you weren't there. I find it easier just to create the entire world at once and simulate everything. This also makes world gen easier since you can make decisions based on the entire world, not just the thin slices that already exist. Our world is small enough for now that the extra time to create it and memory it takes up it shouldn't be a problem.&lt;br /&gt;&lt;br /&gt;In order to build better caves we're going to have to work with coordinates a lot. We should make a class to represent a point in space.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.Collections;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;public class Point {&lt;br /&gt;    public int x;&lt;br /&gt;    public int y;&lt;br /&gt;    public int z;&lt;br /&gt;&lt;br /&gt;    public Point(int x, int y, int z){&lt;br /&gt;        this.x = x;&lt;br /&gt;        this.y = y;&lt;br /&gt;        this.z = z;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Two points that represent the same location should be treated as equal. These are known as &lt;a href="http://c2.com/cgi/wiki?ValueObject"&gt;value objects&lt;/a&gt; as opposed to &lt;a href="http://c2.com/cgi/wiki?ReferenceObject"&gt;reference objects&lt;/a&gt;. We can tell Java that by overriding the hashCode and equals methods. Here's what Eclipse generated:&lt;br /&gt;&lt;pre class="brush: java"&gt;@Override&lt;br /&gt;    public int hashCode() {&lt;br /&gt;        final int prime = 31;&lt;br /&gt;        int result = 1;&lt;br /&gt;        result = prime * result + x;&lt;br /&gt;        result = prime * result + y;&lt;br /&gt;        result = prime * result + z;&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public boolean equals(Object obj) {&lt;br /&gt;        if (this == obj)&lt;br /&gt;         return true;&lt;br /&gt;        if (obj == null)&lt;br /&gt;         return false;&lt;br /&gt;        if (!(obj instanceof Point))&lt;br /&gt;         return false;&lt;br /&gt;        Point other = (Point) obj;&lt;br /&gt;        if (x != other.x)&lt;br /&gt;         return false;&lt;br /&gt;        if (y != other.y)&lt;br /&gt;         return false;&lt;br /&gt;        if (z != other.z)&lt;br /&gt;         return false;&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We're also going to spend a lot of time working with points that are adjacent to something. This will be much easier if we can just ask a point for a list of it's eight neighbors.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public List&amp;lt;point&amp;gt; neighbors8(){&lt;br /&gt;    List&amp;lt;point&amp;gt; points = new ArrayList&amp;lt;point&amp;gt;();&lt;br /&gt;  &lt;br /&gt;    for (int ox = -1; ox &amp;lt; 2; ox++){&lt;br /&gt;        for (int oy = -1; oy &amp;lt; 2; oy++){&lt;br /&gt;            if (ox == 0 &amp;amp;&amp;amp; oy == 0)&lt;br /&gt;                continue;&lt;br /&gt;    &lt;br /&gt;            points.add(new Point(x+ox, y+oy, z));&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    Collections.shuffle(points);&lt;br /&gt;    return points;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We shuffle the list before returning it so we don't introduce bias. Otherwise the upper left neighbor would always be checked first and the lower right would be last which may lead to some odd things.  Now that we have a Point class, let's use it to make some better caves.  First we need to add two new tile types, stairs up and stairs down. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;STAIRS_DOWN('&amp;gt;', AsciiPanel.white),&lt;br /&gt;    STAIRS_UP('&amp;lt;', AsciiPanel.white);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then, in the WorldBuilder class, we create a region map. Each location has a number that identifies what region of contiguous open space it belongs to; i.e. if two locations have the same region number, then you can walk from one to the other without digging through walls. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private WorldBuilder createRegions(){&lt;br /&gt;        regions = new int[width][height][depth];&lt;br /&gt;    &lt;br /&gt;        for (int z = 0; z &amp;lt; depth; z++){&lt;br /&gt;            for (int x = 0; x &amp;lt; width; x++){&lt;br /&gt;                for (int y = 0; y &amp;lt; height; y++){&lt;br /&gt;                   if (tiles[x][y][z] != Tile.WALL &amp;amp;&amp;amp; regions[x][y][z] == 0){&lt;br /&gt;                       int size = fillRegion(nextRegion++, x, y, z);&lt;br /&gt;              &lt;br /&gt;                       if (size &amp;lt; 25)&lt;br /&gt;                           removeRegion(nextRegion - 1, z);&lt;br /&gt;                   }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will look at every space in the world. If it is not a wall and it does not have a region assigned then that empty space, and all empty spaces it's connected to, will be given a new region number. If the region is to small it gets removed. When this method is done, all open tiles will have a region assigned to it and we can use the regions array to see if two tiles are part of the same open space.&lt;br /&gt;&lt;br /&gt;The removeRegion method does what it sounds like. It just zero's out the region number and fills in the cave so it's solid wall. I prefer caves where the smaller areas have been filled in but this step isn't necessary. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void removeRegion(int region, int z){&lt;br /&gt;    for (int x = 0; x &amp;lt; width; x++){&lt;br /&gt;        for (int y = 0; y &amp;lt; height; y++){&lt;br /&gt;            if (regions[x][y][z] == region){&lt;br /&gt;                regions[x][y][z] = 0;&lt;br /&gt;                tiles[x][y][z] = Tile.WALL;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The fillRegion method does a flood-fill starting with an open tile. It, and any open tile it's connected to, gets assigned the same region number. This is repeated until there are no unassigned empty neighboring tiles.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private int fillRegion(int region, int x, int y, int z) {&lt;br /&gt;        int size = 1;&lt;br /&gt;        ArrayList&amp;lt;Point&amp;gt; open = new ArrayList&amp;lt;Point&amp;gt;();&lt;br /&gt;        open.add(new Point(x,y,z));&lt;br /&gt;        regions[x][y][z] = region;&lt;br /&gt;    &lt;br /&gt;        while (!open.isEmpty()){&lt;br /&gt;            Point p = open.remove(0);&lt;br /&gt;&lt;br /&gt;            for (Point neighbor : p.neighbors8()){&lt;br /&gt;                if (regions[neighbor.x][neighbor.y][neighbor.z] &amp;gt; 0&lt;br /&gt;                  || tiles[neighbor.x][neighbor.y][neighbor.z] == Tile.WALL)&lt;br /&gt;                    continue;&lt;br /&gt;&lt;br /&gt;                size++;&lt;br /&gt;                regions[neighbor.x][neighbor.y][neighbor.z] = region;&lt;br /&gt;                open.add(neighbor);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return size;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To connect all the regions with stairs we just start at the top and connect them one layer at a time.  &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public WorldBuilder connectRegions(){&lt;br /&gt;        for (int z = 0; z &amp;lt; depth-1; z++){&lt;br /&gt;            connectRegionsDown(z);&lt;br /&gt;        }&lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To connect two adjacent layers we look at each region that sits above another region. If they haven't been connected then we connect them.&lt;br /&gt;&lt;pre class="brush: java"&gt;private void connectRegionsDown(int z){&lt;br /&gt;    List&amp;lt;String&amp;gt; connected = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;  &lt;br /&gt;    for (int x = 0; x &amp;lt; width; x++){&lt;br /&gt;        for (int y = 0; y &amp;lt; height; y++){&lt;br /&gt;            String region = regions[x][y][z] + "," + regions[x][y][z+1];&lt;br /&gt;            if (tiles[x][y][z] == Tile.FLOOR&lt;br /&gt;              &amp;amp;&amp;amp; tiles[x][y][z+1] == Tile.FLOOR&lt;br /&gt;              &amp;amp;&amp;amp; !connected.contains(region)){&lt;br /&gt;                connected.add(region);&lt;br /&gt;                connectRegionsDown(z, regions[x][y][z], regions[x][y][z+1]);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The region variable is just a way to uniquely combine two numbers into one. This way we just need a list of the region strings instead of some list of pairs or something. If java had tuples then we could use that instead of this way.&lt;br /&gt;&lt;br /&gt;To connect two regions, we get a list of all the locations where one is directly above the other. Then, based on how much area overlaps, we connect them with stairs going up and stairs going down.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void connectRegionsDown(int z, int r1, int r2){&lt;br /&gt;        List&amp;lt;Point&amp;gt; candidates = findRegionOverlaps(z, r1, r2);&lt;br /&gt;    &lt;br /&gt;        int stairs = 0;&lt;br /&gt;        do{&lt;br /&gt;            Point p = candidates.remove(0);&lt;br /&gt;            tiles[p.x][p.y][z] = Tile.STAIRS_DOWN;&lt;br /&gt;            tiles[p.x][p.y][z+1] = Tile.STAIRS_UP;&lt;br /&gt;            stairs++;&lt;br /&gt;        }&lt;br /&gt;        while (candidates.size() / stairs &amp;gt; 250);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finding which locations of two regions overlap is pretty straight forward.  &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public List&amp;lt;Point&amp;gt; findRegionOverlaps(int z, int r1, int r2) {&lt;br /&gt;        ArrayList&amp;lt;Point&amp;gt; candidates = new ArrayList&amp;lt;Point&amp;gt;();&lt;br /&gt;    &lt;br /&gt;        for (int x = 0; x &amp;lt; width; x++){&lt;br /&gt;         for (int y = 0; y &amp;lt; height; y++){&lt;br /&gt;             if (tiles[x][y][z] == Tile.FLOOR&lt;br /&gt;                  &amp;amp;&amp;amp; tiles[x][y][z+1] == Tile.FLOOR&lt;br /&gt;                  &amp;amp;&amp;amp; regions[x][y][z] == r1&lt;br /&gt;                  &amp;amp;&amp;amp; regions[x][y][z+1] == r2){&lt;br /&gt;              candidates.add(new Point(x,y,z));&lt;br /&gt;             }&lt;br /&gt;         }&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        Collections.shuffle(candidates);&lt;br /&gt;        return candidates;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After all that, the method for making caves needs to be amended.  &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public WorldBuilder makeCaves() {&lt;br /&gt;        return randomizeTiles()&lt;br /&gt;             .smooth(8)&lt;br /&gt;             .createRegions()&lt;br /&gt;             .connectRegions();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The world class obviously needs to be changed. Not only does a third layer need to be added to the tiles array, but most methods need to accept a new z parameter. I'm suer you can find all the changes that need to be made. Here's an example of an updated method:   &lt;br /&gt;&lt;pre class="brush: java"&gt;public void addAtEmptyLocation(Creature creature, int z){&lt;br /&gt;        int x;&lt;br /&gt;        int y;&lt;br /&gt;    &lt;br /&gt;        do {&lt;br /&gt;         x = (int)(Math.random() * width);&lt;br /&gt;         y = (int)(Math.random() * height);&lt;br /&gt;        }&lt;br /&gt;        while (!tile(x,y,z).isGround() || creature(x,y,z) != null);&lt;br /&gt;    &lt;br /&gt;        creature.x = x;&lt;br /&gt;        creature.y = y;&lt;br /&gt;        creature.z = z;&lt;br /&gt;        creatures.add(creature);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Creature class also needs a new z coordinate and many of it's methods need to be updated too. I'll leave that to you as well. Here's my new version of the moveBy method.   &lt;br /&gt;&lt;pre class="brush: java"&gt;public void moveBy(int mx, int my, int mz){&lt;br /&gt;        Tile tile = world.tile(x+mx, y+my, z+mz);&lt;br /&gt;    &lt;br /&gt;        if (mz == -1){&lt;br /&gt;            if (tile == Tile.STAIRS_DOWN) {&lt;br /&gt;                doAction("walk up the stairs to level %d", z+mz+1);&lt;br /&gt;            } else {&lt;br /&gt;                doAction("try to go up but are stopped by the cave ceiling");&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;        } else if (mz == 1){&lt;br /&gt;            if (tile == Tile.STAIRS_UP) {&lt;br /&gt;                doAction("walk down the stairs to level %d", z+mz+1);&lt;br /&gt;            } else {&lt;br /&gt;                doAction("try to go down but are stopped by the cave floor");&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        Creature other = world.creature(x+mx, y+my, z+mz);&lt;br /&gt;    &lt;br /&gt;        if (other == null)&lt;br /&gt;            ai.onEnter(x+mx, y+my, z+mz, tile);&lt;br /&gt;        else&lt;br /&gt;            attack(other);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The CreatureAi classes, PlayScreen class, and a few others need to be updated. Your IDE should tell you which ones are broken by the new thrid dimension. You should be able to fix all of these on your own.  One thing is that the greater than and less than keys used to move up and down stairs don't work with the keyCode() method so that needs to be handled differently. Here's how I did it:   &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;switch (key.getKeyChar()){&lt;br /&gt;        case '&amp;lt;': player.moveBy( 0, 0, -1); break;&lt;br /&gt;        case '&amp;gt;': player.moveBy( 0, 0, 1); break;&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This one was a lot of work. The algorithm for creating 3d caves that are connected takes a while to explain even though each step isn't too difficult on it's own. The real bummer was having to update all the places where we were using coordinates. I suppose that if we were using a Point class from the beginning then adding a z coordinate to the Point would have required far fewer changes. Or we could have stuck to a list of 2d arrays and just tracked the current one instead of simulating a 3d world all at once. Oh well. Maybe you wont make the same mistake with the next roguelike you make.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut07.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8716619626580897105?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8716619626580897105/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-07-z-levels-and.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8716619626580897105'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8716619626580897105'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-07-z-levels-and.html' title='roguelike tutorial 07: z levels and deeper caves'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-5463650478750899649</id><published>2011-09-06T13:00:00.000-07:00</published><updated>2011-09-06T13:00:02.796-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 06: hitpoints, combat, and messages</title><content type='html'>Currently our hero just gets rid of any foe it bumps into. I think it's time our creatures had a real attack.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void attack(Creature other){&lt;br /&gt;        int amount = Math.max(0, attackValue() - other.defenseValue());&lt;br /&gt;    &lt;br /&gt;        amount = (int)(Math.random() * amount) + 1;&lt;br /&gt;    &lt;br /&gt;        other.modifyHp(-amount);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void modifyHp(int amount) {&lt;br /&gt;        hp += amount;&lt;br /&gt;    &lt;br /&gt;        if (hp &amp;lt; 1)&lt;br /&gt;         world.remove(this);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;There's many many different ways to handle how much damage is done but I'll stick with something simple: the damage amount is a random number from 1 to the attackers attack value minus the defenders defense value. It's easy to code, easy to understand, and using only two variables worked fine for Catlevania: Symphony Of The Night. The IDE tells us what else we need to add to our creature class to support this.&lt;br /&gt;&lt;pre class="brush: java"&gt;private int maxHp;&lt;br /&gt;    public int maxHp() { return maxHp; }&lt;br /&gt;&lt;br /&gt;    private int hp;&lt;br /&gt;    public int hp() { return hp; }&lt;br /&gt;&lt;br /&gt;    private int attackValue;&lt;br /&gt;    public int attackValue() { return attackValue; }&lt;br /&gt;&lt;br /&gt;    private int defenseValue;&lt;br /&gt;    public int defenseValue() { return defenseValue; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We can rely on constructor injection to set the values.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature(World world, char glyph, Color color, int maxHp, int attack, int defense){&lt;br /&gt;    this.world = world;&lt;br /&gt;    this.glyph = glyph;&lt;br /&gt;    this.color = color;&lt;br /&gt;    this.maxHp = maxHp;&lt;br /&gt;    this.hp = maxHp;&lt;br /&gt;    this.attackValue = attack;&lt;br /&gt;    this.defenseValue = defense;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then update our CreatureFactory.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature newPlayer(){&lt;br /&gt;    Creature player = new Creature(world, '@', AsciiPanel.brightWhite, 100, 20, 5);&lt;br /&gt;    world.addAtEmptyLocation(player);&lt;br /&gt;    new PlayerAi(player);&lt;br /&gt;    return player;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature newFungus(){&lt;br /&gt;    Creature fungus = new Creature(world, 'f', AsciiPanel.green, 10, 0, 0);&lt;br /&gt;    world.addAtEmptyLocation(fungus);&lt;br /&gt;    new FungusAi(fungus, this);&lt;br /&gt;    return fungus;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As always, play around with the numbers and find something you like.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;Now that we have some stats, let's display them on the PlayScreen. Here's what I added to the end of displayOutput:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;String stats = String.format(" %3d/%3d hp", player.hp(), player.maxHp());&lt;br /&gt;    terminal.write(stats, 1, 23);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That went well. Adding new functionality was kept to a few small and isolated changes. The earlier decision to move all the creature creation to a factory also reduced the number of places we had to update. Now go fight some fungi!&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;Since this post is so short let's add another feature: messages. There are so many ways to do this but we're going to think of a way that avoids globals. A global message queue seems like the easiest way to do messaging - and it probably is - but sometimes it's a good idea to stick to a guideline like "don't use global variables" to see if it works in all scenarios and find where the guideline works against you and should be abandoned. I've found that small roguelikes are a perfect place for this kind of experimentation.&lt;br /&gt;&lt;br /&gt;Since we're not using a global message queue to hold messages, where should we put the messages? Messages are just extra text for the GUI so maybe they should be part of the PlayScreen? But they're created by things in the world and everything has access to the world so maybe there? That doesn't seem right though. Messages are meant for the player so maybe the PlayerAi should be the receiver of the messages? That kind of make sense because it already gets called by the creature class and creatures are probably going to be the source of most messages. We can pass messages to the ai and any non-player ai can just ignore the messages.&lt;br /&gt;&lt;br /&gt;Add a notify method to the creature class. To make it easier for the callers to build messages you can take the string and any parameters the caller passes and format the string for them. A nice convenience.&lt;br /&gt;&lt;pre class="brush: java"&gt;public void notify(String message, Object ... params){&lt;br /&gt;    ai.onNotify(String.format(message, params));&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Which means you need the corresponding empty method for the CreatureAi class. We'll make the PlayerAi class add the messages to a list. Other CreatureAi's will just ignore it.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;public class PlayerAi extends CreatureAi {&lt;br /&gt;&lt;br /&gt;    private List&amp;lt;String&amp;gt; messages;&lt;br /&gt;&lt;br /&gt;    public PlayerAi(Creature creature, List&amp;lt;String&amp;gt; messages) {&lt;br /&gt;        super(creature);&lt;br /&gt;        this.messages = messages;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void onEnter(int x, int y, Tile tile){&lt;br /&gt;        if (tile.isGround()){&lt;br /&gt;         creature.x = x;&lt;br /&gt;         creature.y = y;&lt;br /&gt;        } else if (tile.isDiggable()) {&lt;br /&gt;         creature.dig(x, y);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void onNotify(String message){&lt;br /&gt;        messages.add(message);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Instead of creating a getter for the message list we rely on constructor injection. That means the list comes from somewhere else that may already have a reference to it. We can create the list in the PlayScreen and pass it to the creature factory which passes it to the new PlayerAi. Since the PlayScreen already has the list, it can easily display any messages that show up and clear the list afterwards.&lt;br /&gt;&lt;br /&gt;Here's the update to the CreatureFactory:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature newPlayer(List&amp;lt;String&amp;gt; messages){&lt;br /&gt;    Creature player = new Creature(world, '@', AsciiPanel.brightWhite, 100, 20, 5);&lt;br /&gt;    world.addAtEmptyLocation(player);&lt;br /&gt;    new PlayerAi(player, messages);&lt;br /&gt;    return player;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here's the update to the PlayScreen:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private List&amp;lt;String&amp;gt; messages;&lt;br /&gt;&lt;br /&gt;public PlayScreen(){&lt;br /&gt;    screenWidth = 80;&lt;br /&gt;    screenHeight = 21;&lt;br /&gt;    messages = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;    createWorld();&lt;br /&gt;    &lt;br /&gt;    CreatureFactory creatureFactory = new CreatureFactory(world);&lt;br /&gt;    createCreatures(creatureFactory);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void createCreatures(CreatureFactory creatureFactory){&lt;br /&gt;    player = creatureFactory.newPlayer(messages);&lt;br /&gt;    &lt;br /&gt;    for (int i = 0; i &amp;lt; 8; i++){&lt;br /&gt;        creatureFactory.newFungus();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Displaying messages can also be done many different ways. If you haven't guessed by now I like to start simple. The simplest way I can think of is to just list them all on the screen at once.&lt;br /&gt;&lt;pre class="brush: java"&gt;private void displayMessages(AsciiPanel terminal, List&amp;lt;String&amp;gt; messages) {&lt;br /&gt;    int top = screenHeight - messages.size();&lt;br /&gt;    for (int i = 0; i &amp;lt; messages.size(); i++){&lt;br /&gt;        terminal.writeCenter(messages.get(i), top + i);&lt;br /&gt;    }&lt;br /&gt;    messages.clear();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Just call that from the displayOutput method. Before clearing the message list, the messages could be copied into a separate list (or list of lists) so the history is preserved.&lt;br /&gt;&lt;br /&gt;All we need now is some actual messages. Go ahead and notify the creature wherever it does something interesting or has something happen to it. Here's a sample of what I added to the attack method:&lt;br /&gt;&lt;pre class="brush: java"&gt;notify("You attack the '%s' for %d damage.", other.glyph, amount);&lt;br /&gt;        other.notify("The '%s' attacks you for %d damage.", glyph, amount);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now the player can receive notices of what's going on.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;What about notifying nearby creatures when something happens? Here's one way to do that:&lt;br /&gt;&lt;pre class="brush: java"&gt;public void doAction(String message, Object ... params){&lt;br /&gt;        int r = 9;&lt;br /&gt;        for (int ox = -r; ox &amp;lt; r+1; ox++){&lt;br /&gt;         for (int oy = -r; oy &amp;lt; r+1; oy++){&lt;br /&gt;             if (ox*ox + oy*oy &amp;gt; r*r)&lt;br /&gt;                 continue;&lt;br /&gt;         &lt;br /&gt;             Creature other = world.creature(x+ox, y+oy);&lt;br /&gt;         &lt;br /&gt;             if (other == null)&lt;br /&gt;                 continue;&lt;br /&gt;         &lt;br /&gt;             if (other == this)&lt;br /&gt;                 other.notify("You " + message + ".", params);&lt;br /&gt;             else&lt;br /&gt;                 other.notify(String.format("The '%s' %s.", glyph, makeSecondPerson(message)), params);&lt;br /&gt;         }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;The method makeSecondPerson does a small bit of string manipulation to make it grammatically correct. It assumes the first word is the verb, but that's easy enough to do as long as you don't plan on supporting other languages. It's best to avoid implicit rules like this since the only way to know about it is to already know it or watch it fail when you don't follow the implicit rule. It feels dirty to have gramer rules in with the Creature code so remember to move it somewhere better.&lt;br /&gt;&lt;pre class="brush: java"&gt;private String makeSecondPerson(String text){&lt;br /&gt;    String[] words = text.split(" ");&lt;br /&gt;    words[0] = words[0] + "s";&lt;br /&gt;    &lt;br /&gt;    StringBuilder builder = new StringBuilder();&lt;br /&gt;    for (String word : words){&lt;br /&gt;        builder.append(" ");&lt;br /&gt;        builder.append(word);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    return builder.toString().trim();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then you can call doAction in your creature code and anyone nearby will be notified. Here's some examples:&lt;br /&gt;&lt;br /&gt;when the FungusAi spawns a child:&lt;br /&gt;&lt;pre class="brush: java"&gt;creature.doAction("spawn a child");&lt;br /&gt;&lt;/pre&gt;while attacking:&lt;br /&gt;&lt;pre class="brush: java"&gt;doAction("attack the '%s' for %d damage", other.glyph, amount);&lt;br /&gt;&lt;/pre&gt;or when dying:&lt;br /&gt;&lt;pre class="brush: java"&gt;public void modifyHp(int amount) {&lt;br /&gt;    hp += amount;&lt;br /&gt;    &lt;br /&gt;    if (hp &amp;lt; 1) {&lt;br /&gt;        doAction("die");&lt;br /&gt;        world.remove(this);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now when playing you can get messages about all these details going on in the world. The more you add the more detailed and deep the world will be.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut06.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-5463650478750899649?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/5463650478750899649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-06-hitpoints-combat.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5463650478750899649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5463650478750899649'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-06-hitpoints-combat.html' title='roguelike tutorial 06: hitpoints, combat, and messages'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-1941735420979853205</id><published>2011-09-02T13:00:00.000-07:00</published><updated>2011-09-02T15:48:01.385-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 05: stationary monsters</title><content type='html'>Time for some monsters! I want to keep things simple so our first monster will be a stationary fungus. Sounds too boring? Well then I'll throw in a little something to make things interesting.&lt;br /&gt;&lt;br /&gt;If our world's going to have a bunch of creatures then we should start there. Add a list for them to the World class and initialize it in the World constructor.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private List&amp;lt;creature&amp;gt; creatures;&lt;/pre&gt;&lt;br /&gt;We need a way to get the creature at a specific location.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature creature(int x, int y){&lt;br /&gt;    for (Creature c : creatures){&lt;br /&gt;        if (c.x == x &amp;amp;&amp;amp; c.y == y)&lt;br /&gt;            return c;&lt;br /&gt;    }&lt;br /&gt;    return null;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And we'll update the addAtEmptyLocation method to make sure we don't add a creature where one already is. This will also be the place to add new creatures to our list.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void addAtEmptyLocation(Creature creature){&lt;br /&gt;    int x;&lt;br /&gt;    int y;&lt;br /&gt;  &lt;br /&gt;    do {&lt;br /&gt;        x = (int)(Math.random() * width);&lt;br /&gt;        y = (int)(Math.random() * height);&lt;br /&gt;    } &lt;br /&gt;    while (!tile(x,y).isGround() || creature(x,y) != null);&lt;br /&gt;  &lt;br /&gt;    creature.x = x;&lt;br /&gt;    creature.y = y;&lt;br /&gt;    creatures.add(creature);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If we want multiple creatures then we have to change how we display the world. Currently we just display the player. Get rid of that line and let's change the displayTiles method to show any creatures in the section of the world we're interested in.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void displayTiles(AsciiPanel terminal, int left, int top) {&lt;br /&gt;    for (int x = 0; x &amp;lt; screenWidth; x++){&lt;br /&gt;        for (int y = 0; y &amp;lt; screenHeight; y++){&lt;br /&gt;            int wx = x + left;&lt;br /&gt;            int wy = y + top;&lt;br /&gt;&lt;br /&gt;            Creature creature = world.creature(wx, wy);&lt;br /&gt;            if (creature != null)&lt;br /&gt;                terminal.write(creature.glyph(), creature.x - left, creature.y - top, creature.color());&lt;br /&gt;            else&lt;br /&gt;                terminal.write(world.glyph(wx, wy), x, y, world.color(wx, wy));&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div style="margin:0.5em; padding:0.5em; border:1px solid grey;"&gt;This is actually a very inefficient way to do this. It would be far better to draw all the tiles and then, for each creature, draw it if it is in the viewable region of left to left+screenWidth and top to top+screenHeight. That way we loop through &lt;code&gt;screenWidth * screenHeight tiles + the number of creatures&lt;/code&gt;. The way I wrote we loop through &lt;code&gt;screenWidth * screenHeight * the number of creatures&lt;/code&gt;. That's much worse. I don't know why I didn't realize this when I first wrote this since I've always drawn the creatures after the tiles. Consider this an example of one way to &lt;em&gt;not&lt;/em&gt; do it.&lt;/div&gt;&lt;br /&gt;You should be able to run it. It will look the same as before but now we can throw in some more creatures and they'll show up too.  Our fungus first needs it's own ai: &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public class FungusAi extends CreatureAi {&lt;br /&gt; &lt;br /&gt;    public FungusAi(Creature creature) {&lt;br /&gt;        super(creature);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And add it to our CreatureFactory so we can make them. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature newFungus(){&lt;br /&gt;    Creature fungus = new Creature(world, 'f', AsciiPanel.green);&lt;br /&gt;    world.addAtEmptyLocation(fungus);&lt;br /&gt;    new FungusAi(fungus);&lt;br /&gt;    return fungus;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now modify the PlayScreen to populate the world with fungus. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public PlayScreen(){&lt;br /&gt;    screenWidth = 80;&lt;br /&gt;    screenHeight = 21;&lt;br /&gt;    createWorld();&lt;br /&gt;  &lt;br /&gt;    CreatureFactory creatureFactory = new CreatureFactory(world);&lt;br /&gt;    createCreatures(creatureFactory);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void createCreatures(CreatureFactory creatureFactory){&lt;br /&gt;    player = creatureFactory.newPlayer();&lt;br /&gt;  &lt;br /&gt;    for (int i = 0; i &amp;lt; 8; i++){&lt;br /&gt;        creatureFactory.newFungus();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you run it now you should see some green f's standing about.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;We've got some monsters but to way to slay them. Let's work on that now; starting with the Creature class.  &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void moveBy(int mx, int my){&lt;br /&gt;    Creature other = world.creature(x+mx, y+my);&lt;br /&gt;  &lt;br /&gt;    if (other == null)&lt;br /&gt;        ai.onEnter(x+mx, y+my, world.tile(x+mx, y+my));&lt;br /&gt;    else&lt;br /&gt;        attack(other);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void attack(Creature other){&lt;br /&gt;    world.remove(other);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For now an attack will be an instant kill - just tell the world to get rid of the creature. Later on we can add hitpoints and whatnot. &lt;br /&gt;&lt;br /&gt;Here's how the world class can remove a killed creature:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void remove(Creature other) {&lt;br /&gt;    creatures.remove(other);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There you go. Stationary monsters waiting to be slain.  &lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;And now time to throw in a little something to make things interesting.  What if the fungi were able to reproduce and spread? I really like games where each creature has something special instead of all being the same things with slightly different stats so this could be interesting. We first need to let each creature know when it's time to update itself and whatever else it wants to do for it's turn. First and add a method to the World class that lets each creature know it's time to take a turn. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void update(){&lt;br /&gt;    for (Creature creature : creatures){&lt;br /&gt;        creature.update();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Your IDE is probably letting you know that the Creature class doesn't have an update method. Let's add one that delegates to the ai.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void update(){   &lt;br /&gt;    ai.onUpdate();  &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now your IDE is probably letting you know that the CreatureAi class doesn't have an onUpdate method so go ahead and add an empty one.  We want the fungi to spread to a nearby open space every once in a while as part of it's behavior during updating. Here's what I came up with: &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public class FungusAi extends CreatureAi {&lt;br /&gt;    private CreatureFactory factory;&lt;br /&gt;    private int spreadcount;&lt;br /&gt; &lt;br /&gt;    public FungusAi(Creature creature, CreatureFactory factory) {&lt;br /&gt;        super(creature);&lt;br /&gt;        this.factory = factory;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void onUpdate(){&lt;br /&gt;        if (spreadcount &amp;lt; 5 &amp;amp;&amp;amp; Math.random() &amp;lt; 0.02)&lt;br /&gt;            spread();&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    private void spread(){&lt;br /&gt;        int x = creature.x + (int)(Math.random() * 11) - 5;&lt;br /&gt;        int y = creature.y + (int)(Math.random() * 11) - 5;&lt;br /&gt;  &lt;br /&gt;        if (!creature.canEnter(x, y))&lt;br /&gt;            return;&lt;br /&gt;  &lt;br /&gt;        Creature child = factory.newFungus();&lt;br /&gt;        child.x = x;&lt;br /&gt;        child.y = y;&lt;br /&gt;        spreadcount++;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can play around with how far it spreads, how often it spreads, and how many times it can spread. Don't forget to modify the newFungus method to pass itself into the FungusAi constructor.  The last thing you need to do is tell the world to let everyone take a turn by calling world.update() in the PlayScreen after handling user input. The user's input makes the player move and each creature can move after that. Since we're relying on Java's event handling it would be very cumbersome to make our code pause and wait for user input during the player's onUpdate like with many other roguelikes.  Just one tiny detail left. If you run this then you will probably see some exceptions happen because we're adding new creatures to our list while looping through the same list &amp;mdash; which you can't do. There are different ways of dealing with this but the easiest I know of is to just make a copy of the list and loop through that instead. &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void update(){&lt;br /&gt;    List&amp;lt;creature&amp;gt; toUpdate = new ArrayList&amp;lt;creature&amp;gt;(creatures);&lt;br /&gt;    for (Creature creature : toUpdate){&lt;br /&gt;        creature.update();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut05.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;br /&gt;So now you've got some underground caves with a hero and some fungi. You can attack and they can spread. We're a bit closer to being an actual game. Next up, actual combat.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-1941735420979853205?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/1941735420979853205/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-05-stationary.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1941735420979853205'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1941735420979853205'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/09/roguelike-tutorial-05-stationary.html' title='roguelike tutorial 05: stationary monsters'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-2991939577590574582</id><published>2011-08-30T13:00:00.000-07:00</published><updated>2011-08-30T13:00:00.195-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 04: the player</title><content type='html'>Alright, let's get a hero so we can explore these caves.&lt;br /&gt;&lt;br /&gt;We'll need something to represent our player and eventually monsters. They'll all have an x and y coordinate, a glyph, and a color. Since they will be interacting with the world, they should have a reference to that too.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.awt.Color;&lt;br /&gt;&lt;br /&gt;public class Creature {&lt;br /&gt;    private World world;&lt;br /&gt;&lt;br /&gt;    public int x;&lt;br /&gt;    public int y;&lt;br /&gt;&lt;br /&gt;    private char glyph;&lt;br /&gt;    public char glyph() { return glyph; }&lt;br /&gt;&lt;br /&gt;    private Color color;&lt;br /&gt;    public Color color() { return color; }&lt;br /&gt;&lt;br /&gt;    public Creature(World world, char glyph, Color color){&lt;br /&gt;        this.world = world;&lt;br /&gt;        this.glyph = glyph;&lt;br /&gt;        this.color = color;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I made the x and y coordinate publicly accessible since they'll be used a lot, we don't need to constrain them or do anything when they change, and I'd rather not have to deal with getters and setters. Getters and setters are almost always a better idea than public fields (especially when using C# properties) but part of software engineering is knowing the rules and part is knowing when to break them. If this turns out to be a bad idea and we wish we used getters and setters instead, then it's not a big deal since most IDE's can automatically create getters and setters and rewrite your code to use those (Encapsulate Field or Generate Getters And Setters in Eclipse).&lt;br /&gt;&lt;br /&gt;To implement all the different behaviors of all the different creatures, we could use a bunch of flags representing creature traits, or we could use subclassing, but let's use something that's usually more flexible: delegation. Each creature will have a reference to a CreatureAi and the creatures can let their ai decide what to do. Instead of using what's called &lt;i&gt;constructor injection&lt;/i&gt; and passing the CreatureAi in the constructor like we do with the world, glyph, and color, &lt;a href="http://misko.hevery.com/2009/02/19/constructor-injection-vs-setter-injection/"&gt;which is usually a good way of doing things&lt;/a&gt;, we'll use &lt;i&gt;setter injection&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private CreatureAi ai;&lt;br /&gt;public void setCreatureAi(CreatureAi ai) { this.ai = ai; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;Since the caves we have so far aren't all connected, the player can only walk around in the open area he starts in. We could change how we build the world to make sure that all open cave floors are connected but there's a much easier way to make sure the player can explore everything; we'll let creatures dig through the walls.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void dig(int wx, int wy) {&lt;br /&gt;    world.dig(wx, wy);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And here's our addition to the World class allowing us to dig into cave walls.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void dig(int x, int y) {&lt;br /&gt;    if (tile(x,y).isDiggable())&lt;br /&gt;        tiles[x][y] = Tile.FLOOR;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the Tile class needs an isDiggable method. This way we don't even have to know what the tile is we can just care about if it can be dug through. If we later add new tiles, no-dig zones, or something else we just need to update this method.&lt;br /&gt;&lt;pre class="brush: java"&gt;public boolean isDiggable() {&lt;br /&gt;    return this == Tile.WALL;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that's the end of our "stuck in a tiny cave" problem. Any day I solve a tricky problem by adding a few little methods is a good day indeed.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;Getting back to our Creature class, creatures will also move around in the world. What happens when they try to enter a new tile is up to the creature's ai.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void moveBy(int mx, int my){&lt;br /&gt;    ai.onEnter(x+mx, y+my, world.tile(x+mx, y+my));&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I think were done with the Creature class for now so let's start on the CreatureAi. Remember when we created the setter for the creature's ai? We'll use that to wire up the creature and the creature's ai. The ai also needs to do deal with the creature trying to enter a new tile. We're going to have a specific ai for the player so it doesn't matter what you use here since we are just going to override it. Here's what I came up with:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public class CreatureAi {&lt;br /&gt;    protected Creature creature;&lt;br /&gt;&lt;br /&gt;    public CreatureAi(Creature creature){&lt;br /&gt;        this.creature = creature;&lt;br /&gt;        this.creature.setCreatureAi(this);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void onEnter(int x, int y, Tile tile) { }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Remember when I said we're going to have a specific ai for the player a minute ago? Well here it is:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public class PlayerAi extends CreatureAi {&lt;br /&gt;&lt;br /&gt;    public PlayerAi(Creature creature) {&lt;br /&gt;    super(creature);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We need to override the onEnter method to dig through walls and walk on ground tiles.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void onEnter(int x, int y, Tile tile){&lt;br /&gt;    if (tile.isGround()){&lt;br /&gt;        creature.x = x;&lt;br /&gt;        creature.y = y;&lt;br /&gt;    } else if (tile.isDiggable()) {&lt;br /&gt;        creature.dig(x, y);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If your world has doors then you can make the player automatically open them by walking into them with code very similar to this.&lt;br /&gt;&lt;br /&gt;Instead of checking the tile type directly we just ask if it can be walked on or dug through like with isDiggable. Here's our little addition to the Tile class to support that.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public boolean isGround() {&lt;br /&gt;    return this != WALL &amp;amp;&amp;amp; this != BOUNDS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We've got a creature class and classes for creature ai &amp;mdash; so far so good. We're going to create a lot of creatures that have the same values, all goblins will have a g glyph etc, and we need to make sure we always wire up the correct ai for each new creature. To centralize and hide all this assembly we'll create a class that's responsible for nothing else: the CreatureFactory. Using a &lt;a href="http://en.wikipedia.org/wiki/Factory_method_pattern"&gt;factory&lt;/a&gt; means the other code doesn't have to deal with all this assembly each time a new creature is created.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public class CreatureFactory {&lt;br /&gt;    private World world;&lt;br /&gt;&lt;br /&gt;    public CreatureFactory(World world){&lt;br /&gt;        this.world = world;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The only creature we're assembling so far is the player so that's the only method we need to add.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Creature newPlayer(){&lt;br /&gt;    Creature player = new Creature(world, '@', AsciiPanel.brightWhite);&lt;br /&gt;    world.addAtEmptyLocation(player);&lt;br /&gt;    new PlayerAi(player);&lt;br /&gt;    return player;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Since the creature needs to start on some empty space and we don't really care which one, we'll add an addAtEmptyLocation method to the world class and let the world take care of that for us.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public void addAtEmptyLocation(Creature creature){&lt;br /&gt;    int x;&lt;br /&gt;    int y;&lt;br /&gt;&lt;br /&gt;    do {&lt;br /&gt;        x = (int)(Math.random() * width);&lt;br /&gt;        y = (int)(Math.random() * height);&lt;br /&gt;    }&lt;br /&gt;    while (!tile(x,y).isGround());&lt;br /&gt;&lt;br /&gt;    creature.x = x;&lt;br /&gt;    creature.y = y;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;All that's left is updating the PlayScreen to display and control our @ rather than scrolling on it's own. Our player is very similar to the current scrolling stuff so it won't be difficult to swap it out.&lt;br /&gt;&lt;br /&gt;Remove the centerX and centerY variables and use a variable named player instead. Remove the scrollBy method and replace calls to scrollBy with calls to player.moveBy. (find and replace is your friend for things like this). Also replace references to centerX and centerY with player.x and player.y.&lt;br /&gt;&lt;br /&gt;Now that we have a real live hero, we can display that rather than the 'X'.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;terminal.write(player.glyph(), player.x - left, player.y - top, player.color());&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you missed any of these steps then the compiler or IDE should let you know. Just remember: error messages are your friend and will help you find what needs to be done.&lt;br /&gt;&lt;br /&gt;Now you need to make a CreatureFactory and use it to create the player's creature. This can be done as the last step of the PlayScreen constructor.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;CreatureFactory creatureFactory = new CreatureFactory(world);&lt;br /&gt;player = creatureFactory.newPlayer();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Just about everything here was adding new classes or a few little methods. The only code we had to really muck around with was a fairly straightforward swap out of the temporary scrolling stuff. That may have seemed like a lot of work just to replace the X on the screen with an @, but we did a lot more than that and are in a really good position for moving on and adding other monsters.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut04.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-2991939577590574582?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/2991939577590574582/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/08/roguelike-tutorial-04-player.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2991939577590574582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2991939577590574582'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/08/roguelike-tutorial-04-player.html' title='roguelike tutorial 04: the player'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-1005065351369620704</id><published>2011-08-26T13:00:00.001-07:00</published><updated>2012-02-09T17:22:39.937-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 03: scrolling through random caves</title><content type='html'>Time to work on actual gameplay. Sort of. Well ... not really. A place for gameplay to happen. A world for our heroes, foes, and treasures.&lt;br /&gt;&lt;br /&gt;Roguelikes happen somewhere. A somewhere made of floors, walls, rivers, trees, caves, doors, or whatever you can imagine. Since this is a tutorial to show the basics, we'll start with two kinds of environment tiles: cave floors and cave walls. I've found it's often useful to have another kind that represents out of bounds. That way instead of having to always check if something is out of bounds before checking the map about a specific tile, we can just ask and the map and it can tell us it's out of bounds and we can handle that however we want. If you're familiar with the &lt;a href="http://en.wikipedia.org/wiki/Null_Object_pattern"&gt;NullObject&lt;/a&gt; design pattern then it's very similar; I guess you could call it an OutOfBoundsObject.&lt;br /&gt;&lt;br /&gt;Since we're talking about tiles, let's have a Tile class. Each Tile needs to be displayed so we need a glyph to display and a color to display it with. Since we only have a few different tile types, and all tiles of the same type look and behave the same, we can represent the tiles as a java enum.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.awt.Color;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public enum Tile {&lt;br /&gt;    FLOOR((char)250, AsciiPanel.yellow),&lt;br /&gt;    WALL((char)177, AsciiPanel.yellow),&lt;br /&gt;    BOUNDS('x', AsciiPanel.brightBlack);&lt;br /&gt;&lt;br /&gt;    private char glyph;&lt;br /&gt;    public char glyph() { return glyph; }&lt;br /&gt;&lt;br /&gt;    private Color color;&lt;br /&gt;    public Color color() { return color; }&lt;br /&gt;&lt;br /&gt;    Tile(char glyph, Color color){&lt;br /&gt;        this.glyph = glyph;&lt;br /&gt;        this.color = color;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I like using extended ascii characters since AsciiPanel supports &lt;a href="http://en.wikipedia.org/wiki/Code_page_437"&gt;code page 437&lt;/a&gt;, but if you want to use '#' and '.', or something else entirely, go ahead. This is the place to do that.&lt;br /&gt;&lt;br /&gt;Now that we have cave walls and floors, we need a World to hold them.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.awt.Color;&lt;br /&gt;&lt;br /&gt;public class World {&lt;br /&gt;    private Tile[][] tiles;&lt;br /&gt;    private int width;&lt;br /&gt;    public int width() { return width; }&lt;br /&gt;&lt;br /&gt;    private int height;&lt;br /&gt;    public int height() { return height; }&lt;br /&gt;&lt;br /&gt;    public World(Tile[][] tiles){&lt;br /&gt;        this.tiles = tiles;&lt;br /&gt;        this.width = tiles.length;&lt;br /&gt;        this.height = tiles[0].length;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now that we have a world made up of tiles we can add some methods to get details about them.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Tile tile(int x, int y){&lt;br /&gt;        if (x &amp;lt; 0 || x &amp;gt;= width || y &amp;lt; 0 || y &amp;gt;= height)&lt;br /&gt;            return Tile.BOUNDS;&lt;br /&gt;        else&lt;br /&gt;            return tiles[x][y];&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public char glyph(int x, int y){&lt;br /&gt;        return tile(x, y).glyph();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public Color color(int x, int y){&lt;br /&gt;        return tile(x, y).color();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By checking for bounds here we don't need to worry about out of bounds errors and check everythime we ask the world about a location.&lt;br /&gt;&lt;br /&gt;That's perfect for getting details about our world of tiles but we don't have a way of creating the tiles a World is made of. We could add a bunch of methods to create a World, but I like having the World class only responsible for the running of a world not creating it. Creating a new world is an entirely different and complicated subject that's only relevant at the beginning of a game and should be forgotten about right after we have a world to work with. Something else needs to create, or build, a world. And if we have something else who's only responsibility is building a new world, you could use the &lt;a href="http://en.wikipedia.org/wiki/Builder_pattern"&gt;Builder pattern&lt;/a&gt; and call it a WorldBuilder.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;To create a WorldBuilder you need a world size. Then you can call methods, in literate style, to build up a world. Once you've specified how to build the world you want, you call the build method and you get a new World to play with.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;public class WorldBuilder {&lt;br /&gt;    private int width;&lt;br /&gt;    private int height;&lt;br /&gt;    private Tile[][] tiles;&lt;br /&gt;&lt;br /&gt;    public WorldBuilder(int width, int height) {&lt;br /&gt;        this.width = width;&lt;br /&gt;        this.height = height;&lt;br /&gt;        this.tiles = new Tile[width][height];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public World build() {&lt;br /&gt;        return new World(tiles);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The simplest interesting (i.e. randomized) world I know of is a world of caves. I came up with a basic algorithm to build randomized caves myself, although it's just a simple form of cellular automata and I'm &lt;a href="http://roguebasin.roguelikedevelopment.org/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels"&gt;not the first to come up with it&lt;/a&gt;. The process is to fill the area with cave floors and walls at random then to smooth everything out by turning areas with mostly neighboring walls into walls and areas with mostly neighboring floors into floors. Repeat the smoothing process a couple times and you have an interesting mix of cave walls and floors.&lt;br /&gt;&lt;br /&gt;So the builder should be able to randomize the tiles.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private WorldBuilder randomizeTiles() {&lt;br /&gt;        for (int x = 0; x &amp;lt; width; x++) {&lt;br /&gt;            for (int y = 0; y &amp;lt; height; y++) {&lt;br /&gt;                tiles[x][y] = Math.random() &amp;lt; 0.5 ? Tile.FLOOR : Tile.WALL;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And repeatedly smooth them.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private WorldBuilder smooth(int times) {&lt;br /&gt;        Tile[][] tiles2 = new Tile[width][height];&lt;br /&gt;        for (int time = 0; time &amp;lt; times; time++) {&lt;br /&gt;&lt;br /&gt;         for (int x = 0; x &amp;lt; width; x++) {&lt;br /&gt;             for (int y = 0; y &amp;lt; height; y++) {&lt;br /&gt;              int floors = 0;&lt;br /&gt;              int rocks = 0;&lt;br /&gt;&lt;br /&gt;              for (int ox = -1; ox &amp;lt; 2; ox++) {&lt;br /&gt;                  for (int oy = -1; oy &amp;lt; 2; oy++) {&lt;br /&gt;                   if (x + ox &amp;lt; 0 || x + ox &amp;gt;= width || y + oy &amp;lt; 0&lt;br /&gt;                        || y + oy &amp;gt;= height)&lt;br /&gt;                       continue;&lt;br /&gt;&lt;br /&gt;                   if (tiles[x + ox][y + oy] == Tile.FLOOR)&lt;br /&gt;                       floors++;&lt;br /&gt;                   else&lt;br /&gt;                       rocks++;&lt;br /&gt;                  }&lt;br /&gt;              }&lt;br /&gt;              tiles2[x][y] = floors &amp;gt;= rocks ? Tile.FLOOR : Tile.WALL;&lt;br /&gt;             }&lt;br /&gt;         }&lt;br /&gt;         tiles = tiles2;&lt;br /&gt;        }&lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We put the new tile into tiles2 because it's usually a bad idea to update data that you're using as input to next updates. It's hard to explain but if you change the code to not use the tiles2 variable you'll see what I mean.&lt;br /&gt;&lt;br /&gt;I don't like all those nested loops. &lt;a href="http://www.codinghorror.com/blog/2006/01/flattening-arrow-code.html"&gt;Arrow code&lt;/a&gt; like this is usually a bad sign but this is simple enough and only used during world gen so I'll leave it as it is for now. This is also just part of working with multi dimentional arrays in java.&lt;br /&gt;&lt;br /&gt;And that's how you can make some caves.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public WorldBuilder makeCaves() {&lt;br /&gt;    return randomizeTiles().smooth(8);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So now we can create a World of Tiles to play around in. But in order to play in our new world of cave floors and cave walls, we need to display it. Scrolling is easy to implement so we'll add that now. All this talk of playing reminds me of our PlayScreen class, which makes since because it's responsible for displaying the world we're playing in and reacting to player input.&lt;br /&gt;&lt;br /&gt;If we want the PlayScreen class to display a world then we need to make some changes to it. We need to track the world we're looking at, what part we're looking at, and how much of the screen is used for displaying the world. Here's the variables and constructor to add to the PlayScreen:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private World world;&lt;br /&gt;    private int centerX;&lt;br /&gt;    private int centerY;&lt;br /&gt;    private int screenWidth;&lt;br /&gt;    private int screenHeight;&lt;br /&gt;&lt;br /&gt;    public PlayScreen(){&lt;br /&gt;        screenWidth = 80;&lt;br /&gt;        screenHeight = 21;&lt;br /&gt;        createWorld();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The createWorld method does exactly that, create's a world. I have a feeling this is going to expand as we make the world more interesting so putting it in a separate method will reduce how tangled it get's with other code and make changes easier later on.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void createWorld(){&lt;br /&gt;        world = new WorldBuilder(90, 31)&lt;br /&gt;              .makeCaves()&lt;br /&gt;              .build();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We need a method to tell us how far along the X axis we should scroll. This makes sure we never try to scroll too far to the left or right.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public int getScrollX() {&lt;br /&gt;    return Math.max(0, Math.min(centerX - screenWidth / 2, world.width() - screenWidth));&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And we need a method to tell us how far along the Y axis we should scroll. This makes sure we never try to scroll too far to the top or bottom.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;public int getScrollY() {&lt;br /&gt;    return Math.max(0, Math.min(centerY - screenHeight / 2, world.height() - screenHeight));&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We need a method to display some tiles. This takes a left and top to know which section of the world it should display.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void displayTiles(AsciiPanel terminal, int left, int top) {&lt;br /&gt;    for (int x = 0; x &amp;lt; screenWidth; x++){&lt;br /&gt;        for (int y = 0; y &amp;lt; screenHeight; y++){&lt;br /&gt;            int wx = x + left;&lt;br /&gt;            int wy = y + top;&lt;br /&gt;&lt;br /&gt;            terminal.write(world.glyph(wx, wy), x, y, world.color(wx, wy));&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now that we have a world to look at, we need to update the displayOutput method to show the section we're looking at on part of the screen - the rest of the screen is for user stats, messages, etc.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;int left = getScrollX();&lt;br /&gt;        int top = getScrollY();&lt;br /&gt;   &lt;br /&gt;        displayTiles(terminal, left, top);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Might as well show where were actually looking while we are here.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;terminal.write('X', centerX - left, centerY - top);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We also need a new method to actually scroll. It should make sure we're never trying to scroll out of bounds.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;private void scrollBy(int mx, int my){&lt;br /&gt;        centerX = Math.max(0, Math.min(centerX + mx, world.width() - 1));&lt;br /&gt;        centerY = Math.max(0, Math.min(centerY + my, world.height() - 1));&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lastly, we need to add cases the the respondToUserInput code se we scroll based on user input.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;case KeyEvent.VK_LEFT:&lt;br /&gt;        case KeyEvent.VK_H: scrollBy(-1, 0); break;&lt;br /&gt;        case KeyEvent.VK_RIGHT:&lt;br /&gt;        case KeyEvent.VK_L: scrollBy( 1, 0); break;&lt;br /&gt;        case KeyEvent.VK_UP:&lt;br /&gt;        case KeyEvent.VK_K: scrollBy( 0,-1); break;&lt;br /&gt;        case KeyEvent.VK_DOWN:&lt;br /&gt;        case KeyEvent.VK_J: scrollBy( 0, 1); break;&lt;br /&gt;        case KeyEvent.VK_Y: scrollBy(-1,-1); break;&lt;br /&gt;        case KeyEvent.VK_U: scrollBy( 1,-1); break;&lt;br /&gt;        case KeyEvent.VK_B: scrollBy(-1, 1); break;&lt;br /&gt;        case KeyEvent.VK_N: scrollBy( 1, 1); break;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now you have some random caves that you can look around in.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;That seems like a fair bit of work. On the other hand, we did create a way to build thousands of worlds (90 tiles by 32 tiles with 2 tiles types = &lt;strike&gt;5,760&lt;/strike&gt; 2^2880&amp;nbsp;possible worlds) and a way to look around in them so hopefully it's worth it. Not only that, but most of this was from adding new code and not modifying old code; that's always a good sign. You should also try adding new tile types, or tweaking the cave algorithm, changing the world size, importing a hard coded level, or even implementing &lt;a href="http://roguebasin.roguelikedevelopment.org/index.php/Articles#Map"&gt;other world generation algorithms&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://sites.google.com/site/trystansprojects/rltut03.zip?attredirects=0&amp;amp;d=1"&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-1005065351369620704?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/1005065351369620704/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/08/roguelike-tutorial-03-scrolling-through.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1005065351369620704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/1005065351369620704'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/08/roguelike-tutorial-03-scrolling-through.html' title='roguelike tutorial 03: scrolling through random caves'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-4950834496539056646</id><published>2011-08-19T13:00:00.000-07:00</published><updated>2011-08-19T13:00:03.097-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 02: input, output, modes, and screens</title><content type='html'>Nearly all games follow the same basic main loop:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;while the game isn't over:&lt;br /&gt;    show stuff to the user&lt;br /&gt;    get user input&lt;br /&gt;    respond to user input&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Roguelikes are no different. But showing stuff to the user, getting user input, and responding to the input doesn't always mean the same thing. Usually we're showing the world and waiting for a player's command but sometimes we're showing a list of spells and waiting for the user to tell us which one to cast or maybe we're showing the player how he died and asking if he wants to play again. Said another way, sometimes we're in "play" mode, sometimes in "select spell" mode, and sometimes "you lost" mode. Each mode has a different way of handling input and output, and I've found that having a different class for each mode with it's own input and output logic is a good way of handling that — much better than having a big mess of if statements and mode-related  variables.&lt;br /&gt;&lt;br /&gt;Each mode will be represented by a different screen. Each screen displays output on our AsciiPanel and responds to user input; this abstraction can be represented as a simple Screen interface.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public interface Screen {&lt;br /&gt;    public void displayOutput(AsciiPanel terminal);&lt;br /&gt;&lt;br /&gt;    public Screen respondToUserInput(KeyEvent key);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The displayOutput method takes an AsciiPanel to display itself on and the respondToUserInput takes the KeyEvent and can return the new screen. This way pressing a key can result in looking at a different screen.&lt;br /&gt;&lt;br /&gt;The first screen players will see is the StartScreen. This is just a screen that displays some info and sets us in "play" mode when the user hits enter.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public class StartScreen implements Screen {&lt;br /&gt;&lt;br /&gt;    public void displayOutput(AsciiPanel terminal) {&lt;br /&gt;        terminal.write("rl tutorial", 1, 1);&lt;br /&gt;        terminal.writeCenter("-- press [enter] to start --", 22);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Screen respondToUserInput(KeyEvent key) {&lt;br /&gt;        return key.getKeyCode() == KeyEvent.VK_ENTER ? new PlayScreen() : this;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The PlayScreen class will be responsible for showing the dungeon and all it's inhabitants and loot — but since we don't have that yet we'll just tell the player how much fun he's having. It will also respond to user input by moving the player and either setting us in "win" mode if we won the game, or "lose" mode if we lost.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public class PlayScreen implements Screen {&lt;br /&gt;&lt;br /&gt;    public void displayOutput(AsciiPanel terminal) {&lt;br /&gt;        terminal.write("You are having fun.", 1, 1);&lt;br /&gt;        terminal.writeCenter("-- press [escape] to lose or [enter] to win --", 22);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Screen respondToUserInput(KeyEvent key) {&lt;br /&gt;        switch (key.getKeyCode()){&lt;br /&gt;        case KeyEvent.VK_ESCAPE: return new LoseScreen();&lt;br /&gt;        case KeyEvent.VK_ENTER: return new WinScreen();&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yes, using escape and enter to lose and win is pretty lame, but we know it's temporary and we can swap it out for real stuff later.&lt;br /&gt;&lt;br /&gt;The WinScreen will eventually display how awesome our brave hero is and ask if they'd like to play again. But not yet.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public class WinScreen implements Screen {&lt;br /&gt;&lt;br /&gt;    public void displayOutput(AsciiPanel terminal) {&lt;br /&gt;        terminal.write("You won.", 1, 1);&lt;br /&gt;        terminal.writeCenter("-- press [enter] to restart --", 22);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Screen respondToUserInput(KeyEvent key) {&lt;br /&gt;        return key.getKeyCode() == KeyEvent.VK_ENTER ? new PlayScreen() : this;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The LoseScreen will eventually display how lame our foolish hero was and ask if they'd like to play again. But not yet.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut.screens;&lt;br /&gt;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public class LoseScreen implements Screen {&lt;br /&gt;&lt;br /&gt;    public void displayOutput(AsciiPanel terminal) {&lt;br /&gt;        terminal.write("You lost.", 1, 1);&lt;br /&gt;        terminal.writeCenter("-- press [enter] to restart --", 22);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Screen respondToUserInput(KeyEvent key) {&lt;br /&gt;        return key.getKeyCode() == KeyEvent.VK_ENTER ? new PlayScreen() : this;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's it for the screens. Each one is only a dozen or so lines and does only a few simple things. That's a really good sign: small classes with few related responsibilities are good. Very good.&lt;br /&gt;&lt;br /&gt;The ApplicationMain class needs to be updated though. It now has to display the current screen when the window repaints and pass user input to the current screen. It's generally best to avoid changing current code but this is only delegating input and output to other things, exactly what ApplicationMain is for. If we were changing it so it handles game logic then I'd be worried, but this is ok.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import javax.swing.JFrame;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;import java.awt.event.KeyEvent;&lt;br /&gt;import java.awt.event.KeyListener;&lt;br /&gt;import rltut.screens.Screen;&lt;br /&gt;import rltut.screens.StartScreen;&lt;br /&gt;&lt;br /&gt;public class ApplicationMain extends JFrame implements KeyListener {&lt;br /&gt;    private static final long serialVersionUID = 1060623638149583738L;&lt;br /&gt;&lt;br /&gt;    private AsciiPanel terminal;&lt;br /&gt;    private Screen screen;&lt;br /&gt;&lt;br /&gt;    public ApplicationMain(){&lt;br /&gt;        super();&lt;br /&gt;        terminal = new AsciiPanel();&lt;br /&gt;        add(terminal);&lt;br /&gt;        pack();&lt;br /&gt;        screen = new StartScreen();&lt;br /&gt;        addKeyListener(this);&lt;br /&gt;        repaint();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void repaint(){&lt;br /&gt;        terminal.clear();&lt;br /&gt;        screen.displayOutput(terminal);&lt;br /&gt;        super.repaint();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void keyPressed(KeyEvent e) {&lt;br /&gt;        screen = screen.respondToUserInput(e);&lt;br /&gt;        repaint();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void keyReleased(KeyEvent e) { }&lt;br /&gt;&lt;br /&gt;    public void keyTyped(KeyEvent e) { }&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        ApplicationMain app = new ApplicationMain();&lt;br /&gt;        app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);&lt;br /&gt;        app.setVisible(true);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Don't forget to update the AppletMain class to handle input and delegate everything to it's current screen.&lt;br /&gt;&lt;br /&gt;I'm sure you want to have an @ running around quaffing potions and saying dragons but what we've done so far is very good. Not only are we ready for inventory screens, targeting screens, help screens, and any other screen you can think of, but we've already begin thinking about how user input and output is different than actual gameplay and should be in it's own separate code. Let me repeat, bold, and italicize that: &lt;i&gt;&lt;b&gt;It's good when input, output, and gameplay logic are separate&lt;/b&gt;&lt;/i&gt;. Once you start mixing them, everything goes downhill. In fact, all we really need to do now is implement the guts of PlayScreen. We'll start that next.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://sites.google.com/site/trystansprojects/rltut02.zip?attredirects=0&amp;amp;d=1"&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-4950834496539056646?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/4950834496539056646/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/08/roguelike-tutorial-02-input-output.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/4950834496539056646'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/4950834496539056646'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/08/roguelike-tutorial-02-input-output.html' title='roguelike tutorial 02: input, output, modes, and screens'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-6741279377656737465</id><published>2011-08-16T13:00:00.000-07:00</published><updated>2011-08-16T16:29:25.629-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial 01: Java, Eclipse, AsciiPanel, application, applet</title><content type='html'>This tutorial will be written in Java since I'm familiar with it and it's a decent enough language, has many tools and libraries, a large and helpful community, and runs on Mac, Windows, linux, and web browsers. It's assumed you know at least the basics but I'll explain some of it as I go along. If you have a preferred IDE then go ahead and use that; otherwise, download the latest version of &lt;a href="http://www.eclipse.org/downloads/"&gt;Eclipse&lt;/a&gt; since it's powerful and used by may developers, including me. The one other thing this tutorial uses is AsciiPanel (&lt;a href="https://github.com/trystan/AsciiPanel"&gt;source&lt;/a&gt;,&amp;nbsp;&lt;a href="https://github.com/downloads/trystan/AsciiPanel/asciiPanel.jar"&gt;jar&lt;/a&gt;), a side project I started to help display the old-school ascii graphics so common to roguelikes. Even if you want to support graphics, starting with ascii will let us get started quickly.&lt;br /&gt;&lt;br /&gt;So let's get started. Download and start Eclipse (or whatever IDE you're most familiar with) and start up a new project. I'm calling mine "rltut". Download the AsciiPanel jar file and add that to your project.&lt;br /&gt;&lt;br /&gt;We'll start with something very simple: just a window with some text on it.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import javax.swing.JFrame;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public class ApplicationMain extends JFrame {&lt;br /&gt;    private static final long serialVersionUID = 1060623638149583738L;&lt;br /&gt;&lt;br /&gt;    private AsciiPanel terminal;&lt;br /&gt;&lt;br /&gt;    public ApplicationMain(){&lt;br /&gt;        super();&lt;br /&gt;        terminal = new AsciiPanel();&lt;br /&gt;        terminal.write("rl tutorial", 1, 1);&lt;br /&gt;        add(terminal);&lt;br /&gt;        pack();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        ApplicationMain app = new ApplicationMain();&lt;br /&gt;        app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);&lt;br /&gt;        app.setVisible(true);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-KOXq-IlBf9U/Tjl_jIdsbkI/AAAAAAAAACg/n5gjXVYhTXE/s1600/Screen+shot+2011-08-03+at+10.03.34+AM.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-KOXq-IlBf9U/Tjl_jIdsbkI/AAAAAAAAACg/n5gjXVYhTXE/s1600/Screen+shot+2011-08-03+at+10.03.34+AM.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-size: x-small;"&gt;If you're using Eclipse, your project&lt;br /&gt;should look something like this&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;A humble beginning, but it means that we have all our tools, libraries, and project settings wired up correctly, which can be one of the most frustrating parts. We'll need a few minor changes but the ApplicationMain class is going to stay simple, it's only responsibility is to create a window and delegate input and output to other things.&lt;br /&gt;&lt;br /&gt;The serialVersionUID is suggested by Eclipse and helps to prevent show-stopping failures when serializing different versions of our class. We won't be doing that in this tutorial but it's almost always a good idea to take care of compiler and IDE warning as soon as possible; It will save much trouble down the line.&lt;br /&gt;&lt;br /&gt;The ApplicationMain constructor has all the set up code. So far that's just creating an AsciiPanel to display some text and making sure the window is the correct size. The AsciiPanel defaults to 80 by 24 characters but you can specify a different size in it's constructor - go ahead and try it. Play around with the write method while you're at it.&lt;br /&gt;&lt;br /&gt;The main method just creates an instance of our window and show's it, making sure that the application exits when the window is closed. Simple as can be.&lt;br /&gt;&lt;br /&gt;For extra awesomeness you can make your roguelike run from the users browser as an applet. Just add a file like this to your project:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package rltut;&lt;br /&gt;&lt;br /&gt;import java.applet.Applet;&lt;br /&gt;import asciiPanel.AsciiPanel;&lt;br /&gt;&lt;br /&gt;public class AppletMain extends Applet {&lt;br /&gt;    private static final long serialVersionUID = 2560255315130084198L;&lt;br /&gt;&lt;br /&gt;    private AsciiPanel terminal;&lt;br /&gt;&lt;br /&gt;    public AppletMain(){&lt;br /&gt;        super();&lt;br /&gt;        terminal = new AsciiPanel();&lt;br /&gt;        terminal.write("rl tutorial", 1, 1);&lt;br /&gt;        add(terminal);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void init(){&lt;br /&gt;        super.init();&lt;br /&gt;        this.setSize(terminal.getWidth() + 20, terminal.getHeight() + 20);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void repaint(){&lt;br /&gt;        super.repaint();&lt;br /&gt;        terminal.repaint();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's a good start. You don't have much but anyone can play it since it runs on any modern computer either from the user's browser or downloaded and run from the user's machine.&lt;br /&gt;&lt;br /&gt;&lt;a href='https://sites.google.com/site/trystansprojects/rltut01.zip?attredirects=0&amp;d=1'&gt;download the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-6741279377656737465?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/6741279377656737465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/08/roguelike-tutorial-01-java-eclipse.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6741279377656737465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6741279377656737465'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/08/roguelike-tutorial-01-java-eclipse.html' title='roguelike tutorial 01: Java, Eclipse, AsciiPanel, application, applet'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-KOXq-IlBf9U/Tjl_jIdsbkI/AAAAAAAAACg/n5gjXVYhTXE/s72-c/Screen+shot+2011-08-03+at+10.03.34+AM.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-7348179585875541296</id><published>2011-08-13T17:46:00.000-07:00</published><updated>2011-08-13T17:46:15.663-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><title type='text'>roguelike tutorial: the what and why</title><content type='html'>Let's make a roguelike!&lt;br /&gt;&lt;br /&gt;I've made a few roguelikes — mostly for myself — and I think they're a fun mix of computer science algorithms, code design decisions, and game design decisions. They are also fun to make and a great way to try new programing ideas and new game ideas.&lt;br /&gt;&lt;br /&gt;I've created a 20 part series that I'm going to post here where I stumble my way through creating a roguelike that has most of the features any mainstream roguelike has. I consider myself a decent programmer but I'll try some things that may or may not work out and, for the sake of explanation via blog posts, cut some corners and leave out a few details. Because of that, don't take this as an example of the ideal rogulike or ideal code but if you stumble through it with me and add your own ideas, you'll have a roguelike too and, more importantly, may be better armed to make your own roguelike or contribute to someone else's roguelike.&lt;br /&gt;&lt;br /&gt;Here's the rundown of what's to come:&lt;br /&gt;&lt;br /&gt;Part 01: Java, Eclipse, AsciiPanel, application, applet&lt;br /&gt;Part 02: input, output, modes and screens&lt;br /&gt;Part 03: scrolling through random caves&lt;br /&gt;Part 04: the player&lt;br /&gt;Part 05: stationary monsters&lt;br /&gt;Part 06: hitpoints, combat, and messages&lt;br /&gt;Part 07: z levels and deeper caves&lt;br /&gt;Part 08: vision, line of sight, and field of view&lt;br /&gt;Part 09: wandering monsters&lt;br /&gt;Part 10: items, inventory, inventory screens&lt;br /&gt;Part 11: hunger and food&lt;br /&gt;Part 12: weapons and armor&lt;br /&gt;Part 13: aggressive monsters&lt;br /&gt;Part 14: experience and leveling up&lt;br /&gt;Part 15: help, examine, and look screens&lt;br /&gt;Part 16: throwing and ranged weapons&lt;br /&gt;Part 17: smarter monsters&lt;br /&gt;Part 18: potions and effects&lt;br /&gt;Part 19: mana, spells, and magic books&lt;br /&gt;Part 20: item appearance and identification&lt;br /&gt;&lt;br /&gt;Some experience with Java and object oriented programming is expected but I can answer any questions that you may have. Not only am I including the code for each part, but I've got a final version, with a few additional tweaks, &lt;a href="http://trystans.blogspot.com/2011/08/nameless-roguelike-for-tutorial.html"&gt;running as an applet on my blog&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-7348179585875541296?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/7348179585875541296/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/08/roguelike-tutorial-what-and-why.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/7348179585875541296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/7348179585875541296'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/08/roguelike-tutorial-what-and-why.html' title='roguelike tutorial: the what and why'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-5465166919058994200</id><published>2011-08-13T17:27:00.000-07:00</published><updated>2011-08-17T09:19:32.971-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='roguelike'/><title type='text'>Nameless roguelike for tutorial</title><content type='html'>Here's a little roguelike I made over a few days. It was mostly for my own amusement and to try a few different programming things so it doesn't have anything new or great. I also &lt;a href="http://trystans.blogspot.com/2011/08/roguelike-tutorial-what-and-why.html"&gt;created a tutorial&lt;/a&gt; about how I wrote it. This applet can be downloaded and run from the desktop too.&lt;br /&gt;&lt;br /&gt;&lt;applet archive="rltutfinal.jar,asciiPanel.jar" code="rltut.AppletMain.class" codebase="https://sites.google.com/site/trystansprojects/" height="390" id="app1" name="app1" width="730"&gt;&lt;br /&gt;&lt;/applet&gt;&lt;br /&gt;&lt;br /&gt;Standard story: descend the Caves Of Slight Danger, find the lost Teddy Bear, and return to the surface to win. Use what you find to avoid dying. Press [?] for help.&lt;br /&gt;&lt;br /&gt;main jar file: &lt;a href="https://sites.google.com/site/trystansprojects/rltutfinal.jar"&gt;https://sites.google.com/site/trystansprojects/rltutfinal.jar&lt;/a&gt;&lt;br /&gt;display jar file: &lt;a href="https://sites.google.com/site/trystansprojects/asciiPanel.jar"&gt;https://sites.google.com/site/trystansprojects/asciiPanel.jar&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;[edit]&lt;/b&gt;Some people were having problems with it not displaying correctly. I think I've fixed it but let me know if there are problems.&lt;b&gt;[/edit]&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-5465166919058994200?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/5465166919058994200/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/08/nameless-roguelike-for-tutorial.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5465166919058994200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5465166919058994200'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/08/nameless-roguelike-for-tutorial.html' title='Nameless roguelike for tutorial'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-7906115898780673584</id><published>2011-08-06T18:50:00.000-07:00</published><updated>2011-08-13T17:10:15.510-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unit tests'/><title type='text'>Living with observational tests</title><content type='html'>I'd say that at least half of the unit tests I look at end up having a ton of extra work and extra assertions. It's always really frustrated me but I think some developers just find this style of tests to be more helpful. I think some people view tests as a central claim with proof and others view it as a record of observations.&lt;br /&gt;&lt;br /&gt;I view unit tests like a math proof or logical argument. I have a claim (also known as an &lt;em&gt;assertion&lt;/em&gt;) and I have the simplest proof of that claim I can think of. If my claim is that new bank accounts start with a zero balance then my test will mention the claim in the title and assertion. Maybe something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;[Test]&lt;br /&gt;public void NewBankAccountShouldStartWithAZeroBalance()&lt;br /&gt;{&lt;br /&gt;    BankAccount newBankAccount = new BankAccount();&lt;br /&gt;    Assert.AreEqual(0, newBankAccount.Balance, "A new bank account should start with a zero balance.");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;My recent insight is that others seem to view tests more like a record of observations. Imagine walking through a park and writing what you see and hear along the way. "There's a dog. The dog is barking. There's a tree. There's a guy in a suit. The dog is still barking. There's a jogger." etc. Many of the unit test I see are like this, a walk through some scenario or object lifetime with observations made along the way:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;[Test]&lt;br /&gt;public void BankAccountTest()&lt;br /&gt;{&lt;br /&gt;    BankAccount account = new BankAccount();&lt;br /&gt;    Assert.IsTrue(account != null, "The account is not null");&lt;br /&gt;    Assert.IsTrue(account.Balance == 0, "The balance is zero."); &lt;br /&gt;    &lt;br /&gt;    account.DoSomething("ABC", 123);&lt;br /&gt;    Assert.IsTrue(account.Things.Count == 3, "The account has three things."); &lt;br /&gt;    Assert.IsTrue(account.Balance == 0, "The balance is zero."); &lt;br /&gt;&lt;br /&gt;    // 60 more lines of stuff somewhat related to bank accounts....&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;My frustrations came from realizing that these tests were written differently than what I prefer but not realizing that it's because there's a completely different way of looking at what a test is. The assertion messages are worded differently, the test is named differently, the assertions are different, the only similarity is that both styles are run from NUnit. These aren't unit tests in the strict meaning, they aren't integration tests, and they aren't really system tests; they're what I think of as observational tests &amp;mdash; just a bunch of observations. I couldn't figure out what the point of this kind of test was because there is no point to the test. The assertions seemed irrelevant or superfluous because the assertions aren't used as proof of a central claim, they're just things that the original developer noticed or had on his mind when writing the test. They don't say what something &lt;em&gt;should&lt;/em&gt; be or &lt;em&gt;why&lt;/em&gt; because they're just observations of how things &lt;em&gt;are&lt;/em&gt; &amp;mdash; there is no expectation or reasoning behind it &amp;mdash; just a bunch of observations.&lt;br /&gt;&lt;br /&gt;I'm still not sure what to do with failing assertions on these observational tests. I don't want to delete the entire test because they provide some value to someone (well, actually yes I do want to delete them but I won't). These observational tests often fail but I've &lt;em&gt;never&lt;/em&gt; found a bug in the code from these; it's always been a bad test or a valid change in the code that broke the now invalid assertion. There's no point in trying to change the developers who like these tests since not only do they prefer these observational tests but I've already tried and failed. I think that if it doesn't look like an obvious bug then I should just update the assertion or delete it. That's what I've always ended up doing anyway but now I won't waste more than a couple minutes on it since it's not worth the frustration.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-7906115898780673584?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/7906115898780673584/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/08/living-with-observational-tests.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/7906115898780673584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/7906115898780673584'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/08/living-with-observational-tests.html' title='Living with observational tests'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-5763103623631637240</id><published>2011-08-05T22:39:00.000-07:00</published><updated>2011-08-05T22:39:38.144-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unit tests'/><category scheme='http://www.blogger.com/atom/ns#' term='writing'/><title type='text'>A unit test case study</title><content type='html'>I've been trying to pay more attention to my process for fixing broken unit tests and I'm trying to improve my writing skills, especially when it comes to descriptive narration, so I'm going to fix a test and write down my thoughts as I go. I'll change the business object names to Widgets and Things, but everything else is exactly what happened as it happened. Here goes.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;I ran some of our unit tests and the first one to fail has this error message:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;XXX.Test.YYY.Widgets.WidgetsTest:&lt;br /&gt;  Two things should be available.&lt;br /&gt;  Expected: True&lt;br /&gt;  But was:  False&lt;br /&gt;&lt;/pre&gt;Uh, ok. No indication of which two things were expected or which two things were found. Not a very useful error message. The test name doesn't help me much since the Widgets domain encompasses so much. The Widgets code does use Things though so that part makes sense.&lt;br /&gt;&lt;br /&gt;I'm guessing the requirements and code changed so instead of two Things available there are now more. This is what happens with at least 95% of the broken tests I look at. I've gotten all I can from the test name and assertion message so it's time to take a look at the test itself.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public void WidgetsTest()&lt;br /&gt;{&lt;br /&gt;    // 44 lines of code&lt;br /&gt;    Assert.IsTrue(thingList.Count == 2, "Two things should be available.");&lt;br /&gt;    // 17 more lines of code&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Ugh. I'm not even going to try to wrap my head around this yet. Some people are really good at reading through long methods where state winds in and out of methods but I'm not. I'll start with a small change and use AreEqual instead of IsTrue so the failure message indicates what was expected and what was found.&lt;br /&gt;&lt;pre class="brush: csharp"&gt;public void WidgetsTest()&lt;br /&gt;{&lt;br /&gt;    // 44 lines of code&lt;br /&gt;    Assert.AreEqual(2, thingList.Count, "Two things should be available.");&lt;br /&gt;    // 17 more lines of code&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;At least now I can see how many were available. It would be better if I know which two Things were expected so I could assert that only ThingA and ThingB are available. Even better than that would be knowing why so I could add that to the message, something like "ThingA and ThingB should be available if Widget.IsAwesome()". That way someone could see it and say "yup, there's a bug" or "no that's not true anymore". At least that would work better for me but I remind myself that I can't really fault others for writing tests in the style they prefer to work with.&lt;br /&gt;&lt;br /&gt;Now I wait nearly five minutes while the tests rebuild then wait about a minute for the test to fail again. New error message:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;XXX.Test.YYY.Widgets.WidgetsTest:&lt;br /&gt;  Two things should be available.&lt;br /&gt;  Expected: 2&lt;br /&gt;  But was:  3&lt;br /&gt;&lt;/pre&gt;That's what I expected. I'm pretty sure no one updated the test when they changed the code. Had it said 0 or 500 or something interesting like that then it might be a bug but I'd say there's now a 99% chance it's just an invalid assertion. There is a reference in the test to an issue number so I look that up....&lt;br /&gt;&lt;br /&gt;WTF?&amp;nbsp;This issue&amp;nbsp;has absolutely nothing to do with with Things or Widgets! Maybe it's a typo? Maybe it's a bad April Fools joke? Maybe the test was copied and pasted and this reference was left in? I've noticed that's common; I guess people copy the entire thing and leave in a bunch of irrelevant code and assertions that are no longer needed. I search the tests for the original assertion and see that there's one other test with the same line of code. Not only is almost the entire test is the same as the one that's failing but this one is also older so it's almost certainly a copy-and-paste job. This original test does have a comment with an issue number but when I look that up I find the issue has nothing to do with Widgets or Things either. Maybe I can check source control to see when these tests were made and who made them?&lt;br /&gt;&lt;br /&gt;The original developer of both tests is long gone so I can't ask him what this is really about. Another developer who's focus is a different domain has made changes to the failing test eight months ago. He probably won't remember but I'll ask him later if I run into a dead end.&lt;br /&gt;&lt;br /&gt;I try looking at our continuous integration logs to see when the test started failing but no luck: our logs only go back a couple months and this has been broken the whole time. *sigh* We've always got broken tests but this one has actually been broken since the beginning of recorded history. Looks like I'll have to dive into the test code and see what it's doing. &lt;br /&gt;&lt;br /&gt;The test starts by dropping the database then reloading a backup (I've been here 5 or so years and brought this up before and I still don't know why so many of our tests do this), creates a handful of objects while doing a ton of work with them, reading and writing to the file system and database through it all. It even clears some caches along the way; I've always wondered if so many of other peoples tests do this because of superstition or to make sure we do as much work as possible. There are several comments but they just say the same thing as the code following it with no indication of why any of it is being done. This test seems to be running through some very specific scenario involving Widgets from beginning to end. There's a lot of data and I'm not sure what's relevant and what just happened to be that way. There are 14 scattered assertions, not including the assertions buried within other methods, on seemingly random objects and values. A lot of this work is somewhat related to Widgets but this is the only line related to Things. In fact, only one of the assertions is directly related to Widgets.&lt;br /&gt;&lt;br /&gt;Time to recap: I've got a failing assertion in a large test filled with seemingly arbitrary assertions and a lot of work. The test name says nothing about what is expected or why. This failing assertion says nothing about what is expected or why. The issue it refers to is completely unrelated to Things or Widgets and the original developer has moved on to another job. I have no idea when it started failing or why and what it's asserting on doesn't even seem to be relevant to the other work happening in the test. This is all quite normal for our tests. Luckily it's the end of the day so I send an email to the other developer who worked on this eight months ago and leave this until tomorrow.&lt;br /&gt;&lt;br /&gt;Here's the email:&lt;br /&gt;&lt;div style="-moz-border-radius: 5px; -webkit-border-radius: 5px; border-bottom: grey 1px solid; border-left: grey 1px solid; border-radius: 5px; border-right: grey 1px solid; border-top: grey 1px solid; margin: 0.5em; padding-bottom: 0.5em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.5em;"&gt;Unit test XXX.Test.YYY.Widgets.WidgetsTest has a failing assertion on line 2199. The message is "Two Things should be available.". It looks like this test is from you and Mr Doesn't Work Here Anymore. Are there two specific Things or are any two Things acceptable? Why are those two Things expected and why are three Things a problem?&lt;/div&gt;&lt;br /&gt;Hopefully he has some answers.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;When I get to work the next day there's no response from the developer I emailed. I move on to some other tasks until I can ask him or someone else.&lt;br /&gt;&lt;br /&gt;Eventually another developer is available to help me with this one and we walk through almost the same steps that I did. It turns out that if you look at the beginning of the test a certain way and you look at the issue a certain way, then the test looks like it's related to something implied by part of the issue. The weird part is the test has only one assertion related to that and that assertion is the exact opposite of what the issue says.&lt;br /&gt;&lt;br /&gt;After about an hour of rereading it and commenting out chunks of the test to see what happens we figure that the last half of the test is completely unnecessary and we delete it — including the failing assertion. Now the test passes and I can move on to the next test.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-5763103623631637240?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/5763103623631637240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/08/unit-test-case-study.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5763103623631637240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5763103623631637240'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/08/unit-test-case-study.html' title='A unit test case study'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8915706026329265679</id><published>2011-07-29T08:00:00.000-07:00</published><updated>2011-07-29T08:00:05.588-07:00</updated><title type='text'>git pre-commit hook and hspec</title><content type='html'>While working on some hspec stuff, I decided to try out git's pre-commit hook and see if I can make it refuse to commit unless the unit tests pass. It was easy. Here's what I came up with:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;echo "Running specs.hs"&lt;br /&gt;exec runhaskell hspec.hs --format=progress --specs specs.hs&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Where specs.hs are the file with the specs for the hspec library and the --specs option tells the hspec executable to run it's own specs.&lt;br /&gt;&lt;br /&gt;On my Mac I need to make the hook executable by running &lt;code&gt;chmod +x .git/hooks/pre-commit&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8915706026329265679?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8915706026329265679/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/07/git-pre-commit-hook-and-hspec.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8915706026329265679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8915706026329265679'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/07/git-pre-commit-hook-and-hspec.html' title='git pre-commit hook and hspec'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-5760201471608314094</id><published>2011-07-20T13:14:00.000-07:00</published><updated>2011-08-08T07:05:27.562-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roguelike'/><title type='text'>Some roguelike ideas</title><content type='html'>I've always got a few ideas for roguelikes in my mind so I figured I should write some down before I forget.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Allied chat&lt;/b&gt;. Instead of having some command list, telling allies what to do would involve typing out a command like you're talking to another player. If NPC's understand a few verbs (attack, go to, defend, follow, help, get, etc.), adjectives (fire, nearby, weak), and nouns (me, the skeleton, a sword, etc.) then you could give simple commands to specific allies, or whoever is in earshot of you.&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;follow me&lt;/li&gt;&lt;li&gt;Mr Wizzard: defend the temple&lt;/li&gt;&lt;li&gt;get better armor&lt;/li&gt;&lt;li&gt;give me your weapon&lt;/li&gt;&lt;li&gt;priests: help the king&lt;/li&gt;&lt;li&gt;use frost weapons on fire imps&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Allies could be made even smarter by having default ones or keeping a history of certain commands and the most recent ones take precedence. So if you tell your allies "defend villagers" then "attack archers", they will attack archers if there are any visible, otherwise they will defend any visible villagers. Or that may be more frustration than it's worth; I don't know.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Random monsters&lt;/b&gt;. Each game has several species of monsters that have random attributes for each game. Identifying what their strengths and weaknesses are is part of the fun, just like with potions and scrolls. So in one game Forest Monsters can fly, are immune to poison, and have strong armor; in the next game they leave poisoned corpses, regenerate quickly, and can summon insect swarms. Imagine if none of the random monsters leave corpses or if they're all resistant to magic.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Nomic&lt;/b&gt;. A &lt;a href="http://en.wikipedia.org/wiki/Nomic"&gt;nomic&lt;/a&gt; is a type of game that is played by changing the rules of the game. I've seen some very strange things happen so it would need to be limited and simplified for a computer game. Imagine if a roguelike let you vote on new rules from time to time. Maybe you could make using magic a crime, grant money to players for each rat they kill, create a fee for those who want to use large weapons, or even crown yourself emperor. Influencing the other voters could become a game in itself.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-5760201471608314094?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/5760201471608314094/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/07/some-roguelike-ideas.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5760201471608314094'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/5760201471608314094'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/07/some-roguelike-ideas.html' title='Some roguelike ideas'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-4985712439078534568</id><published>2011-06-22T15:21:00.000-07:00</published><updated>2011-06-22T15:21:48.381-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='play'/><category scheme='http://www.blogger.com/atom/ns#' term='latin'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>ANNOUNCING www.LatinLiteracy.com</title><content type='html'>Using the Java &lt;a href="http://www.playframework.org/"&gt;Play Framework&lt;/a&gt;&amp;nbsp;and hosting on &lt;a href="http://www.playapps.net/"&gt;www.playapps.net&lt;/a&gt; was so easy that I went ahead and bought my own domain:&amp;nbsp;&lt;a href="http://www.LatinLiteracy.com/"&gt;www.LatinLiteracy.com&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It's just a small start for now, but as I learn more Latin I will continue to add to it. I hope that, in time, it will help me and others learn to read and write Latin.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-4985712439078534568?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/4985712439078534568/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/06/announcing-wwwlatinliteracycom.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/4985712439078534568'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/4985712439078534568'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/06/announcing-wwwlatinliteracycom.html' title='ANNOUNCING www.LatinLiteracy.com'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-9073903097110504245</id><published>2011-06-17T13:22:00.000-07:00</published><updated>2011-06-17T13:22:27.197-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='play'/><category scheme='http://www.blogger.com/atom/ns#' term='latin'/><category scheme='http://www.blogger.com/atom/ns#' term='project'/><title type='text'>Play framework</title><content type='html'>I took a look at the &lt;a href="http://www.playframework.org/"&gt;Play Framework&lt;/a&gt; for java web apps and while poking around and trying this and that, I ended up implementing most of my Latin app. I wasn't really trying to, I was just testing how to do certain things and before I knew it, I had a working web app. So far, it's been really easy to work with and really fun.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-9073903097110504245?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/9073903097110504245/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/06/play-framework.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/9073903097110504245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/9073903097110504245'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/06/play-framework.html' title='Play framework'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8979425774996543462</id><published>2011-06-10T11:09:00.000-07:00</published><updated>2011-06-13T08:30:29.408-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unit tests'/><title type='text'>No longer fit</title><content type='html'>I recently added long vowels to my latin project. When spoken, long vowels are held a little longer than regular ones and are indicated by putting a little bar, called a &lt;a href="http://en.wikipedia.org/wiki/Macron"&gt;macron&lt;/a&gt;, above the vowel: ā, ē, ī, ō, or ū. Java can handle these unicode characters if you specify "&lt;span class="Apple-style-span" style="font-family: &amp;quot;Courier New&amp;quot;, &amp;quot;Courier&amp;quot;, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: x-small;"&gt;-encoding utf8&lt;/span&gt;&lt;/span&gt;" when compiling but apparently fit can not; so any fit tests that were comparing strings that had macrons were failing. I tried changing my code to convert long vowels into regular vowels when comparing but that was messy and didn't always work right since long and regular vowels are different phonemes. Eventually I decided to do away with fit and switch to &lt;a href="http://www.junit.org/"&gt;JUnit&lt;/a&gt;. I lost at least two programming sessions to fiddling with fit, working around fit's limits, and finally reimplementing the tests in JUnit.&lt;br /&gt;&lt;br /&gt;So even though I was really looking forward to using fit and I think it could solve many problems I've seen with the *Unit frameworks, for me, at least this time, the costs of using fit vastly outweighed the supposed benefits.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8979425774996543462?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8979425774996543462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/06/no-longer-fit.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8979425774996543462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8979425774996543462'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/06/no-longer-fit.html' title='No longer fit'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-2489798836687759281</id><published>2011-06-08T00:13:00.000-07:00</published><updated>2011-06-13T08:30:19.813-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unit tests'/><title type='text'>Finally fit</title><content type='html'>I'm finally using Fit. I've been interested in it for a long while but I've never had a side project where I really though it would be useful, until lately.&lt;br /&gt;&lt;br /&gt;One of my new years resolutions is to learn Latin. And, as usual, while learning that I began thinking about ways to apply it to programming such as an Android app to help someone learn and review Latin. Latin is like everything else invented by people: it's got complicated rules with many exceptions and special cases; perfect fit for TTD and Fit in particular.&lt;br /&gt;&lt;br /&gt;I don't have tests around the android specific stuff like moving between actions and handling the gui and persistence, just around conjugating, declining, and translating Latin. Although it's been very helpful it has slowed large scaled refactoring.&amp;nbsp;As I learn more about Latin&amp;nbsp;I have to make major changes to how words and phrases are implemented and then I have to rewrite all the tests so they pass.&lt;br /&gt;&lt;br /&gt;It feels helpful but I'll have to wait and see if maintaining all these tests really is worth the extra time and effort.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-2489798836687759281?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/2489798836687759281/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/06/finally-fit.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2489798836687759281'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/2489798836687759281'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/06/finally-fit.html' title='Finally fit'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-3536680516151303379</id><published>2011-06-02T20:19:00.000-07:00</published><updated>2011-06-14T07:59:02.743-07:00</updated><title type='text'>Does work encourage bad habits?</title><content type='html'>&lt;blockquote&gt;"these virtues are formed in man by his doing the actions" -- Aristotle, Nicomachean Ethics&lt;/blockquote&gt;Sometimes I wonder about programming as a job. Does having to add new functionality as fast as possible change how I act when programming for fun? Does having to follow the patterns and decisions others have laid out before me close my mind to better ideas when I'm working on my own projects? At home, I can polish a rough working prototype into something works well and that I'm proud of. At work, I often struggle with what's already there to do even the simplest things and I rarely want others to know I had a hand in the final result.&lt;br /&gt;&lt;br /&gt;I'm able to use TTD, BDD, constructor injection, profiling, and all kinds of other techniques on my side projects but for some reason, trying those things at work brings only frustration. They don't work with the frameworks we use, the processes we use, other people don't like them, we don't have time to do it that way, or I'd have to rewrite large sections of code just to get it to work. I work with a lot of smart people and, over the last 6 or 7 years, they've created a product that deals with an amazing amount of externally imposed complexity, but the best advice of the best developers rarely helps. My best attempts almost always fall far short and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;if&lt;/span&gt; statements and hard coding rule the day; I consider it a good day if I'm able to use polymorphism instead of scattered &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;if&lt;/span&gt; statements.&lt;br /&gt;&lt;br /&gt;One small example: I recently noticed my git commits are too large and include too many changes - just like at work. It's like that at work because of the source control tool and our processes for dealing with it encourage few large commits rather than many small ones. It's very frustrating to try and determine what changed in a file, finding that 60 lines changed, then finding that 50 of those lines were just formatting, and the other 10 lines were for several different reasons. Small and focused commits can prevent this, but if you're not in the habit of doing that, then you forget about it even when you have the chance to. I'm beginning to wonder how much of what I do is due to habits that serve me well enough at work but that I don't want to carry into my personal projects.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-3536680516151303379?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/3536680516151303379/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/06/does-work-encourage-bad-habits.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3536680516151303379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/3536680516151303379'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/06/does-work-encourage-bad-habits.html' title='Does work encourage bad habits?'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-6716744224950420468</id><published>2011-05-27T16:58:00.000-07:00</published><updated>2011-06-13T08:29:57.640-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code'/><title type='text'>Interesting metric</title><content type='html'>Recently I did a search through some code for the expression "? &lt;span style="color: blue;"&gt;true&lt;/span&gt;&amp;nbsp;:&amp;nbsp;&lt;span style="color: blue;"&gt;false&lt;/span&gt;". Nearly 20 matches from several different developers. At least it's not breaking anything.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-6716744224950420468?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/6716744224950420468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/05/interesting-metric.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6716744224950420468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/6716744224950420468'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/05/interesting-metric.html' title='Interesting metric'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-8631136772680668646</id><published>2011-05-20T08:23:00.000-07:00</published><updated>2011-05-20T08:23:39.457-07:00</updated><title type='text'>Thank you Michael Feathers</title><content type='html'>Michael Feathers's book &lt;a href="http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052"&gt;Working Effectively With Legacy Code&lt;/a&gt; is probably the computer book I get the most use out of. There's a lot of books out there that will tell you the principles of good design but this is the very best book for the more tactical stuff, i.e. how to work with the messy code you're given. This book is full of real word scenarios and practical advice and you can tell it's written by a battle-scarred veteran. Because of Working Effectively, I've been able to improve the design of some of the biggest, messiest, and most convoluted static classes I've run into. When I think of which programing book has been the most useful and mind-expanding for me, this one is tied with the &lt;a href="http://mitpress.mit.edu/sicp/full-text/book/book.html"&gt;Structure and Interpretation of Computer Programs&lt;/a&gt; and anyone who programs should read both.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9121642240025958832-8631136772680668646?l=trystans.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trystans.blogspot.com/feeds/8631136772680668646/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trystans.blogspot.com/2011/05/thank-you-michael-feathers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8631136772680668646'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9121642240025958832/posts/default/8631136772680668646'/><link rel='alternate' type='text/html' href='http://trystans.blogspot.com/2011/05/thank-you-michael-feathers.html' title='Thank you Michael Feathers'/><author><name>Trystan</name><uri>http://www.blogger.com/profile/15653418292042541807</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9121642240025958832.post-3312050914082425363</id><published>2011-03-20T17:20:00.000-07:00</published><updated>2011-06-13T08:29:23.045-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='project'/><category scheme='http://www.blogger.com/atom/ns#' term='roguelike'/><title type='text'>Postmortem for Twelve Hours</title><content type='html'>The 2011 7DRL challenge has come and gone and although I finished my entry Twelve Hours, it wasn't as fun or interesting as I expected. I learned some stuff about programming and design as well as game mechanics and gameplay - and I get to write a &lt;a href="http://www.codinghorror.com/blog/2006/11/the-project-postmortem.html"&gt;postmortem&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;I knew from the beginning I wanted you to be one of many heroes but I wasn't sure if you'd be on the same team or rivals or what the story would be. It took a few days to come up with the 'defend the village' story and until then there was a lot of changing my mind and trying little changes and general aimlessness. The final game still feels unfocused and I believe much of that is because I was unsure what the story would be for the first %50 of the time. The survival theme also wasn't as fun as I expected. Once while playing Star Craft I remember a heart pounding, grueling, gut wrenching level that ended with me nervously watching the timer count down as the Zerg swarmed my Terran base, demolishing my major buildings and cutting through my inner defense, rushing toward my heroes as my last marines fell to give me the few seconds I needed to win. Playing Twelve hours should have been more like that, or any decent zombie movie right before the good guys bust in and save everyone, but it ended up with me either relaxing in the town center while the villagers fend off ghosts or with me trying to keep up with the fighter chasing down a few stray skeletons. I tried to emphasize the heroes and working together to save the weaker ones by de-emphasizing the stats and inventory options but I ended up with a game where those aspects just feel weak and only partially implemented. &lt;br /&gt;&lt;br /&gt;As far as technical learning, this was the first time I used the A* algorithm (I had always used Dijkstra maps) and even though there's a lot of hard-coded junk in there, it went well and I can add that to my toolbox. Creating separate Creature and CreatureController classes was, I supp
