<?xml version="1.0" encoding="UTF-8" ?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc=" http://purl.org/dc/elements/1.1/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>briancarper.net (λ) (Tag: Gaming)</title><link>http://briancarper.net/tag/66/gaming</link><description>Some guy's blog about programming and Linux and cows.</description><item><title>Making an RPG in Clojure (part one of many?)</title><link>http://briancarper.net/blog/making-an-rpg-in-clojure-part-one-of-many</link><guid>http://briancarper.net/blog/making-an-rpg-in-clojure-part-one-of-many</guid><pubDate>Sat, 20 Feb 2010 16:27:40 -0800</pubDate><description>&lt;p&gt;What do you get when you combine old-school Final Fantasy-style RPGs with Clojure?  Fun times for all.  Well, for me at least.&lt;/p&gt;

&lt;p&gt;I'm working on a sort of RPG engine in Clojure so I can make my own RPG.  Click the thumbnail for a &lt;strong&gt;very preliminary video demo&lt;/strong&gt; (6.5 MB) showing the engine in action.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/clojure/rpg-demo.avi&quot;&gt;&lt;img src=&quot;/clojure/rpg-demo.png&quot; alt=&quot;RPG Demo&quot; title=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All I do in the video is walk around, and eventually start adding random NPCs to the map to test collision detection.  Not all that exciting, but I'm proud nonetheless.&lt;/p&gt;

&lt;p&gt;To forestall questions, yes I'll eventually post the source code, but no, not yet.  It barely works.  Just a proof of concept so far.&lt;/p&gt;

&lt;p&gt;Right now I can walk around a map while NPCs also randomly walk around the map, not much more.  So there isn't much to talk about.  But not bad for 4 days and 600 lines of code (one tenth of which is ASCII art... more on that later).  Keep in mind that I have no idea what I'm doing.  &lt;/p&gt;

&lt;p&gt;Collision detection works so people don't walk through walls or each other, and after endless tweaking I got all the animations to be very smooth, even when I add a few dozen NPCs to the map (as I do in the video).  The video is a bit jerky but it looks better in person.  All of the admittedly poor artwork is also created by myself, thanks to the GIMP and some hastily-read &lt;a href=&quot;http://gas13.ru/v3/tutorials/sywtbapa_making_sprite.php&quot;&gt;tutorials on pixel art&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It all runs on plain old Swing in Clojure.  Here's some of what went right and what went wrong so far.&lt;/p&gt;

&lt;!--more Successes and failures... --&gt;

&lt;h1&gt;Background&lt;/h1&gt;

&lt;p&gt;I've played a lot of RPGs, but I've never programmed a game more complex than Pong.  I knew what double-buffering is, and that's as far as my knowledge of game programming went when I started.&lt;/p&gt;

&lt;p&gt;My first idea was to use a plain old Swing JFrame.  I'd make a bunch of sprites saved as PNG files, read them all in and draw them on the JFrame, as many times per second as I could manage.  Then there's some global state to keep track of where everything is.  Simple enough.&lt;/p&gt;

&lt;p&gt;(PS I have no idea what I'm doing.)&lt;/p&gt;

&lt;h1&gt;Failures&lt;/h1&gt;

&lt;p&gt;My first version used Clojure agents (i.e. threads) for everything.  The game logic was a thread, the renderer ran in a thread, every NPC was its own thread.  The world itself was a single ref that all of these agents banged on. So the NPCs would tell the ref &quot;I want to move down one square&quot;, another thread might say &quot;Brian just pushed 'left' on the keyboard, so start scrolling the map&quot;.  The world-ref would kindly oblige while preventing two NPCs from standing on each other or letting the PC walk through the walls.&lt;/p&gt;

&lt;p&gt;Clojure is awesome in letting you do this in a completely safe and coordinated way.  Everything worked well.  But even for the crappy 2D graphics I'm using, all those threads caused way too much lag.  I could get a good framerate if everyone was standing still, but if I was walking while the NPCs were walking, I'd get lots of lag.  It was even worse on a slower computer.&lt;/p&gt;

&lt;p&gt;My best guess is that the reason for the lag was the constant restarting of canceled transactions due to multiple threads trying to edit the world ref 50+ times per second.  My sucky 2-core CPU couldn't keep up.  It isn't surprising that this failed, in hindsight.&lt;/p&gt;

&lt;h1&gt;OpenGL?&lt;/h1&gt;

&lt;p&gt;Next, I decided to try out OpenGL.  There are multiple options for OpenGL in Java.  One is &lt;a href=&quot;http://kenai.com/projects/jogl/pages/Home&quot;&gt;JOGL&lt;/a&gt; and another is &lt;a href=&quot;http://lwjgl.org/&quot;&gt;lwjgl&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I program in Linux, and my video card is ancient.  I can barely get OpenGL to work in the best of times.  Installing JOGL was a slight chore (Gentoo doesn't even include it in its repo).  JOGL is not just a JAR you throw onto CLASSPATH, you need some native extensions, obviously.  I got it running somehow, but I wouldn't want to explain to someone else how I did it.&lt;/p&gt;

&lt;p&gt;I did get jwjgl to work too, eventually, which was nice.  There is a really nice Java 2D game framework called &lt;a href=&quot;http://slick.cokeandcode.com/&quot;&gt;Slick&lt;/a&gt; which uses lwjgl.  Some good games were created using this, for example &lt;a href=&quot;http://meatfighter.com/stickvania/&quot;&gt;Stickvania&lt;/a&gt;, which recently hit Reddit recently. I got Slick up and running in short order.&lt;/p&gt;

&lt;p&gt;Unfortunately Slick doesn't play nicely with a Clojure REPL.  I could build and start a game, but once the game is stopped, it never runs properly again without restarting the REPL.  Slick caches images to try to be speedy, and it seems like the cache is either corrupted or destroyed when you close down your game, for one thing.  This is not conducive to Clojure REPL-style development, and I didn't want to spend a lot of time fixing it.&lt;/p&gt;

&lt;p&gt;Another issue is that I'd really like this game to be cross-platform and available to non-hackers, and the thought of trying to tell the average gamer how to install JOGL or lwjgl was daunting, given the bullcrap I had to go through.  Not sure if I can just throw JOGL into a JAR and distribute it, maybe I can, but I didn't want to bother reading about it.  Swing on the other hand runs everywhere with no effort.  &lt;/p&gt;

&lt;p&gt;My main problem is that I know even less about OpenGL programming than I do about Swing, and don't have a month to learn.  Back to the drawing board.&lt;/p&gt;

&lt;h1&gt;Success&lt;/h1&gt;

&lt;p&gt;It turns out I don't need OpenGL anyways.  All I need is program more intelligently.  (Did I mention I have no idea what I'm doing?)  Instead of dozens of threads, my current (working) version has one thread.  It updates the game logic, then renders the game, then waits 10 milliseconds or so, then repeats this (forever).  &lt;/p&gt;

&lt;p&gt;With the single-threaded version, the logic is actually more complex than the multi-threaded version.  Agents and refs were really nice and braindead-easy to work with.  But such is life.&lt;/p&gt;

&lt;p&gt;Once I learned about keeping track of things in realtime by counting milliseconds instead of counting frames or using timeouts to do logic/render updates, things worked better.  (Did I mention I have no idea what I'm doing?)  The game loop looks like this now:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn game-loop [#^Canvas canvas]
  (loop [last-time (get-time)]
    (let [curr-time (get-time)
          delta (- curr-time last-time)]
      (do-logic delta)
      (do-render canvas)
      (when @RUNNING
        (Thread/sleep 10)
        (recur curr-time)))))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The code to actually start the game, to give you an idea:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn start-world [world]
  (let [#^JFrame frame (doto (JFrame. &quot;Game&quot;)
                         (.addWindowListener (proxy [WindowAdapter] []
                                               (windowClosing [e] (stop)))))
        #^JPanel panel (doto (.getContentPane frame)
                         (.setPreferredSize (Dimension. REAL-WIDTH REAL-HEIGHT))
                         (.setLayout nil))
        #^Canvas canvas (Canvas.)]
    (doto canvas
      (.setBounds 0 0 REAL-WIDTH REAL-HEIGHT)
      (.setIgnoreRepaint true)
      (.addKeyListener (proxy [KeyAdapter] []
                         (keyPressed [e] (handle-keypress e))))
      (.addMouseListener (proxy [MouseAdapter] []
                           (mouseClicked [e] (handle-mouse e))))
      )
    (.add panel canvas)
    (doto frame
      (.pack)
      (.setResizable false)
      (.setVisible true))
    (.createBufferStrategy canvas 2)
    (dosync (ref-set RUNNING true)
            (ref-set PAINTER (agent canvas))
            (ref-set WORLD world))
    (send-off @PAINTER game-loop)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That's about it for the Swing side of things, aside from scribbling on the Canvas in the render function.&lt;/p&gt;

&lt;p&gt;The agent I wrap around the Canvas controls the thread that runs the game loop.  The agent helpfully keeps track of any exceptions that happen during the loop, and I can view those exceptions via &lt;code&gt;agent-error&lt;/code&gt;, which is handy for debugging.&lt;/p&gt;

&lt;p&gt;Note how little code it is to set up a keyboard event handler, thanks to &lt;code&gt;proxy&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    (doto canvas
      ...
      (.addKeyListener (proxy [KeyAdapter] []
                         (keyPressed [e] (handle-keypress e))))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A couple lines of Clojure for what would be a lot of senseless boilerplate in Java.  Notice how you the keyboard handler calls a normal Clojure function &lt;code&gt;handle-keypress&lt;/code&gt;.  Clojure / Java interop really is seamless.&lt;/p&gt;

&lt;h1&gt;Maps&lt;/h1&gt;

&lt;p&gt;My maps are made using ASCII art.  (Did I mention I have no id-... never mind.) Here's the code for the test map, for example.  &lt;code&gt;Map&lt;/code&gt; here is a &lt;code&gt;deftype&lt;/code&gt; (available in bleeding-edge Clojure), which takes a map of tiles, a &quot;pad&quot; tile, the map, and then a mask showing walls / tiles where the player shouldn't be allowed to walk.  My &lt;code&gt;Map&lt;/code&gt; type acts like a Clojure hash-map most of the time, but it also lets me name the fields that all maps should share, and has a proper &quot;type&quot;, among other things.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(deftype Map [tileset pad tiles walls]
  clojure.lang.IPersistentMap)

(def test-map (cache-map
               (Map {\  (tile &quot;ground&quot;)
                     \/ (tile &quot;ground-shadow-botright&quot;)
                     \&amp;lt; (tile &quot;ground-shadow-left&quot;)
                     \d (tile &quot;dirt&quot;)
                     \| (tile &quot;wall_vertical&quot;)
                     \- (tile &quot;wall_horizontal&quot;)
                     \1 (tile &quot;wall_topleft&quot;)
                     \2 (tile &quot;wall_topright&quot;)
                     \3 (tile &quot;wall_bottomleft&quot;)
                     \4 (tile &quot;wall_bottomright&quot;)
                     \u (tile &quot;below-wall&quot;)
                     \v (tile &quot;below-wall-shadow&quot;)
                     \w (tile &quot;wood-floor&quot;)
                     \c (tile &quot;cobble&quot;)
                     \b (tile &quot;bush&quot;)}

                    (tile &quot;ground&quot;)

                    [&quot;                 1----2                            &quot;
                     &quot;                 |vuuu|                            &quot;
                     &quot;1----------------4&amp;lt;bbb3--------------------------2 &quot;
                     &quot;|vuuuuuuuuuuuuuuuu/   uuuuuuuuuuuuuuuuuuuuuuuuuuu|&amp;lt;&quot;
                     &quot;|&amp;lt;1------------2               cccccccc d        |&amp;lt;&quot;
                     &quot;|&amp;lt;|vuuuuuuuuuuu|&amp;lt;       d      c      c          |&amp;lt;&quot;
                     &quot;|&amp;lt;|&amp;lt;           |&amp;lt; cccccccccccccc    d ccccccc d  |&amp;lt;&quot;
                     &quot;|&amp;lt;3------ -----4&amp;lt; c                         ccccc|&amp;lt;&quot;
                     &quot;|&amp;lt;uuuuuuu/uuuuuu/ c  1-------2    1------------2 |&amp;lt;&quot;
                     &quot;|&amp;lt;1---2 bcb       c  |vuuuuuu|&amp;lt;   |vuuuuuuuuuuu|&amp;lt;|&amp;lt;&quot;
                     &quot;|&amp;lt;|vuu|&amp;lt; c  d     c  |&amp;lt; 1-- -4&amp;lt;   |&amp;lt;           |&amp;lt;|&amp;lt;&quot;
                     &quot;|&amp;lt;3- -4&amp;lt;bcb       c  |&amp;lt; |vucuu/   3------ -----4&amp;lt;|&amp;lt;&quot;
                     &quot;|&amp;lt;uu/uu/ c        c  3--4&amp;lt; c      uuuuuuu/uuuuuu/|&amp;lt;&quot;
                     &quot;|&amp;lt;  c  dbcb       c  uuuu/ cccc         bcb      |&amp;lt;&quot;
                     &quot;|&amp;lt;  cccccccccccccccccccccccc  cccccccc   c   d   |&amp;lt;&quot;
                     &quot;|&amp;lt;  d        d                     d ccccc       |&amp;lt;&quot;
                     &quot;|&amp;lt;                    1--------------------------4&amp;lt;&quot;
                     &quot;3---------------------4uuuuuuuuuuuuuuuuuuuuuuuuuuu/&quot;
                     &quot;uuuuuuuuuuuuuuuuuuuuuuu/                           &quot;]

                    [&quot;                 xxxxxx                            &quot;
                     &quot;                 x    x                            &quot;
                     &quot;xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx &quot;
                     &quot;x                                                x &quot;
                     &quot;x xxxxxxxxxxxxxx                                 x &quot;
                     &quot;x x            x                                 x &quot;
                     &quot;x x            x                                 x &quot;
                     &quot;x xxxxxxx xxxxxx                                 x &quot;
                     &quot;x                    xxxxxxxxx    xxxxxxxxxxxxxx x &quot;
                     &quot;x xxxxx x x          x       x    x            x x &quot;
                     &quot;x x   x              x  xxx xx    x            x x &quot;
                     &quot;x xx xx x x          x  x         xxxxxxx xxxxxx x &quot;
                     &quot;x                    xxxx                        x &quot;
                     &quot;x       x x                             x x      x &quot;
                     &quot;x                                                x &quot;
                     &quot;x                                                x &quot;
                     &quot;x                     xxxxxxxxxxxxxxxxxxxxxxxxxxxx &quot;
                     &quot;xxxxxxxxxxxxxxxxxxxxxxx                            &quot;]
                    )))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This lets me make all of my tiles be simple PNG files with sane names.  I could dork around with sprite sheets, but why bother?  Emacs or Vim column-editing and overwrite modes make it easy enough to make a map this way.  Swing thankfully handles transparency for me if I use PNGs, so it's a no-brainer to make multiple map layers later, which I'll need later.  The code for reading in PNGs is brain-dead simple thanks to Java's ImageIO:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn tile [name]
  (ImageIO/read (File. (str &quot;img/&quot; name &quot;.png&quot;))))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;cache-map&lt;/code&gt; function (below) iterates over the ASCII art (via &lt;code&gt;seq&lt;/code&gt;s on the Strings), using each character as a key into the map of real images, and builds a BufferedImage out of it.  This is cached, since the renderer needs to re-draw the background every frame.  In this function I'm also drawing a &quot;padding&quot; layer to sit under the background, so that I see endless fields of grass when I walk close to the edge of the map, instead of garbled, smeared-out graphical artifacts.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn cache-map [amap]
  (let [height (count (:tiles amap))
        width (count (first (:tiles amap)))

        #^BufferedImage img (BufferedImage. (tile-to-real width) (tile-to-real height) BufferedImage/TYPE_INT_ARGB)
        #^Graphics2D g (.createGraphics img)

        #^BufferedImage pad (BufferedImage. (tile-to-real width) (tile-to-real height) BufferedImage/TYPE_INT_ARGB)
        #^Graphics2D padg (.createGraphics pad)]
    (doseq [[y row] (cseq/indexed (:tiles amap))
            [x tile] (cseq/indexed row)]
      (.drawImage padg (:pad amap) (tile-to-real x) (tile-to-real y) REAL-TILE-SIZE REAL-TILE-SIZE nil)
      (if-let [#^Image img ((:tileset amap) tile)]
        (.drawImage g img (tile-to-real x) (tile-to-real y) REAL-TILE-SIZE REAL-TILE-SIZE nil)
        (throw (Exception. (str &quot;Missing tile &quot; tile &quot;.&quot;)))))
    (assoc amap
      :map-image img
      :pad-image pad
      :max-map-x width
      :max-map-y height)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This code is nasty, mostly due to converting between tile-based coordinates and pixel-based coordinates.  This code needs to be cleaned up a bit.  But that's about the most complex code you'll find in my program so far.&lt;/p&gt;

&lt;h1&gt;Who needs mutable state?&lt;/h1&gt;

&lt;p&gt;Clojure is a mostly functional language, in the sense of strongly discouraging unnecessary use of mutable state, and this program is no different.  I'm sometimes amazed how far I can get before I need mutable state at all.  The vast majority of my functions take a world value (a plain old hash-map) as an argument, and return a new world value after making changes to it.  The current state of the world is whatever value is currently in the global WORLD ref.&lt;/p&gt;

&lt;p&gt;The render loop grabs a snapshot of the world from the ref on each iteration, and then draws it.  Thanks to Clojure refs, the snapshot of the world is guaranteed to be consistent (e.g. no NPC objects in the middle of mutating themselves) and persistent (the world value sticks around as long as the renderer needs it, even if the WORLD ref is changing in another thread).  Once it's been drawn, the renderer throws the world snapshot away and it's garbage-collected later.&lt;/p&gt;

&lt;p&gt;This all happens around 50-100 times per second in my game, and there's no noticeable lag.  So that's a good thing.&lt;/p&gt;

&lt;h1&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;That's about it.  I'll post the full source code once it doesn't suck as much.&lt;/p&gt;

&lt;p&gt;In my opinion, exploring a new area of study is some of the best fun a person can have.  It's a flood of information and a surprise every 10 seconds. &lt;/p&gt;

&lt;p&gt;Making a game has been like that.  There's a huge wealth of knowledge about this kind of programming that I never knew existed.  Everything I read on this topic is new to me and fascinating.  The saddest(?) part of this whole thing is that I'm going to have more fun programming this game than I usually have playing games I buy in the store.&lt;/p&gt;</description></item><item><title>Spriting and learning</title><link>http://briancarper.net/blog/spriting-and-learning</link><guid>http://briancarper.net/blog/spriting-and-learning</guid><pubDate>Mon, 07 Sep 2009 13:57:09 -0700</pubDate><description>&lt;p&gt;In the mid-1990's I was really into Nintendo games, as was everyone.  My favorite was the original NES Final Fantasy.  Sometime in my teens I got my first computer, and I decided it would be cool if I had some sprites of that game on my computer.&lt;/p&gt;

&lt;h1&gt;Before&lt;/h1&gt;

&lt;p&gt;My first computer ran at 640x480 with 16 colors.  I had Windows 3.1 and the most sophisticated image manipulation program around was MS Paint.  How could I get sprites into my computer?  Well, I had a strategy guide for the game, with blurry photos of all of the enemies, so I just opened up MS Paint, zoomed waaaaaaaay in, and drew all of the sprites pixel-by-pixel.  Insane?  Maybe, but it's a fun kind of insane.&lt;/p&gt;

&lt;p&gt;This took about a year of off-and-on work, but in the end I had something I thought was great.  I still have the file:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://briancarper.net/random/ff1.gif&quot;&gt;&lt;img src=&quot;/random/thumbs/ff1.gif&quot; alt=&quot;FF1&quot; title=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;!--more Click for more spriting adventures (it gets better) --&gt;

&lt;p&gt;Note that my computer couldn't even produce sophisticated colors like &quot;orange&quot; and &quot;brown&quot;, so I had to tile red and yellow together so it looks orange from a distance.  Oh how computers have progressed since then.&lt;/p&gt;

&lt;p&gt;At this point I loved computers but I literally didn't even know what programming was.  I had never heard of the internet.  I didn't even get a taste of programming until high school.&lt;/p&gt;

&lt;p&gt;In 1999 I was in college but still largely ignorant of programming.  I decided to start my &lt;a href=&quot;http://ffclassic.net/&quot;&gt;first website&lt;/a&gt;, which was about the NES Final Fantasy that I still liked.  I decided to put some sprites on the site.  For the first version I chopped up my old image file from above into individual sprite files, but the quality of these was terrible.  &lt;/p&gt;

&lt;p&gt;So the way I got good sprites was by taking screen-captures of the game in an NES emulator, then in Paint Shop Pro I cropped out the backgrounds.  This was much faster than hand-drawing, but it still took months.&lt;/p&gt;

&lt;p&gt;This is an example of using a thousand-dollar piece of equipment as a hammer to pound in a nail.  It's the most rudimentary form of computer use.  It's the kind of thing I cringe at when I see coworkers do it today.&lt;/p&gt;

&lt;h1&gt;After&lt;/h1&gt;

&lt;p&gt;Last weekend, many years older and hopefully a tiny bit wiser, I pulled out my copy of the most recent remake of FF1, for the PSP.  I decided it'd be cool if I could rip some sprites from this.&lt;/p&gt;

&lt;p&gt;So, first I got an ISO of the game and mounted it loopback so I could view the files.  In the ISO there's a 100MB BPK file.  I didn't know what this is so I opened it in a hex editor and saw that it was some kind of archive.  You could clearly see an initial list of filenames with byte offsets and some other flags for each, then a bunch of binary data.&lt;/p&gt;

&lt;p&gt;A few google searches later and I found &lt;a href=&quot;http://forum.xentax.com/viewtopic.php?f=10&amp;amp;t=2594&amp;amp;p=29143&quot;&gt;this&lt;/a&gt; where someone else had the same idea as I did.  There's an extraction script there in some language I don't know, but it wasn't hard to figure out what it was doing.&lt;/p&gt;

&lt;p&gt;So then I was going to write a script to extract the BPK but thankfully someone in Japan &lt;a href=&quot;http://www.geocities.jp/junk2ool/&quot;&gt;already did&lt;/a&gt; which saved me the trouble of even doing that.  Some of the extracted files were themselves archives, but after running the script on its own output a few times I had a bunch of GIM files.&lt;/p&gt;

&lt;p&gt;What's a GIM file?  Never heard of it.  But a quick google search for &quot;GIM to BMP&quot; will net you a program called &lt;code&gt;gimconv&lt;/code&gt;.  Sadly it's Windows-only, but a batch file or two later and I had a bunch of BMPs like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/random/KURO.bmp&quot; alt=&quot;KURO&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;These files have solid backgrounds, but I want transparent backgrounds.  But it isn't hard to make an image's background transparent in Linux using ImageMagick and its &lt;a href=&quot;http://www.imagemagick.org/Usage/&quot;&gt;nice documentation&lt;/a&gt;.  One snag is that all the images have different background colors, but I can tell ImageMagick to use the color of the top-left pixel as the transparency color:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;for f in *.bmp; do convert -matte -fill none -draw 'color 0,0 replace' $f ${f/bmp/png}; done
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Some of these images look like sprite sheets.  So let's pick one, chop the sprites into individual files, then make an animated .gif from these sprites (while also adding a transparent border around it).&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;convert KURO.png -crop 32x32 +repage KURO%d.gif
convert -matte -bordercolor none -border 28 -compose Copy KURO?.gif -delay 25 -dispose Background KURO.gif
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Giving us:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/random/KURO.gif&quot; alt=&quot;KURO&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So after a matter of 2-3 hours (most of which was puzzling over some hex data and then googling around), I am already pretty much done.  Barely any skill on my part required other than knowing what to look for.&lt;/p&gt;

&lt;p&gt;The moral of this story is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The internet is awesome.  It's easy to forget how much better life is with so much knowledge at our fingertips.  I can't even remember what it was like without the internet and I'd never want to go back to that.&lt;/li&gt;
&lt;li&gt;Learning is fun.  What was a year-long job became a few-minutes job with even a rudimentary knowledge of scripting.&lt;/li&gt;
&lt;li&gt;ImageMagick is pretty handy.&lt;/li&gt;
&lt;li&gt;Old school games are great.  FF1 is still fun after 20 years. They keep re-making it for new systems for a good reason.&lt;/li&gt;
&lt;li&gt;I got a late start in programming.  I wish I would've started at a younger age.  Think of how much more I could've accomplished with my time.  I'm still playing catch-up in many ways.&lt;/li&gt;
&lt;li&gt;Sitting here in front of 3840x1200 pixels worth of million-color monitor screen, typing a story people are going to read in a few minutes in countries I'll never visit, ripping sprites from a portable game device that's probably thousands of times faster than my old NES, I can't even imagine what we're going to be doing with computers in another 15 years.&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Stylus DIY, hand health</title><link>http://briancarper.net/blog/stylus-diy-hand-health</link><guid>http://briancarper.net/blog/stylus-diy-hand-health</guid><pubDate>Sun, 01 Feb 2009 14:39:52 -0800</pubDate><description>&lt;p&gt;The stylus that comes with a Nintendo DS is a very mild form of hand torture.  Not sure whose hands those were designed for, but not mine.  In googling for a good replacement, I chanced upon a blog post which suggests &lt;a href=&quot;http://www.arghyle.com/2007/06/25/the-best-ds-lite-stylus-diy/&quot;&gt;finding a nice big ballpoint pen and jamming a DS stylus inside&lt;/a&gt; so just the tip sticks out.  This works amazingly well.  It's not as portable, but I will make that sacrifice to prevent being crippled.&lt;/p&gt;

&lt;p&gt;I am in fact &lt;a href=&quot;http://briancarper.net/2008/06/08/emacs-pinky/&quot;&gt;always a bit worried&lt;/a&gt; about preserving the health of my hands.  I have no hard data to support this, but I suspect my generation may have major hand-related problems in the coming decades.  What with computer keyboards and tiny cell-phone and PDA keys and lots of other techy things.  Many of us use our hands to communicate almost as much as our voices.  Until we have Star Trek voice-recognition software, this will be a problem.&lt;/p&gt;

&lt;p&gt;I started experiencing a lot of aches and pains in my hands and wrists a decade or so ago, and I attributed it to computer use.  Since I started paying more attention, things are better.   I maintain a very comfortable typing position for my hands.  I have a nice big comfortable mouse.  And so on.  My hands don't hurt any longer nowadays, which is nice.  If I become unable to type someday, I'm completely screwed.  How can I work as a programmer if I can't input text into a computer?  And I won't be able to draw or do origami or play video games or do many other things I enjoy.&lt;/p&gt;</description></item><item><title>OpenVPN in Gentoo in 15 minutes</title><link>http://briancarper.net/blog/openvpn-in-15-minutes</link><guid>http://briancarper.net/blog/openvpn-in-15-minutes</guid><pubDate>Sat, 08 Sep 2007 08:58:12 -0700</pubDate><description>&lt;p&gt;Situation: You have two computers at different locations.  One of them is behind a very restrictive firewall that doesn't permit any incoming connections, and may be running Windows (ugh).  The other is running Gentoo and is more permissive.&lt;/p&gt;

&lt;p&gt;Problem: You want to play a game on these two computers, like ZSNES, which requires both machines to accept a connection on some port.   ZSNES ideally uses UDP protocol for this.  Or, you want to play some game that requires connections on multiple ports.  For these or some other reason, an SSH tunnel isn't practical.  So you must defeat the firewall.&lt;/p&gt;

&lt;p&gt;Concerns:  There are only two computers involved and no chance of more than two ever being involved.  You don't care about encryption; if someone wants to snoop your Final Fantasy 6 traffic, so be it.  You want to get something up and running in 15 minutes.  You want something that is fast and easy to use from that point forward.  You need something that works in Windows.&lt;/p&gt;

&lt;p&gt;Solution:  OpenVPN works really well for this.  If you don't care about encryption and such, &lt;a href=&quot;http://openvpn.net/static.html&quot;&gt;this &quot;static&quot; HOWTO&lt;/a&gt; is very effective at getting something working quickly.  Gentoo can also help a lot in this regard, via some nice &lt;a href=&quot;http://gentoo-wiki.com/HOWTO_OpenVPN_primer&quot;&gt;wiki entries on Gentoo-Wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Anyways, this is how I did it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Make sure &lt;code&gt;Universal TUN/TAP device driver support&lt;/code&gt; is enabled in your kernel for any Linux machines involved. (If you don't already have this included in your kernel, I guess there goes your 15 minutes right there. Oops!)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Have the client install OpenVPN on their computer. (For Windows, download &lt;a href=&quot;http://openvpn.net/download.html&quot;&gt;here&lt;/a&gt;.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the server machine, run:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;emerge openvpn
cd /etc/openvpn
openvpn --genkey --secret static.key
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Edit &lt;code&gt;/etc/openvpn/openvpn.conf&lt;/code&gt; and put this in it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;dev tun
ifconfig 10.8.0.1 10.8.0.2
secret static.key
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Edit &lt;code&gt;client.ovpn&lt;/code&gt; and put this in it (filling in your proper server IP address or domain):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;remote SERVER.IP.ADDRESS
dev tun
ifconfig 10.8.0.2 10.8.0.1
secret static.key
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Send &lt;code&gt;client.ovpn&lt;/code&gt; and &lt;code&gt;static.key&lt;/code&gt; to the client by whatever means necessary. You should also keep a copy of &lt;code&gt;static.key&lt;/code&gt; in /etc/openvpn on the server. You can get rid of client.ovpn on the server machine.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start up the server:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/etc/init.d/openvpn start
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it, you should now be able to fire up any program and point to 10.8.0.2 to access the client from the server, or 10.8.0.1 to access the server from the client, any port, TCP or UDP protocol.&lt;/p&gt;

&lt;p&gt;If you really care about encryption or security, there are much better ways of doing this using real public/private key sharing.  It may be worth taking the extra half hour or so to set up OpenVPN the right way depending on your needs.&lt;/p&gt;</description></item><item><title>pSX</title><link>http://briancarper.net/blog/psx</link><guid>http://briancarper.net/blog/psx</guid><pubDate>Sat, 25 Aug 2007 16:54:19 -0700</pubDate><description>&lt;p&gt;I found a need to install a Playstation emulator today, and
recalling the minor nightmare installing ePSXe always turns out to
be, I searched the Gentoo forums. There's a (somewhat) new emulator
called &lt;a href=&quot;http://psxemulator.gazaxian.com/&quot;&gt;pSX&lt;/a&gt; which works really
amazingly well. No need to hunt down video/audio/CD-ROM plugins,
thank God. It just works. The source code appears to be sadly
unavailable so it'll be a binary install only. If you need to
install it in Gentoo, it depends on &lt;code&gt;dev-cpp/gtkglextmm&lt;/code&gt; and
&lt;code&gt;x11-libs/gtkglext&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>That's better</title><link>http://briancarper.net/blog/thats-better</link><guid>http://briancarper.net/blog/thats-better</guid><pubDate>Fri, 20 Jul 2007 18:05:14 -0700</pubDate><description>&lt;p&gt;This is why I like Gentoo. I installed ZSNES and I got version 1.51 from the Portage tree, but that version doesn't have netplay.  The version I want was some funky 1.42n version somebody wrote with netplay built in.  It took me all of five minutes to write an ebuild based on the build in the Portage tree and mask the later versions and install what I wanted.  In Ubuntu I'd have been back to ./configure &amp;amp;&amp;amp; make &amp;amp;&amp;amp; sudo make install and it probably would've bombed due to missing libraries.&lt;/p&gt;

&lt;p&gt;Lots of chatter on blogs lately about Gentoo management problems.  As usual, I guess.  Something I've been guilty of in the past myself is ignoring all the good and focusing on the bad.  It's somehow easy to install 1000 packages and focus on the one that fails and respond with &quot;Holy crap Gentoo is broken, stupid lazy devs&quot;.  It's easy to see one offhand comment from a dev on a mailing list that may be very vaguely insulting to someone if read in a certain way and conclude that Gentoo devs as a group hate non-dev users and think they're scum.  (How can &lt;a href=&quot;http://forums.gentoo.org/viewtopic-t-570789.html&quot;&gt;a one-paragraph email spawn a five-page forums thread&lt;/a&gt;?)  I'm making a concerted effort to ignore all of this crap as much as possible.&lt;/p&gt;

&lt;p&gt;There's so much good here if you open your eyes to it.  Sure some things don't work sometimes, but so many things do work so much of the time and it's easy to lose sight of those.  This is one reason I think it's important to make some noise when things DO work sometimes, even if it seems redundant.  Just to keep perspective.&lt;/p&gt;</description></item><item><title>PHP design patterns?</title><link>http://briancarper.net/blog/php-design-patterns</link><guid>http://briancarper.net/blog/php-design-patterns</guid><pubDate>Tue, 23 Jan 2007 18:44:48 -0800</pubDate><description>&lt;p&gt;As one of my professors used to say, &quot;There's no problem that can't be solved by adding another layer of abstraction&quot;.  He was joking, but there's a little bit of truth to that.&lt;/p&gt;

&lt;p&gt;I've been working on my &lt;a href=&quot;http://ffclassic.net&quot;&gt;other website&lt;/a&gt; for a while.  It's all PHP4, not entirely by my choice but I'm limited what can be installed on my server.  PHP4 turned out to be a lot more powerful than I'd initially thought.  Namely the OOP options available are mostly adequate and in a few places surprisingly OK.  PHP has some nice meta-programming sorts of functions like &lt;code&gt;get_func_args()&lt;/code&gt; and &lt;code&gt;call_user_func()&lt;/code&gt; which let you play around with doing weird things dynamically at runtime, which were almost essential in putting all this crap together.  It lacks the completeness or elegance of Ruby, and PHP's syntax is generally horrific and painful, but it'll do.&lt;/p&gt;

&lt;p&gt;I recently read &lt;a href=&quot;http://www.oreilly.com/catalog/hfdesignpat/&quot;&gt;Head First Design Patterns&lt;/a&gt; which is quite a good book.  I was able to use plenty of what I learned there in designing a sort of templating system for my other website.  Basically I have tons and tons of tables of different sorts of data.  CSS goes a long way to making the presentation of the tables easy to change given that they are properly marked-up, but actually marking them up properly and consistently was the problem.&lt;/p&gt;

&lt;p&gt;So I put together a bunch of classes that generate HTML tables.  One of the things from Design Patterns that really makes sense when you hear it is to lean toward composition rather than inheritance.  In other words, an object that has another object as an instance variable is often easier to deal with than an object that inherits the same functionality from a parent class; the reason being that you can use polymorphism to your advantage, and have your object contain a member of a GENERIC type, and then you can give your object an object of any subtype of that generic type you want.  So your class then does not depend on the type of its instance variable at all, so long as all possible objects that end up in that variable have the same interface.  Loose coupling and all that.&lt;/p&gt;

&lt;p&gt;I have used this to great advantage in adding flexibility in marking up table cells.  A cell will be a different class (actually a different composition of lots of classes) depending on the kind of data it stores.  A TextCell holds text, so I know to pad it and format it (differently than an ImageCell, or a cell containing an unordered list).  I can composite TextCells with other more specific types; a value that represents a percentile is a PercentileCell; the class handles formatting the numeric value and sticking a % on the end.  I could composite it with an ImportantCell which will give it a special background color.  It's as simple as
&lt;code&gt;$cell = new TextCell( new ImportantCell( new PercentileCell( 56 )));&lt;/code&gt;
The nifty thing is that I can composite the cells arbitrarily in any order or combination, because each simply holds a generic &quot;cell&quot; member and calling a method on an object only causes it to call the same method on its child object, all the way down the stack.  Also, if I later decide to take the % off the end of all my percentile values, I can do it by changing a single class definition. That's the kind of thing you can't handle well with HTML/CSS alone.  Before, it'd take me a month and half to go mucking around in static HTML files fixing it all.  (Or an hour worth of Perl regexing, but still, no fun.)   Or if I want my HTML to validate as XHTML Strict, I can change the HTML for all my tables at once by screwing with the base Cell class.  If I want to turn it into XML or some other markup, I could do that too, in theory.&lt;/p&gt;

&lt;p&gt;At this point I'm essentially writing most of my pages entirely in PHP, never needing to write any raw HTML by hand at all, which is a very nice change of pace.  I wish they'd have taught more of this sort of thing in college.  We learned how to write searching and sorting algorithms until blue in the face.  But we never really touched on how to design software at a very high level.  I still think I'm not too good at it in many ways, but I'm learning, at least.&lt;/p&gt;</description></item><item><title>PHP makes me sad; programming makes me happy</title><link>http://briancarper.net/blog/php-makes-me-sad</link><guid>http://briancarper.net/blog/php-makes-me-sad</guid><pubDate>Fri, 24 Nov 2006 20:21:40 -0800</pubDate><description>&lt;p&gt;My other site is about &lt;a href=&quot;http://ffclassic.net&quot;&gt;Final Fantasy (the original video game of the series, for the NES)&lt;/a&gt;.  That site is how I learned HTML, a good 7 years ago.  It's still my baby and (perhaps sadly) one of what I consider my greatest accomplishments.  I wish I had more time to work on it nowadays, but such is life.&lt;/p&gt;

&lt;p&gt;Oh how the internet has changed.  I started out on that site with plain old static HTML files.  It used to take me months to do or change anything.  Eventually I learned about server-side includes and I started using those.  A few years later I delved into PHP and started using it as a more sophisticated &quot;include&quot; mechanism and templated a good bit of the site.&lt;/p&gt;

&lt;p&gt;Fast forward to today, and I'm trying to get most of my data in a mysql database.  I learned a lot from work about how to properly design a database.  That part I think I did pretty well at, on my FF1 site.  The PHP frontend is where I started having problems.  PHP is just such an un-fun language to work with.  I hear PHP5 is nicer than PHP4, but PHP4 is what I'm stuck with.  At first I tried a purely functional solution, and it very quickly become such a tangled mess of spaghetti code that it was nearly impossible to use.  Today I rewrote that crap using the extremely weak OOP constructs PHP4 has to offer, and weak though they may be, my code is ten times shorter and probably ten times easier to read.  &lt;/p&gt;

&lt;p&gt;Sometimes I'll be sitting here banging out code, and it will be not working.  The grunt-work of programming is debugging.  You write something you think will work, and 10 times out of 10, it will fail the first time.  So you find the first bug, fix it, rerun it.  Failure.  Find the next bug, fix it, run it, failure.  Find, fix, run, failure.  And you repeat this over and over and over.  But eventually you hit that point where you find the very last show-stopper bug, and you fix it, and run your program, and it all of a sudden it WORKS.  You almost never expect it to work. Success flies out of nowhere and hits you in the head, and it's really exciting. You get screen after screen of delicious output, all thanks to that last little tweak.  That's the moment that makes the slog of computer programming all worth it.&lt;/p&gt;</description></item><item><title>Fullscreen games under TwinView</title><link>http://briancarper.net/blog/fullscreen-games-under-twinview</link><guid>http://briancarper.net/blog/fullscreen-games-under-twinview</guid><pubDate>Fri, 06 Oct 2006 22:13:16 -0700</pubDate><description>&lt;p&gt;The only thing bad about having two monitors in Linux is fullscreen apps, from time to time.  Mplayer for example is smart, it fullscreens to whatever screen it's on and leaves the other alone.  VLC for example bugs out but at least it sticks to one monitor while doing so. &lt;/p&gt;

&lt;p&gt;NWN is not so smart, and tries to run at an enormous resolution which either bleeds across both screens or takes up all of one screen and runs off the edge.  &lt;/p&gt;

&lt;p&gt;One solution is to run in windowed mode.  This is less than optimal for all kinds of reasons, especially if you happen to have an auto-raising kicker or something similar.&lt;/p&gt;

&lt;p&gt;Found this on the &lt;a href=&quot;http://forums.gentoo.org/viewtopic-t-420196.html&quot;&gt;Gentoo forums&lt;/a&gt; though;  putting a NULL into one of your MetaModes works:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Option &quot;MetaModes&quot; &quot;1280x1024,1680x1050;NULL,1680x1050&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It turns off one monitor and leaves the other on.  &lt;code&gt;CTRL-ALT-+&lt;/code&gt; to switch to and from meta-modes, obviously.  NWN recognized the smaller resolution and let me run at 1680x1050.  Good old Gentoo forums, never let me down.&lt;/p&gt;</description></item><item><title>I think I can handle this</title><link>http://briancarper.net/blog/i-think-i-can-handle-this</link><guid>http://briancarper.net/blog/i-think-i-can-handle-this</guid><pubDate>Fri, 06 Oct 2006 20:51:01 -0700</pubDate><description>&lt;p&gt;After I emerged Neverwinter Nights, I got this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; * The NWN linux client is now installed.
 * Proceed with the following step in order to get it working:
 * Run /opt/nwn/fixinstall as root
&amp;gt;&amp;gt;&amp;gt; Regenerating /etc/ld.so.cache...
&amp;gt;&amp;gt;&amp;gt; games-rpg/nwn-1.67-r1 merged.
&amp;gt;&amp;gt;&amp;gt; Recording games-rpg/nwn in &quot;world&quot; favorites file...

&amp;gt;&amp;gt;&amp;gt; No packages selected for removal by clean.

&amp;gt;&amp;gt;&amp;gt; Auto-cleaning packages...

&amp;gt;&amp;gt;&amp;gt; No outdated packages were found on your system.


 * GNU info directory index is up-to-date.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I hope I didn't run that step out of order.  : (&lt;/p&gt;</description></item></channel></rss>

