Conversations and cut scenes

In order to simplify the creation of conversations, for The Ditty of Carmeana I developed a custom format that is a bastardization of Screenplay format, Theatre play format, and Unix config file.

Dialogue looks a bit like a screenplay. The character name is in all caps and indented (though not centered like it would be in a screenplay); this is followed by the spoken dialogue on the next line.

Stage directions are in brackets, as they appear in plays for the theatre. Except in The Ditty of Carmeana, the stage directions are commands to the game engine, but they kind of still read like stage directions.

The Unix config file part is for stuff like camera definitions and some of the interactive options, since those sections consist of a line with a keyword of some sort, followed by other data on the same line separated by spaces (which is the syntax of just about every traditional Unix config file).

Although it started as a way to automate simple conversations, the format proved convenient enough, and I proved lazy enough, that I started using it for cut scenes as well. (Typically in game development, cut scenes are designed in the 3D editor, and then the motions and timings are exported to the game engine. But not in this one.) Here’s an example of what they look like:

[Set time.flashback to 550]
[Dialog box off]
[Cut to ridgecam]
[Music "birds-ambient.ogg" on]

[Fade in over 7 seconds]

[Enter Carmeana at Carmeana1]
[Enter Iduge at Guide1]

[Carmeana starts walking to Carmeana2]
[Iduge walks to Guide2]

[Carmeana stands]
[Dialog box on]
[Volume "birds-ambient.ogg" 0.3]

    CARMEANA

There it is, Sir Iduge! The Sacred Temple.
Home of the great holy relic of Riconism,
the Ricohedron.

    IDUGE

It is still quite a distance away,
Your Highness.

    CARMEANA

[Volume "birds-ambient.ogg" 0.1]

I know. We best move on if we want to
reach the temple before noon. Come!
Updated: July 17, 2020 — 3:18 AM

Progress Report

Today, May 24, 2020, in the middle of quarantine, I would like to announce that I have completed the Main Quest for The Ditty of Carmeana. One can now start the game, and play through to the end (of what I’m planning to release, anyway), and hit every point in the Main Quest.

As such, I am entering a “soft freeze” phase of development. The soft freeze means the Main Quest is fixed; I will be making no more content for the Main Quest.

It’s only a soft freeze because I still intend to develop more content for Side Quests, and for generally fleshing out the detail in the game, and for a few minor things I patched over in the Main Quest. Since I’ve been focusing on the Main Quest, I haven’t been doing things like fleshing out towns. I have several towns but they aren’t really towns, per se. There are not enough shops, characters, money-making opportunities, or goodies. I still have to create content for all that.

(Fun story: when I played through the game from start to finish, I found out that, due to the way I set things up, there were insufficient ways for Lance to earn enough money to buy things he needed for the Main Quest. I never noticed this during development because I have a developer key that can give me as much money as I want. In my playthrough, I had to rely on some backhanded ways to make progress in a few places. It wasn’t cheating, since I was planning to have those backhanded ways exist in the real game. But still, I need to let the player advance without neeting too much creative thinking.)

Furthermore, there is a whole lot of revising, bug testing, and odds and ends to get to. A whole lot. The game isn’t close to even beta quality. It still crashes here and there (though it’s far more stable then during some earlier stages).

But as for the Main Quest, it’s pretty much done.

Fair warning: the last time I implemented a feature freeze was in 2012, to get the game ready for a Demo. The very last new content I put into the game were the Rickroll and the background music. After this, it took me two years of just fixing bugs and improving the interface until I had the game ready for a Demo release.

This release will have about 4-5 times as much content as the demo, so, yeah, there’s a lot of fixing things up left to do. In fairness, a lot of the time I spent getting the Demo ready was just getting the control scheme right, and I don’t have to do that again. It’s not a linear thing; a lot factors affect how much time it takes to finish bugs. But don’t think I’m almost done. I’m not.

And one last point. With one possible minor exception, this progress report is the one and only progress report I’m going to give you until I have a release date. So enjoy it.

Updated: May 24, 2020 — 7:26 PM

Maps

In case anyone is wondering why maps are so important in games, read this anecdote.

One of the dungeons I made for the Ditty of Carmean has a layout very close to the layout of the house I spent my teenage years living in. (I wanted something super disturbing and scary, as this dungeon has a horror theme, and boy did I hate that house.)

Recently I let my brother play though this dungeon. Now, of course, my brother grew up in the same house as me, so he should have been able to find his way around easily. He knew it was our old house.

He was just flat out lost. Couldn’t find his way around at all. And this was a house he grew up in.

Someone who didn’t grow up in that house?

Yeah, that’s why maps are pretty spiffy things to have in games.

Updated: May 23, 2019 — 8:16 PM

The most insidious enemy in the game: The Camera

The Ditty of Carmeana is, at its heart, a parody of The Legend of Zelda. As such, a lot of the early decisions I made concerning the game I just copied directly from Zelda. One of those decisions is that it would be designed to work with a gamepad. Zelda, being a console game, used a gamepad.

There’s another reason, too. I hate keyboard/mouse control. Just flat out hate it. I can see the appeal of the mouse for games that need precision aiming control (aka games that I never play), and of course sometimes you need to type or use menus which is much easier with keyboard and mouse, but other than that it’s just a terrible kludge. Using a keyboard to move a character around is awful. You’re basically limited to motion in eight discrete directions, at one or two different speeds. Gah. So I designed it for a gamepad and didn’t really worry about keyboard/mouse controls.

But, unfortunately, real-world concerns brought me around to keyboard/mouse. I wasn’t going to get this thing released on a console, and I knew that almost no one with a PC would go to the trouble of plugging in a gamepad just to play my silly little game. As a concession to usability, I added keyboard/mouse control to the game, and I spent a whole lot of aggrivating, hair-pulling time coming up with a control scheme that worked naturally with either method. And I think I did a great job of it.

But in all that time I focused on the controls, I really didn’t worry a whole lot about the camera. I did make one major camera change. When using keyboard to move the camera around, the camera remains in a fixed orientation relative to the character, unless it collides with something. With a gamepad, the camera is dragged along as the character moves. There is a reason for this.

Irrespective of the controls scheme, if you’re just roaming around it’s generally better to drag the camera along than to keep it at a fixed angle. The is the more natural and cinematic way to show a scene: the camera automatically tends to point itself at where you’re headed and presumably what you need to look at. The problem is, if the camera is swivelling as you move, it makes it pretty much impossible to run in a straight line with keyboard controls unless you position the camera directly behind the character, since a keyboard doesn’t allow movement in arbitrary directions. Because of this limitation, gamer expectations are that the camera doesn’t swivel as you move around when using keyboard controls. So that’s how I did it in The Ditty of Carmeana. (Have I mentioned how much I hate keyboard for moving a character around?)

But I only did it that way for keyboard controls. When using a gamepad the camera is dragged along by the character and swivels to keep the character in view. With a joystick it’s easy to run in a straightish line even when the camera is swiveling, because you can move in an arbitrary direction. Since that is usually the preferrable way to position the camera, I kept it for the gamepad.

And because I know some uber-gamer-bro is fuming over this (silently: there’s a reason there are no comments in this blog), I acknowledge that dragging the camera is not ideal for combat. This is why many gamepad control schemes include a button to constrain the camera motion. Some games, like Zelda, have auto-targeting. Other games have something like a “camera strafe” button, that fixes the camera angle. (And you can afford to do this with a gamepad because you don’t have three whole fingers devoted to character motion; you can spare one to fix the camera.)

Anyway, I’m getting philosophical and off-track here. The point is, for The Ditty of Carmeana, the only major change to the camera I implemented for keyboard/mouse controls was to fix the camera angle when using keyboard controls.

Here’s the problem. As I said, I prefer to play with a gamepad, so I had done the vast majority of my game testing with a gamepad up to the demo. I think all I did to test the keyboard/mouse controls was a single playthough, and decided that everything worked well enough. But, looking back I realize that although stuff worked, and the game was playable, it was really suboptimal. Almost all the early decisions I made with camera positioning were quite satisfactory for the gamepad. For the keyboard mouse, they made things awkward.

Here’s an example. This image shows the default positioning of the camera after you go through a door.

Why does the camera do that, you ask? Because that is where Zelda places the camera after you pass through a door. That’s the only reason. And, surprisingly, it works quite well… with a gamepad. Because of the way the camera tracks, the camera will very quickly end up in a suitable position, quite automatically, with only a little motion from the character. Failing that, a gamepad usually has a convenient camera reset button. (Again, you can usually spare a finger for this with a gamepad.)

But with keyboard/mouse controls, as they are now, it’s just painful. You can’t even swing the mouse around to get a better angle because it collides with the wall (and there’s currently no provision to allow the camera to move closer to the character). So you have to walk out toward the camera, at an angle for awhile, then swing the camera around. Not very fun.

And now that I’m adding things to the game like combat and some dungeons with narrow passages, it’s going to be even worse than it was in the demo. So I can no longer avoid it, but I’m going to have to revisit the camera positioning. And I do not want to. But it must be done.

Updated: June 28, 2018 — 7:54 PM

PyGame is gone, hello GLFW

I finally got rid of the last legacy software library that I have in The Ditty of Carmeana. The Demo came with a whole slew of open source code that I built off, but it turns out some of them (namely Pango, Cal3D, and PyGame) turned out to be not-so-great decisions.

Cal3D, an open source library to deform models, was the first to go; the vertex shader took over that job. Next came Pango, which I was using for paragraph layout. It is probably the most awful, evil, and saddistic library in existence. I wrote my own layout code on top of Freetype to get rid of that pestilence.

Now I am rid of PyGame. PyGame was actually not so bad, and I’d defintely recommend it for less ambitious games, but eventually my game just outclassed it and I needed something better. Problem is, it’s built on top of another library called SDL, and SDL is good at getting a window open and graphics on the screen, and nothing else. Things like input, operating system and window interaction, and innocuous things handling like screen resizing, are not handled very well in SDL and therefore not in PyGame either. One of my big goals (and one of the reasons people playing the game in Twitch don’t bang their heads on a desk) is for the game to not do insane things like crashing or going blank when you try to resize the window. SDL made that difficult, and I’m not sure I ever managed to work around all those issues.

Now I’ve relaced it with GLFW, which is smooth and sleek, and does all the little things right. It’s also really easy to hack, which is nice because it’s missing a few features (like joystick event callbacks) and had a couple bugs, but overall it’s just a breath of fresh air compared to SDL.

So at last, the Ditty of Carmeana is free of old decisions I now regret, at least on the coding side.

(And yes, I did actually say that my game outclassed something.)

Updated: February 6, 2018 — 7:28 PM

Enemy logic

This is a snippet from the code used to control an enemy. If you have any expertise in computer science concepts, you can probably recognize this as implementing a finite state machine (FSM). Lots of repetitive code. This is why enemies and NPCs in video games often use domain-specific langauges.

I did it the hard way.

def setup_state(self,*args,**kwargs):
    if self.state == "looking":
        self.to_feet()
        self.role = acting.HoldStill(self)
    elif self.state == "returning":
        self.to_feet()
        self.role = acting.MoveToPoint(self,*self.path.get_pointvec(0.0))
    elif self.state == "patrolling":
        self.to_path(self.path,0.0)
        self.role = acting.Pace(self)
    elif self.state == "charging":
        self.to_feet()
        self.role = acting.Chase(self,game.lance,speed=10.0,nearthresh=0.5)
    elif self.state == "swatting":
        self.to_feet()
        self.role = acting.MeleeAttack(self,self.swat_choreo,"goblin_swat",duration=0.667)
    elif self.state == "recoiling":
        self.to_feet()
        self.role = acting.Recoil(self,*args,**kwargs)
    elif self.state == "dying":
        self.to_feet()
        self.role = acting.Shift(self,self.supine_choreo,0.5)
        self.weakness.targetable = False
    else:
        raise ValueError("unknown state %s" % self.state)

def check_transitions(self):
    strikedir = self.strike
    self.strike = None
    if self.state == "looking":
        if self.life <= 0:
            self.transition("dying")
        elif strikedir is not None:
            self.transition("recoiling",strikedir)
        elif self.timer >= 0.4 and self.hero_is_swattable():
            self.transition("swatting")
        elif self.timer >= 0.4 and self.hero_is_visible():
            self.transition("charging")
        elif self.timer > 2.0:
            self.transition("returning")
    elif self.state == "returning":
        if self.life <= 0:
            self.transition("dying")
        elif strikedir is not None:
            self.transition("recoiling",strikedir)
        elif self.hero_is_swattable():
            self.transition("swatting")
        elif self.hero_is_visible():
            self.transition("charging")
        elif self.role.completed:
            self.transition("patrolling")
    elif self.state == "patrolling":
        if self.life <= 0:
            self.transition("dying")
        elif strikedir is not None:
            self.transition("recoiling",strikedir)
        elif self.hero_is_swattable():
            self.transition("swatting")
        elif self.hero_is_visible():
            self.transition("charging")
    elif self.state == "charging":
        if self.life <= 0:
            self.transition("dying")
        elif game.register["lance.life"] <= 0:
            self.transition("returning")
        elif strikedir is not None:
            self.transition("recoiling",strikedir)
        elif self.hero_is_swattable():
            self.transition("swatting")
    elif self.state == "swatting":
        if self.life <= 0:
            self.transition("dying")
        elif strikedir is not None:
            self.transition("recoiling",strikedir)
        elif self.role.completed:
            self.transition("looking")
    elif self.state == "recoiling":
        if self.life <= 0:
            self.transition("dying")
        elif strikedir is not None:
            self.transition("recoiling",strikedir)
        elif self.role.completed:
            self.transition("looking")
    elif self.state == "dying":
        if self.timer > 5.0:
            game.scene.remove_object(self)

Edit: June 28, 2018: As I reread this I realized that it’s not actually a finite state machine: one of the states (“recoil”) is parameterized with a 3-D real-valued vector, which means it’s an infinite number of states. Though techincally speaking, since real numbers are discretized, it still has a finite number of states, but it’s not really an FSM in spirit.

Updated: June 28, 2018 — 5:25 PM

The Macguffin

A few detailed oriented fans of the original Demo might have noticed in one of the walls of text I included in the game for world-building purposes mentioned an ancient holy artifact known as the Ecrofirt, to serve as the game’s Macguffin.  This was, of course, part of the larger scope of the game, but didn’t come into play, at all, for the demo.

Any idiot can figure out that “Ecrofirt” this is just “Triforce” spelled backwards.  I was using the name more or less as a placeholder until inspiration stuck me with a better one.

For the updated bugfix Demo, I decided that even “Ecrofirt” wasn’t obvious enough.  I renamed it the Triorb (which I pictured as three green glass balls arranged in a triangle) which I hoped would remind people of the Triforce, so they’d know it was important when they saw it mentioned.

 

Recently, in my work to create 1/3 of the game for a general release, two things happened.  First, I had a sudden inspiration for what to call the Macguffin: the Ricohedron (never mind where that term comes from), which is two tetrahedra joined at their bases.  Second, I realized that the way to set up the Ricohedron as the game’s Macguffin is to actually show it being stolen (duh).  Therefore, I am having the game’s opening cut scene show this, and I might as well show Princess Carmeana being kidnapped while I’m at it.

 

Anyway, here is The Ditty of Carmeana’s very own Triforce, the powerful holy relic known as the Ricohedron:

Underwheming, isn’t it?

Updated: March 13, 2016 — 5:37 AM

A Mystery Solved

I’ve watch quite a few playthroughs of Carmeana on Youtube, and one of the things that appeared odd to me is that sometimes the sky seems to go black on the screen captures.

Originally, I figured that the capture software is interfering with the graphics state in some way (and, actually that probably is it), but today I was debugging an issue where the sky wasn’t showing up on my end, and I realized I was doing something astronomically stupid.  The graphics drivers I was using had been “doing the right thing” (which is to say, they were doing something that was either invalid or undefined according to the OpenGL standard, but generally was what you wanted to happen) but they were actually hiding a bug.  I ended up changing something slightly that prevented the drivers from “doing the right thing” and it showed up on my end.

The graphics drivers that some people were playing through on were not “doing the right thing” at all (or were being hindered from “doing the right thing” due to the screen recording), which is to say, what they were doing was perfectly valid.  It’s just that my code was buggy and I didn’t realize it because my own graphics hardware was “protecting” me.

Bah.  I have a little pet peeve for drivers that “do the right thing” even when I do the wrong thing, and this is why.

However, in fairness, one of the lesser graphics drivers that I was testing on was an Intel i910, on a now seven-year-old refurbished laptop that was old for a refurb when I got it.  I keep testing the game on this laptop because A. it keeps me grounded–the game runs full speed on the laptop most of th time, and B. it has crappy graphics so I have an idea what my LCD should be.  But even the lowly Intel i910 drivers have protected me from this bug.

Anyway, bug should be fixed now, and playthroughs of the released game should show blue skies even on older, dumber hardware.

Updated: December 31, 2015 — 7:42 PM

Cal 3D gone

I really should be working on adding content.  But I am “that guy” who has to go and correct regrettable decisions of the past before I can move on.

Anyway I reached a major milestone by eliminating this really old and questionable library called Cal 3D, which I was using to do my character animation.  Cal 3D has three big issues:

  1. It’s written in C++
  2. It uses a terrible representation of quaternions
  3. It does all the vertex skinning calculations on the main CPU

I really don’t like C++, and now that Cal 3D is gone, The Ditty of Carmeana is Certified C++ Free, and I am happy about that.  It now uses only Python and plain C.  (Unfortunately, I’ll have to add C++ code back in when I add Steam interactions.)

Cal 3D for some reason uses a representation of quaternions that no one else uses.  (I have my suspicions that this representation was the result of some coder with too much time on his hands trying to optimize away a single addition instruction and realizing he could do that by shifting the sign and order of the components.)  Anyway, I now use normal quaternions and that’s spiffy.

But those were just annoyances.  The real benefit of this move is that I can start animating poses using the vertex shader, which is no small thing seeing how I need the CPU for the slowness of Python.  I haven’t actually added the shader yet (that’s another milestone) but everything’s finally set up for it.

Pango, you’re next.

Updated: October 22, 2015 — 1:01 AM

Work/Life Balance

In the year or so leading up to the release of the Demo of The Ditty of Carmeana, I worked a full-time job but managed to find plenty of time to work on the game to get it ready for a release.

Now that I’m planning to turn the demo into a full release, I can’t manage it.  Figures, right?

Actually it does, non-ironically.  When I was finishing up the demo, I had frozen all features and the problems I was facing were narrowly-focused: testing, controls, fixing bugs, stuff like that.  Now that I’m back into full development mode (or trying to be at least) the problems I face are very open, and it’s a lot harder to focus and find time with a full work schedule.

This is not a major roadblock though.  In a few weeks I am reducing my hours to 50%.  (This was made easier for me because some changes at work made it into not quite as nice a job as it used to be.  Also, depending on stuff, my reduction in hours will either be perfect timing or the worst timing imaginable, but either way I’m doing it.)  So, hopefully I reduce hours and tip the scale of work/life balance in a direction than can accommodate game development, I’ll start to make some real progress.

Updated: August 11, 2015 — 10:31 PM
Tampered Evidence LLC © 2015-2022 Frontier Theme