Narthenian, Part 3

Screenshot of Narthenian game with an innkeeper greeting the player

I managed to fit a couple of hours in today for adding innkeepers and staying at an inn to The Narthenian Conspiracy. Since inns charge for a stay, I had to add gold to the game, and let the player collect gold from slain monsters. I also added a new sprite that will be used for merchants and innkeepers. It looks more like a wizard or a gnome, or maybe a gnome wizard, but that’s the extent of my art skills.

Innkeepers are their own class, and Merchants will be also. I could probably avoid some unnecessary code duplication by making NPC an abstract class, and subclassing the already existing NPCs, Merchants, and Innkeepers from it. I would probably have to turn the NPC vector associated with each map into a vector of pointers in order to make use of polymorphism. Whatever. I’ll deal with the consequences of my design choices for now and learn the lesson for later.

Staying at an inn fades the screen to black, and then fades back in to the usual display as a simple visual effect for going to sleep. I did this simply by drawing a black rectangle over the screen with alpha going from 0 to 255 and back. It probably goes a little to quickly as it is, so I may come back and tweak it to take longer later. Initially, the screen went straight to black; fading out and in wasn’t working. I realized that SDL3 ignores the alpha channel when drawing rectangles unless you call SDL_SetRenderDrawBlendMode on the renderer and set it to SDL_BLENDMODE_BLEND. This is probably also the case for earlier versions of SDL, but I have always copied portions of textures rather than drawn plain rectangles.

I wanted to put in a sound effect for sleeping at the inn, but haven’t created the sound yet. I may go with a different approach, and have the game play a “chiptune” arrangement of Brahm’s Lullaby during the whole interaction with the innkeeper, rather than using a sound effect just during the sleeping state. That’s something I’ll decide on soon: I’m currently learning my way around MilkyTracker in order to put together some background music for the game (it will ultimately be in .wav or .mp3 files; I don’t feel like implementing a reader/player for .mod files or pulling in too many external libraries for a relatively simple game). In any case, the decision need not be permanent if I don’t like how it turns out.

The game is becoming a little more playable with the ability to sleep at an inn and recover HP, so that going out and fighting and leveling up can continue. There is a lot to go with tweaking enemy stats, adding more enemies, adding items and spells, adding quests, etc., etc., etc. But little by little there is more to the game. While I’m working on the music, I probably need to also finish up the Carathusia town map, and put together some maps for the floors of the castle. Anyway, it’s easy to get overwhelmed thinking about everything that still needs to be done. More fun to focus on the enjoyment of seeing the little steps forward.

Narthenian, Part 2

Dialog scene from the game

I got the initial framework for NPCs and dialog set up, and I have uploaded the code to Github. You can find the game in its current, very early state, at https://github.com/jstoddard/narthenian. There are only a couple of NPCs and very simple dialog at this point: They only say one thing at you. There are some data structures and framework for a more complicated dialog system in which there can be some back and forth, with the player selecting a keyword or phrase from a menu to continue the dialog, and with NPCs able to give the player an item or update a quest. These features, however, need a lot more work to be implemented.

NPCs are kept in a vector in the AreaMap in which they are located. In the drawing routine that is run each frame, the game iterates over the NPCs in the current map and identifies and draws the ones that are within the bounds of the screen. The game interprets an ENTER keypress, and in the future the A button on an XBox controller or corresponding button on another gamepad (this was already implemented in the BASIC prototype) as a command called “ACTION1.” When the game receives an ACTION1 command, it looks for an NPC in the tile that the player is facing. If an NPC is there, the game goes into dialog mode, which currently comprises displaying the NPC’s dialog on screen and waiting for the player to hit ENTER again (or SPACE a.k.a. “ACTION2”) before returning to the normal mode of letting the player wander around the map.

Before fleshing out the dialog system with the upcoming features, I will probably set up merchants and inkeepers so that the player can rest and buy weapons, armor, and other items. I think that doing so will go a long way toward making this early stage of the game somewhat fun to play—an important consideration, since I need to spend a lot of time playing it, even in its unfinished state, for testing and bug-catching. I am not sure yet whether I will subclass NPCs for these characters, or make separate classes for them. There are some tradeoffs to consider. If they are subclassed from NPC, a single loop can handle drawing of all three character types, and another single loop can handle the ACTION1 command as discussed above. However, the latter is somewhat complicated by the fact that a merchant or inkeeper will often be behind a counter, so checking will be a little different, and we will want to switch to a shopping system or sleep system that will probably be distinct from the dialog system. This can be handled by moving the checking and mode-switching code from the loop into virtual functions in the NPC class, and just having the loop call those functions instead. The Merchant and Inkeeper subclasses would just override the functions.

Well, I’ll decide what to do over the next day or two. Meanwhile, I have a couple more monsters to add to the world, as I think I mentioned in the last post.

Narthenian, Part 1

I think I’m going to keep a devlog of a simple tile-based RPG that I’m working on. Although it’s not a huge undertaking, progress is and will be slow, since it’s just a spare-time project worked on a few hours a week in order to learn a little bit of modern C++. The original prototype was actually written in QB64 Phoenix Edition, a somewhat-modernized variant of the QuickBasic language. Here is a video of that early prototype in action:

I have rewritten it in C, and have been adding additional features in hopes of eventually turning it into a complete game. The tiles are based on a set from Lanea Zimmerman, username “Sharm” on OpenClipArt.Org. The melee scenes—which now have a nicer background than seen on the video—are based on AI slop with some editing. I’m not proud of that, but that’s the current situation.

The game is currently being written on an old Lenovo Thinkpad purchased for $150 and running FreeBSD. I have gotten tired of plain text editors and command lines, and, remembering the nice programming environments from the days of QuickC and Turbo C++, I have opted to work in the CLion IDE and enjoy letting a program handle most of the housekeeping tasks, provide easy refactoring and debugging, and generally keeping things easy. It’s nice having nothing to prove through programming asceticism. Graphics are edited on a MacBook Pro. The maps, for now, are put together with a simple map editor written in QB64 Phoenix, and the sound effects are recorded from sounds generated in QB64 Phoenix. The sound effects are simple and could be generated directly in C++, but QB64’s sound statement is almost like working with an old Yamaha sound chip, allowing me to set tone, duration, effects, and noise, which is easier than generating the waveform directly.

The current state of the program is that the player can wander around the world and fight a couple of enemies (the serpent and gigarat seen in the video, with more designed and about to be added), level up, and switch maps (i.e., enter and exit a town). I’m currently working on setting up NPCs and dialog, after which I expect to upload the work-in-progress to Github. Items and quests are in the works together with NPCs and dialog, but I don’t know how complete they will be before the initial Github commit. Spells need to be implemented, but I’m not sure when that will happen. (I’m about to set up a Mage enemy, but it will be stuck on plain attacks for a while!) Saving and loading games will be added once all those things are working. The overworld map is also sort of sparse, and there’s only one unfinished town working so far, so there is still a long way to go before a reasonably complete RPG is ready to play.

I’ll put up screenshots and discuss the development process as things proceed. For now, here’s the general background story behind the game:

When the heavens and the underworld made a joint attack on the people of the earth, the six cities united under the leadership of Carathusia for defense. Narthenus, one of the greatest warriors of the Forest People, disapproved of his people’s decision to submit to Carathusia, and betrayed both his army and the joint army’s battle plans to the underworld. The traitor’s actions nearly brought an end to the world, but under the Carthusian general’s leadership, the six cities managed to save the mortal races.

The unified kingdom lasted for centuries, until four thousand years after the great war, when a cult calling themselves the Narthenians attempted to overthrow the kingdom and break up the rule of the people once again. Civil war lasted for many years until the cult was finally driven out, but not destroyed. The leader of the cult, Tarpeya, fled to the deserts in the north with her few remaining followers. Rumors from the desert city Erimea suggest that the cult has established camp in the western part of the desert.

Although Carathusia’s resources were nearly exhausted after the civil war, the king was troubled by the reported movements of the remaining Narthenians. Concerned that Tarpeya was up to something more than just hiding out, he summoned a young warrior who had distinguished himself in service during the civil war. The young hero was sent to investigate and advised to travel first to Adon, where the dungeon held a former adviser to Tarpeya who may know more about her activities.

The rest of the story will come out during the game. Please be aware that some of these posts will likely contain spoilers. I’ll try to note that at the top of posts, but I can’t promise I’ll be perfect…

A Little Bit of Luddism

The internet is primarily a source of outrage and pornography. If that is not the most original thought, it is because it is apparent to many.

That is not to say that the internet provides nothing else. Certainly it offers unceasing advertisements for those of insufficient digital sophistication to use an ad blocker, as well as privacy invasions, and even the occasional malware. It may even provide something useful now and then, but these are not the central purpose of the modern internet.

Do I exaggerate? Maybe. But it takes only a moment to remember how productive I was before the internet became widely adopted. Being a little older provides the opportunity to remember a world that those just a little younger did not get to experience. It also provides the opportunity to join the chorus of every generation lamenting the fall of society and claiming that things were better in the old days.

Write me off as such a geezer, if you wish. You may be correct. I am writing my thoughts, but I do not feel it important that anyone accept them as correct. Nonetheless, I will still reminisce and make my comparisons. Perhaps I will even take some action to see if my life now might be improved by what I consider to be acquired wisdom.

Where was I again? Oh yes, productivity. I ought to be infinitely more productive with the vast repository of the world’s knowledge at my fingertips—a veritable tower of babel, if you believe those who assert that it was built as a central library of all human understanding. Between Wikipedia, scientific journals, legal and illegal book downloads, how-to videos on YouTube or how-to texts on websites, and seemingly infinite other sources of information, in principle I can learn how to do virtually anything.

I could, in principle. Yet I did, and much better, before I had all these resources available at all times. The encyclopedia, the library, and the bookstore, clubs and classes, provided what I needed before. Perhaps the greater expenditure of time and energy left me more insistent on finishing a project. Or perhaps the lack of immediately available entertainment, or links to related videos, left me undistracted from the task at hand. But without YouTube tutorials on Python, I managed to learn programming: First BASIC, then C, and even just a little bit of assembly language. Books and trial and error were sufficient teachers, since I found no teachers for this topic at high school. BBSes provided useful text files, but I often ended up turning to books that provided the same information in a more easily understood format. I learned the ins and outs of the PC… after learning the ins and outs of the Apple IIe.

I continued to learn programming after getting connected to the internet, but progress increasingly slowed as the internet became more immersive and faster. I learned new programming languages, I learned new libraries, but I no longer learned new things about programming. It was all “new” ways of doing the same thing, perhaps with a nicer interface, and perhaps networked, but no longer innovative. And it took me longer to learn each new, not-new thing.

I do not think it is merely me getting old, or having learned all there is to learn about programming. Once in a while I do learn something new and profound, and I have moments of great productivity in both coding and learning. This happens on the rare occasions when I close the web browser, pick up a book, and try out what it is trying to teach.

Programming is just the easiest example on which I can reflect. This phenomenon has affected several aspects of my life. Online examples were generally unhelpful when I was studying physics at university. If I had trouble understanding something, the library was where I solved the issue. My best work in law school occurred when I turned away from LexisNexis and opened up restatements, Corpus Juris Secundum, and the case reporters in the library. Even with gardening, YouTube videos are neat, but have not translated into great real-world results like just getting outside and experimenting with shovel, seeds, and a notebook.

This very article is being written on an Apple IIe. Otherwise, it probably would not be written: With a wide internet and infinite entertainment at one’s fingertips, who has time to write? Ironically, that Apple IIe is actually connected to the internet, but naturally such access is quite limited, and the non-multitasking machine will not browse the web while the AppleWorks word processor is open.

I could be that crazy old guy who is remembering the past a little rosier than it actually was. I don’t think so—I remember it as slightly more productive, not perfect—but it is possible. But my luddite self is testing the hypothesis right now. I am not crazy enough to entirely forego the internet. For better or worse, modern life depends on the internet. I could not do my job without it, and I would lose out on many great conveniences. I did say that it may provide something useful now and then, after all. So I am merely cutting out a significant chunk of leisure internet. When I would spend a couple of hours watching YouTube, or browsing news (or polemics) or social media, I am picking up a book, or using an old computer like this Apple IIe to write or do some old-style programming, or going outside for a walk.

It will be interesting to see what, if anything, I can get done that I was not doing before. Will I write about the results? Maybe. I can’t say. As I mentioned, I do not feel it important that you believe me, anyway.

Sonregistraĵo de “La novaj vestoj de la reĝo”

Jen sonregistraĵo de “La novaj vestoj de la reĝo”, traduko de fabelo de Andersen fare de Zamenhof kaj trovebla en la Fundamenta Krestomatio. Mi rekomendas, ke oni (kiu havas la eblon) akiru la lastan eldonon de Fundamenta Krestomatio, kiu haveblas ĉe UEA: En tiu eldono, la redaktoro indikas kelkajn malaktualajn esprimojn.

Oni permesas kopiadon de la sondosiero laŭ la kondiĉoj de la licenco CC-BY-SA 4.0.

(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.

Objective reality

According to quantum mechanics, a particle is in multiple states, called a “superposition,” until an observation (e.g., a measurement of its position or momentum) is made, at which point its wave function “collapses,” reducing the particle to a single state. A couple of years ago, physicists performed an experiment that showed that an observer could collapse the wave function of a photon, yet a subsequent experimenter could find the photon in a state of superposition. A writer for MIT Technology Review reported on this experiment and concluded that it means “there’s no such thing as objective reality.”

One could instead interpret the results as meaning that science is inherently unreliable, something we have known for some time already.

La urbo kaj la montoj

Hodiaŭ estas bela, klara tago en San Diego. Tra la fenestro de mia oficejo mi vidas, unue, la urbon, kaj je longa distanco malantaŭ la urbo montojn. Ho, la montoj! Kvankam ili aspektas kiel ombroj en la distanco, ili ŝajnas pli realaj ol la urbo. Ekde ĉi tie ili ĉiuj aspektas preskaŭ same, sed mi konas ilin. Kelkaj staras nudaj, kiel grandaj teramasoj. Aliaj estas vestitaj per de ĉi tie nevideblaj pinoj. Tiuj lastaj baldaŭ kovros siajn kapojn per neĝĉapo. Jes, ili ĉiuj apektas same en la distanco, sed ĉiu el ili estas unika, samkiel ĉiu homo estas malsama ol ĉiu alia.

Male, ĉiu konstruaĵego de la urbo aspektas malsame, sed ili ĉiuj estas same artefaritaj, makuloj sur la tero. En ili ŝrumpas la homaj animoj pro la nerealeco de la hodiaŭa vivo. Tia estas la urbo, la natura hejmo de kleruloj kaj profesiuloj, kiuj jam delonge perdis la saĝon. Ili arogas al si la rajton regi kaj gvidi la vivon de la kamparanoj kaj laboristoj, kvankam tiuj pli bone konas kaj pli akre perceptas la realon.

Ho mi estu fore de ĉi tie, mi estu en la montoj, kie la steloj vere brilas dum la nokto! En la urbo, la steloj mankas, dronite en la artefarita stratlumo. Tio estas ĝusta, taŭga bildo de la blindeco de la urbanoj, blindaj gvidantoj de la homaro! Ne, ne en la urbo kuŝas vera saĝo, sed en la freneza senhejmulo, la ermito kiu loĝas en la montoj. Sed la montoj ne estas mia hejmo, kaj mi ne taŭgas por la vera vivo tie. Post momenta revo la nuboj forigas la klarecon de la tago; mi memoras mian miopecon, kaj revenas al la nerealeco, al kiu mi apartenas.

Ŝpinado

Lastatempe mi eklernis—aŭ, pli bone, provis eklerni—ŝpinadon kaj trikadon. Mi sukcesis triki malgrandan trikoton, eĉ se ne tre bone, sed mi estas terure nekompetenta pri ŝpinado. Verdire, mi neniam estis tre kompetenta pri laboro per la manoj; tial mi laboras en oficejo. Tamen mi ne estas tiel facile venkita, do mi daŭre laboros kaj ekzercos min, ne nepre ĝis perfekteco, sed ĝis mi povos fari ion pri kio mi ne hontos!

Mia celo estas lerni teksi per teksilo. Malgraŭ tio, mi volis komenci per ŝpinado, ĉar mi scivolis pri kiel oni faris fadenon. Hodiaŭ oni faras ĝin per maŝinoj laŭ la plej nova teknologio, sed mi eĉ ne uzas radŝpinilo (eble tio estas mia problemo!); mi uzas—aŭ provas uzi—simpla spindelo en la manoj, samkiel oni faris dum miloj aŭ dekmiloj (aŭ eble pli) da jaroj. Kiam mi fariĝos almenaŭ iomete kompetenta pri ŝpinado, mi aĉetos teksilon kaj komencos lerni teksadon. Fakte, estas “gildo” de teksistoj ĉi tie en San Diego, al kiu mi aliĝos por pli bone lerni la arton.

Kion vi, karaj esperantistoj, faras kiel ŝatokupon?