(Mc)CLIM and Becoming Enlightened

This is a cross post from a Gemini site I recently set up.

About four years ago (based on Github’s estimate), I took an interest in the Common Lisp Interface Manager (CLIM), and more particularly in the McCLIM implementation of CLIM. I made a couple of YouTube videos of myself poking around and learning how to use it, but I did not get far at the time: I had quit my programming job some years earlier and was in law school, and mostly quit programming even as a hobby shortly thereafter. My favorite of the videos was a snake game, by the way:

Drawing in McCLIM (And a Snake Game)

When I decided to jump back into Common Lisp a couple of weeks ago, one of the first things I did was go back to that game, clean up the code a bit, and remove a line using a feature that is not present in current versions of McCLIM (and thus prevented the game from running). I had a lot of fun trying to learn McCLIM, but I constantly felt somewhat overwhelmed. The documentation seemed to hint at great power, but I could not see it. It seemed too complicated for a GUI library with a very dated appearance.

I finally obtained the first spark of enlightenment while writing my Gemini client. The client suffers from the same problem of treating McCLIM like a GUI library, but a couple of tasks changed my perspective. The first was links: I needed a way to get some “clickable” text onto a page, and perform an action when it was clicked. The second was similar: Bookmarks. In Observatory’s bookmark interface, bookmarks act like links that you can click on to visit a page, but have the additional option of deleting the bookmark when you bring up a context menu by right-clicking. The bookmark issue was fairly easy after I got links working (and, admittedly, gave up on a more complicated bookmark interface).

I spent several hours reading through the CLIM specifications and McCLIM’s user guide in order to figure out what to do about links. I had thought about just using push buttons (I think there may be a Gemini client out there that did just that), but I really wanted to use normal, albeit perhaps colored, text that was clickable. I had the idea that this thing called “presentations” might do the trick. I read through the portions of the documentation that describe presentations a couple of times before deciding that they wouldn’t work: They seemed to be about presenting an object to the user, and perhaps requesting user input to manipulate the object. I mean, you write translators to change objects into other objects, and these are somehow associated with presentations. What was that all about, and what could it possibly have to do with just making text clickable? Too much for links, and not quite relevant. I looked into putting gadgets (CLIM’s term for widgets) into the stream (the main pane of the output window is set up as a stream in Observatory, so the document can be displayed by pointing standard output to it), but again, I decided I wouldn’t be satisfied with buttons for links. Finally I read about presentations again, and this time I carefully reviewed the with-output-as-presentation and define-presentation-to-command-translator macros. This was the beginning of enlightenment.

Here is the code for writing a link to the application window in Observatory:

(defmethod write-doc-part ((line link-line))
  (let ((res (link-line-uri line)))
    (if res
    (if (string= (resource-protocol res) "gemini")
        (with-drawing-options (t :ink *link-color*)
          (with-output-as-presentation (t line 'link-line)
        (format t "~a" (resource-get-uri res))))
        (format t "~a" (resource-get-uri res)))))
  (format t " -- ~a~%" (link-line-description line)))

It works a little different from links in other Gemini clients: Instead of showing the link description and making it clickable, it displays the URI and makes it clickable, with the link description beside it. It would be trivial to make it work like other clients, but I found this to be more informative to the user about where he or she is being taken. But that is not relevant here. The point is, the clickable portion of the link is within with-drawing-options (which changes the color to blue) and with-output-as-presentation. The later is the important part: It associates the text that gets printed out by the call to format with ‘line’, an instance of the class ‘link-line’. We make it clickable by creating the aforementioned presentation to command translator:

(define-presentation-to-command-translator follow-link
  (link-line com-follow-link observatory-app
         :gesture :select
         :menu t)
  (object) (list object))

The above code associates the ‘link-line’ class with the command (defined elsewhere) ‘com-follow-link’. This command is run whenever the ‘select’ gesture is performed on a presentation of a link-line, resulting in the link being followed. The ‘select’ gesture is associated with a left-click by default in CLIM. (As an aside, “:menu t” makes the command show up in a context menu when you right click, so there are actually two ways to follow a link.)

Bookmarks have another option, since they can be deleted. So they have their own class with another presentation to command translator. This one also shows up in the right-click context menu:

(define-presentation-to-command-translator delete-bookmark
    (bookmark-holder com-delete-bookmark observatory-app
             :gesture :delete
             :menu t)
    (object) (list object))

The delete gesture is associated with SHIFT+middle-click, if I understand the documentation correctly, but I just assume users will use the right-click menu to perform the action. I expect ‘bookmark-holder’ will end up as a subclass of ‘link-line’ eventually, as I clean up some things in Observatory, assuming I continue to work on it.

Where is the enlightenment, you ask? Well, I learned from this experience that I should not think of CLIM as a GUI library. It is first and foremost an abstraction for presenting objects to the user, and providing him or her with operations, such as commands, that act on or using those objects. The interface is secondary, and should arise naturally from the commands made available to the user. This is a different paradigm from traditional event-driven GUI libraries, in which one crafts out the interface and then associates widgets with callback functions. It has its event loop, sure, but the programmer is expected to think more in terms of asking the user for input and acting on it, like a command line program, rather than waiting for events. Or really, a strange hybrid of the two programming styles, with some new concepts about how users interact with objects in the program. If my memory retains a correct history of those videos I made a few years ago, I remember being impressed by McCLIM’s interactor pane (which does not make an appearance in Observatory, by the way). I thought it could be something neat and useful, but I never really figured it out. I think I understand now: It is another way to get commands and references to objects from the user.

The next time I write a CLIM program, I will start out with a different type of thinking and different expectations. The interface will grow out of the needs of the program, rather than the program being made to fit into a predetermined type of interface. I will think about what type of things I want to do, and what data I want to operate on, and start out by providing commands for the user to work on that data. When a need arises for the user to provide input to a command, such as a reference to an object, that is where a bit of the interface will arise, naturally to fulfill a purpose.

Casus part I: planning the game

Before jumping into writing a program, I like to have at least a general idea of where I’m going. The amount of detail depends on the particulars of a project, but I generally don’t start without at least some sort of goal. For my asteroid game, due to its simplicity, my pre-planning consisted essentially of “a spaceship will navigate around asteroids.” Casus, on the other hand, is intended to be a role-playing game with at least a few hours of playability. It therefore required a little more detail in its planning. The caveat is that these details may undergo significant changes during development, but they at least provide a general road map for development.

“Casus” is Latin for “the fall.” As mentioned, the game is originally going to be written in Latin; an English translation of the game will be provided on the App stores, but at a small price since I’d rather encourage use of the original game. This is an RPG taking place in a Latin-speaking world, and I think Latin is part of the uniqueness and charm of the game. (In any case, if you get through this blog series, you’ll probably be able to figure out what’s going on in the game!) The overall story goes thus:

Carathusiæ rex Matthæus, homo boni cordis non autem paucæ stultitiæ, populo bonam fortunam petens tulit inopiam. Jacobus magus regis debilitatem videns Matthæi fidem obtinuit regeque ignorante populum ad res novas egit. Ut rex exercitús mitteret ad regni finem ad bellum gerendum cum barbaris persuasit. Interea, scelerum grex a Jacobo convocatus est. Jurejurando accepto, gregales in palatium intraverunt regem necatum.

Regis autem uxor magorum erat artis perita, et cantamine maritum servavit. Grex qui non potuit regem necare in carcerem cum uxore ejecerunt. Regis reginæque vero filius a grege non inventus est nam servus regis cum infante egressus ex palatio procul puerum aluit. Die natali XVI pueri servus omnia eum docuit et puer novum facinus cœpit ad patres liberandos…

An English translation might be along these lines:

Matthew, king of Carathusia, was a good-hearted but incompetent man. He sought for the good of his people, but only managed to leave them in poverty. Jacob the sorceror saw the weakness of the king and obtained his trust. Meanwhile, unbeknownst to the king, he urged the people to insurrection. Jacob persuaded the king to send off his armies to the borders of the kingdom to fight with foreigners, while the sorcerer gathered a band of rogues and bound them in an oath to kill the king.

The king’s wife was also skilled in sorcery, and protected her husband with a spell. The band realized that the king could not be killed and threw him and his wife into the dungeon instead. The king and queen, however, had a son who was not found by the conspirators. A servant of the king absconded from the palace with the infant and carried him to a faraway place. On the boy’s sixteenth birthday, the servant recounted the events to the boy, who then began a new undertaking to free his parents…

I love JRPGs, and the Dragon Quest series in particular, and I took some inspiration from the original Dragon Quest (“Dragon Warrior” on my NES) game. Nonetheless, I decided to follow more closely RPGs such as Ultima and Wizardry. In particular, the plan is to have some amount of player control over character creation and development.

Upon starting a new game, the player will be able to set up some initial character attributes. At least, the player will enter a name and select a profession. Currently contemplated professions are eques/caballarius (“knight”; probably “eques” will be the term in the game, but the medieval term “caballarius” is being considered); magus (“sorcerer”); fur (“thief”); monachus (“monk”); and scelus (“rogue”). The profession will determine the avatar and initial attributes, but the player will be able to develop the character in different ways during the game.

The planned character attributes are common in the RPG world; they’ll be in English from now since they’re not made up from scratch but a part of the generic makeup of RPGs: hit points; magic points; strength; defense; agility; intelligence; alignment; and experience (which may be connected to a level). The player will also have a quantity of aureum (gold) or pecunia (money) in order to buy and sell with NPCs. Alignment can be good or evil; the eques and monachus start out with “good” alignment, fur and scelus start out as “evil,” and magus can go either way. The alignment is expected to have certain in-game effects. For example, it is planned that an “evil” character will not be permitted to trade with merchants, but fures (thieves), instead of attacking, will offer to trade with the player.

Weapons include the baculum (stick), clava (club), pugio (dagger), gladius (short sword), and spatha (long sword).

Armor includes lorica hamata (chain mail), lorica squamata (scale armor), and lorica squamata (segmented armor). Additionally, there are ocreæ (metal greaves), manicæ (gauntlets), caligæ (boots), and the galea and cassis (leather helmet and metal helmet, respectively). Shield options include the scutum (leather-covered wooden shield) and the clipeus (metal shield).

A number of magic spells are planned to be available to the player, including sanare (heal), nocere (hurt), curare (a stronger heal spell), lædere (a stronger hurt spell), debilitare (weaken), terrere (terrify), firmare (strengthen), and necare (kill; i.e. “Avada Kedavra”).

Enemies include the mus (mouse or rat), vespertilio (bat), vipera (snake), fur (thief), miles (footsoldier), spectrum (ghost), venefica (witch), veneficus (warlock), cyclops (hopefully you know this one), versipellis (werewolf), draco (dragon), and gorgo (gorgon/Medusa). This list is apart from Jacobus and possible other bosses.

There will be a number of kingdoms other than Carathusia (at least three), each with its own castle and two or more towns. Indeed, the player will start outside of Carathusia, away from the notice of the sorceror Jacobus. The player will have the opportunity, if he or she so desires, to visit each of the other kingdoms and seek the support of their kings before going up against Jacobus.

In the game, there will be three general location types: the overworld, in a top-down perspective; the towns, also in a top-down perspective; and dungeons, currently planned to be done in a 3D perspective. The 3D perspective of the dungeons Ultima and Wizardry will be updated somewhat with a nicer appearance, but I’m not an artist and don’t have a large budget, so don’t expect the Legend of Grimrock here. One side of me does want to change the plans somewhat and use 2D procedurally generated dungeons in the spirit of Nethack. These plans are guidelines, not set in stone. We’ll see what happens. In either case, the dungeons will provide ample opportunity for the grind, allowing the player to level up before fighting bosses.

With the above in mind, I began writing some code to try out displaying a map for the character to wander around. This served as sort of a “proof of concept” for the overworld. The next blog post will discuss the initial steps.

Asteroid Belt

Recently, instead of paying attention in Constitutional Law class I sloppily slapped together a simple game in order to reacquaint myself with the LibGDX framework. The result is a game of dodging and shooting relatively slow-moving asteroids.

I threw the game up (as a free app) on the Google Play Store and the Amazon App Store (the latter is an affiliate link, FYI). One of the draws of LibGDX is that it supports various platforms, one of which is Javascript/HTML5 using Google Web Toolkit. Because of this, you can also play “Asteroid Belt” online (left and right arrows move; spacebar shoots).

The “Asteroid Belt” game was preliminary practice in anticipation of a more ambitious project. This new project is a western-style computer RPG in the tradition of “Ultima” and “Wizardry.” The current plan is for the game itself, called “Casus,” to be available as a free download on Google Play and Amazon, but with the twist that it will be in Latin. An English version (perhaps eventually localized with other languages) will be available for a small price.

I’m a huge fan of free (in the GNU sense) software, so I would like to eventually provide Casus under the GPL. Nonetheless, I would like app store exclusivity for my games for at least a certain amount of time. I’m trying to figure out the best way to work this out. One option is an initial release as a proprietary app, and a later GPL release. Another is releasing the source code under the GPL from the start, but using a restrictive license for the assets (graphics, sound, possibly story line), at least initially.

For now, expect some upcoming blog posts discussing the development of the forthcoming game…