<?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-9081655444803149702</id><updated>2011-04-21T20:33:39.810-04:00</updated><category term='rakefile'/><category term='contest'/><category term='xml'/><category term='nagafimo'/><category term='intro'/><category term='continuation'/><category term='tutorial'/><category term='vm'/><category term='xslt'/><category term='erb'/><category term='antipattern'/><category term='radar'/><category term='yaml'/><category term='regex'/><category term='branches'/><category term='lore'/><category term='comingstorm'/><category term='ptotd'/><category term='mass distribution'/><category term='rubygem'/><category term='text'/><category term='smalltalksolutions'/><category term='git'/><category term='polish'/><category term='missions'/><category term='market'/><category term='templating'/><category term='search'/><category term='windows'/><category term='sitdown'/><category term='port'/><category term='progress'/><category term='profiling'/><category term='svn'/><category term='deploy'/><title type='text'>Kuiper DevBlog</title><subtitle type='html'>A Ruby programmer's foray into game programming with Rubygame.  Except, given that it's a huge project, it's more a multi-month expedition.  Will he return alive?</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>70</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-3503555302558838297</id><published>2009-01-01T14:43:00.003-05:00</published><updated>2009-01-01T14:57:28.334-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='progress'/><title type='text'>Year 1</title><content type='html'>Today is the first day of the new year.&lt;br /&gt;&lt;br /&gt;If you're browsing any of the open-source game websites out there, you've no doubt noticed that my game is not on them.  Nor is it just pending; sadly, I missed my goal for last year, which was to actually finish this game.&lt;br /&gt;&lt;br /&gt;For a recap of what happened, see my &lt;a href="http://kuidev.blogspot.com/2008/11/day-323-game-over.html"&gt;postmortem&lt;/a&gt; - nothing's changed since then, except for the "this is doable" part.  Obviously I missed that one :)&lt;br /&gt;&lt;br /&gt;Still, the stats on the right show the story:  As of right now 327 hours and over 27,000 lines of code.  What I didn't really grasp when I started this last year was the fact that this game is &lt;i&gt;huge&lt;/i&gt;!  November really brought that into light, considering that given the effort I put in during just one month I could have finished an entire novel.&lt;br /&gt;&lt;br /&gt;Where next?  I'm not sure.  The game is still fairly near to completion, so I'm confident I'll actually get it out this year given a similar amount of work.  The question is:  Do I want to put in that much work?  As far as I see it, here's my options:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Keep working on the game&lt;/b&gt;, an hour a day, until it's done.  This is the option most likely to actually get something done, at the risk of burning myself out.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Work on a different game&lt;/b&gt;: "All Kuiper, All the Time" gets very tiresome after a while.  Especially over the last month, just contemplating working on the game was an unpleasant experience.  Doing something completely different might help.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Do something completely different&lt;/b&gt;:  Go back to writing novels and gaming, programming on the side.  Essentially this is the eclectic blend of stuff I was doing before I sat down and made a schedule for myself.  The upside being that it's far more relaxing, the downside being that nothing ever actually gets done.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Obviously, I'm leaning toward the second option, or maybe even the first with a somewhat relaxed schedule.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-3503555302558838297?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/3503555302558838297/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=3503555302558838297' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3503555302558838297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3503555302558838297'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2009/01/year-1.html' title='Year 1'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-3144543690677649630</id><published>2008-12-09T18:38:00.003-05:00</published><updated>2008-12-09T19:00:30.136-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><title type='text'>Day 332: Hindsight</title><content type='html'>In retrospect, it would have been a good idea to make the "have you completed this mission" system a little more unit-test friendly.  More on that in a moment.&lt;br /&gt;&lt;br /&gt;Every fleet the game is going to have for part I is done.  You may have noticed how I keep talking about parts of the game and whatnot.  Why is this?  Well, the plot's in three parts.  How will the player know?  That's the other major ticket I completed today:  Banner states.  Simply put, this scrolls things (probably words) upward so the player can read them, so I can now create all the "PART II: The Revenge" scrolling goodness I want.  I've already implemented the game's credits screen this way.&lt;br /&gt;&lt;br /&gt;I also started making the random missions for my game.  In any coding project, I'm always a bit wary of returning to some functionality I haven't used in a while, for fear it'd broken somehow in the meanwhile.  As superstitious as this sounds, it happened to be the truth this time around:  My junk was broke.  Some of this was changes I'd made in the meantime, and some of it was the fact that parts never worked correctly in the first place.&lt;br /&gt;&lt;br /&gt;For instance, random missions.  My testing of this involved generating one and then flying around and making sure it worked.  That was fine.  What I neglected to do Way Back When I first made the functionality was test it any further.  It turns out, when a random mission generator makes more than one mission, it tacked on the conditions for &lt;b&gt;every other mission&lt;/b&gt; it generates.  Oops.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-3144543690677649630?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/3144543690677649630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=3144543690677649630' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3144543690677649630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3144543690677649630'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/12/day-332-hindsight.html' title='Day 332: Hindsight'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1509010305457168168</id><published>2008-12-02T19:39:00.003-05:00</published><updated>2008-12-02T19:53:47.287-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 325: Label Randomly</title><content type='html'>Yesterday I took a well-deserved rest.&lt;br /&gt;&lt;br /&gt;Today I figured I'd take an easy ticket, the one whereby I make random missions accept labels.  As though welcoming me back to coding my game, this was far more difficult than I'd wanted it to be.&lt;br /&gt;&lt;br /&gt;The first step was to adapt the KuiAtCondition to also respond to labels.  As it is, you give it a list of destinations (either planets, organizations, or sectors) and if the player is there, it passes.  It seemed simple to extend this for labels....&lt;br /&gt;&lt;br /&gt;It's never easy.&lt;br /&gt;&lt;br /&gt;For one, only the root KuiObject knows how to handle labels, and I'd hardcoded that in.  My choices were:  Refactor labeling out to a more generic module, or copy and paste the code.  For once, I did the responsible thing and refactored.  It took an hour!&lt;br /&gt;&lt;br /&gt;The refactoring turned out to be worthwhile, as it happened.  I needed to do similar label handling in at least two other places later, so as usual, it paid for itself.&lt;br /&gt;&lt;br /&gt;Still, it's never easy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1509010305457168168?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1509010305457168168/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1509010305457168168' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1509010305457168168'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1509010305457168168'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/12/day-325-label-randomly.html' title='Day 325: Label Randomly'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-7793059805018778399</id><published>2008-11-30T19:19:00.003-05:00</published><updated>2008-11-30T20:20:47.060-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='nagafimo'/><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><title type='text'>Day 323: Game Over</title><content type='html'>Today marks the last day of National Game Finishing Month.  I closed five tickets today, mostly content-related.  Every ship that's in part1 of the game is now created.  Every weapon that's in the game entirely is done.  Even helpful standard addons have been made.&lt;br /&gt;&lt;br /&gt;In conclusion, of the list I made yesterday, half of the items are now done.  So now, the NaGaFiMo postmortem!&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;What went right&lt;/b&gt;&lt;br /&gt;  &lt;ol&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;Effort&lt;/b&gt;  The spirit of this exercise was to devote my Nanowrimo time to the game.  I've done more than that - as of today, I'm nearly 4.5 hours ahead of schedule.  If it were a novel, it'd be done by now.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;Learning&lt;/b&gt;  While I won't say that I haven't learned anything from the rest of the programming I've done, I've definitely learned more this month.  Specifically, I had to create new art via &lt;a href="http://www.blender.org"&gt;Blender&lt;/a&gt;, which I knew how to do long ago but have since forgotten.  Making art is definitely out of my comfort zone, but I learned quite a bit.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;&lt;a href="http://t-a-w.blogspot.com/2007/02/atomic-coding-with-subversion.html"&gt;Atomic Coding&lt;/a&gt;&lt;/b&gt; The linked article talks about the practice with Subversion, but I've found it even more effective using git.  Having a local repository lets you commit nearly everything once you've made the smallest workable change, yet not embarrass yourself by pushing out to the remote (public!) repository.&lt;/li&gt;&lt;br /&gt;  &lt;/ol&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;What Went Wrong&lt;/b&gt;&lt;br /&gt;  &lt;ol&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;It's Not Done!&lt;/b&gt;  The goal of this was to actually have the game finished by now.  That didn't happen, but given that I actually put in all the work, I count this as a failure of estimation rather than of effort.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;Learning&lt;/b&gt;  Yes, that was above on the 'what went right' portion.  The downside to learning how to use a tool like Blender is that I had to take time away from the rest of the project in order to do it.  If I'd already known blender or already done all this, I'd have ended up a lot closer to the original goal.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;Going Dark&lt;/b&gt;  Though I updated the blog when I was done with major features, I didn't update daily.  As I've found the blog to be useful in the past, I'd like to keep it up.&lt;/li&gt;&lt;br /&gt;  &lt;/ol&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;What I learned&lt;/b&gt;&lt;br /&gt;  &lt;ol&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;This is Doable&lt;/b&gt;  This particular schedule works for me.  I initially thought 1.5 hours a day might be somewhat difficult, but though not easy, it is at least workable.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;Get it Done&lt;/b&gt;  Rather than putting off seemingly unrelated work until it absolutely must be done, it's probably better to learn these things when it won't interfere with big goals&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;People Might Actually Play This&lt;/b&gt;  I've had a number of requests for the demo, which while being exceedingly out of date is still a reasonable facsimile of how the game plays.  My novels have had only a few token readers, with the vast majority of people who inquire not bothering to actually read them.  Everyone who asked for the demo has played it however, making me think that I may have better luck!&lt;/li&gt;&lt;br /&gt;  &lt;/ol&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-7793059805018778399?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/7793059805018778399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=7793059805018778399' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7793059805018778399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7793059805018778399'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/11/day-323-game-over.html' title='Day 323: Game Over'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-5808176013302575557</id><published>2008-11-29T21:30:00.002-05:00</published><updated>2008-11-29T21:44:32.894-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='nagafimo'/><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><title type='text'>Day 322: The End is Nigh</title><content type='html'>National Game Finishing Month is nearly over.  Tomorrow I plan to do a marathon of getting stuff into the system then blog resignedly about it.  Today, however, was all work.&lt;br /&gt;&lt;br /&gt;Specifically, I animated the rest of the ships.  Then I created the standard ones in-game, and used Mass Distribution to push them out to every standard planet in the galaxy.&lt;br /&gt;&lt;br /&gt;High profile things left to do:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Random Missions should use labels:&lt;/b&gt; The mission system in general could use an overhaul to use the labels, too.  It'd be handy for an "are we there yet" check to include "anywhere with this label" as a satisfying condition.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Fleets:&lt;/b&gt; Once I have the ships done, I need to put the patrols, pirates, traders, visitors, etc, in their place so they'll show up in the sectors they're supposed to.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Alliance/Rebel weaponry:&lt;/b&gt; The war-fighters should have better weapons than you as a regular pilot should get (but you can get them later, when you join)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Alliance Ships:&lt;/b&gt; The alliance has three ships specific to them.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Rebel Ships:&lt;/b&gt; Likewise, the rebellion also has ships just for themselves.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Standard Addons:&lt;/b&gt; I don't think I'll have faction-specific addons unless I really get inspired.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Random Missions:&lt;/b&gt; These are essentially the filler of the game.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Scientist Scout Missions:&lt;/b&gt; This is the essense of the plot for the entire game, right here.&lt;/li&gt;  &lt;br /&gt;&lt;li&gt;&lt;b&gt;Alliance/Rebel Scout Missions:&lt;/b&gt; The only thing close to a major sidequest, this is the initial alliance/rebel war.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-5808176013302575557?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/5808176013302575557/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=5808176013302575557' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5808176013302575557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5808176013302575557'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/11/day-322-end-is-nigh.html' title='Day 322: The End is Nigh'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-5630370106587302277</id><published>2008-11-26T21:05:00.003-05:00</published><updated>2008-11-26T21:35:40.807-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><title type='text'>Day 319: Blockified</title><content type='html'>Every single time I do graphics for anything, I have to re-teach myself &lt;a href="http://blender.org"&gt;Blender&lt;/a&gt;, which I'm actually somewhat decent at.  It turns out my wife has an eye for what would make spaceships look even better, so I've created quite a few.  I should now have every ship I need.&lt;br /&gt;&lt;br /&gt;I rendered all 36 frames of one of the ships before remembering that I had no way to change the 36 individual .png files into the one giant .png file that the engine could handle.  I've written this script easily twice before (once for TraderMissions, and once more for a &lt;a href="http://pyweek.org/"&gt;pyweek&lt;/a&gt; competition).  But I couldn't locate the first script, and the second one wasn't suited to my current engine.  So I had to, &lt;i&gt;once again&lt;/i&gt;, write the script.&lt;br /&gt;&lt;br /&gt;It was an easy task, for once.  blockify.rb is a pretty brittle utility without a lot of other uses, but it gets the job done and hopefully I won't have to re-write it next time I need it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-5630370106587302277?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/5630370106587302277/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=5630370106587302277' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5630370106587302277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5630370106587302277'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/11/day-319-blockified.html' title='Day 319: Blockified'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-7888683159899892320</id><published>2008-11-23T17:42:00.002-05:00</published><updated>2008-11-23T17:51:20.823-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><title type='text'>Day 316: Weapons of Ordinary Destruction</title><content type='html'>I fixed an important bug today:  In the old system, changing an object's tag after it had been created was a bad idea.  The game would simply crash upon reloading, if not sooner.  The new system exchanged this fatal bug for a more subtle one:  Re-naming caused duplicates to exist in the repository (the old name stuck around).&lt;br /&gt;&lt;br /&gt;This sounds trivial to fix - just see if there's an old tag and delete it.  However, objects which are duplicated (like, say, weapons) always have the old tag of the object they were created from.  So any new weapons were destroying the old instances.&lt;br /&gt;&lt;br /&gt;But can't I check to see if they're being duped?  Yes, I can - now.  Most of my coding work today was enabling exactly that.  So now you can change an object's tag, and unless you change it from object_thing to object_thing:4490, you're fine.&lt;br /&gt;&lt;br /&gt;Hint:  Don't do that.&lt;br /&gt;&lt;br /&gt;Finally, I created every weapon I wanted to be available to the player at the start of the game, and distributed them to all the planets where I wanted them to be available.&lt;br /&gt;&lt;br /&gt;Unfortunately, I used up all of the weapon art I had in TraderMissions, thus meaning I'm going to have to create new stuff soon.  Re-learning Blender, ahoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-7888683159899892320?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/7888683159899892320/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=7888683159899892320' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7888683159899892320'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7888683159899892320'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/11/day-316-weapons-of-ordinary-destruction.html' title='Day 316: Weapons of Ordinary Destruction'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1837851876046531174</id><published>2008-11-22T19:58:00.003-05:00</published><updated>2008-11-22T20:11:37.459-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><category scheme='http://www.blogger.com/atom/ns#' term='mass distribution'/><category scheme='http://www.blogger.com/atom/ns#' term='market'/><title type='text'>Day 315: How not to use Mass Distribution</title><content type='html'>The next big thing on my plate was to create the various cargos that the player could buy and sell.  This was aided by the fact that I'd already diagrammed out how much I wanted the low,high,and medium versions of those cargos to cost.&lt;br /&gt;&lt;br /&gt;I started with 'Food'.  It's cheap on Earth because of the lots of landmass.  So I created a cheap version of food, and moved on.&lt;br /&gt;&lt;br /&gt;It then occurred to me that this was going to take /forever/.  I'd have to create 3 states for each of the different cargos I wanted to sell (and as of now there are 6).  Also there'd be no consistency between prices.  A low 'food' price could be half the price of medium food, while low specimens might end up half a buck off.&lt;br /&gt;&lt;br /&gt;"Wouldn't it be nice", I thought, "To be able to automate this in the editor somehow?"&lt;br /&gt;&lt;br /&gt;Yes, yes it would.  And since I'm the guy who wrote the editor, I did it.  There are now 'auto low', 'auto medium', and 'auto high' buttons in the cargo editor, which set the values and names of the cargo to 15% lower, the same, or 15% higher than the base price, respectively.&lt;br /&gt;&lt;br /&gt;This would still require me to create all 18 cargos.  Wouldn't it be nice, I thought, if when you create a cargo blueprint it automatically created the low/med/hi versions of itself?&lt;br /&gt;&lt;br /&gt;Yes, yes it would.  And so again, I did it.&lt;br /&gt;&lt;br /&gt;Here's where my brilliant plan fell down.  The planets hadn't been tagged for specific cargo.  So while a number of planets shared the feature that their food was cheap, I hadn't tagged them to take advantage of that and use the MD system to do all of this at once.  I had two alternatives:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Go through all the planets and re-tag them&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Go through all the planets and just add all the cargo I wanted&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;As these were equivalently labor intensive, I did the second.  That's when I ran into another problem:  When the cargos are automatically created as outlined above, they're just tossed into the repository.  The repository stores everything in a gigantic dictionary, which of course means they're unsorted.  So I still end up looking at a list of 18 different cargo items, all in arbitrary order.&lt;br /&gt;&lt;br /&gt;Wouldn't it be nice, I thought, if those were sorted?&lt;br /&gt;&lt;br /&gt;Yes, yes it would be.  Done!&lt;br /&gt;&lt;br /&gt;I again reiterate that, as frustrated as I've been in the past by editors for other games, it's enormously liberating to actually be able to do something about it :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1837851876046531174?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1837851876046531174/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1837851876046531174' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1837851876046531174'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1837851876046531174'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/11/day-315-how-not-to-use-mass.html' title='Day 315: How not to use Mass Distribution'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-6425685335141227618</id><published>2008-11-16T15:19:00.003-05:00</published><updated>2008-11-16T15:33:32.920-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><category scheme='http://www.blogger.com/atom/ns#' term='mass distribution'/><title type='text'>Day 309: Mass Distribution</title><content type='html'>The Mass Distribution (or, as I shall refer to it from now on because I'm already tired of typing the whole thing, 'MD') system took me a few days, and then it was done.&lt;br /&gt;&lt;br /&gt;To recap:  The idea behind MD was that I could label objects (say, sectors) in a certain way, and then distribute objects (say, fleets) to aforementioned sectors.  So if I'd created a trading fleet, I could put it in every inhabited sector at once rather than one-by-one going through each sector and adding the fleet manually.&lt;br /&gt;&lt;br /&gt;The only thing this really required was some GUI work (the actual adding of the objects was, as it turns out, ridiculously easy), but it required something I hadn't created before:  Namely, a way to find any object in the entire universe.  Thus was born:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_pIj5Y9n3oFo/SSCCR-qNUmI/AAAAAAAAAto/XpN3unQS2Xo/s1600-h/omnichooser.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 370px; height: 302px;" src="http://3.bp.blogspot.com/_pIj5Y9n3oFo/SSCCR-qNUmI/AAAAAAAAAto/XpN3unQS2Xo/s400/omnichooser.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5269354809412899426" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;center&gt;&lt;b&gt;THE OMNICHOOSER&lt;/b&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;On the top-left, all the model classes in the game.  When selected, the results show every object of that type.  The top-right features every label in the game, and when it's chosen it'll show every object with any of the selected labels.  When both are active, it shows all objects with any of the selected labels of the given type.  For my example above, I picked "KuiFleet" and "traders" to get the trading fleet I'd just mentioned.  There's another one of these for the 'destination' objects (i.e. all the sectors in the universe) - there I chose "KuiSector" and "inhabited".  A few button clicks later and I'd placed 20-some-odd fleets.&lt;br /&gt;&lt;br /&gt;What this means is that I'm likely to return to creating actual content soon.  I haven't kept the blog updated because I've been doing the same thing every day ("Look, listboxes are broken!  Look, the OmniChooser is broken!  Look, both are broken at once!") but hopefully now the MD will speed everything up and I'll have more to say.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-6425685335141227618?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/6425685335141227618/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=6425685335141227618' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/6425685335141227618'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/6425685335141227618'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/11/day-309-mass-distribution.html' title='Day 309: Mass Distribution'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_pIj5Y9n3oFo/SSCCR-qNUmI/AAAAAAAAAto/XpN3unQS2Xo/s72-c/omnichooser.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-2607815935824216980</id><published>2008-11-09T15:14:00.004-05:00</published><updated>2008-11-09T15:33:40.138-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ptotd'/><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><category scheme='http://www.blogger.com/atom/ns#' term='xslt'/><title type='text'>Day 302: RTFS</title><content type='html'>I finished Phase II of the great XML change yesterday.  I would have posted then, but there was testing to be done to make sure it was Actually Fixed This Time, Dammit.  I'm discouraged by the number of bugs that this change uncovered in my code - that is, not bugs in the new code I wrote, but code I wrote months ago and has been working fine since.  This includes bugs of the "How did this ever work in the first place?" variety.&lt;br /&gt;&lt;br /&gt;But that's all fixed, at least until I discover the bugs in this code a few months from now.&lt;br /&gt;&lt;br /&gt;I've been trying to use XSLT as my patch tool of choice, and so far so good.  Only I found a bug where it wouldn't preserve linefeeds in attributes, which was important because a lot of my descriptions ended up being like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;sector name="foo" description="Name.&lt;br /&gt;&lt;br /&gt;  Here is some other stuff about this&lt;br /&gt;sector.  That linefeed's important&lt;br /&gt;because I don't want to go back and&lt;br /&gt;manually put linefeeds where they&lt;br /&gt;should have been."/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;No problem, I think, because I'm still somewhat new to the XSLT scene there's probably something I missed.  Hours of all the whitespace-preserving options I can find, and no luck.  Finally, I find the answer.&lt;br /&gt;&lt;br /&gt;The coding gurus of &lt;a href="http://www.stackoverflow.com"&gt;Stackoverflow&lt;/a&gt; were the lucky ones to inform me:  &lt;a href="http://stackoverflow.com/questions/260436/preserving-attribute-whitespace-in-xslt"&gt;It's not a bug&lt;/a&gt;.  Not preserving whitespace in the attributes is actually part of the &lt;a href="http://www.xml.com/axml/target.html#AVNormalize"&gt;XML Spec&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The only way I got away with it for as long as I did was because Ruby's XML parser was ignoring this fact.  So I wrote a ruby script to do the transformation.  I felt somewhat relieved that it wasn't actually my fault things weren't working out, there was a bug in REXML.  Then I realized that, if I hadn't broken the spec to begin with, I wouldn't be in this mess.&lt;br /&gt;&lt;br /&gt;Thus the &lt;b&gt;Programming Tip of the Day: Even when it's not your fault, it's probably your fault.&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-2607815935824216980?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/2607815935824216980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=2607815935824216980' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2607815935824216980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2607815935824216980'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/11/day-302-rtfs.html' title='Day 302: RTFS'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-4645227449151184368</id><published>2008-11-01T22:15:00.002-04:00</published><updated>2008-11-01T23:01:04.847-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='nagafimo'/><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><title type='text'>Day 294: NaGaFiMo</title><content type='html'>It's National Game Finishing Month!&lt;br /&gt;&lt;br /&gt;My next big part of the game to do is mess with the XML some more - specifically, making it look sane as I advocated a few entries ago.  But that would require me to muck with XML again and after the nightmare it was last time, I declined to do so.&lt;br /&gt;&lt;br /&gt;Instead, I closed out some old improvements I'd been meaning to do for a while:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Ownership:&lt;/b&gt; The player will now see what faction his/her target belongs to.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Fuel Regeneration:&lt;/b&gt; By default, fuel will now (extremely) slowly regenerate now.  So if you're stuck out in the middle of nowhere with no fuel, you can just wait.  It'd probably still be faster to save your game and change your ship's 'fuel' value in the XML, but now people less inclined toward cheating can continue to play.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Description Comments:&lt;/b&gt; Sometimes I want to remember something about a planet or sector, i.e. "This is the place where Act I ends".  There's no place for comments, so instead of taking the easy route and making a do-nothing 'comments' field, I made it so any string surrounded by /* ... */ would not appear if the game wasn't in edit mode.  Instant comments!&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I've got to get to that XML at some point, though....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-4645227449151184368?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/4645227449151184368/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=4645227449151184368' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4645227449151184368'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4645227449151184368'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/11/day-294-nagafimo.html' title='Day 294: NaGaFiMo'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1893125052540123321</id><published>2008-10-31T21:29:00.002-04:00</published><updated>2008-10-31T22:29:03.586-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='nagafimo'/><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><title type='text'>Day 293: No Nano</title><content type='html'>Lately I haven't been hewing to my 'one hour per day' quota.  At all.  Whatsoever.  And suddenly, hey, it's November and I'm looking at two months until my deadline for releasing this wacky thing.  November is, as I've alluded to before, &lt;a href="http://www.nanowrimo.org"&gt;National Novel Writing Month&lt;/a&gt;, and I've wanted to participate this year.&lt;br /&gt;&lt;br /&gt;THAT ISN'T HAPPENING!&lt;br /&gt;&lt;br /&gt;Instead, I'm dedicating my NaNo time to working on this game - an hour and a half per day, every day.  My goal is to actually finish everything by the end of the month, giving me a month to hand over the beta to people and watch as they rain bug reports upon my poor undefended soul.&lt;br /&gt;&lt;br /&gt;I started a day early - I began phase 1 of my XML overhauls quite a while back.  I was suffering from "arrow XML", which looked a lot like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;object&amp;gt;&lt;br /&gt;  &amp;lt;child name="foo"&amp;gt;&lt;br /&gt;    &amp;lt;other_object&amp;gt;&lt;br /&gt;      &amp;lt;child name="bar"&amp;gt;&lt;br /&gt;        &amp;lt;yet_another_object/&amp;gt;&lt;br /&gt;      &amp;lt;/child&amp;gt;&lt;br /&gt;    &amp;lt;/other_object&amp;gt;&lt;br /&gt;  &amp;lt;/child&amp;gt;&lt;br /&gt;&amp;lt;/object&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now imagine it nested about twenty levels deep.&lt;br /&gt;&lt;br /&gt;It seemed like it'd be fairly straightforward to write each object out one by one and then just refer to them by ID, but - as it turned out - it wasn't.  Changing the way my parser worked exposed some deep bugs in the system that I hadn't seen before, and I was forced to quash them!&lt;br /&gt;&lt;br /&gt;Now, however, it works.  Thus, National Game Finishing Month begins!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1893125052540123321?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1893125052540123321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1893125052540123321' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1893125052540123321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1893125052540123321'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/10/day-293-no-nano.html' title='Day 293: No Nano'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-8339480344147040552</id><published>2008-10-14T18:47:00.004-04:00</published><updated>2008-11-09T15:20:51.364-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><category scheme='http://www.blogger.com/atom/ns#' term='xslt'/><title type='text'>Day 276: Rebel Yell</title><content type='html'>Much like the last blog, much work was done this time around on the planets.  As you might deduce from the title, this time around I did all the rebels.  What this means is that I'm done with planets....&lt;br /&gt;&lt;br /&gt;Which pretty much means I'm done with normal content creation altogether.  I'm going to have to go ahead with my earlier mass-distribution system, which means lots of coding.  I'm not entirely sure how I feel about that.  On the one hand, it was always less tedious to code than make new content for the game.  On the other hand, making new content was a very casual thing, I could just sit down and work for a bit.  We'll see how it turns out.&lt;br /&gt;&lt;br /&gt;Other things include me re-thinking the XML I'm doing.  Here's what it looks like now:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;sector x=5 y=7 ...&amp;gt;&lt;br /&gt;  &amp;lt;child name="planets"&amp;gt;&lt;br /&gt;    &amp;lt;planet .../&amp;gt;&lt;br /&gt;  &amp;lt;/child&amp;gt;&lt;br /&gt;&amp;lt;/sector&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There's a lot of stuff being replaced by those ellipses.  And I mean a /lot/.  Multi-line text fields are the most common.  I'd like to merge them as their own elements.  So it'd be something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;sector tag="foo"&amp;gt;&lt;br /&gt;  &amp;lt;fields&amp;gt;&lt;br /&gt;    &amp;lt;x&amp;gt;5&amp;lt;/x&amp;gt;&lt;br /&gt;    &amp;lt;y&amp;gt;7&amp;lt;/y&amp;gt;&lt;br /&gt;  &amp;lt;/fields&amp;gt;&lt;br /&gt;  &amp;lt;children&amp;gt;&lt;br /&gt;    &amp;lt;planets&amp;gt;&lt;br /&gt;      &amp;lt;planet/&amp;gt;&lt;br /&gt;    &amp;lt;/planets&amp;gt;&lt;br /&gt;  &amp;lt;/children&amp;gt;&lt;br /&gt;&amp;lt;/sector&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's more verbose, but I also think it's an easier read.  At least, when I'm debugging, it's a pain in the ass to try to locate the one field I need in a sea of text.  This way they're at least on their own line.&lt;br /&gt;&lt;br /&gt;Converting between the two will be... interesting.  I think I'll start a data-format versioning system so new versions of the game will be able to understand old/differently formatted data, or at least reject it out of hand.  Then I'll use the magic of XSLT to change everything I've written so far from the old format to the new.&lt;br /&gt;&lt;br /&gt;I just learned XSLT recently, and I'm liking it.  It may end up being my patch tool of choice!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-8339480344147040552?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/8339480344147040552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=8339480344147040552' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8339480344147040552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8339480344147040552'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/10/day-276-rebel-yell.html' title='Day 276: Rebel Yell'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-8424894747314774411</id><published>2008-10-07T22:42:00.002-04:00</published><updated>2008-10-07T22:59:44.192-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><title type='text'>Day 269: Alliance</title><content type='html'>Oh, my lacking blogging/development schedule.&lt;br /&gt;&lt;br /&gt;Today I finished up every single sector of the Alliance by tagging and placing planets where they belong.  And very little - if any - code was written in the process.&lt;br /&gt;&lt;br /&gt;As evidenced by this rather short entry, I have a great deal less to say when I'm making content for the game then when I'm coding.&lt;br /&gt;&lt;br /&gt;Ah well.  Entry post go!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-8424894747314774411?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/8424894747314774411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=8424894747314774411' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8424894747314774411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8424894747314774411'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/10/day-269-alliance.html' title='Day 269: Alliance'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-7493966307505709976</id><published>2008-09-15T20:03:00.002-04:00</published><updated>2008-09-15T20:16:14.192-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><category scheme='http://www.blogger.com/atom/ns#' term='regex'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><title type='text'>Day 247: Express Yourself</title><content type='html'>As mentioned yesterday, in the Bad Old Days (i.e. those before today) you had to name something and then give it a descriptive tag which was often just the thing name re-branded.  Well those days are over!&lt;br /&gt;&lt;br /&gt;The 'auto' button now takes the name and creates a tag for it.  So:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Name: The Best Player in the World is from a City in an Arboretum&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Becomes:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Tag: player_best_player_world_city_arboretum&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;So the tag is based on the type of object, plus a scaled down version of the name.  As you no doubt noticed, I took out a bunch of connector words there (mainly articles).  This involved regular expressions.&lt;br /&gt;&lt;br /&gt;At first, I was doing something like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;name.gsub!(/\sthe\s/,' ')&lt;br /&gt;name.gsub!(/\sa\s/,' ')&lt;br /&gt;name.gsub!(/\san\s/,' ')&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Yes, I had one line for each thing I wanted to remove, which is of course silly.  So I made a list of all the excluded words and tried again, only I ran into a problem.  It's not a string I'm using for matching here, it's a regular expression - meaning I couldn't just throw the current loop value in the middle and expect it to work!&lt;br /&gt;&lt;br /&gt;Luckily, there's a way to do exactly that:&lt;br /&gt;&lt;code&gt; &lt;br /&gt;exclusions.each do |x|&lt;br /&gt;  excludeMiddle = Regexp.new("\s#{x}\s")&lt;br /&gt;  name.gsub!(excludeMiddle,' ')&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Regexp.new takes a string, from which it'll create a regular expression.  And I can do all the manipulating I want within strings!&lt;br /&gt;&lt;br /&gt;Also I made a few more planets using the new autotagging feature.  But that's not as exciting.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-7493966307505709976?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/7493966307505709976/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=7493966307505709976' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7493966307505709976'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7493966307505709976'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/09/day-247-express-yourself.html' title='Day 247: Express Yourself'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1590738645837719056</id><published>2008-09-14T21:20:00.004-04:00</published><updated>2008-09-14T21:25:48.117-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><title type='text'>Day 246: Checking In</title><content type='html'>This time, I've been half working, half slacking.  Though I did take a few days off, I also continue to face the fact that "Made some planets and tagged them" remains a very boring blog entry.  So I'm trying to split my time up even further - 30 to 45 minutes for the planets, then 15 to whatever else I feel like doing for one of my many features I haven't yet implemented.&lt;br /&gt;&lt;br /&gt;Today's feature was putting an 'auto' button next to the 'tag' input.  So now, instead of naming something "Monster Island" and then having to click down to 'tag' and type "planet_monster_island", you can now just hit the 'auto' button and it'll come up with that for you.  Well, it would if it worked - right now it just says "You clicked the auto button!"&lt;br /&gt;&lt;br /&gt;Features I did previous to today include accidentally creating a planet I didn't mean to and then realizing that I had no way to delete planets.  You'd think I'd have learned my lesson after making the exact same mistake with Sectors.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1590738645837719056?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1590738645837719056/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1590738645837719056' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1590738645837719056'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1590738645837719056'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/09/day-246-checking-in.html' title='Day 246: Checking In'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-4064150341455688406</id><published>2008-09-07T19:05:00.003-04:00</published><updated>2008-09-07T19:17:50.896-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><category scheme='http://www.blogger.com/atom/ns#' term='mass distribution'/><title type='text'>Day 239: Mass Effect</title><content type='html'>I started this blog because I saw a number of my fellow developers making dev-blogs for their projects.  I had two thoughts:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;I won't have anything to blog about:&lt;/b&gt; This was the primary reason I haven't blogged in the past, I'm just not that interesting.  In this case, though, I can blog often because this is a daily project.  Lately, as you've no doubt noticed, this practice has fallen by the wayside.  I've continued to work, but writing up the blog post takes a lot more time and so I've tended to skip it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Nobody will read it:&lt;/b&gt; How I'm developing my game is of little interest to anyone except pretty hardcore game developers who are familiar with ruby, rubygame, and interested in my game enough to read about it instead of working on their own.  This set is maybe three people, five tops.  I've since discovered that having the blog is &lt;i&gt;invaluable&lt;/i&gt; - I've searched through it a number of times to find how I did something earlier, and the fact that I blogged about it made it simple to look up.  Even if no other living soul even glances at this blog, it's already been of enormous use.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;So that said, the slow pace of blogging may continue.  When I'm writing code, I often have something to say about how I implemented it.  When I'm developing content, what I have to say is pretty much "Created a few sectors".&lt;br /&gt;&lt;br /&gt;Now, however, I'm coding.  I created a ton of sectors and a few planets, and it occurred to me that later, when I've made a "standard array" of ships I want the player to be able to buy, I'm going to have to go back to &lt;b&gt;every single planet&lt;/b&gt; I made and put those ships up for sale there.&lt;br /&gt;&lt;br /&gt;The upside to being the author of a game editor is that if you don't like how it's doing something, you can change it.  Today I began work on the "Mass Distribution" system.  Each object has labels (like tags, only I call them 'labels' because I'm already using 'tag') that group it, and the Mass Distribution system will allow you to take a ship, a weapon, a mission, etc, and give it to everything that shares a label.  So if I, while creating these new planets, label them something like "alliance, standard, human", I can go back and easily give the standard array of weapons,ships, etc, to all the planets which share whatever tags I want.&lt;br /&gt;&lt;br /&gt;I may modify the random mission generator to use this technology as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-4064150341455688406?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/4064150341455688406/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=4064150341455688406' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4064150341455688406'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4064150341455688406'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/09/day-239-mass-effect.html' title='Day 239: Mass Effect'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-7525640238416002712</id><published>2008-08-31T16:42:00.004-04:00</published><updated>2008-08-31T16:50:46.767-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lore'/><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><category scheme='http://www.blogger.com/atom/ns#' term='port'/><title type='text'>Day 232: Re-re-port</title><content type='html'>It's been a while since I blogged but unlike last time that happened, I've actually been busy in the meantime.  Here's what I've been up to:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Ported everything to new-style events.  This was fairly easy and straightforward&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The player can now give their pilot a name.  Eventually this will be rolled into savegames, so you can have more than one pilot per module&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I fixed a Rubygame bug where if your application was minimized and lost focus at the same time, it'd crash.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A freaking ton of map work!  I have to say that creating maps is a lot easier with an editor.  Coming up with ideas for a ton of sectors, however, remains difficult.  I made the mistake of giving my initial generic filler sector a description, and so now have to go out of my way to explain how boring the other dozen filler sectors are.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;The map descriptions I've written already depict a lot of lore pre-game.  There was a time where humanity didn't know hyperspace lanes could go to empty sectors, for instance.  After that, the Sandwall spurred the invention of shielding.  There was an alien invasion scare at some point.  All these are completely divorced from the plot, just things I've come up with as I made the map.&lt;br /&gt;&lt;br /&gt;I was asking myself the question: Should I write this down somewhere so I remember it?  Then it occurred to me:  I just did!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-7525640238416002712?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/7525640238416002712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=7525640238416002712' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7525640238416002712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7525640238416002712'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/08/day-232-re-re-port.html' title='Day 232: Re-re-port'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-3375177260425743069</id><published>2008-08-27T20:55:00.003-04:00</published><updated>2008-08-27T21:14:20.983-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='deploy'/><category scheme='http://www.blogger.com/atom/ns#' term='vm'/><title type='text'>Day 228: Demo!</title><content type='html'>So today I fixed a bug.  It was less a bug and something I didn't realize was missing.  I mis-clicked while making the map for the new scenario, and ended up creating a sector I didn't need.  That's when I realized the map editor didn't have a 'delete' button!&lt;br /&gt;&lt;br /&gt;What an accomplishment!  I'm totally spent.&lt;br /&gt;&lt;br /&gt;Oh, yeah, and I made a &lt;a href="http://llynmir.net/~roger/kuiper/kuiper-0.0.1.tgz"&gt;demo&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;DEMO!!!!!!!!!!&lt;br /&gt;&lt;br /&gt;That's right!  I loaded Fedora 9 onto my VM and tested the file I just linked to right there.  On a pristine Fedora 9 load, it works with no issues.  It also runs on Ubuntu!  It even runs on both at the same time!  On the VMs it chugged down a bit during the combat, which says to me that I've still got more optimization to do, but it may also have been due to the fact that I was running it on a VM while I was also running it on another VM.&lt;br /&gt;&lt;br /&gt;That link may not be permanent.  It's my home server, so be gentle :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-3375177260425743069?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/3375177260425743069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=3375177260425743069' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3375177260425743069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3375177260425743069'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/08/day-228-demo.html' title='Day 228: Demo!'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-701999130463686701</id><published>2008-08-26T21:59:00.003-04:00</published><updated>2008-08-26T22:16:59.386-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='deploy'/><title type='text'>Day 227: Execute!</title><content type='html'>My current build does not work with the current Rubygame/dev-2.4 branch due to one of my patches having not yet made its way in.  It'll die the moment an AI-controlled ship attempts to figure out how to move.  This will hopefully change soon.&lt;br /&gt;&lt;br /&gt;This should hopefully not matter; the demo itself is looking even more likely to come out this week.  Rubyscript2exe is everything I'd ever hoped for and more!    Mainly because it will use whatever libraries I have on my system, including my already-patched version of Rubygame.  I unintstalled Rubygame from my system entirely, as a matter of fact, and the executable still ran.  I'm waiting until I can get it to run on a pristine Fedora VM, and then I'm releasing the demo to you, the public.&lt;br /&gt;&lt;br /&gt;Rake can now make .tgz files out of the entire distribution (actually, it could always do that, but I only just figured out how today).  This makes my life even easier, because it means I can use rake to build the executable and then put it in a .tgz file for distribution in one step!&lt;br /&gt;&lt;br /&gt;Finally, it turns out that Rubygame 2.4, which I'm now targeting as I can't live without my mousewheel support, has the new event classes that will one day be part of 3.0 and which I just finished moving all my code &lt;span style="font-style:italic;"&gt;away&lt;/span&gt; from.  Luckily, I knew I'd have to switch back and made a giant list a few posts ago about what I changed.  And I did, in fact, tag the last commit before changing over in git, so a (very) careful merge may turn out to be the easy answer.&lt;br /&gt;&lt;br /&gt;Then again, I seem to remember that &lt;a href="http://kuidev.blogspot.com/2008/06/day-151-its-never-easy.html"&gt;it's never easy&lt;/a&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-701999130463686701?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/701999130463686701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=701999130463686701' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/701999130463686701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/701999130463686701'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/08/day-227-execute.html' title='Day 227: Execute!'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-8466620845782718442</id><published>2008-08-25T21:19:00.003-04:00</published><updated>2008-08-25T21:39:40.734-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comingstorm'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Day 226: The Coming Storm</title><content type='html'>Instead of doing all that releasey stuff I mentioned yesterday, I instead started creating the map for the module I intend to include with the game when it ships.&lt;br /&gt;&lt;br /&gt;It was yesterday, actually, that I started writing up the outline, and three hours later it was included; those who don't want the plot spoiled for them should avoid poking around too deeply in the &lt;a href="http://github.com/atiaxi/kuiper/tree/master"&gt;Github repository&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Oh, did I mention?  Kuiper's on &lt;a href="http://github.com"&gt;Github&lt;/a&gt; now.  It's nice having a backup of my work that's not just another machine in my house.  Right now if you want to play the game, you'll have to use Rubygame's &lt;a href="http://github.com/jacius/rubygame/tree/dev-2.4"&gt;2.4-dev&lt;/a&gt; branch, and to be perfectly honest I haven't exactly tried downloading it and integrating with it.  That's a task for Soon(tm).&lt;br /&gt;&lt;br /&gt;Anyway, the point of the post is that the new module is called, creatively enough, "The Coming Storm".  The short description is:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;  Humanity is alone among the stars and, as always when humanity has only itself to deal with, war is constant.  An inner circle of systems are allied against separatist rebels who seek their independence.  It would be a tale told a thousand times, if there were not much worse in store....&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Yeah?  Seemed like I was going to go with a typical empire vs. rebels thing there, but then I promised it totally wouldn't go down that way!  Remember that when you're playing Part I and being all like "This is a typical empire vs. rebels thing!"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-8466620845782718442?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/8466620845782718442/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=8466620845782718442' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8466620845782718442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8466620845782718442'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/08/day-225-coming-storm.html' title='Day 226: The Coming Storm'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-4252984025304820037</id><published>2008-08-24T16:50:00.003-04:00</published><updated>2008-08-24T17:05:48.504-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='deploy'/><category scheme='http://www.blogger.com/atom/ns#' term='vm'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 225: More Returning</title><content type='html'>Getting back into the swing of things is difficult.  Sundays are great for working on the game because I feel pressure to make something out of my weekend and time's running out; every other day... not so much.&lt;br /&gt;&lt;br /&gt;That said, &lt;a href="http://llynmir.net/projects/kuiper/milestone/Tutorial"&gt;milestone:Tutorial&lt;/a&gt; is complete.  Sometime soon, hopefully this week, I'm going to look at &lt;a href="http://www.erikveen.dds.nl/rubyscript2exe/"&gt;RubyScript2Exe&lt;/a&gt;, see if I can get it integrated into Rake, and have a 'preview' available.  I've started to set up a few &lt;a href="http://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt; VMs for testing.  Likely the preview release will be linux-only, unless I get very very inspired to build it on windows.&lt;br /&gt;&lt;br /&gt;I'm at a critical juncture for this game, because it was right around &lt;b&gt;exactly this point&lt;/b&gt; that I stopped developing for TraderMissions.  The engine was there, I'd written a tutorial that worked perfectly, I was all ready to create my new universe, and then my enthusiasm for the project vanished entirely.&lt;br /&gt;&lt;br /&gt;I'm hoping that won't happen this time.  One of the big differences between TM and Kuiper is that Kuiper has a built-in editor.  A lesson I learned from TM was that I don't especially enjoy creating content when I could be programming.  Hopefully the editor will streamline these efforts for me.&lt;br /&gt;&lt;br /&gt;While we're hoping, let's hope I continue working on this and you can all see a blog from me tomorrow!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-4252984025304820037?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/4252984025304820037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=4252984025304820037' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4252984025304820037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4252984025304820037'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/08/day-225-more-returning.html' title='Day 225: More Returning'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1820918521517354810</id><published>2008-08-17T14:41:00.004-04:00</published><updated>2008-08-25T21:25:52.054-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='port'/><category scheme='http://www.blogger.com/atom/ns#' term='branches'/><title type='text'>Day 218: Re-port</title><content type='html'>Today's major task was to port Kuiper from the old 3.0 codebase to the currently developed Rubygame 2.4.  This seems to have gone off without any problems, with the exception that I'd made a number of changes to the 3.0 codebase (mouse wheel support, etc) that hadn't been backported yet.&lt;br /&gt;&lt;br /&gt;Well, I'm not a Rubygame developer for no reason, so I went and backported them myself.  As &lt;a href="http://kuidev.blogspot.com/2008/06/day-153-contest.html"&gt;alluded to earlier&lt;/a&gt;, I'm starting to use &lt;a href="http://git.or.cz/"&gt;Git&lt;/a&gt;, and it turns out that you don't even have to be an official developer to make changes to Rubygame if you're going the Git route.  Instead, you fork the github Rubygame project, make changes to that, and send a 'pull request' back.  If you didn't understand any of what I just said, feel better knowing that I didn't really either.  I'll have to wait and see if I did it right :)&lt;br /&gt;&lt;br /&gt;So on that list, three of the four things I mentioned I wanted to do have mostly been done.  I just need to make a little more balance for the tutorial, and then I'm doing a preview release!&lt;br /&gt;&lt;br /&gt;The following are the notes I made to myself on the porting process, so later when I need to re-port the game to Rubygame 3.0, I remember what I had to change.  You can safely ignore these:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Had to change the screen portion of engine to use 'or's instead of an array&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Put support in 2.4 for key repeats&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Put support in 2.4 for mouse wheel&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Fixed bug in ftor new_from_to&lt;/li&gt;&lt;br /&gt;&lt;li&gt;text.rb keyTyped used symbols, changed to Rubygame::K_*&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Options.rb, change symbols there to Rubygame::K_*&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Clock.wait uses milliseconds, Clock.tick returns milliseconds&lt;/li&gt;&lt;br /&gt;&lt;li&gt;mouseUp in state used symbols, replace with Rubygame::*&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Change emergency hooks from syms to Rubygame::K_*&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Change unit tests to use constants instead of syms&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Map::update used syms for arrow-key movement&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1820918521517354810?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1820918521517354810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1820918521517354810' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1820918521517354810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1820918521517354810'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/08/day-218-re-port.html' title='Day 218: Re-port'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-6631173025811411065</id><published>2008-08-16T22:44:00.006-04:00</published><updated>2008-08-17T14:41:40.089-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ptotd'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><category scheme='http://www.blogger.com/atom/ns#' term='profiling'/><title type='text'>Day 217: Extended Rest</title><content type='html'>The subject is a reference to &lt;a href="http://wizards.com/default.asp?x=dnd/welcome"&gt;D&amp;D 4th edition&lt;/a&gt;.  A normal extended rest lasts about 6-8 hours.  In my case, it was nearly two months!&lt;br /&gt;&lt;br /&gt;But I have resumed.  It turns out that optimizing code is fairly soul-crushing.  The flipside of "Premature optimization is the root of all evil" is today's &lt;b&gt;Programming Tip of the Day:&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;b&gt;It's not premature optimization if it's actually time to optimize&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;But if you've been approaching development the entire time with the attitude that you don't need to optimize, it becomes quite an adjustment to go back to the problem parts and realize how horrible your code actually was.&lt;br /&gt;&lt;br /&gt;I want to post this before midnight, so I'm not going to go into a lot of detail on the fixes.  Most involved caching a value rather than recalculating it, something I know is faster but won't do unless the situation &lt;i&gt;really&lt;/i&gt; calls for it, as I've been bitten in the ass before by out-of-date cache data.  I replaced a linear search of an array that happened every frame with a dictionary lookup, which provided the expected results.&lt;br /&gt;&lt;br /&gt;So on one hand, not optimizing early is a good idea.  On the other hand, you have to pay for it later when the time comes.  On the gripping hand, the code's so poor that I really had nowhere to go but up!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-6631173025811411065?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/6631173025811411065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=6631173025811411065' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/6631173025811411065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/6631173025811411065'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/08/day-168-extended-rest.html' title='Day 217: Extended Rest'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-8277296870347306341</id><published>2008-06-19T23:12:00.004-04:00</published><updated>2008-06-19T23:29:08.095-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalksolutions'/><category scheme='http://www.blogger.com/atom/ns#' term='profiling'/><title type='text'>Day 159: Profiling</title><content type='html'>Today was my first in-depth profiling.  Yesterday I did some looking over, but even with &lt;a href="http://ruby-prof.rubyforge.org/"&gt;ruby-prof&lt;/a&gt;'s excellent HTML output, it was taking me a while to get my head around exactly where the bottlenecks in my code were.&lt;br /&gt;&lt;br /&gt;Then I found out that ruby-prof can output in a format that &lt;a href="http://kcachegrind.sourceforge.net/cgi-bin/show.cgi"&gt;KCacheGrind&lt;/a&gt; understands.  I also found out that KCacheGrind exists, which was likewise helpful, and suddenly I had a tool which could walk me through parts that needed optimizing.&lt;br /&gt;&lt;br /&gt;My first surprise was that Set::merge was called &lt;span style="font-weight:bold;"&gt;FOUR MILLION TIMES&lt;/span&gt;. No, I'm not making that number up.  When you ask a KuiObject for its attributes, children, etc, it collects all of the ones it knows about in a set and then merges it with the ones its superclass knows about.  Except there was no caching here, it was doing it every single time.&lt;br /&gt;&lt;br /&gt;Even that wouldn't have been a problem, because you'd think I wouldn't be asking what every single attribute of an object is except when I'm loading/saving (and then time is not as important).  But it turns out that it was heavily used in comparison.  Two KuiObjects are == if all their children, attributes, etc, are equal, and this requires me to call Set::merge above.&lt;br /&gt;&lt;br /&gt;So I fixed the caching problem, and that sped things up.  Then I made it so that two KuiObjects are == if their tags are ==.  This changed a dozen+ string comparisons and countless object comparisons into one string comparison.  That sped things up a lot.&lt;br /&gt;&lt;br /&gt;The beauty of using profiling is that I can tell whether harebrained ideas I have for optimization actually work.  For instance, I thought that the string comparisons were likely to also be too slow, and I figured symbol comparisons would be faster.  Turns out, no, they're not, converting the strings to symbols and comparing them is a little bit slower than just comparing the strings to begin with.&lt;br /&gt;&lt;br /&gt;My other optimization idea, to take a line of the form (angle / 10).to_i and change it to (angle.to_i / 10), did speed things up a bit, due to the fortunate elimination of floating point division.&lt;br /&gt;&lt;br /&gt;At this point the game is smooth again in sectors with lots of ships, but not when they start shooting the hell out of someone.  There are a few other spots in the code I can optimize, fortunately.  I would like to avoid having to drop down to C, as I know that this more likely indicates a failure to optimize on my part than it does a shortcoming on Ruby's part.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-8277296870347306341?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/8277296870347306341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=8277296870347306341' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8277296870347306341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8277296870347306341'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-159-profiling.html' title='Day 159: Profiling'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-3988034048081462230</id><published>2008-06-19T01:22:00.005-04:00</published><updated>2008-06-19T23:27:25.847-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='contest'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalksolutions'/><category scheme='http://www.blogger.com/atom/ns#' term='profiling'/><title type='text'>Day 158: Reno</title><content type='html'>I'm currently attending the &lt;a href="http://www.stic.st/stic?content=sts2008call"&gt;Smalltalk Solutions&lt;/a&gt; conference in Reno, where we sent a man the #kill message, just to see if he understood.&lt;br /&gt;&lt;br /&gt;Trust me, if you know &lt;a href="http://en.wikipedia.org/wiki/Smalltalk"&gt;Smalltalk&lt;/a&gt;, that's a very funny joke.&lt;br /&gt;&lt;br /&gt;Good news/bad news on the development front.  The good news is that I finished the two tickets I mentioned: &lt;a href="http://llynmir.net/projects/kuiper/ticket/179"&gt;#179&lt;/a&gt; was for making lasers that weren't slower than the ships that fired them, and &lt;a href="http://llynmir.net/projects/kuiper/ticket/180"&gt;#180&lt;/a&gt; was all about adding "scenery" ships to the tutorial.&lt;br /&gt;&lt;br /&gt;That's where I ran into the bad news.  The first problem was that adding weapons to the ship in the editor &lt;b&gt;still wasn't working&lt;/b&gt;, despite all the testing I'd done.  What happened on the back end is that any time you give an object a tag, if it had a tag before it deletes it from the repository and re-registers with the new tag.  Whenever I created a new weapon for a ship, it replaced the original!  So if I had 2 lasers on board, suddenly the object in the repository would be those same two lasers.  It was a mess to track down, but thankfully a relatively easy fix.&lt;br /&gt;&lt;br /&gt;The worse news was that it's slow!  There are noticeable hiccups in even sectors where there's no combat.  The traditional profiling approach of using 'ruby -r profile' had so much overhead I couldn't actually run the game.  Thankfully, &lt;a href="http://ruby-prof.rubyforge.org/"&gt;Ruby-prof&lt;/a&gt; exists.  It's got a small enough overhead that I can actually play my game.&lt;br /&gt;&lt;br /&gt;I have a lot of data to go over for optimizing, and I'm not releasing the tutorial until I'm done, so I have one more ticket to close, &lt;a href="http://llynmir.net/projects/kuiper/ticket/96"&gt;#96&lt;/a&gt;, which I have aptly titled "OH GOD MY CPU".&lt;br /&gt;&lt;br /&gt;For those interested in my contest entry, I am temporarily hosting it &lt;a href="http://llynmir.net/~roger/porttown/"&gt;on my own machine&lt;/a&gt;, so get it while it's still up!  I think the contest turned out really well and I'm happy that my entry ended up as good as it was.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-3988034048081462230?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/3988034048081462230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=3988034048081462230' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3988034048081462230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3988034048081462230'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-158-reno.html' title='Day 158: Reno'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-5274286384612003742</id><published>2008-06-13T21:45:00.002-04:00</published><updated>2008-06-13T21:57:28.338-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='contest'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 153: Contest</title><content type='html'>I'm taking today, tomorrow, and Sunday off to participate in the &lt;a href="http://rubygameforums.com/viewtopic.php?f=10&amp;t=28&amp;sid=21481798a7fb0276b61a3ff4bdf47e8b"&gt;RubyWeekend&lt;/a&gt; game creation contest.  This is the first extended break I've taken from the game since I moved into my new house, and I need it!&lt;br /&gt;&lt;br /&gt;I did get some stuff done yesterday, mostly tweaks.  Specifically, &lt;a href="http://llynmir.net/projects/kuiper/ticket/183"&gt;fixing the combat tutorial&lt;/a&gt; and &lt;a href="http://llynmir.net/projects/kuiper/ticket/172"&gt;making tutorial items cost money&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This puts me two tickets away from finishing the milestone and therefore the tutorial.  I'll probably do a few things at that point:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Create a &lt;a href="https://github.com/"&gt;github&lt;/a&gt; project for the game and upload it there.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Branch the current version off and name it kuiper_rubygame_3.0.0.  This will be a reference branch, in case I need to port everything back to Rubygame 3.  Why make this branch?  Because of the next step:&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Backport the game to Rubygame 2.3.  I've had people interested in at least downloading and playing with the game, and as I'm using the bleeding edge my-version-may-break-daily version of Rubygame, it discourages people from doing so.  I'll have the Rubygame 3 branch ready when I need to switch over.  Finally, I'll:&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Release a 'preview'.  This will consist of the tutorial and be announced here and maybe a few other places (forums only, no formal announcements yet).  Get feedback, etc.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;And after that, I start work on the main campaign!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-5274286384612003742?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/5274286384612003742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=5274286384612003742' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5274286384612003742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5274286384612003742'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-153-contest.html' title='Day 153: Contest'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-885838319509494505</id><published>2008-06-11T21:18:00.003-04:00</published><updated>2008-06-11T21:34:55.812-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='radar'/><category scheme='http://www.blogger.com/atom/ns#' term='ptotd'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 151: It's Never Easy</title><content type='html'>Today's subject is a ripoff of a line from the &lt;a href="http://www.imdb.com/title/tt0079714/"&gt;Phantasm&lt;/a&gt; series of movies.  The movie's been around for as long as I've been alive, so I'm about to spoil the ending.&lt;br /&gt;&lt;br /&gt;When the intrepid heroes finally kill the bad guy, they sit back and have a moment to recuperate.  They do all their "Sure is better now that the bad guy's dead!" talk, and one of them says "Thank God, it's over"&lt;br /&gt;&lt;br /&gt;That's when the bad guy appears behind them, stating "It's Never Over", and the movie ends.  All the sequels followed this pattern.  This was the first horror movie I saw that that didn't have a "protagonists escape and everything's better" ending (I hadn't yet seen &lt;a href="http://www.imdb.com/title/tt0087800/"&gt;A Nightmare on Elm Street&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;So just when I think I've got an easy task ahead of me, I open the editor and the Tall Man appears, looming, intoning "It's Never Easy!" and then throwing killer spheres with knives into my head.  Trust me, this is very scary if you've seen the movie.  Especially if you saw it at the age of six.&lt;br /&gt;&lt;br /&gt;The easy task that actually &lt;span style="font-style:italic;"&gt;was&lt;/span&gt; easy today was &lt;a href="http://llynmir.net/projects/kuiper/ticket/99"&gt;#99&lt;/a&gt;, better radar coloration.  Since everyone can get ahold of the player's ship, it was trivial to change the color if the current ship was either the target of the player or targeting the player.&lt;br /&gt;&lt;br /&gt;I had plenty of time left over after that, though, so I tried my hand at &lt;a href="http://llynmir.net/projects/kuiper/ticket/184"&gt;#184&lt;/a&gt;, having a 'nearest hostile target' button.  Writing it was not hard, and as it turns out the logic was mostly right.  The problem came in testing.  I'd start up the game, shoot someone until they were angry at me, and hit the button.&lt;br /&gt;&lt;br /&gt;The game would freeze.&lt;br /&gt;&lt;br /&gt;What this usually means is that it's about to crash and Ruby is about to give me a stacktrace.  I don't know if it's Rubygame or my machine or what, but sometimes this takes anywhere from 5 seconds to several minutes to actually happen, and it's eating up 100% of the CPU the entire time.  Very inconvenient for debugging, and incredibly inconvenient for detecting infinite loops.&lt;br /&gt;&lt;br /&gt;I spent an hour.  I thought the error was in the comparison code, as that's one place that it'd be easy to get stuck in a loop.  I had print statements everywhere.  It was yet another horror of coding, and nothing I did helped.&lt;br /&gt;&lt;br /&gt;Until I put a print statement at the very end of the targeting code, that is.  This entire time, I'd been assuming it was this new code locking up the game.  But it wasn't!  Debugging told me that the targeting logic was all working.  Furthermore, looking at the output showed me exactly what I was doing wrong.  I was doing this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;player_ship.target = chosen if chosen&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But 'chosen' in this case is a sprite, not a ship.  The player_ship is expecting a model object, not a view!  All that work, for a six-byte change:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;player_ship.target = chosen.model if chosen&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Thus, today's &lt;span style="font-weight:bold;"&gt;Programming Tip of the Day&lt;/span&gt;: Don't try to use your view as your model.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-885838319509494505?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/885838319509494505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=885838319509494505' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/885838319509494505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/885838319509494505'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-151-its-never-easy.html' title='Day 151: It&apos;s Never Easy'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-5602696948151368789</id><published>2008-06-10T19:27:00.003-04:00</published><updated>2008-06-10T19:51:05.385-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='text'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><title type='text'>Day 150: On Automatic</title><content type='html'>Today I set out to do &lt;a href="http://llynmir.net/projects/kuiper/ticket/178"&gt;#178&lt;/a&gt;, the in-game quicksave and autosave.  Because I already had a utility function that could save any time from anywhere, I figured this'd be a cinch.  Create a new control and if it's pressed, save.  Turns out, it really was that easy....&lt;br /&gt;&lt;br /&gt;My wife came in at this point and asked me what I was doing, so I mentioned that I was working on autosave.  She said:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;"Oh good, but make it so that it doesn't overwrite the main save file, I hate it when games do that!"&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Guess how my save function works!  It can't save using any filename other than the universe's name, which happens to be the main save file.&lt;br /&gt;&lt;br /&gt;The bulk of that work, then, was fixing that problem.  The issue is that if I just tack '-autosave' on to the end of a universe's name and save it, that name persists.  So the player, upon loading their autosave and autosaving /that/, gets a name like 'scenario-autosave-autosave.ksg'  That would not do.&lt;br /&gt;&lt;br /&gt;The save code now looks for two identifiers ('autosave' and 'quicksave'), strips them out, and then adds them back on if you are, in fact, quicksaving or autosaving.  I also added in code to put the player's name into the filename as well, so more than one person can play per user account and not have their saves clobber each other.  Now if only I could &lt;a href="http://llynmir.net/projects/kuiper/ticket/181"&gt;name the player&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I had some extra time, so I got &lt;a href="http://llynmir.net/projects/kuiper/ticket/177"&gt;#177&lt;/a&gt; done.  That was literally a two-line change, to make some of the editing field bigger.  I had time left over.&lt;br /&gt;&lt;br /&gt;So, why not, I figured I'd tackle another "easy" task: &lt;a href="http://llynmir.net/projects/kuiper/ticket/160"&gt;#160&lt;/a&gt;, backporting all the nifty changes to multi-line inputs to the old one-line input.&lt;br /&gt;&lt;br /&gt;The plan was, make the one line input a subclass of the multiline, and disallow the creation of new lines.  This was easy, but the line was still multi-enabled; it'd wrap if you put too much text in it.&lt;br /&gt;&lt;br /&gt;Disabling wrapping was difficult - the MultiLineInput itself delegates the work to the MultiLineLabel.  I ended up having to have my subclass override its parent's rendering code entirely (as that's where the wrapping happened), and then set a number of variables that the parent class expected that the rendering would set. It was a pain.&lt;br /&gt;&lt;br /&gt;Oh well.  At least no global variables have bitten me in the ass.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-5602696948151368789?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/5602696948151368789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=5602696948151368789' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5602696948151368789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5602696948151368789'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-150-on-automatic.html' title='Day 150: On Automatic'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-2252890925576574447</id><published>2008-06-09T17:57:00.003-04:00</published><updated>2008-06-09T18:08:45.889-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='search'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><title type='text'>Day 149: Dep^H^H^HBreadth First Search</title><content type='html'>It'd be nice if I could implement a &lt;a href="http://en.wikipedia.org/wiki/Breadth-first_search"&gt;Breadth-first search&lt;/a&gt; without first accidentally creating a &lt;a href="http://en.wikipedia.org/wiki/Depth-first_search"&gt;Depth-first search&lt;/a&gt; like twelve freaking times.&lt;br /&gt;&lt;br /&gt;If the last entry was a blast from the past, this one's an archaeological find.  I closed &lt;a href="http://llynmir.net/projects/kuiper/ticket/4"&gt;#4&lt;/a&gt;, which implements the "Plot Course" button.  I did this because the multi-jump tutorial mentions it, and I want everything I mention in said tutorial to be working before I move to the next milestone.  This is my new way of putting things off; mentioning them in the tutorial and going "Well, gotta implement this!"&lt;br /&gt;&lt;br /&gt;Only this time, I wasn't putting anything off.  I've finished &lt;a href="http://llynmir.net/projects/kuiper/ticket/157"&gt;#157&lt;/a&gt;, and &lt;a href="http://llynmir.net/projects/kuiper/ticket/158"&gt;#158&lt;/a&gt;, which were the last two editor-intensive parts of the tutorial.  So I'm perfectly justified in coding up whatever I feel like.&lt;br /&gt;&lt;br /&gt;This blog entry is backwards, because before I did either of those things, I did &lt;a href="http://llynmir.net/projects/kuiper/ticket/176"&gt;#176&lt;/a&gt;, which lets you display a message to the user once and only once (so they're not constantly pestered with tutorial reminders).&lt;br /&gt;&lt;br /&gt;Before I did that, I fixed a bug whereby weapons weren't taking up the right amount of space (their expansion and hardpoints cost was only counted once, regardless of the number of weapons you had).  Once again, I discovered that unit testing will only help me if I actually write the tests; I asked myself "Didn't I test for this?" when the bug came up, and the answer turned out to be a big fat "NO!"&lt;br /&gt;&lt;br /&gt;And before I did &lt;span style="font-style:italic;"&gt;that&lt;/span&gt;, I proliferated MiniBuilders to weapons and ammo, which wasn't using them before.  At this point I tend to take my editor's stability for granted, so it's very disconcerting when I click a button that's still there because I haven't put a minibuilder in and everything dies.&lt;br /&gt;&lt;br /&gt;Finally, the global variable has been quiescent, not biting anything. But I'm watching it....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-2252890925576574447?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/2252890925576574447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=2252890925576574447' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2252890925576574447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2252890925576574447'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-149-dephhhbreadth-first-search.html' title='Day 149: Dep^H^H^HBreadth First Search'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-6702188331340869561</id><published>2008-06-06T19:59:00.004-04:00</published><updated>2008-06-06T20:39:35.357-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='antipattern'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><title type='text'>Day 146: Blast from the Past</title><content type='html'>Yesterday I finished &lt;a href="http://llynmir.net/projects/kuiper/ticket/168"&gt;#168&lt;/a&gt;, which prevents the user from seeing (in detail) sectors they haven't been to yet, only to discover that when you add them to your itinerary you still see the names (&lt;a href="http://llynmir.net/projects/kuiper/ticket/175"&gt;#175&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Today's subject comes from the fact that, as of earlier today, I had 42 outstanding tickets in &lt;a href="http://llynmir.net/projects/kuiper/milestone/Polish"&gt;milestone:Polish&lt;/a&gt;.  This is the milestone I throw things in which aren't immediately necessary, but that I want to get done before I ship the game.  It's got more tickets in it than any other milestone, because anytime I feel like putting something off, there it goes!&lt;br /&gt;&lt;br /&gt;So today I finished &lt;a href="http://llynmir.net/projects/kuiper/ticket/76"&gt;#76&lt;/a&gt;, the ability to do a quicksave of a universe while you're editing it.  This is invaluable because the alternative is to go all the way back to the first editor screen, save, and then immediately load up your scenario again to continue editing.&lt;br /&gt;&lt;br /&gt;I had to make two changes in order for this to happen.  First, I had to make it possible to respond to a keypress at the engine level; because the editor can be in any one of a dozen states at any time, the engine is the only place they have in common.  I was already doing this with the 'kill switch' that shuts the game down (good for testing full-screen!), so I extended it to call arbitrary code in response to any button.&lt;br /&gt;&lt;br /&gt;The second change was surprisingly easy.  Games-in-progress and scenarios-being-edited, while sharing the same file format, are saved in entirely different ways.  I have plans (&lt;a href="http://llynmir.net/projects/kuiper/ticket/169"&gt;#169&lt;/a&gt;) to widen that gulf even further.  So if I'm going to be allowing the person running Kuiper to save the universe they're editing at any time, there needs to be a way to know that the person is, in fact, editing a universe and not playing a game.&lt;br /&gt;&lt;br /&gt;Kuiper uses a number of gameplay displays as editors; the map and sector states are examples.  Each changes its behavior if it's in "edit mode".  A number of other things change if the user is editing.  So each thing which cares about edit mode has its edit_mode set to true (usually in its initializer).  The problem is, half of the project cares if it's in edit mode!  TraderMissions did something similar, but worse; the repository itself was given to everything that needed it, which turned out to be everything.  I've done it enough and had it cause enough code bloat to dub it my own personal &lt;a href="http://en.wikipedia.org/wiki/Anti-pattern"&gt;Anti-pattern&lt;/a&gt;: &lt;span style="font-weight:bold;"&gt;Constantly passing objects that should be global.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So I committed one of the cardinal sins of programming:  I made a global variable.  It's one of those things you're supposed to do only if you know what you're doing, and usually that rules me out.  This time, though, it's making life a lot easier.  And that is the story of &lt;a href="http://llynmir.net/projects/kuiper/ticket/174"&gt;#174&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The other thing of note I did today was create the KuiSellItemsAction.  No, it doesn't force the player to sell items, like I keep thinking when I see it, it puts the items you indicate up for sale on the planets you tell it to.  You can sell weapons, cargo, ships, and/or addons.  This is useful for, say, simulating technology advances in-game.  I use it in the tutorial to only offer weapons to the player after they get the weapons tutorial.&lt;br /&gt;&lt;br /&gt;Now taking bets on how long it'll take the global variable to bite me in the ass!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-6702188331340869561?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/6702188331340869561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=6702188331340869561' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/6702188331340869561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/6702188331340869561'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-146-blast-from-past.html' title='Day 146: Blast from the Past'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-842419061153807395</id><published>2008-06-04T20:22:00.002-04:00</published><updated>2008-06-04T20:43:44.298-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='market'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 144: Your Ship has Come In</title><content type='html'>So I decided on a whim to cater to that tiny but vocal minority of players who might one day want a ship other than the horrible tiny one they start with.  &lt;a href="http://llynmir.net/projects/kuiper/ticket/170"&gt;#170&lt;/a&gt; is thus done!&lt;br /&gt;&lt;br /&gt;In the fine tradition of making things worse for myself, I then made &lt;a href="http://llynmir.net/projects/kuiper/ticket/171"&gt;#171&lt;/a&gt;, &lt;a href="http://llynmir.net/projects/kuiper/ticket/172"&gt;#172&lt;/a&gt;, and &lt;a href="http://llynmir.net/projects/kuiper/ticket/173"&gt;#173&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;#171 should prevent people from jettisoning mission-related cargo, which is a surprisingly hard fix.  The part which displays this is too general to know that it's even displaying cargo, let alone which is mission related.  I plan to cheat and have it check to see if its object understands :mission_related, and disallow jettisoning.  This also opens things up to one day having missions give the player temporary weaponry and/or addons.&lt;br /&gt;&lt;br /&gt;#173 is my pie-in-the-sky ship previewer.  When comparing ships, it'd be great to get a side-by-side comparison, complete with how many more (or less) of a given attribute the new ship has.  That's easily extended to addons, too.&lt;br /&gt;&lt;br /&gt;#172 is, um... making tutorial items actually cost money.&lt;br /&gt;&lt;br /&gt;One of my bosses at work has a Staples &lt;a href="http://www.staples.com/office/supplies/StaplesProductDisplay?&amp;storeId=10001&amp;langId=-1&amp;catalogId=10051&amp;partNumber=606396&amp;cm_mmc=GoogleBase-_-Shopping-_-Furniture_%253E_Decorative_Accessories_-_-606396-12438-CC&amp;ci_src=14110944&amp;ci_sku=606396"&gt;easy button&lt;/a&gt; that says "That was easy!" when pressed.&lt;br /&gt;&lt;br /&gt;I'm lobbying to have the voice clip replaced with "That was surprisingly difficult!"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-842419061153807395?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/842419061153807395/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=842419061153807395' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/842419061153807395'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/842419061153807395'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-144-your-ship-has-come-in.html' title='Day 144: Your Ship has Come In'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-5636393914851888671</id><published>2008-06-03T18:19:00.003-04:00</published><updated>2008-06-03T18:27:34.361-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erb'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><title type='text'>Day 143: Addons</title><content type='html'>First, the huge news:  &lt;a href="http://llynmir.net/projects/kuiper/ticket/149"&gt;#149&lt;/a&gt; is done!&lt;br /&gt;&lt;br /&gt;Okay, that's not huge news.  As alluded to &lt;a href="http://kuidev.blogspot.com/2008/05/day-109-blogtitle.html"&gt;before&lt;/a&gt;, I've been planning to remove ERB for a while.  Today, I wanted to blow another 15 minutes.  Thus, an easy ticket (which turned out to &lt;span style="font-style:italic;"&gt;actually&lt;/span&gt; be easy) was closed.&lt;br /&gt;&lt;br /&gt;In more important development related news, &lt;a href="http://llynmir.net/projects/kuiper/ticket/156"&gt;#156&lt;/a&gt; was also closed today.  That's the Addon tutorial.  It took surprisingly long because, as I was testing the tutorial module I asked myself "Self, what if I set out to specifically disregard the tutorial's instructions."&lt;br /&gt;&lt;br /&gt;It breaks things, is what!&lt;br /&gt;&lt;br /&gt;I had to create a new condition that would check if you'd actually finished a mission, just for this.  Now all the tutorial missions are only awarded if you've finished their prerequisite mission.  I expect that condition to be handy for multi-mission story arcs, and now you don't have to use flags (which are a pain) to do the same thing.&lt;br /&gt;&lt;br /&gt;Finally, I made ticket &lt;a href="http://llynmir.net/projects/kuiper/ticket/170"&gt;#170&lt;/a&gt; because it occurred to me that maybe - &lt;span style="font-style:italic;"&gt;perhaps&lt;/span&gt; - a small minority of players could, at some point in their gameplay experience, want to buy a new ship.&lt;br /&gt;&lt;br /&gt;Yeah.  I forgot about that until now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-5636393914851888671?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/5636393914851888671/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=5636393914851888671' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5636393914851888671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5636393914851888671'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-143-addons.html' title='Day 143: Addons'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1748138255994401973</id><published>2008-06-02T18:52:00.003-04:00</published><updated>2008-06-02T19:02:39.466-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ptotd'/><category scheme='http://www.blogger.com/atom/ns#' term='market'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 142: Two Hundred Hours</title><content type='html'>Actually, because I was only a half hour short yesterday, I'm at 201 hours.  But still, it's a milestone so I'm naming my blog after it.&lt;br /&gt;&lt;br /&gt;Today's work was finishing &lt;a href="http://llynmir.net/projects/kuiper/ticket/166"&gt;#166&lt;/a&gt; which means the market dialog is shiny and good and happy.  You get details, # of everything it requires that it requires, # of that thing you have free, and the ability to buy/sell more than one of them at a time.  I even made it work with weapons, even though that ticket's not up yet.&lt;br /&gt;&lt;br /&gt;That's not to say things went extremely well: I had to quash a lot of typos.  15 minutes into some refactoring I decided I didn't like the way it was going, so I ended up reverting all my work up until that point.  I kept putting Python syntax in Ruby, thus leading to today's &lt;span style="font-weight:bold;"&gt;Programming Tip of the Day:&lt;/span&gt; To make a new object, use "new".&lt;br /&gt;&lt;br /&gt;Still, many things went right.  For instance, everything that can be bought and sold has its price stored stored in an instance variable named 'price'.  Thank you, me, for making that consistent!  Getting weapons to work was literally four lines of code, all of which were copied and pasted from the addons adapter.  I got an extra half hour of work in.&lt;br /&gt;&lt;br /&gt;Finally, now that the market dialog is handling everything market-related, and the minibuilder and new builder dialog is handling everything build-related, I deleted the old builders.  Three entire files (addons.rb, cargo.rb, and weapons.rb) housed nearly identical GUI code that is now handled in one place.&lt;br /&gt;&lt;br /&gt;Of course, the downside of this is that, according to my spreadsheet, I wrote -190 lines of code today.  I'm still calling it a win.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1748138255994401973?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1748138255994401973/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1748138255994401973' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1748138255994401973'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1748138255994401973'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-142-two-hundred-hours.html' title='Day 142: Two Hundred Hours'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-5523216670735218194</id><published>2008-06-01T19:01:00.003-04:00</published><updated>2008-06-01T19:14:43.864-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='market'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 141: Slow Ride</title><content type='html'>No, that's not the title because I took it easy today; it's because my lines of code increased by a whopping 30 over 2.25 hours of work.&lt;br /&gt;&lt;br /&gt;Of course, LoC don't tell the whole story - I'm surprised it was positive, as I did some refactoring today and removed a fair bit of code (the old market dialog, for instance).  Sometimes the most productive days are the ones where the LoC count goes backwards.  That's one of the other ways this differs from &lt;a href="http://www.nanowrimo.org/"&gt;Nano&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Today was touchup.  &lt;a href="http://llynmir.net/projects/kuiper/ticket/165"&gt;#165&lt;/a&gt; required me to put scrollers around the new fancy editors I'd made, and I did it and it was surprisingly easy.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://llynmir.net/projects/kuiper/ticket/166"&gt;#166&lt;/a&gt;, on the other hand, was not so easy.  It requires me to have a 'preview' of the cargo/addon/weapon you want to buy.  Not a big deal, the preview part was easy.  It was the "This requires 1 hardpoint and 2 expansion space, and you have 0 hardpoints and 0 expansions left" part.  I essentially made it so any Buyable adapter knows what kind of requirements the things it holds have.  The ShipCargoAdapter has "cargo", ShipAddonAdapter will have "expansion" and "hardpoints", etc.  These are mapped to actual function names, so I can then dynamically generate the message.&lt;br /&gt;&lt;br /&gt;That part's done for the "You have 3 cargo free" part, but not for the requirements.  I'm already over my quota for the day, though, so on to tomorrow!&lt;br /&gt;&lt;br /&gt;Procrastination for the win!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-5523216670735218194?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/5523216670735218194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=5523216670735218194' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5523216670735218194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5523216670735218194'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/06/day-141-slow-ride.html' title='Day 141: Slow Ride'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-8197510929138881472</id><published>2008-05-31T22:01:00.003-04:00</published><updated>2008-05-31T22:16:58.829-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 140: Map and Jumping</title><content type='html'>I finished &lt;a href="http://llynmir.net/projects/kuiper/ticket/155"&gt;#155&lt;/a&gt; today, and named my blog post after it.  The total explanation for how to use the map and how to jump out of the sector was maybe two paragraphs, and it took the entire two hours to do.  Thankfully, it's not because I'm a slow typist.&lt;br /&gt;&lt;br /&gt;No, the reason it was so problematic was &lt;a href="http://llynmir.net/projects/kuiper/ticket/162"&gt;#162&lt;/a&gt;.  The previous tutorial mission which tells you to land on the planet only ends when you get the map-and-jumping mission.  In order to do this, map-and-jumping sets a flag upon receipt, and land-on-planet checks this flag to see if it's ending.  If it is, it removes itself.&lt;br /&gt;&lt;br /&gt;I have a condition check for this.  It had three fields: is_set, number_is and note_is.  Those last two are to check if the flag is set to the given number or has the given note attached.  Is_set was, I thought, to merely check to see if the flag was set.&lt;br /&gt;&lt;br /&gt;I checked the unit tests to find that its purpose was originally the exact opposite.  If you didn't specify is_set, it would return true if the flag was &lt;span style="font-style:italic;"&gt;not&lt;/span&gt; set.  Otherwise, the note or number had to match.&lt;br /&gt;&lt;br /&gt;So there was some refactoring.  Now is_set does what I originally wanted, and there's a new is_not_set which, if selected, returns true if the flag hasn't been set yet.&lt;br /&gt;&lt;br /&gt;That was working, the land-on-planet mission was vanishing at the right time, but the map-and-jumping mission wasn't working at all: it was supposed to pop up an informational box about how to, well, pull up the map and jump the ship around.  It wasn't.  I started having flashbacks to &lt;a href="http://kuidev.blogspot.com/2008/05/day-100-wild-and-crazy-ride.html"&gt;my two-day multi-hour coding binge of doom&lt;/a&gt;.  I really didn't want there to be something I'd have to re-code the entire notification system for.&lt;br /&gt;&lt;br /&gt;And, lucky me, there wasn't.  In fact, it was the other mission, the land-on-the-planet mission, that was causing the problem.  The loop which checks all the missions looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@player.missions.each do |m|&lt;br /&gt;  m.check&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The problem being that check can end the mission, and thus remove it from the @player.missions array!  This messes up the loop, and it ends up thinking it's done and not checking the other mission.&lt;br /&gt;&lt;br /&gt;Thus, @player.missions.dup  Life was surprisingly easy after that!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-8197510929138881472?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/8197510929138881472/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=8197510929138881472' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8197510929138881472'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8197510929138881472'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-140-map-and-jumping.html' title='Day 140: Map and Jumping'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-7559715457688322540</id><published>2008-05-30T22:31:00.003-04:00</published><updated>2008-12-13T04:06:28.330-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='market'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 139: Miscalculation</title><content type='html'>"Hey Roger, when did you start this thing, anyway?"&lt;br /&gt;Yesterday, you might have thought I started it sometime in early February, which would have made it day 119 and been a rather belated New Year's Resolution.  I actually started keeping track of everything in a spreadsheet on January 12th.  I just today discovered a mistake on my spreadsheet that wasn't counting 20 days of work.  So the answer to your question is no, watching the Lost season finale did not catapult you 20 days into the future.&lt;br /&gt;&lt;br /&gt;Today I got &lt;a href="http://llynmir.net/projects/kuiper/ticket/120"&gt;#120&lt;/a&gt; and &lt;a href="http://llynmir.net/projects/kuiper/ticket/131"&gt;#131&lt;/a&gt; done, which means the new market dialog is live!  I didn't take any 'before' screenshots, so imagine something that's worse than this 'after' screenshot:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_pIj5Y9n3oFo/SEC7ERkwlfI/AAAAAAAAAe8/S2CXzHUV1y8/s1600-h/market_after.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_pIj5Y9n3oFo/SEC7ERkwlfI/AAAAAAAAAe8/S2CXzHUV1y8/s400/market_after.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5206366851351746034" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Also, those numbers in the 'status' bit haven't changed, but they're important because I made a huge change to the way cargo was handled.  All the tests still pass, though.  Ship it!&lt;br /&gt;&lt;br /&gt;Wait, no, don't ship it yet.  I was kidding!  Put down the packing tape!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-7559715457688322540?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/7559715457688322540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=7559715457688322540' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7559715457688322540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7559715457688322540'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-139-miscalculation.html' title='Day 139: Miscalculation'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_pIj5Y9n3oFo/SEC7ERkwlfI/AAAAAAAAAe8/S2CXzHUV1y8/s72-c/market_after.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-2398870373999712766</id><published>2008-05-29T22:59:00.003-04:00</published><updated>2008-05-29T23:11:01.929-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 119: Lost</title><content type='html'>My excuse for today's entry being short is that I'm typing it during the &lt;a href="http://abc.go.com/primetime/lost"&gt;LOST&lt;/a&gt; season finale.  I recall, when I first started this blog up, I feared that all my entries would be short.  A friend of mine reassured me:  "This isn't &lt;a href="http://www.nanowrimo.org/"&gt;NaNoWriMo&lt;/a&gt;.  You don't have to pad your word count."  She makes an excellent point!&lt;br /&gt;&lt;br /&gt;Today I finished &lt;a href="http://llynmir.net/projects/kuiper/ticket/154"&gt;#154&lt;/a&gt;, which is the tutorial telling people what you can do on a planet.  It goes into some detail on trading, which is special because that doesn't actually work yet.  This brings me nicely to my next topic:  I didn't so much get things done today as create a whole slew of things to do:&lt;br /&gt;&lt;br /&gt;Moved &lt;a href="http://llynmir.net/projects/kuiper/ticket/120"&gt;#120&lt;/a&gt; and &lt;a href="http://llynmir.net/projects/kuiper/ticket/131"&gt;#131&lt;/a&gt; to this milestone, because I'll need them in order for the tutorial to be accurate.  Created &lt;a href="http://llynmir.net/projects/kuiper/ticket/162"&gt;#162&lt;/a&gt;, &lt;a href="http://llynmir.net/projects/kuiper/ticket/163"&gt;#163&lt;/a&gt;, &lt;a href="http://llynmir.net/projects/kuiper/ticket/164"&gt;#164&lt;/a&gt;, &lt;a href="http://llynmir.net/projects/kuiper/ticket/165"&gt;#165&lt;/a&gt; as cleanup tickets to make things look better and more functional.&lt;br /&gt;&lt;br /&gt;In short, my total number of tickets for this milestone went from 7 to 11, and my completed number only increased by one :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-2398870373999712766?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/2398870373999712766/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=2398870373999712766' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2398870373999712766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2398870373999712766'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-119-lost.html' title='Day 119: Lost'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-5005178116029183891</id><published>2008-05-28T19:50:00.004-04:00</published><updated>2008-12-13T04:06:28.671-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='text'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 118: Medicated, again</title><content type='html'>If you glance back at the blog there, you'll see the last time I was medicated was at the very beginning of the month.  Believe it or not, this is a huge improvement.&lt;br /&gt;&lt;br /&gt;Regardless, this will still be a short entry.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://llynmir.net/projects/kuiper/ticket/125"&gt;#125&lt;/a&gt; is done.  You can scroll to your heart's content, and all will be well.&lt;br /&gt;&lt;br /&gt;Before:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_pIj5Y9n3oFo/SD3w8xkwldI/AAAAAAAAAes/1qedgF14vHg/s1600-h/textbox_before.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_pIj5Y9n3oFo/SD3w8xkwldI/AAAAAAAAAes/1qedgF14vHg/s320/textbox_before.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5205581671200495058" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_pIj5Y9n3oFo/SD3x8xkwleI/AAAAAAAAAe0/-2l-qWFRbzU/s1600-h/textbox_after.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_pIj5Y9n3oFo/SD3x8xkwleI/AAAAAAAAAe0/-2l-qWFRbzU/s320/textbox_after.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5205582770712122850" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-5005178116029183891?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/5005178116029183891/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=5005178116029183891' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5005178116029183891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5005178116029183891'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-118-medicated-again.html' title='Day 118: Medicated, again'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_pIj5Y9n3oFo/SD3w8xkwldI/AAAAAAAAAes/1qedgF14vHg/s72-c/textbox_before.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-5188731933148110138</id><published>2008-05-27T20:20:00.002-04:00</published><updated>2008-05-27T20:33:44.604-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='text'/><category scheme='http://www.blogger.com/atom/ns#' term='ptotd'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 117: Caret</title><content type='html'>Short blog today, as I'm still not feeling so hot.  And I ought to, considering that it's 90 freaking degrees outside.&lt;br /&gt;&lt;br /&gt;Today I got 'home' and 'end' to go to the beginning and end of line.  I got the editor to correctly recognize more than one return character (this was also causing off-by-one errors when it wasn't working).  It turns out that &lt;a href="http://ruby-doc.org/core/classes/String.html#M000818"&gt;String::split&lt;/a&gt; will ignore trailing whitespace unless you tell it not to.  Yes, I know the docs I just linked to say that, but I hadn't considered looking there until things weren't going right.&lt;br /&gt;&lt;br /&gt;Programming Tip of the Day: Read the Fine Manual.&lt;br /&gt;&lt;br /&gt;Other work includes interpreting mouse clicks and positioning the cursor appropriately.  After that I did arrow up and arrow down; the clicking was needed because I simulate moving the caret up by pretending there's a click on the line above the current one.  This works remarkably well.&lt;br /&gt;&lt;br /&gt;Back to sleep!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-5188731933148110138?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/5188731933148110138/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=5188731933148110138' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5188731933148110138'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5188731933148110138'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-117-caret.html' title='Day 117: Caret'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-4882569047708117224</id><published>2008-05-26T19:15:00.005-04:00</published><updated>2008-05-26T19:34:06.421-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='text'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='sitdown'/><title type='text'>Day 116: Line10k</title><content type='html'>I finished my compulsory two hours of work today two lines of code short of 10,000, so I went on to work for another two hours.  If you look at the summary to the right, you'll note that in that two hours, I wrote a whopping 10 lines of code.  This was because there were tons of bugs and it required a lot of subtle fixes that were also wrong.&lt;br /&gt;&lt;br /&gt;All four hours were spent on &lt;a href="http://llynmir.net/projects/kuiper/ticket/125"&gt;#125&lt;/a&gt;, the ticket to put in saner text editors.  As it was before, you could only change the last bit of anything you were typing.  So if you'd made a typo a paragraph earlier and just now noticed it, you'd have to delete your way to it, fix it, and then re-type everything.&lt;br /&gt;&lt;br /&gt;Today marks the second time I've had to &lt;a href="http://kuidev.blogspot.com/2008/04/day-85.html"&gt;sit down&lt;/a&gt; and do some work on paper.  It initially seemed easy to figure out where a cursor should be drawn; I was iterating through each line of the input, adding the size of that line to a running total, which I would then compare to the caret's position.  This was error prone and hard to tune.&lt;br /&gt;&lt;br /&gt;Then, after writing down an example, I figured out that I could determine which row of the input the caret was on by counting the number of newlines before it.&lt;br /&gt;&lt;br /&gt;Later, I kept getting these strange off-by-one errors.  I'd been thinking that the caret - an index pointing to where in the original string any changes will be made - was off from the displayed string because the display had extra newlines put in there from word wrapping.  It wasn't until I wrote down a sample situation that I realized the extra newlines were replacing spaces (because that's where the words would wrap) and my intricate correction code was causing the off-by-one problems.&lt;br /&gt;&lt;br /&gt;Additionally, because I want 'home' and 'end' to be valid text editing keys, I had to change the "emergency end the program" key away from 'end'.  It's the backslash now.  I accidentally ended the program twice today anyway.&lt;br /&gt;&lt;br /&gt;For those interested, the 10,000th line of code is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;      &amp;lt;child name='flags'/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-4882569047708117224?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/4882569047708117224/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=4882569047708117224' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4882569047708117224'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4882569047708117224'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-116-line10k.html' title='Day 116: Line10k'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1392188610944356864</id><published>2008-05-25T20:38:00.003-04:00</published><updated>2008-05-25T20:54:50.453-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Day 115: Tutorial</title><content type='html'>Programming work was done yesterday, but by the time I finished it was no longer day 114.  1.75 hours of programming was enough to finish up the Missions milestone!  From now on, it's module development.&lt;br /&gt;&lt;br /&gt;The problem with tracking my progress is that every file saved by Kuiper was just one line.  One gigantic line with no linebreaks - it's XML, so this actually doesn't matter, but it makes reading it with an ordinary text editor a pain.  It's this way because any whitespace was interpreted as a text node, and my code wouldn't check for that, instead trying to re-create a ship from whitespace.  This is even more impossible than it sounds :)&lt;br /&gt;&lt;br /&gt;So the first part of my day was spent changing the way the XML serialization works.  It now skips over any text nodes (I don't use them; all text ends up in attributes, which is a bit messy but works) and once more passes the tests.  With that out of the way, I can now use &lt;a href="http://cloc.sourceforge.net/"&gt;Cloc&lt;/a&gt; to count work on my module as lines of code.&lt;br /&gt;&lt;br /&gt;The first module I'm making is, in my opinion, one of the most important:  &lt;a href="http://llynmir.net/projects/kuiper/milestone/Tutorial"&gt;The Tutorial&lt;/a&gt;.  One of the reasons I'm doing this now as opposed to later when the editor is more stable is that I'm trying to shake bugs out of the editor.  Later one of my goals is to make a scenario-making tutorial, and I'll likely rewrite the original tutorial then, so if I mess up now it's not a huge deal.&lt;br /&gt;&lt;br /&gt;Two bugs fixed today in support of the tutorial:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://llynmir.net/projects/kuiper/ticket/159"&gt;#159&lt;/a&gt; If you ever lose sight of the planet, it's very easy to get lost and not find your way back.  Even for me, and I wrote the darn game!  Now, if the center of the sector starts leaving sight, a green arrow appears on the radar to show you which way to head back.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://llynmir.net/projects/kuiper/ticket/132"&gt;#132&lt;/a&gt; Is the first part of the tutorial, giving basic info on how to move around and land on planets.  This one helped to reveal not exactly bugs, but things that would be nice.  For example, I've had &lt;a href="http://llynmir.net/projects/kuiper/ticket/125"&gt;#125&lt;/a&gt;: Better Text Editors open forever, but I've moved it to the tutorial milestone because now I'm typing a lot of text and it'd be nice to be able to edit it in a sane manner.&lt;br /&gt;&lt;br /&gt;My wrist hurts from typing; I'm taking the rest of the night off!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1392188610944356864?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1392188610944356864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1392188610944356864' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1392188610944356864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1392188610944356864'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-115-tutorial.html' title='Day 115: Tutorial'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-4959384901533945691</id><published>2008-05-23T21:42:00.003-04:00</published><updated>2008-05-23T21:54:16.925-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 113: MVC...E?</title><content type='html'>Everyone knows about &lt;a href="http://en.wikipedia.org/wiki/Model-view-controller"&gt;Model-View-Controller&lt;/a&gt;, but given that I've built my editor into my game, I feel like I've added a fourth leg:  Editor.  Now, my editor's pretty smart about inferring what it needs to display from the model, but there are things it can't handle, and that stuff I need to update.&lt;br /&gt;&lt;br /&gt;Eventually, I may be able to change that.  The Kuiper module format (and save game format) is XML, and I'd like to be able to export an &lt;a href="http://www.w3schools.com/Schema/default.asp"&gt;XSD&lt;/a&gt; - but to do that, the models are going to have to have more information about the types they understand.  With that information, I can make my editor smarter, and return to the MVC trifecta.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://llynmir.net/projects/kuiper/ticket/146"&gt;#146&lt;/a&gt; is done, and was easy given that I got all the unit tests yesterday.  I had to mirror the refactoring I'd done to its model in the editor, and that was about it.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://llynmir.net/projects/kuiper/ticket/147"&gt;#147&lt;/a&gt; is also done:  From a model standpoint, random fetch quests look exactly like random fedex quests.  I was halfway through writing the editor when I realized this and scrapped it, using the fedex one for both.  I thought I had a bug where I was getting more than one cargo for one mission, but it's possible I simply accidentally picked up more than one mission.  I've been unable to replicate it, regardless.&lt;br /&gt;&lt;br /&gt;Finally, because doing so makes me within 1 bug of finishing milestone:Missions, I did &lt;a href="http://llynmir.net/projects/kuiper/ticket/142"&gt;#142&lt;/a&gt;.  The halo system was working fine, but ever since I did the de-spawning action (which takes a spawning action to undo), I wanted to backport it to halos.&lt;br /&gt;&lt;br /&gt;I have a tradition where, if I finish a milestone, I can stop work early for that day.  But I kinda want to catch up on lost time (my spreadsheet informs me that I'm still 8.5 hours behind schedule) so I may actually keep going.&lt;br /&gt;&lt;br /&gt;We'll see....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-4959384901533945691?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/4959384901533945691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=4959384901533945691' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4959384901533945691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4959384901533945691'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-113-mvce.html' title='Day 113: MVC...E?'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-2386917937933481940</id><published>2008-05-22T21:50:00.003-04:00</published><updated>2008-05-22T22:03:12.633-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='branches'/><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>Day 112: Branching Out</title><content type='html'>Good news, everyone!  &lt;a href="http://sourceforge.net/project/showfiles.php?group_id=172781"&gt;Rubygame 2.3.0&lt;/a&gt; is out!&lt;br /&gt;&lt;br /&gt;Actually, as it turns out, that's not good news - at least not for me.  2.3.0 is an interim release between the old Rubygame and Rubygame 3000 (or, as it is more properly known, Rubygame 3).  When Rubygame 3 development began, I ported my game over to it.  The release of 2.3.0 isn't a big deal so far as that's concerned, because all I really need to do is make sure that my game is released sometime after Rubygame 3 is, and my game's going to take a while to be released so this isn't a problem.  No, the reason it's an issue is because...&lt;br /&gt;&lt;br /&gt;... I'd have to learn about branches.&lt;br /&gt;&lt;br /&gt;Rubygame trunk switched from 3.0 development to 2.3, and 3.0 was moved into a branch, and I had no freaking clue what that meant.  Thus, I had to &lt;a href="http://svnbook.red-bean.com/en/1.1/ch04.html"&gt;do a little research&lt;/a&gt; into Subversion.  It turns out to be pretty easy.  I went to my rubygame working copy and did a:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;svn update&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then, following the directions as laid forth &lt;a href="http://svnbook.red-bean.com/en/1.1/ch04s05.html"&gt;in this section&lt;/a&gt;, I did:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;svn switch &lt;br /&gt;https://rubygame.svn.sourceforge.net&lt;br /&gt;/svnroot/rubygame/branches/3.0.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(Whitespace added so the edges aren't cut off - that's all actually one line)&lt;br /&gt;That command gave me the following error:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;svn: Failed to add directory &lt;br /&gt;'ext/chipmunk': object of the&lt;br /&gt; same name already exists&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The obvious solution was to blow away the chipmunk directory and update again, which seems to fix it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Disclaimer:&lt;/span&gt; I don't use any of the chipmunk features in my game, it having been begun before the library was integrated into Rubygame.  So you may have to rebuild your chipmunk.&lt;br /&gt;&lt;br /&gt;In the usual style of my posts, all the above text took 15 minutes.  I spent the other 45 refactoring code out of the random cargo generator and into &lt;a href="http://llynmir.net/projects/kuiper/ticket/146"&gt;#146&lt;/a&gt;: Random scout missions.  It was pretty easy to write those, as the cargo generator already had code to generate random destinations, and that's all scouting missions are.&lt;br /&gt;&lt;br /&gt;It passes its tests, but doesn't have an editing component.  That's tomorrow's work!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-2386917937933481940?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/2386917937933481940/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=2386917937933481940' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2386917937933481940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2386917937933481940'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-112-branching-out.html' title='Day 112: Branching Out'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-499331223996434987</id><published>2008-05-21T23:20:00.003-04:00</published><updated>2008-05-21T23:30:08.484-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='yaml'/><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 111: Working for a Living</title><content type='html'>I pulled a 12-hour work day today, and still managed to code something up.  I am super-programmer!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://llynmir.net/projects/kuiper/ticket/148"&gt;#148&lt;/a&gt; was closed today.  I meant to do it yesterday but forgot - given that you can do pretty much everything random mission related at this point, it's pretty much done.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://llynmir.net/projects/kuiper/ticket/150"&gt;#150&lt;/a&gt; was a bit more work.  The bug where the ship claimed to have twice as much cargo space as it ought to was due to the fact that it claimed to have twice as much &lt;span style="font-style:italic;"&gt;everything&lt;/span&gt;!  I had been wondering why my jumps between sectors were taking 6 seconds instead of 3.  This I found was a math error on my part, which I fixed.&lt;br /&gt;&lt;br /&gt;I was repeating myself in the ship cargo code - there were separate arrays for mission and non-mission related items.  This was stupid.  Now there is one, and the ship is smart enough not to lump mission cargo and non-mission cargo into one entity even if they are otherwise similar.&lt;br /&gt;&lt;br /&gt;My game file format is pretty backwards compatible.  If I add an extra field on in a newer version and then load up a scenario made in an older version, whatever extra field I added just gets its default value (which instantly makes my homebrew XML-based solution preferable over YAML, which sets it to nil).  It had a problem doing the opposite, however: if I removed a field and loaded a file which still had it, it would complain loudly and then die.  Now it complains loudly in a warning message duly logged, which you can then change the log level of if you're tired of hearing it.&lt;br /&gt;&lt;br /&gt;Super-programmer, away!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-499331223996434987?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/499331223996434987/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=499331223996434987' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/499331223996434987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/499331223996434987'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-111-working-for-living.html' title='Day 111: Working for a Living'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-5263921574942927158</id><published>2008-05-20T22:06:00.003-04:00</published><updated>2008-05-20T22:21:35.039-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 110: #150!</title><content type='html'>Today I filed my 150th bug.  &lt;a href="http://llynmir.net/projects/kuiper/ticket/150"&gt;#150&lt;/a&gt; exists because I did something I hardly ever do:  I stopped working on fixing something because I felt like I was done for the day.&lt;br /&gt;&lt;br /&gt;Most of the work I did today was on &lt;a href="http://llynmir.net/projects/kuiper/ticket/148"&gt;#148&lt;/a&gt;, specifically letting the player edit the mission generator and then having the planet pick it up.  These were both time consuming, though at least it was for different reasons.&lt;br /&gt;&lt;br /&gt;Editing a random mission generator uses the same setup as editing conditions or actions - because there are many different kinds, the user sees a list on the left of all subclasses, and clicking one gives a more detailed editor on the right.  Until today, I'd been committing sins against object-oriented programming:  The part which rendered editors for the different subclasses was one big case statement.&lt;br /&gt;&lt;br /&gt;I atoned for that sin today, removing the old code altogether and creating a hierarchy of handlers that know which subclasses they understand and render them accordingly.  Much refactoring was done!&lt;br /&gt;&lt;br /&gt;Actually using the editor turned out to be problematic.  Every condition and action has a 'post_load' method which sets up some instance variables.  For the randomly generated conditions and actions, this was never called because they're generated at runtime.  They never load!  This wasn't caught by the unit tests because they explicitly post_load everything.  I've fixed all of this, and conditions/actions no longer rely on post_load working.&lt;br /&gt;&lt;br /&gt;So back to #150.  Everything appears to work!  You can go to a planet, land, pick a number of random missions, accomplish them and get paid.  Only problem is, you can pick &lt;span style="font-style:italic;"&gt;as many&lt;/span&gt; of those missions as you want.  I can't seem to get randomly generated cargo to count against your maximum amount.  Also, that max seems to be reported as '20' instead of '10'.  Strangeness abounds...&lt;br /&gt;&lt;br /&gt;... but I'm sleepy.  It'll wait!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-5263921574942927158?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/5263921574942927158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=5263921574942927158' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5263921574942927158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/5263921574942927158'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-110-150.html' title='Day 110: #150!'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-3182441607919362529</id><published>2008-05-19T21:28:00.004-04:00</published><updated>2008-05-19T21:52:41.416-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='templating'/><category scheme='http://www.blogger.com/atom/ns#' term='erb'/><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 109: %BLOGTITLE%</title><content type='html'>For some reason I didn't make a ticket for this, despite knowing it was what I'd have to do next.  Ah well.&lt;br /&gt;&lt;br /&gt;Today's blog title represents what I did today.  As I suggested I might do in &lt;a href="http://kuidev.blogspot.com/2008/05/day-108-random.html"&gt;yesterday's blog&lt;/a&gt;, I worked on templating.  So now you supply the random mission generator with a template mission.  The resulting missions have a name with %TOKENS% replaced with sane strings (via lookup table), and also have whatever conditions,actions,and checks the template mission had in addition to the ones required for them to work.  There's also a 'cleanup' child on the generator which will be appended to the end of every 'end mission' action sequence, so you can undo what you had your template setup do.&lt;br /&gt;&lt;br /&gt;"Wait a second," you say, "Why do you go through all the trouble for %TOKENS% replacement?  Can't Ruby do this for you?"&lt;br /&gt;&lt;br /&gt;Why yes, yes it can.  Embedded Ruby (ERB) is heavily used by things such as &lt;a href="http://www.rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt;, and manages to interpret ruby in a safe way.  This is one of my big concerns; I don't want someone to distribute a Kuiper adventure that wipes your home directory if you fail a mission!&lt;br /&gt;&lt;br /&gt;The problem with ERB is exactly that it was made for webpages.  Take, for example, this bit of sample code:&lt;br /&gt;&lt;pre&gt;Take 5 tons of Weapons to &lt;br /&gt;&amp;lt;%= @mission.destination.name %&amp;gt;&lt;/pre&gt;&lt;br /&gt;Now, in the context of a webpage, ERB works great.  You, the web designer, provided that string above, so you know it doesn't contain any nasty code.  The part that might be bad is @mission.destination.name, but ERB knows to evaluate that in Ruby's safe mode.&lt;br /&gt;&lt;br /&gt;In the context of a game however, it fails utterly.  Sure, you - the scenario creator - provided the string, so you know it doesn't contain nasty code... but the user doesn't.  The roles have been reversed!  It's the user who has to run a string that might be unsafe.  ERB fails in this instance because the first thing it does is call ruby's eval... on the tainted string.&lt;br /&gt;&lt;br /&gt;Thus, I have created a ticket: &lt;a href="http://llynmir.net/projects/kuiper/ticket/149"&gt;#149&lt;/a&gt; compels me to remove all ERB and replace it with my less capable, but at least safer, alternative.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-3182441607919362529?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/3182441607919362529/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=3182441607919362529' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3182441607919362529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3182441607919362529'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-109-blogtitle.html' title='Day 109: %BLOGTITLE%'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-3086814398231774582</id><published>2008-05-18T20:33:00.004-04:00</published><updated>2008-05-18T20:50:04.129-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><title type='text'>Day 108: Random</title><content type='html'>I think I may stop labeling posts with the bug numbers mentioned in them, mainly because it's generating a &lt;span style="font-style:italic;"&gt;ton of labels&lt;/span&gt;.  Of course, if I stick with that it means I'll have to go through all my old posts and strip them out.  My main reasoning is that, so far, I've never had to go back and look at a bug I fixed, then look it up on the blog.&lt;br /&gt;&lt;br /&gt;Today I got back into the swing of coding by fixing some bugs:  &lt;a href="http://llynmir.net/projects/kuiper/ticket/143"&gt;#143&lt;/a&gt; and &lt;a href="http://llynmir.net/projects/kuiper/ticket/144"&gt;#144&lt;/a&gt;.  So now when you put a planet in a sector it won't vanish mysteriously, and you won't be prompted to take on a mission while you're editing a sector.&lt;br /&gt;&lt;br /&gt;That was just the appetizer, though.  The main event was &lt;a href="http://llynmir.net/projects/kuiper/ticket/111"&gt;#111&lt;/a&gt;: Random missions.  Well, there's now support in the engine for randomly creating fedex quests, though there's none in the editor or in playing the game.  Most of my time, in fact, was spent writing a giant unit test which only took the most basic scenario into account.  Still, it's all working and apparently working well, so tomorrow I'll probably move on to putting it into the game&lt;br /&gt;&lt;br /&gt;To give me a bit of inspiration, I looked back at how TraderMissions had done random missions, and the answer was "Horribly".  There was no difference between random and regular missions, random missions just had a few more children populated (i.e. a 'random cargo' child, which was empty in the case of a non-random mission).&lt;br /&gt;&lt;br /&gt;My current approach separates regular missions and random missions entirely.  Random missions don't even inherit from regular missions, instead they &lt;span style="font-style:italic;"&gt;create&lt;/span&gt; them.  If I were to have discovered this on my own, I'd have named it the &lt;a href="http://en.wikipedia.org/wiki/Factory_method_pattern"&gt;Factory&lt;/a&gt; pattern.&lt;br /&gt;&lt;br /&gt;There's still some issues, though.  The generated missions need details like names, descriptions, etc.  There's enough in common between regular missions and random mission generators that I'd like to avoid repeating myself.  I'm thinking of having a 'template' child for the generator; this child will be a mission - the name of it will get expanded into the names for all of the missions.  So the template mission name would be something like "[%ORGANIZATION%] Take %CARGO% to %DESTINATION%" and the resulting generated mission name would be "[Rebels] Take Fruit to Mars"&lt;br /&gt;&lt;br /&gt;It's good to be back!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-3086814398231774582?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/3086814398231774582/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=3086814398231774582' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3086814398231774582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3086814398231774582'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-108-random.html' title='Day 108: Random'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-7894566626520530087</id><published>2008-05-16T13:21:00.003-04:00</published><updated>2008-05-18T20:53:54.988-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 106: That Bounty is Mine!</title><content type='html'>No blog yesterday because I was incredibly busy at work, and only managed a half hour of my project.  I'm having company visit over the weekend, so expect a lack of entries then, too.&lt;br /&gt;&lt;br /&gt;I did accomplish something, however!  &lt;a href="http://llynmir.net/projects/kuiper/ticket/141"&gt;#141&lt;/a&gt; is working well.&lt;br /&gt;&lt;br /&gt;My first plan for tracking when you killed your bounty was simply to have the sector keep a list of every fleet you'd ever killed.  This is less stupid than it sounds - it would only record the base of the fleet (for example, 'fleet_earth_patrol') rather than the specific instance of the fleet you'd killed.  Since the base fleets already exist in the game, it's not taking up that much more space.&lt;br /&gt;&lt;br /&gt;This wouldn't have worked, however, because you'd have to have a separate fleet for each and every bounty mission you ever wrote.  Considering that I want random bounty generation to be possible, this wasn't the way to go.&lt;br /&gt;&lt;br /&gt;So now the KuiSpawnAction which spawns the ships has a special flag called "watch_for_destruction".  Any mission that performs an action with this flag adds the fleets that spawner made to its list of fleets to watch.  Any time the fleet is destroyed, the mission is notified.  There is now a KuiDestroyedCondition which looks at the fleets that have been wiped out, compares it to the watched fleets, and returns true if it passes.&lt;br /&gt;&lt;br /&gt;That worked fine, it turns out; except that the instant you kill the fleet, it spawns again (because in my test scenario, it has a 100% chance to spawn) even though you've satisfied the conditions to end the mission.  This is because the condition won't be checked until you land on a planet or leave the sector.  Even if I made the conditions be checked immediately, that still wouldn't solve the case where you have two fleets to destroy, one in this sector and one somewhere else.&lt;br /&gt;&lt;br /&gt;I spliced in a bit of the original idea:  When you kill a fleet, if it's watched by any of your missions, the sector remembers that you killed it.  Then, when it comes time to spawn the fleet again, it looks at that list and doesn't do so if the fleet is unique (which almost all of the spawned ones will be).  De-spawner actions remove fleets from the list so it doesn't get gigantic.&lt;br /&gt;&lt;br /&gt;Finally, 'end misson' actions now clear out the list of fleets to watch and fleets that have been killed for their mission.  With that in place, the mission is now repeatable!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-7894566626520530087?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/7894566626520530087/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=7894566626520530087' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7894566626520530087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7894566626520530087'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-106-that-bounty-is-mine.html' title='Day 106: That Bounty is Mine!'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-2900103044918932941</id><published>2008-05-14T20:47:00.004-04:00</published><updated>2008-05-18T20:53:35.071-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 104: Not Everybody Hates Roger</title><content type='html'>Long ago, before I even started this blog, I was testing the game over screen.  There were one of two ways for me to do this: I could either put in a 'self destruct' key that would end the game and then accidentally yet constantly press it while doing other things from that day forward, or create a number of badass ships with tons of weaponry that hated my guts.  I opted for the latter.&lt;br /&gt;&lt;br /&gt;And only &lt;span style="font-style:italic;"&gt;just&lt;/span&gt; today, months after the fact, did I bother to change the faction back to being neutral.&lt;br /&gt;&lt;br /&gt;What can I say, it made things more exciting!&lt;br /&gt;&lt;br /&gt;The reason for the change back was to test the results of &lt;a href="http://llynmir.net/projects/kuiper/ticket/140"&gt;#140&lt;/a&gt;, my first stab at bounty hunting.  I spun off the "verify that the target is dead" part into another ticket, so thus far the only thing you can do is to put fleets into a sector and remove them.&lt;br /&gt;&lt;br /&gt;Initially, I was going to do this by having a special field in the sector for 'mission fleets' which would always spawn.  TraderMissions did this similarly, but more stupidly, by spawning the fleet if you're in the sector when it checks on the mission (much how popups are done).&lt;br /&gt;&lt;br /&gt;Then I thought perhaps the editor wouldn't want them to always spawn, but only sometimes.  I already have a handler for that, and it's the same "potential fleets" field that the sectors already use to show fleets.  So the new actions just use that.  I created a 'unique' flag so if you want to spawn pirates that are guaranteed to appear and never go away, other identical pirate groups don't also spawn in.&lt;br /&gt;&lt;br /&gt;This turns out to be more powerful than the original approach would have been.  Now a mission to deliver, say, planetary defenses could eliminate a (not otherwise mission related) enemy spawn from that sector.  &lt;br /&gt;&lt;br /&gt;The de-spawning action does something new - one of its children can be a spawner action, which the de-spawner then proceeds to undo.  This is because I forsee 'Bounty Hunt' style missions be of the form "Go here, kill this fleet", and you don't want the fleet to still be there once the mission is over.  It's a lot easier to just say "Undo this spawn action" than it is to remember which fleets you spawned and which locations you spawned them in.  I'll probably do something similar for the sector highlights.&lt;br /&gt;&lt;br /&gt;In all this, I had to create a new fleet, which is how I discovered that over the weekend I'd completely broken the "old way" of building things.  So every time I run into this, I'm installing a MiniBuilder, which seems to be the way to go.&lt;br /&gt;&lt;br /&gt;This has been another productive day.  I seem to be alternating.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-2900103044918932941?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/2900103044918932941/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=2900103044918932941' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2900103044918932941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2900103044918932941'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-104-not-everybody-hates-roger.html' title='Day 104: Not Everybody Hates Roger'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-8027002871364064863</id><published>2008-05-13T21:00:00.002-04:00</published><updated>2008-05-18T20:53:46.688-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><category scheme='http://www.blogger.com/atom/ns#' term='continuation'/><title type='text'>Day 103: Blogging - "Kuiper Dev Blog"</title><content type='html'>Today's subject likens to that two-line fix I did today, &lt;a href="http://llynmir.net/projects/kuiper/ticket/35"&gt;#35&lt;/a&gt;.  That's right, when editing a module the game will change its window title to "Kuiper - Editing 'Your Game Title Here'", and when playing it'll be "Kuiper: Your Game Title Here".  This ticket was put in a long time ago because I couldn't tell my editing and testing windows apart.  I had some spare time at the end of the day and hacked it in.&lt;br /&gt;&lt;br /&gt;Sadly, that was the only easy part of today.  Most of the work was on &lt;a href="http://llynmir.net/projects/kuiper/ticket/135"&gt;#135&lt;/a&gt;, getting sectors to do what Planets were doing easily - in this particular case, that's handling missions.  The main problem was that planets' checking was simple, they had to do it once because a new planet GUI is re-created every time you land.  The sector GUI sticks around when you go to other states.&lt;br /&gt;&lt;br /&gt;The 'return from another state' code looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def awaken&lt;br /&gt;  @continue.call if continue&lt;br /&gt;&lt;br /&gt;  do_mission_checks&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It has to be like that, because do_mission_checks requires the continuation support in order to work.  But the problem is, if any other state sets @continue (and they all do), then it's called and the mission checking never happens.&lt;br /&gt;&lt;br /&gt;The fix is, take every state you're transitioning to with a continuation and, when it comes back, force a call to awaken:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def land&lt;br /&gt;  callcc do |continue| &lt;br /&gt;    @continue = continue&lt;br /&gt;    @driver &lt;&lt; planet&lt;br /&gt;  end&lt;br /&gt;  # We'll resume here&lt;br /&gt;  simulate_passage_of_time&lt;br /&gt;  awaken unless @continue&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That way we call awaken when we need it to process mission events, but not if calling it would call a continuation.  All told I wrote about 8 lines of code, net.  If only all days were as simple as yesterday!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-8027002871364064863?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/8027002871364064863/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=8027002871364064863' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8027002871364064863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8027002871364064863'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-103-blogging-kuiper-dev-blog.html' title='Day 103: Blogging - &quot;Kuiper Dev Blog&quot;'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-2623960234385015645</id><published>2008-05-12T22:22:00.003-04:00</published><updated>2008-05-18T20:53:50.569-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><title type='text'>Day 102: Adapting</title><content type='html'>Today was a pretty standard day, code-wise.  I worked on &lt;a href="http://llynmir.net/projects/kuiper/ticket/136"&gt;#136&lt;/a&gt;, which displays all the missions the player has in case they need to be reviewed at any point.  TraderMissions had dialogs for cargo, missions, weapons on board, etc, and they each had a different key you'd have to press to get to them; this time I was determined not to be so annoying.  Thus I'd long ago made &lt;a href="http://llynmir.net/projects/kuiper/ticket/62"&gt;#62&lt;/a&gt;, a request for a show-everything-about-this-ship dialog.&lt;br /&gt;&lt;br /&gt;Well, if you've been reading this blog you know by now that I just can't leave well enough alone.  Rather than start immediately on the mission specific dialog, I decided to make a general dialog.&lt;br /&gt;&lt;br /&gt;I discovered pretty quickly that I'd been thinking about this all along - the cargo dialog inherited from a ShipPartViewer that I'd obviously intended to be the parent of any such states.  So if I wanted to view addons on the ship I could subclass it to create an AddonViewer, etc.  (Indeed, I discovered, I'd actually created those viewers if not hooked them up to events)&lt;br /&gt;&lt;br /&gt;Creating a subclass for every type I want viewed seemed... &lt;a href="http://kuidev.blogspot.com/2008/04/day-85.html"&gt;familiar&lt;/a&gt; somehow.  It's exactly the sort of thing I created the adapters for, so rather than go through the comparatively simple process of writing a MissionViewer subclass of ShipPartViewer, I took the more difficult path of writing a PlayerMissionAdapter, then refactoring ShipPartViewer to use whatever adapter it was given for display purposes.  That done, I could remove all the other ShipPartViewer subclasses entirely!&lt;br /&gt;&lt;br /&gt;This sounds much more complex than it was, mainly because ShipPartViewer was already made to be extendable, so the retrofit was not very difficult.  Unlike the weekend, I did not spend hours tracking down subtle continuation bugs.  I just coded up a few adapters, fixed some typos when it broke, and was finished for the night.&lt;br /&gt;&lt;br /&gt;I need more coding days like this one!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-2623960234385015645?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/2623960234385015645/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=2623960234385015645' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2623960234385015645'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/2623960234385015645'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-102-adapting.html' title='Day 102: Adapting'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1669990510303789617</id><published>2008-05-11T19:24:00.002-04:00</published><updated>2008-05-18T20:53:43.258-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><category scheme='http://www.blogger.com/atom/ns#' term='continuation'/><title type='text'>Day 101: Time Travel</title><content type='html'>FINALLY, I got &lt;a href="http://llynmir.net/projects/kuiper/ticket/137"&gt;#137&lt;/a&gt; done.  It feels like I've been working on it forever, probably because of all the work I had to do yesterday.&lt;br /&gt;&lt;br /&gt;The fun thing is, today I ripped out most of that work and replaced it with an entirely different way of working.  One that was more unit-testable, for one, which made it a lot easier, and what I ended up doing made a lot more sense.&lt;br /&gt;&lt;br /&gt;Let's say you were going to land on a planet that had a plot mission for you.  Here's how it would work before:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;For every plot mission, find out if it's awardable:&lt;/li&gt;&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Return false immediately if there's any condition we can determine right now is failing&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Otherwise, return a list of each condition that's going to need user input.&lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt;&lt;li&gt;If we got a list instead of 'true', go through that list and pop up questions for all of it.  If they're all answered right, then go on to award the mission.&lt;/li&gt;&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;  &lt;li&gt;If any of these actions is going to need to display something on the screen, return them right now before awards are done.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Otherwise, do the actions&lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Again, if we got a list instead of 'true', we have to go through the list and make sure the user sees all of the dialog.  Then call the award function again but tell it to ignore calls to pop stuff up.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;This was incredibly complicated and error-prone, as demonstrated yesterday.  I kept thinking of how handy it'd be to be able to, when we see a condition or action that requires user input, stop right there and do it, then come back.  If only there was a mechanism that let me do that which I'd &lt;a href="http://kuidev.blogspot.com/2008/05/day-100-wild-and-crazy-ride.html"&gt;mentioned&lt;/a&gt; &lt;a href="http://kuidev.blogspot.com/2008/05/day-95-make-mine-mini.html"&gt;before&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;So, continuations to the rescue!  The hardest thing about reimplementing the system to use continuations is that my analogy from the other day was more apt than I knew.  Continuations &lt;span style="font-style:italic;"&gt;are&lt;/span&gt; a time machine.  To explain, let me show you some code I'd played with in irb:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def contFun2&lt;br /&gt;  puts "Is this fun?"&lt;br /&gt;  callcc { |x| return x }&lt;br /&gt;  return 'yes'&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;irb(main):048:0&gt; z = contFun2&lt;br /&gt;Is this fun?&lt;br /&gt;=&gt; #&amp;lt;Continuation:0xb7c99c14&amp;rt;&lt;br /&gt;irb(main):049:0&gt; z&lt;br /&gt;=&gt; #&amp;lt;Continuation:0xb7c99c14&amp;rt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So far so good; when I call the function, I get a continuation from the middle of it.  This continuation remembers things like variables and, most importantly for our purposes,the call stack.  Now watch what happens when I use it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;irb(main):050:0&gt; y = z.call&lt;br /&gt;=&gt; "yes"&lt;br /&gt;irb(main):051:0&gt; y&lt;br /&gt;=&gt; nil&lt;br /&gt;irb(main):052:0&gt; z&lt;br /&gt;=&gt; "yes"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You might be surprised to see that - z.call doesn't appear to return anything, and z itself somehow turned from a continuation to a string!&lt;br /&gt;&lt;br /&gt;What actually happened behind the scenes is that, when I called the continuation, the stack as it was vanished, replaced with what it had been.  And what had been was a call to my function, just after the callcc line.  Thus, the function returns "yes", and that yes is assigned to z, because that assignment was on the stack before the function call.  Once I understood what was going on there, it wasn't hard to make a new mission evaluator:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;For every plot mission, find out if it's awardable:&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Call mission.awardable?  It'll return true, false, or, a [ condition, continuation ] pair - pop up the condition and when we're done with that, call the continuation.  Repeat until we get true or false.&lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt;&lt;li&gt;If it was awardable, award it.&lt;/li&gt;&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Call mission.award.  It'll return true or a [condition, continuation] pair.  Handle this exactly as above.&lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt;&lt;li&gt;There's no step three!&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Tomorrow: Using all of this so you can get missions just from flying into a sector.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1669990510303789617?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1669990510303789617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1669990510303789617' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1669990510303789617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1669990510303789617'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-101-time-travel.html' title='Day 101: Time Travel'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1432982136704440799</id><published>2008-05-10T18:59:00.003-04:00</published><updated>2008-05-18T20:53:43.259-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><category scheme='http://www.blogger.com/atom/ns#' term='continuation'/><title type='text'>Day 100: Wild and Crazy Ride</title><content type='html'>100 days of programming!  Barring a week where my sinuses were killing me and a month where I bought and moved into a new house, I've been programming pretty much nonstop.&lt;br /&gt;&lt;br /&gt;Today's story is, I got quite a bit through having missions able to pop up information to the player, which is the primary way of getting the plot of the game across.  I shall now explain how this came about.&lt;br /&gt;&lt;br /&gt;First, a note:  &lt;a href="http://llynmir.net/projects/kuiper/ticket/127"&gt;#127&lt;/a&gt; was a minor bug that annoyed me.  You'd click on a button in one state, and any buttons in that same location in the parent state would end up clicked too.  I'll come back to that.&lt;br /&gt;&lt;br /&gt;My main work was on &lt;a href="http://llynmir.net/projects/kuiper/ticket/137"&gt;#137&lt;/a&gt;.  Presenting the player with messages when they're given a mission turned out to be very easy, so long as I was doing only that.  It actually displayed the correct message the first time I ran the program, which almost never happens for anything.  I was very happy!&lt;br /&gt;&lt;br /&gt;I'd made the dialog box which displays this general enough that it could handle yes/no questions as well, so I figured I'd go on to the next part, which is having conditions ask questions.  So you could land on a planet, get a question asking "Do you want to be recruited into this planet's defense force?", and if you answer yes you get the mission.&lt;br /&gt;&lt;br /&gt;This worked pretty much first thing as well.  I was quite happy, but noticed something awry.  Specifically, I clicked 'yes' to the mission, but didn't get the follow-up "You accepted the mission" message I'd put in the test mission.&lt;br /&gt;&lt;br /&gt;An aside:  I'd unit tested everything I could about these new conditions and actions, but as they displayed items on screen and rely on the user for input, this turned out to not be much.&lt;br /&gt;&lt;br /&gt;I ended up having to make the evaluator a full-fledged state rather than the 'plugin' sort of existence it had before, because it made handling the continuations that much easier.  This ended up not helping and I'm considering reverting that particular change (because a plugin is /far/ more useful in more situations than a full-fledged state).  I figured something was going wrong with my continuations.  As mentioned before, &lt;a href="http://kuidev.blogspot.com/2008/05/day-95-make-mine-mini.html"&gt;I love continuations&lt;/a&gt; but I'll freely admit they're somewhat of a black art to me.  I know enough to be dangerous and get some things done, but when it starts falling apart I'm not sure where to look.&lt;br /&gt;&lt;br /&gt;Eventually, print statements are everywhere.  What they tell me, in every single case, is that the message dialog is, in fact, being displayed, despite the fact that I can't see it.  I spend a good full hour doing nothing but putting tracer code in my entire setup to find out when this is happening.&lt;br /&gt;&lt;br /&gt;Suddenly, it worked!  I immediately realized that I hadn't done anything.  I frowned, horrible suspicion dawning in my mind.  I ran the program again, clicked the button in the exact middle, and didn't see the message.  One more time through, clicking the button off to the side where there was no corresponding button in the message displayer, however?  That worked fine.&lt;br /&gt;&lt;br /&gt;I'd spent an hour tracking down a bug I already knew existed.  Thanks, #127.&lt;br /&gt;&lt;br /&gt;The rest of my time was spent fixing #127, which proved surprisingly difficult.  I figured that there was a situation where the mouse-up would trigger a state change before the mouse-up was done fully processing, and so the new state would get it.  But that turned out to not be the case.  My army of print statements helped here, because what I saw happen was something like this: (output made up because it's fixed now and I don't have anything to paste here)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Beginning event handling loop&lt;br /&gt;Got mouse down event#&amp;lt;MouseDown #7544&amp;gt;&lt;br /&gt;Got mouse up event #&amp;lt;MouseUp #4556&amp;gt;&lt;br /&gt;State transitioned&lt;br /&gt;Ended event handling loop&lt;br /&gt;...&lt;br /&gt;Beginning event handling loop&lt;br /&gt;Got mouse down event#&amp;lt;MouseDown #7544&amp;gt;&lt;br /&gt;Got mouse up event #&amp;lt;MouseUp #4556&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I wasn't getting duplicate mouse-ups that weren't handled, this was two separate times through the event loop, yet somehow I was getting the &lt;span style="font-style:italic;"&gt;exact same events&lt;/span&gt; as before.&lt;br /&gt;&lt;br /&gt;It was this that led me to the 'time machine' metaphor for continuations.  They don't just return you to where they left off, they change the variables and such to be exactly like they were then.  This is handy because otherwise I wouldn't be able to refer to them, but putting continuations in the middle of my event-handling loop ended up rewinding and then replaying it!&lt;br /&gt;&lt;br /&gt;I fixed this by having the event loop just build an array of MouseUp events, then later - once outside that loop - going through them and calling the actual functions.  It seems to work; that loop itself never gets rewound or if it does it's not causing the same problems.&lt;br /&gt;&lt;br /&gt;But &lt;span style="font-style:italic;"&gt;man&lt;/span&gt;!  What is it about the stuff that's supposed to be easy that makes it so hard?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1432982136704440799?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1432982136704440799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1432982136704440799' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1432982136704440799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1432982136704440799'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-100-wild-and-crazy-ride.html' title='Day 100: Wild and Crazy Ride'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-4083259606610747767</id><published>2008-05-09T21:55:00.003-04:00</published><updated>2008-05-18T20:51:39.314-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ptotd'/><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><category scheme='http://www.blogger.com/atom/ns#' term='polish'/><title type='text'>Day 99: Halo</title><content type='html'>Today's got a double dose of programming tips.&lt;br /&gt;&lt;br /&gt;I started work today on &lt;a href="http://llynmir.net/projects/kuiper/ticket/98"&gt;#98&lt;/a&gt;, map feedback selection.  That's because I wanted to work on &lt;a href="http://llynmir.net/projects/kuiper/ticket/133"&gt;#133&lt;/a&gt;, missions giving halos to sectors.  The idea being that, if you're supposed to take cargo to a certain sector, the mission would highlight the map for you.  It was a neat little action I thought I could do in a day.  And I was right... mostly.&lt;br /&gt;&lt;br /&gt;#98 was pretty easy - I made a Halo type which holds on to a color, and gave sectors the ability to hold onto them.  When you click on a sector on the map, it's given the 'selection halo' and it's pretty obvious where you've clicked.  That halo moves around when you pick new sectors, and is removed entirely when you leave the map screen (otherwise it'd persist in the save games!)&lt;br /&gt;&lt;br /&gt;With 15 minutes to spare, I figured I might as well get started on the #133, and I wrote up the action and its associated unit test.  They passed pretty easily (wasn't a difficult thing to code up).  So I figured I'd put the theory into practice and add a little highlighting to my test mission. I go into the editor, add the action, save the scenario, then run the game.  I get the mission.&lt;br /&gt;&lt;br /&gt;No highlight.  "Hmmm, that shouldn't be.  I tested this!"&lt;br /&gt;&lt;br /&gt;I look at the save game, and I've got the mission all right, but no halos have been attached to anything.  I go back to the tests and note that my unit tests only test one "setup" action, and this particular mission has two.  I add halos to the 'mission cycle' test to make sure they're happening when I get the mission.&lt;br /&gt;&lt;br /&gt;Ah-ha!  They're not!  My previous halo tests had only tested whether or not they worked, not whether they were getting called at all.  Certain now that I'd tracked down a bug where the mission was only doing the first bit of the setup, I swapped the order of the items and ran the tests again, only to find it &lt;span style="font-style:italic;"&gt;still&lt;/span&gt; wasn't happening!&lt;br /&gt;&lt;br /&gt;Finally, I looked into the code that awards the mission.  That's where I found that my 'setup' code isn't being called at all.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Programming Tip of the Day:&lt;/span&gt;  If your method isn't doing anything, try actually calling it.&lt;br /&gt;&lt;br /&gt;I fix it.  I start the game back up again, get the mission, pull up the map, and...&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;STILL NO HALOS!&lt;br /&gt;&lt;br /&gt;I look at the save game, and at least this time they're actually being applied, so I can look somewhere other than the code I'd just fixed.  I know that halos on the map work, because otherwise I wouldn't see any when I select sectors (and those are still showing up) - I look into the part where it sets up the buttons for the sectors to begin with and find out that, hey, it's not actually telling the buttons to have halos, thus the 'halo' array was empty.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Programming Tip of the Day:&lt;/span&gt; If there's nothing in your array, maybe it's because you're not adding anything.&lt;br /&gt;&lt;br /&gt;After all that, they finally displayed, but weren't displaying &lt;span style="font-style:italic;"&gt;correctly&lt;/span&gt;.  A bug made them alternate halo colors, rather than display them in bands from the in-side out (the selection halo should always be on the outside, so the player can tell it's selected).  It actually looked cooler than the intended result, but it would fail horribly on more than one halo, so it had to be fixed.&lt;br /&gt;&lt;br /&gt;At least I got an extra 45 minutes of programming in.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-4083259606610747767?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/4083259606610747767/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=4083259606610747767' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4083259606610747767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4083259606610747767'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-99-halo.html' title='Day 99: Halo'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-427358468432135787</id><published>2008-05-08T20:29:00.005-04:00</published><updated>2008-05-18T20:53:10.460-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ptotd'/><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 98: Ticket Quota</title><content type='html'>Programming Tip of the Day:  If your configuration changes don't actually change anything, make sure you're changing them on the right machine!&lt;br /&gt;&lt;br /&gt;Today I finished the missions cycle; when you get a mission you can now complete it by fulfilling its ending requirements.  This turned out to be pretty easy - I have a MissionEvaluator that helps out in this case.  In the old TraderMission days, it was a full-fledged state, but that meant I could only complete (or give) missions whenever a state would transition, and not when the player's just flying around.  Now, you could create a mission that gives players 1000 credits just for hanging out in a sector for a given amount of time.&lt;br /&gt;&lt;br /&gt;I had a strategy when it came to putting in tickets to the tracker.  I'd only do it if I came up with an idea that I couldn't do immediately (which is why &lt;a href="http://llynmir.net/projects/kuiper/milestone/Polish"&gt;milestone:Polish&lt;/a&gt; currently has 36 outstanding tickets) or at the end of a session so I'd remember what I was working on when I started up next.&lt;br /&gt;&lt;br /&gt;I finished &lt;a href="http://llynmir.net/projects/kuiper/ticket/123"&gt;#123&lt;/a&gt; at the very end of the session.  And I found out that instead of creating no tickets, I created tickets for &lt;span style="font-style:italic;"&gt;anything&lt;/span&gt; that I might want to do next.  It's a much better way of putting tickets in the tracker, and I think does a lot for my productivity. Knowing what I'm going to work on next removes some of the fun.  Now I've got tons to choose from!&lt;br /&gt;&lt;br /&gt;Wait, that might not be a good thing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-427358468432135787?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/427358468432135787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=427358468432135787' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/427358468432135787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/427358468432135787'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-98-ticket-quota.html' title='Day 98: Ticket Quota'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-8172635200584528329</id><published>2008-05-07T21:01:00.004-04:00</published><updated>2008-05-18T20:53:26.048-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 97: Retro</title><content type='html'>Let's talk UI.&lt;br /&gt;&lt;br /&gt;So I finished my new Builder dialog today.  You have two columns; on the left are items that are currently in the thing you're building (i.e. cargo for sale on a planet, weapons on a ship, etc), and on the right are all the things you have to choose from (all possible cargo, all possible weapons, etc).  It's beautiful and works great.  The reason I worked on it was so that it could replace the old build dialog which did the same thing more slowly.  The old build dialog had an offshoot called the MarketDialog which I also intend to replace with this.  So on the left the player will see all the cargo on their ship, and on the right they'll see all the cargo for sale.&lt;br /&gt;&lt;br /&gt;So far so good.  I went through the effort in this milestone because I didn't want to write the "Mission Computer" dialog in the old style when I was going to transition to the new style.&lt;br /&gt;&lt;br /&gt;But - upon completion - it occurred to me that this is a horrible UI for choosing missions.  Everything else: Cargo, weapons, addons, details don't especially matter (well they do, but only to a limited extent).  Whereas with Missions, they drive the plot along.  I need a preview area to allow all this text that describes them.  Also, "Have/available" is not that great a layout for missions.  With weapons, it's important to know that you have a missile launcher before stocking up on missiles.  For missions?  Your current missions don't matter at all; every mission is independent.&lt;br /&gt;&lt;br /&gt;What I needed for missions, in other words, was one list to display them all on the left, a large preview area, and buttons on the right to accept the mission with.&lt;br /&gt;&lt;br /&gt;That is, of course, exactly what the old UI did.  Hooray for diversions!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-8172635200584528329?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/8172635200584528329/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=8172635200584528329' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8172635200584528329'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8172635200584528329'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-97-retro.html' title='Day 97: Retro'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-7808094842863765503</id><published>2008-05-06T20:30:00.003-04:00</published><updated>2008-05-18T20:53:26.049-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 96: Rebuilding</title><content type='html'>The minichooser is done!  It's tiny and choosing things.&lt;br /&gt;&lt;br /&gt;I also fixed a few bugs, the upside of which is that I made my first fully-playable mission today, a fedex mission to deliver cargo to the next planet over.  Thus I begin work on &lt;a href="http://llynmir.net/projects/kuiper/ticket/123"&gt;#123&lt;/a&gt;: letting the player go to the first planet and actually get the mission.&lt;br /&gt;&lt;br /&gt;This presented me with a dilemma:  I needed to write the "Mission Computer" dialog.  I've written a number of dialogs like these before, and in pretty much all cases they're going to be phased out for the MiniBuilder and a new 'multiple add' dialog for it.  So I could write something working right now based on the old framework and transition it, or I could put #123 on hold and create the new framework.&lt;br /&gt;&lt;br /&gt;If you've been reading the blog so far, you'll know I'm a sucker for making changes unrelated to my current objective, so "On hold" it is!  I created &lt;a href="http://llynmir.net/projects/kuiper/ticket/130"&gt;#130&lt;/a&gt; for the new-and-improved builder.  It'll essentially look like an FTP client, and will make moving multiple items to/from something very easy.  The 'add' button on the minibuilder will use this, and once it's done, adding missions will be very simple indeed!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-7808094842863765503?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/7808094842863765503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=7808094842863765503' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7808094842863765503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/7808094842863765503'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-96-rebuilding.html' title='Day 96: Rebuilding'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-3710297191949614106</id><published>2008-05-05T21:19:00.004-04:00</published><updated>2008-12-13T04:06:29.059-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><category scheme='http://www.blogger.com/atom/ns#' term='continuation'/><title type='text'>Day 95: Make Mine Mini</title><content type='html'>Today I made the first addition to the engine.rb file in a very long time; I moved the Waker module over to it.  The Waker module is what implements continuations, which if I'd known about them when I originally started this project, would have been even more integrated into the engine.&lt;br /&gt;&lt;br /&gt;Basically, here's what TraderMissions code for handling a dialog would look like, if TM was in Ruby:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Called every time this state becomes&lt;br /&gt;# the active one.&lt;br /&gt;def activate&lt;br /&gt;  if @dialog.yes?&lt;br /&gt;    do_things&lt;br /&gt;  end&lt;br /&gt;  if @other_dialog.stuff?&lt;br /&gt;    do_other_things&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The activate function has to check every possible state that it might transition into, see if it was called and if so what its result was, and deal with that.  This rapidly becomes horrible.  Whereas with continuations:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def activate&lt;br /&gt;  @continue.call if @continue&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that's set up when I originally transition to the other state:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def verify_cheese&lt;br /&gt;  cv = CheeseVerifier.new&lt;br /&gt;  callcc do | cont |&lt;br /&gt;    @continue = cont&lt;br /&gt;    @driver &lt;&lt; cv&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  enable_cheese if cv.yes?&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;@continue.call moves execution back to after the block where I originally set @continue, thus letting me pick up where I had transitioned away from the current state.  Hooray for continuations!&lt;br /&gt;&lt;br /&gt;Oh, right, the subject.  I'd actually done that continuation stuff months ago, the Waker thing was me moving it.  Most of the day was me creating a simple object selector.  I thought to myself "I need a MiniBuilder equivalent for those children which only have one object.  Thus:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_pIj5Y9n3oFo/SB-0tmMuQ-I/AAAAAAAAAdw/B6gm5iDt7E8/s1600-h/minichooser.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_pIj5Y9n3oFo/SB-0tmMuQ-I/AAAAAAAAAdw/B6gm5iDt7E8/s320/minichooser.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5197071190449865698" /&gt;&lt;/a&gt;&lt;br /&gt;THE MINICHOOSER!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-3710297191949614106?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/3710297191949614106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=3710297191949614106' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3710297191949614106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3710297191949614106'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-95-make-mine-mini.html' title='Day 95: Make Mine Mini'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_pIj5Y9n3oFo/SB-0tmMuQ-I/AAAAAAAAAdw/B6gm5iDt7E8/s72-c/minichooser.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-8239131943907188224</id><published>2008-05-04T15:16:00.004-04:00</published><updated>2008-05-18T20:53:14.611-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 94: Medicated</title><content type='html'>Sinuses have been acting up these past few days, but now, as the title proclaims, I am medicated and much better.  As such, I've gotten some stuff done:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://llynmir.net/projects/kuiper/ticket/110"&gt;#110&lt;/a&gt; is done; the mission editor has minibuilders and they're very pretty and almost exactly how I envisioned them long ago when I drew the design.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://llynmir.net/projects/kuiper/ticket/124"&gt;#124&lt;/a&gt; was quite a bit more work.  The editor for Conditions and Actions doesn't act like any other editor, because you're never working on the base 'Condition' or 'Action' object.  Rather, you select a subclass and work on that.  Turned out to be pretty easy to have a list box on the left that selected the subclass, and the right is an appropriate editor.&lt;br /&gt;&lt;br /&gt;Actually, the 'appropriate editor' part being easy is half-right.  The properties dialog, from which this descends, lays out fields (things like 'name', basic text/numeric stuff) without regard to what class it's doing it for.  But children (i.e. other serializable objects) vary wildly and the properties dialog doesn't do anything with them, leaving it to subclasses to sort it out.&lt;br /&gt;&lt;br /&gt;Thus, I created a helper class to detect which subclass of KuiAction/KuiCondition it was working on, and lay out the children accordingly.  This was annoyingly difficult, because I had to refactor all of the layout logic so a non-property dialog could use it, and then somehow shoehorn it back in.  It's still a bit of a hack, but it gets the job done, and this is the only place I should need it.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Fixed a bug where scrolling in a list would appear to forget your current selection.  Turns out it still remembers the selection behind the scenes, but it didn't bother re-painting it.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Finally, a helpful ruby tip.  This:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;case(object.class)&lt;br /&gt;when KuiBlarg&lt;br /&gt;  setup_kui_blarg&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Doesn't work how you might expect (i.e. at all).  Instead, do this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;case(object)&lt;br /&gt;when KuiBlarg&lt;br /&gt;  setup_kui_blarg&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-8239131943907188224?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/8239131943907188224/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=8239131943907188224' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8239131943907188224'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8239131943907188224'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/05/day-94-medicated.html' title='Day 94: Medicated'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-9078095261334123735</id><published>2008-04-30T14:21:00.006-04:00</published><updated>2008-05-18T20:52:38.709-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='deploy'/><category scheme='http://www.blogger.com/atom/ns#' term='rubygem'/><title type='text'>Day 90: Gemification 2, the revenge</title><content type='html'>Sinuses... stuffed!  Laptop... dying!  Must... complete... article... on... gems!&lt;br /&gt;&lt;br /&gt;(&lt;a href="http://kuidev.blogspot.com/2008/04/day-89-gemification.html"&gt;Part 1&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;So I've got a gem.  'gem install kuiper-0.0.1.gem' went well.  I even added /var/lib/gems/1.8/bin/ to my $PATH so I could run the script.  I started up a new terminal and did this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;roger@roger-laptop:~$ kuiper&lt;br /&gt;roger@roger-laptop:~$&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Gwuh?  Now this was an unexpected result!  Obviously it's finding the file, but running it had no effect.  I tracked this down to a Ruby idiom we've all seen before:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if $0 == __FILE__&lt;br /&gt;  start_kuiper&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;But this doesn't work with gems!&lt;/span&gt;  Rubygems doesn't actually install &lt;span style="font-style:italic;"&gt;your&lt;/span&gt; script in /var/lib/gems/1.8/bin/, they install their own script there, which itself calls yours.  Luckily, Rubygems names this script after the one it's calling, so you can modify the usual idiom as so:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if File.basename($0) == &lt;br /&gt;  File.basename(__FILE__)&lt;br /&gt;&lt;br /&gt;  start_kuiper&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now when I ran the game, I got what I was expecting, which is to say a giant list of tracebacks because it couldn't find the resources it needed.  (Put in a bug to fix that gracefully: &lt;a href="http://llynmir.net/projects/kuiper/ticket/121"&gt;#121&lt;/a&gt;) Locating your data files turns out to be relatively simple:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;begin&lt;br /&gt;  require 'rubygems'&lt;br /&gt;  Gem.activate('kuiper',false)&lt;br /&gt;  rl.dirs &lt;&lt; Gem.loaded_specs['kuiper'].&lt;br /&gt;    full_gem_path&lt;br /&gt;rescue LoadError&lt;br /&gt;  # Do something else&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The begin/rescue part in there serves two reasons: Obviously, it'll catch the case where Rubygems isn't loaded.  Less obviously, Gem.activate will also throw a LoadError if it can't find its gem.  The Gem.activate bit looks for a gem by the name you give it, and 'activates' it - so far as I can tell, this is the same as requiring it via a 'gem' command (which I'll show later).  This step is necessary to populate Gem.loaded_specs with our gem's spec.  Finally, full_gem_path is - like it sounds - a full path to where the gem installed.  In my case, that's:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ls /var/lib/gems/1.8/gems/kuiper-0.0.1/&lt;br /&gt;bin  data  images  lib  tests&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;These directories are all the stuff I told the spec to include (via spec.files) - which includes my data files.&lt;br /&gt;&lt;br /&gt;Finally, a bit on interacting with other gems.  I made my gem depend on the rubygame gem being installed - now how do I go about letting my program require it?&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;begin&lt;br /&gt;  require 'rubygems'&lt;br /&gt;  gem 'rubygame', '&gt;=3.0.0'&lt;br /&gt;rescue LoadError&lt;br /&gt;  # Nope, either no gems or no rubygame&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;require 'rubygame'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Like before, the begin/end block there will make sure I have both gems and rubygame installed.  Here instead of using the Gem API directly, I use a convenience function to see if Rubygame is there. The require line is outside of the begin/end so that, if we don't have gems and/or don't have the rubygame gem, we can still run so long as the user has rubygame installed &lt;span style="font-style:italic;"&gt;somewhere&lt;/span&gt; on their system.&lt;br /&gt;&lt;br /&gt;And that, as they say, is that.  I'm off to drown my troubles in pseudoephedrine!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-9078095261334123735?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/9078095261334123735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=9078095261334123735' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/9078095261334123735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/9078095261334123735'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/04/day-90-gemification-2-revenge.html' title='Day 90: Gemification 2, the revenge'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-3566337569799251426</id><published>2008-04-29T22:12:00.008-04:00</published><updated>2008-06-14T20:10:10.791-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='deploy'/><category scheme='http://www.blogger.com/atom/ns#' term='rubygem'/><category scheme='http://www.blogger.com/atom/ns#' term='rakefile'/><title type='text'>Day 89: The Gemification</title><content type='html'>Buckle in, this is going to be a long one:  I want Kuiper to be installable as a gem.&lt;br /&gt;&lt;br /&gt;The biggest kick in the teeth along this road is that the &lt;a href="http://docs.rubygems.org/read/chapter/5"&gt;Rubygem online book&lt;/a&gt; itself doesn't really tell you how to do it, only that you should "see the DeveloperGuide for the real meat".  The problem in this case is that the Developer Guide does not, so far as I can tell, exist.  Thus I give you this:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Roger's (comparatively) Pain-Free Gem Creation Tutorial&lt;/span&gt; (with a bit of Rakefile stuff thrown in)&lt;br /&gt;&lt;br /&gt;As you might expect, it's weighted heavily toward my game's deployment needs.  Still, it's somewhat useful.&lt;br /&gt;&lt;br /&gt;The first step, because we want this to be an easily re-done process, is to create a &lt;a href="http://rake.rubyforge.org/"&gt;Rakefile&lt;/a&gt;.  This is pretty simple, because it's Ruby code itself.  The first part of my Rakefile, I decided, would run the unit tests &lt;a href="http://kuidev.blogspot.com/2008/04/day-88.html"&gt;I love so very much&lt;/a&gt;.  This does the trick:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'rake/runtest'&lt;br /&gt;&lt;br /&gt;task :test do &lt;br /&gt;  Rake.run_tests 'tests/tests.rb'&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;"tests.rb" is a file that just requires all my other tests; rake knows to do the right thing.&lt;br /&gt;&lt;br /&gt;Next, I made a rule to build my documentation:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'rake/rdoctask'&lt;br /&gt;&lt;br /&gt;Rake::RDocTask.new do |rd|&lt;br /&gt;  rd.main = "lib/kuiper.rb"&lt;br /&gt;  rd.title = "Kuiper RDocs"&lt;br /&gt;  rd.rdoc_files.include("lib/**/*.rb")&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Tasks like the test one are easy; I'd already known about run_tests. But what the frak is a Rake::RDocTask, and how did I know what to do with it? Here the Rake RDocs (coincidentally, the link above to 'Rakefile' takes you straight to the docs) were invaluable.  Every special task is documented there with examples of how to use it.&lt;br /&gt;&lt;br /&gt;Next, the big tomato.  Creating the gem:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'rake'&lt;br /&gt;require 'rake/gempackagetask'&lt;br /&gt;require 'rubygems'&lt;br /&gt;&lt;br /&gt;Rake::GemPackageTask.new(gem_spec) do |pkg| &lt;br /&gt;  pkg.need_tar_bz2 = true&lt;br /&gt;  pkg.need_zip = true&lt;br /&gt;  pkg.need_tar = true&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You didn't really think it'd be &lt;span style="font-style:italic;"&gt;that&lt;/span&gt; easy, did you?&lt;br /&gt;&lt;br /&gt;Each gem relies on a 'spec' to tell it minor things like what to put in the gem and what that gem requires.  That's where 'gem_spec' comes from in the example above.  A reference to everything that goes in it can be found &lt;a href="http://docs.rubygems.org/read/chapter/20"&gt;here&lt;/a&gt; and I relied on it extensively.&lt;br /&gt;&lt;br /&gt;Here's what my spec looks like, broken down:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gem_spec = Gem::Specification.new do |s|&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This creates the actual Specification object and starts a block to initialize the whole thing.  We're going to do some heavy changes to that 's' variable!&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  s.name     = "kuiper" # The name of our gem&lt;br /&gt;  s.author   = "Roger Ostrander" # Me&lt;br /&gt;  s.email    = "denor@users.sourceforge.net"&lt;br /&gt;  s.homepage = "http://llynmir.net/projects/kuiper/"&lt;br /&gt;  s.summary  = "Top down 2D space action/rpg "+&lt;br /&gt;               "hybrid" &lt;br /&gt;  s.description = "Kuiper is a single player "+&lt;br /&gt;                  "top-down 2D space RPG game, "+&lt;br /&gt;                  "in the spirit of Escape "+&lt;br /&gt;                  "Velocity and Tradewars."&lt;br /&gt;  # Do we have rdoc for this package?&lt;br /&gt;  s.has_rdoc = true &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is just some basic information about the game.  Most of it's self-explanatory, I just want to say that 'summary' is what will show up as a result of the 'gem list' or 'gem search' commands.  'description' is basically a longer summary.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  # Our version number is [major,minor,bug]&lt;br /&gt;  s.version  = $KUIPER_VERSION.join(".") &lt;br /&gt;  # This specifically refers to the Rubygame&lt;br /&gt;  # gem.  A .rpm or .deb will not, as of right&lt;br /&gt;  # now, do the trick.&lt;br /&gt;  s.add_dependency('rubygame', '&gt;=3.0.0')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Version info - first comes ours (which I keep in a central place in one of my source files, to make it easy to change) and then those we rely on.  Like the comment says, this specifically refers to other gems; when I installed my gem it complained because I hadn't installed the rubygame gem (even though Rubygame lives elsewhere on my system)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  # The directory of the script to start the game&lt;br /&gt;  s.bindir   = "bin"   &lt;br /&gt;  # Scripts to install  &lt;br /&gt;  s.executables &lt;&lt; 'kuiper'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I'm making a game, not a library, so this is a little awkward.  These lines basically tell the gem system that I'm installing a binary (in reality, the 'kuiper' part there is another ruby script that will start things up), and so to put that file in the gem bin directory.  I then discovered that Ubuntu doesn't include this directory in the $PATH by default, for those wondering it's "/var/lib/gems/1.8/bin/"&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  s.require_paths = ["lib"]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This tells Rubygems where (else) it should look for files it's requiring.  The standard is to put source files in "lib", so I've done that and indicated this fact.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  s.rdoc_options &lt;&lt; '--title' &lt;&lt; 'Kuiper RDocs' &lt;&lt;&lt;br /&gt;                    '--main' &lt;&lt; 'lib/kuiper.rb'&lt;br /&gt;  s.test_file  = 'tests/tests.rb'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is me repeating myself; though we've already got rake tasks to do these, people can test gems on their own, and also get documentation for them (when I installed the gem I created, in fact, it automatically installed the documentation with it).&lt;br /&gt;&lt;br /&gt;Finally,&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  s.files = FileList.new do |fl|&lt;br /&gt;    fl.include(&lt;br /&gt;      "{lib,ext,samples,doc,images,data}/**/*")&lt;br /&gt;    fl.exclude(/svn/)&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;These are all the files to be put into the gem!  And with that, you can go to the command line and go 'rake gem'.&lt;br /&gt;&lt;br /&gt;Tomorrow: &lt;a href="http://kuidev.blogspot.com/2008/04/day-90-gemification-2-revenge.html"&gt;Part 2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;EDITED: Because blogger preview sucks at showing you what will get cut off by its borders.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-3566337569799251426?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/3566337569799251426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=3566337569799251426' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3566337569799251426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3566337569799251426'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/04/day-89-gemification.html' title='Day 89: The Gemification'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1358552538978102978</id><published>2008-04-28T20:39:00.003-04:00</published><updated>2008-05-18T20:53:06.496-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 88</title><content type='html'>Weapon unit tests: &lt;span style="font-weight:bold;"&gt;COMPLETED!&lt;/span&gt;&lt;br /&gt;Minibuilder: &lt;span style="font-weight:bold;"&gt;COMPLETED!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;That's the real content for today.  The rest will be a love letter to unit testing.&lt;br /&gt;&lt;br /&gt;Yesterday when I made the game run on windows, I had to tweak the XML handling - apparently on whatever version of ruby I'm running over there, attributes you give to XML bits aren't automatically made into strings like they are on my linux box.  So I changed it to explicitly use to_s.&lt;br /&gt;&lt;br /&gt;That worked fine until today, when I finished up the weapon unit tests.  I ran the suite (I have a script that runs all of them) and found out that it failed, which wasn't a surprise until I discovered that it wasn't the weapons failing at all!  It was older code that had broken with the above change.  Turns out nil.to_s is '', and '' gets read back in as an empty string, which for my purposes is vastly different than nil.  I solved this by having it only set non-nil attributes in the XML to begin with, but the point is I never would have found it until it bit me on the ass in unpredictable ways if not for testing.&lt;br /&gt;&lt;br /&gt;But finding obscure places where I've broken my code isn't their only purpose!  After finishing the weapons tests, I went back to the minibuilder (also done today!) and fleshed out the functionality.  Removing was enabled, and I found it worked pretty much how I expected and how the tests predicted it would, except in one case:  I couldn't remove secondary weapons.&lt;br /&gt;&lt;br /&gt;I went back to the test cases to find that I'd never written a test for that!  I did so and delighted to see that it failed.  Found the problem, patched it up.  In this case I would probably have tracked down the bug without the tests just fine, but now I'll be alerted if it ever happens &lt;span style="font-style:italic;"&gt;again&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;P.S. I like you, unit testing, do you like me?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;43 tests, 140 assertions, 0 failures, 0 errors&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I'm not hearing a no.... :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1358552538978102978?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1358552538978102978/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1358552538978102978' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1358552538978102978'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1358552538978102978'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/04/day-88.html' title='Day 88'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-4947377662977894158</id><published>2008-04-27T15:17:00.003-04:00</published><updated>2008-04-27T15:42:45.084-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Day 87</title><content type='html'>And now for something completely different!&lt;br /&gt;&lt;br /&gt;Kuiper is working on Windows.  I don't know what, exactly, got me started on the idea, but I began today idly trying to get Rubygame to build on Windows.  I followed along with &lt;a href="http://rubygame.wiki.sourceforge.net/win32_compile"&gt;the wiki instructions&lt;/a&gt; and made some minor changes.  The biggest problem arose when I discovered that &lt;a href="http://kerneltrap.org/man/linux/man3/asprintf.3"&gt;asprintf&lt;/a&gt; wasn't supported.  So now Rubygame will incorrectly convert unicode unto UTF-8 in the unlikely event that it's given a 513-byte unicode character to convert.  (It won't break, however, because &lt;a href="http://kerneltrap.org/man/linux/man9/snprintf.9"&gt;snprintf&lt;/a&gt; &lt;span style="font-style:italic;"&gt;is&lt;/span&gt; supported).  Also the code wasn't freeing the result of asprintf, so I may have actually plugged a memory leak by accident.&lt;br /&gt;&lt;br /&gt;If you're compiling Rubygame 2.x, you're probably fine with the above instructions.  But I was compiling the subversion 3.0 stuff, which includes Chipmunk, and the chipmunk build process isn't integrated with the rest of it.  Building Chipmunk failed with, among a litany of other warnings:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;make: *** No rule to make target '{.', needed by 'chipmunk.obj'.  Stop.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;All the searching in the world couldn't help me build it; in the end I found a &lt;a href="ftp://ftp.cs.auckland.ac.nz/pub/staff/robert-s/chipmunk.so"&gt;prebuilt .so&lt;/a&gt; file (via &lt;a href="http://www.slembcke.net/forums/viewtopic.php?f=1&amp;t=89&amp;p=763"&gt;this thread&lt;/a&gt;), which I plunked down into c:\ruby\lib\ruby\site_ruby\1.8\i386-msvcrt, and it seemed to work.  Of course, if you go this route, you're probably not going to be able to use any of the chipmunk-based features of the new Rubygame, but I'm not so it wasn't a problem for me.&lt;br /&gt;&lt;br /&gt;All this made me think that I need another metric for progress.  I've got time spent, but that's pretty constant.  I've been using lines of code, but all this time I only added one line of code to kuiper proper (if it can't find $HOME, it looks for $USERPROFILE, which is apparently the windows equivalent).  It's killing my LOC/hour performance :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-4947377662977894158?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/4947377662977894158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=4947377662977894158' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4947377662977894158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4947377662977894158'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/04/day-87.html' title='Day 87'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-1334200576491633980</id><published>2008-04-26T15:44:00.004-04:00</published><updated>2008-05-18T20:53:06.498-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ptotd'/><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 86</title><content type='html'>Programming Tip of the Day: There is a difference between '=' and '=='.&lt;br /&gt;&lt;br /&gt;If you take a look back to &lt;a href="http://kuidev.blogspot.com/2008/04/day-85.html"&gt;yesterday's entry&lt;/a&gt; you might notice something about the minibuilder.  Specifically, there are two entries in it that look almost the same: "wpn_lazor" and "wpn_lazor:2279".  What that second one means is that it's been duplicated from the original wpn_lazor.  That's the intended behavior; ships' weapons have to be duplicates of the original because the weapons have information about how long until they can fire again, etc, built into them.  There was a bug early on where one ship got the weapons of every ship in the sector (because they weren't duplicates) and would fire an insane amount of stuff at you while the others flew around dumbly.  So it's necessary.&lt;br /&gt;&lt;br /&gt;But that screenshot illustrates a bug as well!  You should never see the original wpn_lazor - wpn_lazor:2279 should instead internally have an amount of '2'.  This is because they don't compare equal, and so the engine doesn't know to stack them.&lt;br /&gt;&lt;br /&gt;As I delved into the weapons systems to get a unified feel, I kept discovering more and more kludges.  The :weapons accessor only gives you primary weapons.  Ammunition added to a ship somehow ends up getting registered as a primary weapon!  I'm amazed this stuff works at all; it seemed pretty straightforward at the time.&lt;br /&gt;&lt;br /&gt;Thus, today's work was almost entirely on bug #119: Weapon Unit tests.  To make sure, once and for all, that they're working the way I expect.  It feels like I'm going backwards (weapons were, after all, the stuff of the last milestone) but it needs to be done if I want the minibuilder to work, and I need the minibuilder for my current milestone.&lt;br /&gt;&lt;br /&gt;I also created bug #118, wherein we warn the scenario editor if they're trying to put the player's ship up for sale.  Because it's a ship like any other, and scenario creators might think "Hey, people might want to buy their original ship."  If they did that, they'd get a ship that was exactly as damaged as their original ship!  It does give me an idea, however:  When you buy a new ship, the old one can be put up for sale in its old condition - that way you can always trade back.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-1334200576491633980?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/1334200576491633980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=1334200576491633980' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1334200576491633980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/1334200576491633980'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/04/day-86.html' title='Day 86'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-4424279858426671556</id><published>2008-04-25T17:03:00.006-04:00</published><updated>2008-12-13T04:06:29.330-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ptotd'/><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><category scheme='http://www.blogger.com/atom/ns#' term='sitdown'/><title type='text'>Day 85</title><content type='html'>Programming Tip of the Day:  If your sprite isn't rendering, try actually adding it to the screen!&lt;br /&gt;&lt;br /&gt;I got a new server yesterday to replace the one that used to house my trac and SVN repository.  I installed &lt;a href="http://www.ubuntu.com/"&gt;Ubuntu&lt;/a&gt; on it and tried to upgrade, only to find that this is taking forever.  Turns out a new version came out mere hours before my server arrived.  Not only was the install I had out of date, but due to everyone hammering the servers, it'll take forever to get up to date!&lt;br /&gt;&lt;br /&gt;This gave me extra time to think about the blog post I made.  I sat down with an actual honest-to-gods pen and paper and drew what I wanted the mission editor to look like.  Then I was like "Hey, I can do that!  And it'd be hugely useful to other objects, too!"&lt;br /&gt;&lt;br /&gt;Thus, I introduce #117: THE MINIBUILDER!&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_pIj5Y9n3oFo/SBJLqmMuQ9I/AAAAAAAAAdo/5NEhUSZv2qU/s1600-h/minibuild.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_pIj5Y9n3oFo/SBJLqmMuQ9I/AAAAAAAAAdo/5NEhUSZv2qU/s320/minibuild.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5193296515492103122" /&gt;&lt;/a&gt;&lt;br /&gt;As terrifyingly powerful as the old build dialog, but bite-sized!&lt;br /&gt;&lt;br /&gt;All the functionality isn't there yet.  I was halfway through writing up the subclass that would deal specifically with adding/removing weapons from ships (they have special methods to do this) when I thought to myself:  Haven't I done this before?&lt;br /&gt;&lt;br /&gt;Yes, I have.  I made special subclasses of the old builder to handle weapons for ships... and cargo for ships... and addons for ships.  The way ships handle them is different from the way planets handle them is different from the way that you'd just throw them in a list. Wouldn't it be nice, I thought, to have a unified API and then just adapt anything that doesn't conform to it?&lt;br /&gt;&lt;br /&gt;Thus I created an Adapter class!  It has an 'add_item', a remove_item, an 'items', and a 'constraint' (the type that goes into the list).  I only have to write a 'Adding weapons to ships' subclass once instead of for every GUI element that does so. How useful, I thought!  Then I went online to make sure I was spelling the word 'Adapter' correctly and discovered that my invention &lt;a href="http://en.wikipedia.org/wiki/Adapter_pattern"&gt;already exists and is a pattern&lt;/a&gt;.  I still felt proud, having independently discovered it.&lt;br /&gt;&lt;br /&gt;My server is currently formatting a 500GB disk whose reliability has been called into question.  Thus I'm formatting it with the most slow and careful of bad-block checks.  I do not recommend this unless you have a great deal of time on your hands, it's taken nearly 24 hours now.  Still, if the disk is dodgy, this will find out.  And if this doesn't break it, nothing will.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-4424279858426671556?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/4424279858426671556/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=4424279858426671556' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4424279858426671556'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/4424279858426671556'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/04/day-85.html' title='Day 85'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_pIj5Y9n3oFo/SBJLqmMuQ9I/AAAAAAAAAdo/5NEhUSZv2qU/s72-c/minibuild.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-3307600941143975633</id><published>2008-04-24T15:41:00.003-04:00</published><updated>2008-05-18T20:52:47.797-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 84</title><content type='html'>Sitting at home, waiting for my server....&lt;br /&gt;&lt;br /&gt;Today I finished the mission flag system, at least so far as the unit tests are concerned.&lt;br /&gt;&lt;br /&gt;It occurred to me, though, that all I'm doing is putting off the real work.  Granted, mission flags needed to be done at some point, but they didn't need to be done right now.  I have enough conditions and actions to put a basic mission together, in fact, I have for a while. The part I don't want to tackle yet is the editor.  Making domain objects is easy.  Making editors, in fact, is pretty easy with all the refactoring I've done.  The problem is that the mission editor is probably going to be unlike anything else I've coded so far.  Well that's not true; I could code it exactly like the other components, but that's a very clunky way of editing.  Otherwise the scenario-maker is going to have to go through 8 button presses: edit missions, new mission, new condition, pick a condition from a list, accept that condition, new action, pick an action from a list, etc.  And that's just to /get/ the mission.  No, I'll need a better editor.&lt;br /&gt;&lt;br /&gt;A lot of things could use a better editor.  Adding weapons or cargo to a planet's stores, for instance, would be a lot nicer as two side-by-side lists with '&lt;' and '&gt;' button between them rather than the list of items, press 'add', pick from a dialog thing that's going on now.  In fact, I'm making a bug for it now.  Hello, #115!&lt;br /&gt;&lt;br /&gt;I also found a new image to be the titlescreen background, which used to be based on &lt;a href="http://hubblesite.org/newscenter/archive/releases/2006/05/image/a"&gt;this&lt;/a&gt; bit of astronomy.  Turns out nebulae are far more impressive-looking, even if they aren't what the game is named after.  Since the icon and trac logo were based off of that original photo, I had to change them, too.  The trac logo is a part of the nebula now, and the icon is a little spaceship.&lt;br /&gt;&lt;br /&gt;When I was debating if I should start this blog, one of my primary worries was that I wouldn't have anything to say.  Looking back at this and other posts seems to contradict that, but part of me can't shake the paranoia that I'll run out of topics and every update will be "I added 4 lines of code.  Yay me."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-3307600941143975633?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/3307600941143975633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=3307600941143975633' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3307600941143975633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/3307600941143975633'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/04/day-84.html' title='Day 84'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-6494556972040864266</id><published>2008-04-23T20:01:00.007-04:00</published><updated>2008-05-18T20:51:59.921-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ptotd'/><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 83</title><content type='html'>I'd like to introduce a feature to this blog that I use at work quite a bit.  It's called the &lt;span style="font-weight:bold;"&gt;Programming Tip of the Day&lt;/span&gt;, and it's for when I've made bone-headed mistakes.  In Ruby, if you have a method like this:&lt;br /&gt;&lt;pre name="code" class="ruby"&gt;&lt;br /&gt;def check&lt;br /&gt;  self.doImportantStuffHere&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And then later in that same class you have a method like this:&lt;br /&gt;&lt;pre name="code" class="ruby"&gt;&lt;br /&gt;def check&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The second one takes precedence.  Thus today's Programming Tip of the Day:  If nothing's happening, make sure the method where stuff happens isn't being overwritten.&lt;br /&gt;&lt;br /&gt;Today's major task was getting the mission cycle complete; I have a unit test which awards the player a mission (by asking the mission if it's appropriate and, if so, telling it to give itself to the player) and then tells that same mission to check to see if it's done.  Given that the sole condition for completion is that the player be in the starting sector, it is done.  This same check will automatically do any attached actions which, in this case, complete the mission.&lt;br /&gt;&lt;br /&gt;I started work on the next action/condition group, which is to set and check 'flags'.  The player has a set of flags which are essentially a big dictionary of key (the :tag of the flag) value (a string or number) pairs.  Their intent is to be used in multi-leg missions: If you want the player to go from A to B to C, you set a flag when they get to B and then when they get to C check it to see if they jumped through the extra hoop.&lt;br /&gt;&lt;br /&gt;I'm also trying out the google syntax highlighter for my code snippets above.  We'll see how that turns out.&lt;br /&gt;&lt;br /&gt;EDIT: Poorly!&lt;br /&gt;&lt;br /&gt;&lt;script language="javascript" src="http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shCore.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;script language="javascript" src="http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushRuby.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;script language="javascript"&gt;&lt;br /&gt;dp.SyntaxHighlighter.ClipboardSwf = '/flash/clipboard.swf';&lt;br /&gt;dp.SyntaxHighlighter.HighlightAll('code');&lt;br /&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-6494556972040864266?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/6494556972040864266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=6494556972040864266' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/6494556972040864266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/6494556972040864266'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/04/day-83.html' title='Day 83'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-8631424426656837092</id><published>2008-04-22T22:07:00.000-04:00</published><updated>2008-05-18T20:52:29.081-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='missions'/><title type='text'>Day 82</title><content type='html'>My tags are, I think, going to be things like the ticket numbers of what I was working on and the milestone it's for.&lt;br /&gt;&lt;br /&gt;I'm a bit worried about ticket proliferation.  When I made my latest milestone, I created a few tickets for it.  These were some domain objects, and a few conditions and actions I wanted.  It now seems that conditions and actions are dead easy to create and test.  If I made a ticket for every possible condition and action in the game, I'd have a ton of them.&lt;br /&gt;&lt;br /&gt;Thus far my strategy has been to only create tickets for things I need to remember to do but can't do right now, or things I'm not done fixing at the end of a session so I know where I left off.  It's worked so far, but I worry about not knowing when I made a fix/change because it wasn't in the bug tracker.  For instance, assigning the player missions was never in the tracker but I got it done today.  Hopefully the blog will help with this.&lt;br /&gt;&lt;br /&gt;As mentioned, today's work was assigning missions to the player.  The mission object itself is capable of determining if the player can have it (by seeing if all its conditions are met), and also capable of giving itself to the player.  I feel somewhat bad about having this sort of stuff (what more seasoned professionals might call 'business logic') in the model classes.  While it makes it easier to unit test (by far!), I can't get the image out of my head of someone taking a picture of me doing it and adding the LOLCat style caption: "OOP: Ur doin it wrong".&lt;br /&gt;&lt;br /&gt;Other stuff I wanted to mention:&lt;br /&gt;&lt;br /&gt;I've got a spreadsheet which tells me, among other things, how long I've been working on this.  I've decided to name the blog entries after the day.  My entry as of today looks like:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Hours worked:&lt;/span&gt; 1.25&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Goal for today:&lt;/span&gt; 1&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Total Hours Worked:&lt;/span&gt; 132&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Lines of Code:&lt;/span&gt; 7465 (as counted by &lt;a href="http://cloc.sourceforge.net/"&gt;CLOC&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;If this was livejournal, I'd make my current mood the results of my unit tests.  It's not, but today only I think I'll do so.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Current mood:&lt;/span&gt; 37 tests, 98 assertions, 0 failures, 0 errors&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-8631424426656837092?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/8631424426656837092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=8631424426656837092' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8631424426656837092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/8631424426656837092'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/04/day-82.html' title='Day 82'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9081655444803149702.post-86881737669695270</id><published>2008-04-22T21:52:00.000-04:00</published><updated>2008-04-22T22:07:35.347-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='intro'/><title type='text'>KuiDevBlog.new</title><content type='html'>&lt;span style="font-weight:bold;"&gt;What's a Kuiper?&lt;/span&gt;&lt;br /&gt;Kuiper is a single player top-down 2D space RPG game, in the spirit of &lt;a href="http://en.wikipedia.org/wiki/Escape_Velocity_(computer_game)"&gt;Escape Velocity&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Tradewars"&gt;Tradewars&lt;/a&gt;. It is created using the &lt;a href="http://rubygame.sourceforge.net/"&gt;Rubygame&lt;/a&gt; library and is (unsurprisingly) written in &lt;a href="http://www.ruby-lang.org"&gt;Ruby&lt;/a&gt;.  Also, It is heavily influenced by my now-defunct &lt;a href="http://tradermissions.sourceforge.net/"&gt;TraderMissions&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Why?&lt;/span&gt;&lt;br /&gt;Because I want to write a game.  I've been coding up games since I was about 8 years old, but the number of completed games I've finished is a very low number indeed.  Even TraderMissions, my biggest solo effort until this one, remained incomplete.  Finally, I decided that if I was going to get serious about this game development thing, I would have to actually &lt;span style="font-weight:bold;"&gt;finish&lt;/span&gt; a game.&lt;br /&gt;&lt;br /&gt;I took a page from &lt;a href="http://www.nanowrimo.org/"&gt;NaNoWriMo&lt;/a&gt;, an event I've participated (and blogged here!) in a number of times before.  The magic way to get something done, it turns out, is a deadline.  So I set one for myself:  By December 31, 2008, 11:59 pm EST, I'll have released a game or at least submitted it to a number of sites for announcement (I can't control their publishing schedule, after all).&lt;br /&gt;&lt;br /&gt;To that end, I'm doing the same thing now I did with NaNoWriMo - working on the game, every single day, one hour a day.  Two hours on weekends.  It's slow going, but it's progress, and each week I can see noticeable changes in the game.  I'm creating this blog for the same reason I made NaNo blogs - public humiliation.  It's a staple of NaNoWriMo that you announce your intention to create to as many people as you possibly can, so that fear of later embarrassment keeps you going when nothing else does.&lt;br /&gt;&lt;br /&gt;Here goes nothing!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9081655444803149702-86881737669695270?l=kuidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuidev.blogspot.com/feeds/86881737669695270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9081655444803149702&amp;postID=86881737669695270' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/86881737669695270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9081655444803149702/posts/default/86881737669695270'/><link rel='alternate' type='text/html' href='http://kuidev.blogspot.com/2008/04/kuidevblognew.html' title='KuiDevBlog.new'/><author><name>Roger Ostrander</name><uri>http://www.blogger.com/profile/05010748168179539563</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp2.blogger.com/_pIj5Y9n3oFo/SCuJm46CDvI/AAAAAAAAAd8/mzvUrjjT7YY/S220/rogr.jpg'/></author><thr:total>0</thr:total></entry></feed>
