Tag Archives: Michael Rubin

Missions of the Reliant: Happy Belated Anniversary, Reliant!

The one-year anniversary of the project to port Missions to modern Mac OS was one week and 5 days ago. I’m sorry I missed it, but as always, life puts a heck of a damper on the fun we want to be having. Still, I’ve got a few extra minutes to spend on it, and in the last few months I’ve learned a lot of new things. One of them is that having a plan is almost always a good idea! So as gift and apology, here’s the plan for the present and future of Missions of the Reliant!

  1. Because I haven’t worked on the project in a while, and because the time I’ve spent in iOS has taught me a great deal about proper Cocoa programming and OpenGL, my first action in returning to the Xcode project titled “Missions” will be to go through the game’s subsystems and fix some of the rather basic mistakes I made as a less experienced coder. This includes such things as the use of VBOs in OGL, unification of the disparate “node” classes into a proper hierarchy (eliminates an amazing amount of duplicated code), replacement of some of the things I hand-crafted (such as audio file access) with third-party libraries (since the ones I’ve investigated invariably did better at what they were meant for than my efforts), and possibly an even more general refactoring if I decide it’s necessary (a decision which pends further review).
  2. After that comes completion of the missing gameplay functionality. I don’t at this moment have a decent list of what’s missing available, but the stuff that comes to mind are most of the computer functions and the enemy AIs. Once all of that is done, the lot of it has to be tested to make sure I didn’t miss anything above the level of fine details. This leads up to…
  3. … The storyline, the literal “missions” of the Reliant! Those of you who played the original game (almost all of you, I’m sure) will remember that Mike provided three of them – invasion, rebellion, and time travel. These require tying together all of the gameplay elements in their proper linear fashion, which may sound simple, but this is one place where the mass of spaghetti in the original source code isn’t nearly so sad to look at (with, as always, apologies to Mike), because that was the only way to do it without OO features at least as advanced as Objective-C’s.
  4. At that point, the game will be ready for beta testing, and a 1.0 release. Said release will of course be freeware, and I plan to distribute through the App Store as well as on my Web site. I plan to ask Mike for suggestions on how to handle beta testing, but I tentatively plan to open it up to, at the very least, to everyone who subscribed to the mailing list.
  5. With 1.0 behind us, there will still be plenty to do. The game in that state will not be a direct port, and just won’t provide the sense of nostalgia I suspect many of you are, like me, hoping for. There are too many new features and changes in the interface. Some of them were my creativity getting the better of me, some were just inevitable moving between two completely different operating systems and programming languages across a gap of more than a decade. I plan to make a 1.5 (or whatever!) release which provides a “classic gameplay” mode, wiping out all the new features and restoring as close to the original exact interface behavior as possible. User feedback may, of course, convince me that this would not be worth the effort, but that’s something to think about at the time, not now.
  6. Finally, I never saw Missions as a static game. Mike released it as such because, among other things, that was the only kind of game we had back in those days. But just like Kirk and the Enterprise had lots more adventures after the original series, so can this game have more episodes. My vision is of downloadable content, and even a scripting interface so anyone can make new episodes for the game. This is a project almost as large as the game itself, and is another thing to possibly set aside based on the feedback of players, but it’s something I would certainly enjoy doing.
  7. Even further in the future, perhaps a 3D engine and models? The sky’s the limit! But let’s not get ahead of ourselves. At that point we’re talking about an entirely new game, practically an MMO if one takes the thought far enough, and that’s pretty silly if you look at it from right now. It might not be later!

I hope it’s obvious that I’ve put some thought into all of this. I still won’t have a lot of time to work on the game, but any is more than the none I’ve had lately, so expect to see updates coming again.

As always, my deepest thanks go out to you all for your patience as I’ve struggled my way through this project, and I’ll continue to do my best not to disappoint.

P.S.: I have considered making the game require Snow Leopard to play. It’s been long enough since its release that this might not be a bottleneck to most people, and it would considerably simplify some of the code; Apple added quite a few nice things to Cocoa only that recently. Please leave some feedback on this thought in the comments or on the mailing list!

Missions of the Reliant: Broadcasting distress call.

At the suggestion of Jude, seconded by none other than the man himself, I’ve created a mailing list for announcements and discussions about Missions of the Reliant.

Unfortunately, I’m a bit of a newbie when it comes to mailing lists, and I had to settle for a Google Group for simplicity’s sake: http://groups.google.com/group/missions-of-the-reliant. It’s members-only, but anyone can subscribe. Please feel free to sign up and comment; it’s open for discussion as well as news :-).

Missions of the Reliant: He’s dead, Jim.

With heartfelt apologies to DeForest Kelley.

The doctor is now a useful officer. Good luck getting the last word, though. Wounded crew get treated with the very latest technology, the skill of the best surgeons in all the Alliance, and often a grumpy bedside manner well worthy of Robert Picardo. I wish I could make a Doctor Who reference as well, but I know I could never do justice to a series I’ve only seen a tiny slice of.

Aside from the working doctor, I fixed a long list of minor bugs, things like the engineer doing repairs at the wrong speed, wrong text colors in the console, weapons and helm commands not requiring skill from the officers, and so forth. Stay tuned for more!

Missions of the Reliant: Receiving subspace message from Alliance HQ

According to my commit log, I’ve accomplished a lot, but I think a lot of that is stuff I’d already done and mentioned here, and just not committed. Bad programmer! Bad! No treat for you! Anyway, there is one thing I know hasn’t hit the blog yet, and that’s incoming subspace transmissions. Those are the big blobs of text in the middle of the screen containing all that awesome plotline Mike wrote. Nice stuff, if you ask me. Those work now, and they come in correctly via the helmsman, using the reports interface and everything. Only part that isn’t quite right yet is there’s no HQ text flashing at the corner of the viewscreen, but that’s easily done. I’m inching ever so slightly closer to having a fully implemented mission here.

Things still missing to make Assault on the Alliance completely functional:

  • Engineer functionality (repairing damaged systems)
  • Doctor functionality (healing wounded crew)
  • Starbase interaction (don’t actually need all of it to make the mission work, though it won’t be as much fun without a Mat-Trans or crew swapping)
  • Cruiser and battleship AIs (actually less complicated than the fighters)

That’s all! Stay tuned for more progress!

Missions of the Reliant: Thanks for the support!

I don’t have any real progress to report, with one exception: The “Report” command for all crew members, the one which tells you that systems are damaged and that crew members are hurt and that messages are incoming and all that sort of thing, is now complete and working. It’s not tested past the most basic stuff yet, though, so give me time :-).

The real thought behind this post is to thank you all for the unremitting support you’ve all shown these past months as I’ve worked on the game. Every few posts I get a whole list of comments from everyone who says they’ll wait for me to finish it, telling me how much they look forward to the results of the work I’ve put in. I just wanted to say, I truly appreciate it, and I’m certain I wouldn’t have gotten even this far without that to encourage me. Thanks, everyone :-).

And, as always, a special thanks to Michael Rubin; without the enthusiasm he showed when I first approached him with this idea, it couldn’t have happened.

As a gesture of my appreciation, here’s a short QuickTime clip of the Reliant entering warp drive. I’ve been promising you a real look at this thing for a long time. Enjoy! And please blame CoreText and QuickTime for the text layout and sound synch problems! ;-)

QuickTime Player plugin required. Tested on Firefox 3.6 and Safari 5.0. No guarantees about other browsers.

As always, stay tuned!

Missions of the Reliant: Take the weapons station

Well, I cleaned up the command console code quite a huge bit, it now works quite nicely, and also the gunner’s commands are all fully implemented (with the exception of, as with the helmsman, “Report”). I wish I had more progress to report, but I’m still here and I’m still working on the game, so hopefully you won’t lose hope yet.

This project has taken quite a bit longer than I ever expected, I have to admit. I started back in February with the impression that it would be a simple port, but I forgot at the time that I hadn’t yet learned much about OpenGL, OpenAL, and other such things. It’s turned into a tremendous learning experience for me, in everything from subsystems and frameworks to code design and testing procedures. Then real life came and intervened, kicking my progress down to an even slower crawl. That all being said, I’ve been enjoying it all the way through, even when OpenGL and CoreGraphics have been violently frustrating me with the problems of text rendering on a CGL surface :-).

I hope to have a more substantial update this weekend. Wish me luck! As always, my thanks to Michael Rubin for giving me the chance to work at this, even if I have taken just this side of forever ;-)

Missions of the Reliant: Why the bridge is an easy target at the top of the saucer section is something I never understood.

I’ll give you this one, Star Trek fans. It’s a shortening of a quote from the Captain’s Table series of books, specifically book five, “Once Burned”, by Peter David: “Why in God’s name the bridge, arguably the most important strategic point of the vessel, is an easy target at the top of the saucer section is something I never completely understood. Why not just paint a big target on your ship and write, ‘Aim here for best shot at the captain’?” – M’k’n’zy of Calhoun, inner monologue.

Straegic design choices have never been shown to be a strength of the human race. Take, for example, this ever-lengthening thread on Apple’s objc-language mailing list, which can be summarized as an increasingly complicated debate on the merits of the design of the C language. What it really boils down to, though, is two important points:

  1. Designers of any sort, linguistic or otherwise, do not have the power to see into the future.
  2. Users of any sort will cling to the old and proven over the new and possibly better 99 times out of 100.

This is a problem I’ve run into in Missions. My code design as it currently stands has no real place for me to implement the code necessary for handling all the different screens of commands and text necessary to implement crew and computer interaction. Right now, I’d have to duplicate a lot of interface code in two (possibly three) separate places, and unifying it would mean exposing implementation details across two subsystems. True, Mike’s original code exposed everything to everything with global variables, and that’s a legitimate approach for games, but I know it’s not necessary in this code if I take the time to go back and redo some of the code structure correctly. Also, it offends my sense of object-oriented design; I worry that it’ll open me to further trouble later on.

Anyway, the practical upshot of all this is that it’s going to take me a while to do something I should have dealt with much sooner. Refactoring, even across only a minor subset of a code base, is a significant pain. I’ll try to keep tabs on my progress here, but if I don’t post for a week, don’t worry, it just means I’m still working.

Missions of the Reliant: Why having two coordinate spaces is a problem.

The best laid schemes o’ mice an’ men / Gang aft agley
– Robert Burns, “To a Mouse, on Turning Her Up in Her Nest, with the Plough”

The torpedos do work, there’s no doubt about that. There’s just one problem: Due to the fact that velocity in my code is expressed in terms of the game’s galaxy coordinate space rather than screen coordinate space as Mike’s code did, my torpedos have different physics than his.

In the original Missions, a torpedo once fired would have the same visual velocity no matter how the player’s direction and velocity changed. For example, a torpedo would take the same length of time to go off screen whether the player continued at full speed or hit full stop immediately after firing.

In my port, a torpedo obeys more traditional physics: If you fire a torpedo from full speed and then hit full stop immediately, it will zoom off the screen at twice the apparent velocity. If you fire from a full stop and immediately accelerate to full speed, you’ll practically run the torpedo over (though they move fast enough that one can not in fact actually do so).

Implementing the old behavior essentially means installing a velocity listener on the player and adjusting the torpedo’s absolute velocity as the player’s delta-V changes. This change, implemented more sweepingly, would bring back an interesting behavior of the old game. Try launching Missions in SheepShaver and holding down forward thrust and left turn immediately; the starbase sprite will move out of position relative to the player ship.

The old behavior is almost certainly not preferable for starbases and planets and enemies. But it certainly made aiming torpedos a bit easier, based on a bit of testing I’ve done versus the working fighters. Even the AI of the fighters has an easier time hitting me with “stable-velocity” torpedos than it does with realistic physics.

I’m at a bit of a loss for which way to go with this. I’ll continue onto implementing other things, as coming back to this issue is always possible.

Missions of the Reliant: Too late. Hang on!

The Reliant’s laser cannon is now functional. It fires from the wrong spot on the ship, hits the wrong spot on the enemy ships, has the wrong idea about when the enemy ships are in and out of range, plays its sound incorrectly, and doesn’t look quite like the original game’s laser, but it does work, and all but the last of those are trivial fixes.

As for that last, well, there’s this problem of Mike having taken advantage of old technology.

See, in the original game, the line that forms the laser would be drawn in one of two colors, then erased, and it was up to QuickDraw how quickly those pixels were seen by the user. The result in practical use was a semi-random flickering of the laser beam in and out, and a significant (while purely illusory) blending of the two colors. However, I use OpenGL to draw the lines and have no provision for erasing them, so the result is a far more solid line where both colors of the laser are easily visible. I’ll have to experiment a bit with OpenGL modes to fix it.

But the laser does work!

Missions of the Reliant: Watch it, you’ve got one on your tail!

Missions of the Reliant version 3.0 now has the framework for enemies, enemy AI, and those infinitely annoying little fighters that everything lauches in droves at you and you can only hit by draining all the charge from your laser couplings. It took some work, let me tell you.

Mike’s original code expresses differences between facing angles as a function of which sprite is being displayed. Efficient. My code expresses differences between facing angles as atan2(-distanceY, distanceX). Mathematically correct and conceptually accurate.

‘Course, then “am I facing the player within a 120-degree arc?” becomes a completely different numerical test. Didn’t help that you kept setting variables you never used anywhere, Mike :-). Originally, I tried to use pretty much identical code for enemy movement as player movement, but it became clear that there were just too many quirks to it.

The fighters especially have an interesting AI about moving:

  1. Calculate distance from me to the player. If too high, return to origin, else continue. (Target determination.)
  2. Calculate angle from me to the player. If I’m not facing that angle exactly (difference between facing and calculated != 0), turn towards it. (Aim.)
  3. If I’m facing the player within 120° of arc, apply thrust N. (Thrust.)
  4. If I’m within distance D1 of the player, reduce speed by 10%. (Falloff.)
  5. If I’m within distance D2 of the player, and I have a loaded torpedo, and I’m facing the player exactly as in step 2, fire my weapon. (Attack.)
  6. Repeat every tick (or every other tick depending on preference setting) of the game timer.

Sounds simple? The fighter has to track its target, its current vector, its ideal vector, the difference between those two, its distance from target, and its firing delay, and constantly adjust all parameters accordingly. The code specifically intended for fighters is 118 lines including comments. Add 134 lines for code that applies to every enemy, 176 lines for code that applies to all game objects, 50 lines for code that applies to anything physics-capable, and 87 more lines for code that applies to everything that needs to do things based on the game timer. Subtract roughly 50 from that for comments and you get 500 lines of code just to drive that simple little AI, not counting the game timer itself or any of the drawing logic.

And I haven’t implemented the weapon, collision detection for the weapon, or the return to origin logic yet. And it’s got a couple of glitches as is, like if the fighter happens to spawn at exactly a 90, 180, 270, or 0-degree angle to the player while said player is at a full stop, it moves in a straight line rather than arcing like it should. Not sure if that bug’s in the original game since it’s next to impossible to set that particular scenario up deliberately without hacking source.

Did I mention that due to several stupid errors on my part, I ended up having to go back to the original fighter model in Infini-D and re-render it, then use Photoshop Elements to recreate the 36-phase sprite from that render? That was a fun two hours. On the other hand, the fighter model looks more “cool” now. It’s also a little harder to see. Sigh.

If anyone’s interested, here’s a couple of screenshots. I’d post a movie but I don’t have any instantly available way of making one and I’m too lazy to pursue the less instant ways.

Missions of the Reliant Fighter 1 Missions of the Reliant Fighter 2

Missions of the Reliant: I’m haunted by coordinate systems!

As if all the mucking about with coordinates before wasn’t bad enough, next I had to deal with unit vectors, polar/Cartesian coordinate conversion, sign adjustment vs. trigonometric functions… you get the idea.

In this case, my problem wasn’t caused by needing to update the algorithms Mike used at all, but rather by my need to replace the old MacToolbox FixRatio() and AngleFromSlope() functions with modern trigonometrics. Now, I’d already done all this, or else the impulse and warp drives would never have worked for this long, but in poking about in the implementation for mobile enemies, I realized I’d have to generalize the code, or else end up repeating it in about a dozen places, a well-known recipe for disaster.

In literal code, warp speed goes like this:

double diffx = warpCoordX - playerCoordX, diffy = warpCoordY - playerCoordY,
       theta_raw = atan2(-diffy, diffx), theta = round(fma(theta_raw, RAD_TO_DEG_FACTOR, 360 * signbit(theta_raw))),
       // make use of theta in degrees here to calculate a turn factor
       maxSpeed = warpSpeed,
       newDeltaX = cos(theta * DEG_TO_RAD_FACTOR), newDeltaY = -sin(theta * DEG_TO_RAD_FACTOR),
       finalDeltaX = playerDeltaX + newDeltaX, finalDeltaY = playerDeltaY + newDeltaY;
if (fabs(addedDeltaX) >= fabs(maxSpeed) * newDeltaX) finalDeltaX = maxSpeed + newDeltaX;
if (fabs(addedDeltaY) >= fabs(maxSpeed) * newDeltaY) finalDeltaY = maxSpeed + newDeltaY;

Conceptually, this reads:

  1. Calculate the difference between the player’s current position and the destination in Cartesian coordinates.
  2. Take the arctangent of the Cartesian coordinates, adjusted for inverted Y, converted to degrees and adjusted to the range [0,360] (atan2() returns [-Ï€,Ï€]).
  3. Convert the polar coordinates (using the implied mangitude of 1) back to Cartesian coordinates.
  4. Calculate the movement delta with a speed limit.

Why, one wonders, do I do a Cartesian->polar conversion, only to immediately convert back to Cartesian again? Answers: 1) I need the angle to calculate which way to turn the ship towards its destination. 2) The distance between current position and destination is a vector of (usually) rather high magnitude; I need to normalize that vector to get a delta. And the formula for normalizing a Cartesian vector is x/sqrt(x*x+y*y), y/sqrt(x*x+y*y). Two multiplies, an add, a square root, and two divides, all floating point. Without benchmarking I still intuitively think that’s slower (and I’m SURE it’s conceptually more confusing) than cos(atan2(-y, x)), -sin(atan2(-y, x)), two negations, an arctangent, a sine, and a cosine. Maybe I’m crazy.

Of course, typing all this out made me realize that I can, in fact, eliminate the degree/radian conversion entirely, as well as the range adjustment, by changing the conditional in the turn calculation. Once again I fell for the trap of not thinking my way through the code I was porting. At least you weren’t as bad at geometry as me, Mike :-).

Then I had to go and get really curious and benchmark it:

#include #include #include int main(int argc, char **argv) { struct timeval cS, cE, pS, pE; // Volatile prevents compiler from reading the loops as invariant and only running them once. volatile double x1 = 1.0, x2 = 2.5, y1 = 0.4, y2 = 3.2, dx = 0.0, dy = 0.0, inter = 0.0; gettimeofday(&cS, NULL); for (int i = 0; i < 100000000; ++i) { dx = x2 - x1; dy = y2 - y1; inter = sqrt(dx*dx+dy*dy); dx /= inter; dy /= inter; } gettimeofday(&cE, NULL); gettimeofday(&pS, NULL); for (int i = 0; i < 100000000; ++i) { inter = atan2(y2 - y1, x2 - x1); dx = cos(inter); dy = sin(inter); } gettimeofday(&pE, NULL); struct timeval cD, pD; timersub(&cE, &cS, &cD); timersub(&pE, &pS, &pD); printf("Cartesian diff = %lu.%06u\n", cD.tv_sec, cD.tv_usec); printf(" Polar diff = %lu.%06u\n", pD.tv_sec, pD.tv_usec); return 0; } [/c]

Foot in mouth. cos|sin(atan2()) is consistently 3x slower than x|y/sqrt(x^2+y^2) at all optimization levels. Somehow I just can’t see this as being an artifact of the brutal abuse of volatile for the benchmark.

Mike got around the whole issue, in the end. Knowing that he only ever had to calculate cos()/sin() for the angles in the period [0,35]*10, he just precalculated them in a lookup table. And cutting the cosine and sine calls out of the benchmark reduces the difference between the methods to about 1.6x, making his way a win over a four-way compare/branch for the turn calculation.

Live and learn.

Oh, and changes to Missions: Again, nothing you can see in play yet. But at least now I have the right building blocks to make the enemies from.

Missions of the Reliant: Math is fun, or why I wish I hadn’t flunked geometry

At last, an update!

  1. Absolutely nothing visible to the user has changed whatsoever.
  2. The internal structure of the code has been significantly reorganized.

As with the lament of all programmers faced with the demands of the technologically disinclined, I’ve accomplished a great deal, but since it can’t be seen, it might as well be nothing at all. Wasted time, the hypothetical slave driver- I mean, boss- would say. But it isn’t, I swear to all two of you who read this blog!

And now, another math rant.

Once again, as with so many things, the way Mike did things in the original code was correct and logical for the time he did it, but doesn’t fit into the object-oriented model I’m cramming his code into, despite its pitiful cries for mercy from such rigid structure. There are days I wish we were living in times when code could be so freeform as his was and still be comprehensible, but you can’t do that in Cocoa. Oh sure, I could port all the Pascal functions 1-to-1, but the Toolbox calls would be sticky at best. Anyway, in this particular case, I was trying to wrestle with the radar range calculation.

The original code reads something vaguely like: screenPos = Planetabs - (Playerabs - Playerscreen)  inRadarRange = n <= screenPos / 16 <= m. Translating, this means that whether or not a given entity (a planet in this case) is within radar range of the planet is dependant upon the Player’s position in screen coordinates, as well as in the game’s absolute coordinate system.

In the old days, this design made a certain amount of sense. He already had the screen coordinates immediately handy, so why take the hit of indirecting through A5 to touch a global for the absolute position? However, my design makes the screen coordinates a bit dodgy to use. So I had to recalibrate n and m to represent distances in game coordinates.

Algebra to the rescue. The code above, reduced and replacing the inequalities, becomes the algebraic equation (x - (y - z)) / 16 = a, where a is the radar range coordinate. The only screen coordinate term in this equation is z, so solve to eliminate z:

(x - (y - z)) / 16 = a
x - (y - z) = 16a       - multiply both sides by 16
x - y + z = 16a         - distribute the subtraction over the parenthetical expression
x - y = 16a - z         - subtract z from both sides

But, because both a (the radar range) and z (the player’s position on screen) are actually constants, all I had to do was take Mike’s original numbers (let’s use 64 for a and 268 for z) and calculate 16*64 - 268 = 756. Then, retranslating, the equation becomes the inequality inRadarRange = (myPosition - playerPosition) <= 756;. Repeat for the lower and upper bounds of x and y coordinates, and boom, no screen coordinates at all and I can calculate whether or not an object's in radar range based on nothing but its offset from the player.

To be clear, what I did up there was to eliminate a term from the inequalities so that they could be evaluated based on the position of the given entity in game space, rather than on the position of the entity's sprite on the screen.

I can't believe it took me a week to doodle out that bit of math.

Missions of the Reliant: Cleaning up the wreckage of the train crash

I’m back, and I didn’t give up on Missions! I’m sure there must be exactly one person out there who cares :-).

But seriously. I don’t have any new features to show at the moment, unfortunately. When I went to implement the laser cannon for the player, I realized I’d never be able to test it without something to fire at. I also realized the cannon itself would be useless without the target scanner since it has to lock onto a target. The scanner is also useless without something to scan. So, it was time to implement the base code for mobile enemies. Probably should’ve done that long ago, and here’s why…

As we all know, I’m using Objective-C to write this code. That means, among other things, that my code is object-oriented in nature. Up until this point, things like planets, starbases, and the player had all been entirely separate implementations. This is what Mike did with the original code. As always, what he did then was only sensible for the time and environment, but I can avoid a hell of a lot of code duplication by giving everything that exists in space a common superclass: a “Presence”. (Presences are themselves subclasses of the even more general “Responder”, which is used for everything that needs to process game happenings in any way, but that’s only a side note). As one can imagine, since I didn’t have the foresight to design the code this way to begin with, implementing it now required some significant refactoring.

Another issue cropped up halfway through the refactoring: The severe limitations of Apple’s built-in Key-Value Observing, which I use extensively throughout the code to avoid having to call “update this” and “update that” manually for every single affected object whenever something changes. For example, KVO doesn’t let you use blocks for callbacks, and if a superclass and a subclass both register for the same notification, there’s no way to manage the two independantly. Fortunately, Michael Ash noticed these problems some time back, and created a replacement, his MAKVONotificationCenter. Unfortunately, even the updated version published by Jerry Krinock didn’t do everything I needed, at least not in a way that I found usable with blocks added to the equation. Managing observations by tracking the resulting observation objects means having lots of instance variables to hold the observations, and since I’m building for Leopard, I can’t use the new associated objects for the purpose.

“Wait a minute,” you’re saying! “Leopard? Then why are you talking about using blocks?” Answer: I’m using PLBlocks.

So, armed with PLBlocks on one side, and Michael Ash’s typically brilliant code on the other, I dove in and pretty much rewrote the entire MAKVONotificationCenter to do three things it didn’t before:

  1. Block callbacks.
  2. Tagging observations with a simple integer value.
  3. Several alternative ways of specifying groups of observations to remove, based on observer, target, key path, selector, tag, or most combinations thereof.

With that done (and unit tested, and Doxygen-documented), I’m now integrating them into my revised class heirarchy for Missions itself. With any luck, I’ll have at least a screenshot of a fighter flying around before the week is out. Stay tuned, those of you who are crazy enough to stick around for all this :-).

Footnote: I was finally able to find a way to access the original model files for the game’s graphics; with some luck and a bit of help from Mike (I’m clueless when it comes to this stuff), there may be higher-quality graphics to be seen in the screenshots soon.

Missions of the Reliant: More progress

As usual, this will be a quick update. I just don’t have the oomph for the long blog posts at this time of night for some reason :-).

  1. Implemented the About box, keeping Mike’s old credits box exactly as originally written (It says what you were “as of April ’96”, Mike!) and adding some of my own. I have plenty of people to thank too!
  2. Switched from NSSound to OpenAL. NSSound has some serious efficiency and semantic issues that make it questionable at best to use in a game, whereas OpenAL is amazingly simple with a little help from AudioToolbox to import the WAVs.
  3. Made the dialogs that come up on the main menu (new game, about, etc.) look a bit better by rewriting them as application-modal child windows instead of composited views. This little change, very simple in code, solved a lot of cosmetic issues.

Unfortunately that’s about it for user-visible stuff at the moment, almost all the code in the last week has been infrastructure-related. For the curious, my next goal is to make working enemy ships and satellites. That means everything from self-motile sprites to the AI behind them. Mike, once again I’m forced against my will to admire your genius ;-).

Algebraic simplification

I was translating Pascal to C as usual for Missions when I came across the code fragment for computing the time bonus earned on victory given the current game time:

1
2
x := BSR(gCycle, 4) + 1;
x := round(50000 / x) * 10;

Now, I could have translated that to C as timeBonus = (50000 / ((cycle >> 4) + 1)) * 10, but I decided to get clever. Uh oh. I applied algebra to simplify the equation.

To translate it algebraically, I had to convert it to a purely mathematical equation. The right shift operator >> isn’t algebraic, but given the identity that x >> y = x / 2y, a right shift of four bits is an integer divide by 16. “cycle” is just a variable, so call it x, giving us the algebraic equation (50000 / ((x / 16) + 1)) * 10, or:

(50000/((x/16)+1)) * 10

Next, multiply 10 (as 10 / 1) into the equation:

500000 / ((x/16)+1)

Using the equality a/b + c/d = ad + bc / bd, rewrite (x / 16) + 1:

500000 / ((x + 16) / 16)

Rewrite using the equality a / (b / c) = a * (c / b):

8000000 / (x + 16)

Leaving us with two operations (add and divide) where originally there were four (shift, add, divide, multiply). Isn’t math cool?

All equation images generated by Apple’s Grapher program. Nice little feature that thing has, copy equation as TIFF.

Code funnies

I ran across this bit in Mike’s code, and couldn’t help but smile:

IF totalEnergy < 0 THEN totalEnergy := 0; {this line saved my sanity! Believe me, shieldLevel _can_ be negative} [/pascal]

I’ve met few programmers who don’t put funny comments in their code now and again. For example, here’s one of mine from the warp drive subsystem:

if (enteringWarp && velocity > minWarpVelocity)     // We hit 88 miles per hour! Activate the flux capacitor!

I pity anyone whose code review guidelines forbid them from doing things like this. When trying to figure out someone else’s code, a little humor is desperately needed.

Missions of the Reliant: Warp drive online, Captain!

The post title does not decieve; the ship’s warp drive now works.

That was an adventure in arctangents, power-of-two exponents, multiply-add operations, rounding errors… I have to say, this was a particular section of code where Mike’s style was a bit hard to decipher. No offense, Mike, but honestly, wow *sweat*. Let me hasten to clarify that the code wasn’t actually bad, just confusing. Confusing because of sections like this:

i := BSR((s + 1), 1);
j := trunc(72 / i);
z := round(round(exp2((s + 8) / 3)) / i);

Which in C was translated to:

uint32_t i = (s + 1) >> 1,
         j = 72 / i,
         z = lround(exp2((s + 8) / 3) / i);

That was an example where the translation was mostly one-to-one, save for BSR() being >> and trunc() not being needed at all, and one of the round()s being detrimental to the calculation… see how even the simplest-seeming things proliferate? Then there was the calculation of the angle from the player’s current position to the warp destination. In Pascal code that was a lot of fun with FixRatio() and AngleFromSlope() and various manual additions and subtractions of 180 and divisions by 10 and what have you. In C, because I chose to store the current player’s angle in a different form than Mike (I store the actual angle in degrees, whereas he stored an index into the set of 35 ship sprites – which was appropos at the time), I got to do some magic with atan2():

double          dx = d.x - pos.x,
                dy = d.y - pos.y,
                theta = atan2(-dy, dx), theta_deg = round(fma(theta, 180.0 / M_PI, 360.0 * signbit(theta)));

And that just gives me the angle from the player’s current position to the warp destination (nor is this the exact code; there are even more calculations done to get the correct coordinate values that aren’t necessary to this discussion); from there I have to calculate the difference between that and the player’s current facing and turn one increment per “tick” of the game timer to eventually reach the correct facing. Those of you who remember the original game (or have been playing it in SheepShaver, which actually emulates it damn near flawlessly if you run it with a NewWorld ROM and OS 9.0.4) will remember that the ship tends to oscillate back and forth between two facing angles during a warp jump, as there are only 36 sprites, meaning the angle the ship needs to be traveling almost never corresponds to a particular sprite. More multiplies and divides by 10, but there I got a break; the code to handle that was already implemented in the ship navigation subsystem, which handles the turn left and turn right keys. I passed the necessary numbers over to that and it did the job for me.

I was not able to pass off the responsibility of moving the ship to the ship engine subsystem (which handles forward and reverse thrust, as well as full stop), as that code carefully limits the player’s maximum speed for impulse drive. Also, the warp drive has to do some different management of non-maximum speeds; in the end it was better to reimplement it in the warp drive subsystem. The warp drive does, however, rely on the impulse engines to drop out of warp, by requesting a full stop. This had the rather neat side effect of automatically disabling the impulse drive’s user responses while warp was active, without me having to check for that anywhere in the impulse code.

Oh, and the emergency warp drive also works.

But enough about the warp drive. I’ve also got the energy capcaitor (remember? that green bar telling you you’re gonna die ’cause you used up too much power just getting where you were going and had nothing left to charge your lasers with when you got there?) going. The navigation (again, turn left and right) system is now separate from the impulse drive and can take individual damage. Yes that’s right, in version 3.0 of Missions, the turn thrusters can start to die just like everything else, although I was lenient and gave them very low hit-to-damage ratios. Speaking of which, the damage system is implemented too; ship’s systems can now take damage and lose functionality, though right now there’s nothing that does damage to them. Obviously to do the warp drive I had to upgrade the long range scanners, so those are now even closer to fully functional.

Oh, and I also made the “lights” draw exactly correctly at last. They weren’t quite right before.

Yay progress!

Missions of the Reliant: Complications

I’ve accomplished surprisingly little in the last couple of days, in functional terms. I can sum up why pretty easily: I’ve had to stop and puzzle out exactly how Michael did some of the things in his code. Player velocity, especially, is giving me grief.

This isn’t Michael’s fault. He didn’t write obscure code (well, a little…) or implement anything stupidly. The problem was his confinement to old Mac Toolbox APIs in Pascal. One does not simply toss around floating-point math in System 7. (Nor does one simply walk into Mordor, but that’s another story.) Pascal had a floating-point type (REAL), but in those days it was slower than my brain on a Monday morning. I’m not sure why, since almost every Mac since the Mac II had a MC68881 or better FPU, but we were always told to use the FixMath package for efficiency just the same. So we used fixed point types and did fun little things like this:

integerPart := trunc(Fix2X(fixedValue));
fractionPart := fixedValue - Long2Fix(integerPart);

With math like that, and manual compensation for overflow going on, I think I can be forgiven for having to stare blindly at the uncommented code for about two hours before it finally made sense to me. It’s been quite a few years since I’ve worked without native floating-point, and a lot of that time was spent dredging up the memories. 21 lines of Michael’s Pascal code, all of them necessary in the environment it was written for, boil down to a single line in modern C, and in fact a single assembly language instruction too if you care to look at things at that level. In these days of multimedia CPU extensions, if I thought it were necessary for performance I could write it such that all the calculations for all the game objects that needed to move around were done in one vector instruction (SIMD add). I don’t think it’s necessary, but the fact that I could is a sign of just how much CPUs have changed. It’s also a tribute to all those people who did it the hard way 15 years ago.

Other progress includes implementation of the sector object map (meaning planets and starbases, and their locations), reconversion of the rather broken SapirSans font (just opening it in Font Book made ATSUI whine quite loudly, and the italic variant was progmatically indistinguishable from the plain one) into a correctly-formed font suitcase by the very helpful if cryptic FontForge font editor program, and cargo, personnel, and crew management code. As usual, all of this stuff is backend and not visible in a build yet, so I have no screenshots to show. I will soon, though; now that I’ve figured out how the player moves around I can at least make the viewscreen work. Stay tuned.

P.S.: If there’s anyone who follows and enjoys the little roleplay blurbs, speak up in a comment and I’ll continue them. It only takes one voice!

Alliance Headquarters
Stardate 2310.13816640048673

Missions of the Reliant: A report from Alliance HQ

Gwynne: Alliance Commander J.G. Gwynne reporting as ordered, Admiral.
Admiral: What news of the situation, Commander?
Gwynne: It’s all in my report, sir.
She sets an electronic notepad on the Admiral’s desk.
Admiral: Very well, Commander. Dismissed.
As Gwynne leaves, the Admiral picks up the report and begins to read…

Situation Report

It started almost twelve years ago now, the erosion of reality itself into chaos. For a long time, no one understood what was happening. It was as if some great divine entity had decided that this universe had lived out its usefulness, and was now to be allowed to fade into nothing. Our scientists studied the problem to no avail, watching helplessly as the effect began to approach Earth. To this day we still do not understand the phenomenon. Though a young theoretician named Michael Rubin[1] finally determined that it has something to do with wavefunction anticollapse, it brought us no closer to true comprehension.

For reasons unknown, the dissolution of the cosmos halted itself only a few light-years from our home, now once again the last bastion of humanity as we had coalesced to the center. Our best scientist, one Sarah Thobbes[2], was able to build on the discoveries of her predecessors, and found evidence of Poincaré recurrence on the verge of the chaos that had once been an infinite universe. Her investigation (see attch.) finally concluded what we had barely dared hope: A reversal of the phenomenon.

Also mentioned in her report was a transmission in an indecipherable alien tongue, containing only one recognizable word within a mass of gibberish…
1<86>!^H!^H)J)Jfsfse<86>dÆT^@ý\^@^OLÀ\^@T^@T^@L^@L^@T^@L^FD^@L^@D^@L^FD^@D^@8^@D^@þ8^@WLÆLÆ0ÆM<8c>M<8c>NlfsF1 ^?3g3^÷^?9g3^?9gùg3^?9g3g3^?3^?9gùg3o{^?3g9g3^?9g3g9^?3g3o{o{g3^?9g3o{g3^?9gó^÷^?3g3^?9góo{^?3g9^?óg9g3o{^?3gó^?3g 9^?3g9^?3o{^?3g3^?9g3o{^?3g9g3^?9o{g3gó^?3g9fs~sfle<8c>M<8c>M<86>LÆ0Æ0Æ(^@^@^X^@^P^@ö^@^@^G^H^@0^@L^@D^@D^@ 0^@8^@(^@þ ^@þ^P^@Æ^@^@^A.ª^@^@^@^ReliantXÆö!^H^DNle<86>dÀT^@T^@þ\^@5T^@T^@d^FT^@L^@T^@L^FL^ FL^@D^@L^@L^@LÆ8^@D^@D^@0^FD^@0^F8^@0^F0^@(^@0^@0^F(^@0^F(^@0^@0^F0Æ0Æ!^HM<8c>9ÎM<8c>fsNsg3^?9g3 gùg3^?9g3o{g3^?9g3o{g3gùg9^?3þg3^O^?9g3^?9gó^÷^?9g3^?9g3g9g3o{g3g9^?3o{þg3 g9g3g9^?3g9g3g9o{g3o{þg3^?9g3 g3^?3g9g9^?3g3gó^?9g3^?ùg3g9^?9g3^?3fsfse<8c>NlM<86>M<86>LÆ0Æ0À(^@^X^@^X^@^H^@^@D^@D^@þL^@^BT^@T^@ \^@üT^@^DL^@D^@8^@8^@0^@þ

Reliant… the name of the lost ship of heroes. Could they be responsible for saving us again?

Gwynne, Commander, J.G., Interplanetary Alliance
Stardate 2310.11522168986235


Progress today was unfortunately small, interrupted by a sudden power outage and further stalled by an Internet service that refused to be restored, but here’s the usual list of what was accomplished.

  1. Cleaned up the code a bit more
  2. Made the main menu buttons draw and interact. Mostly, anyway; they highlight for mouse clicks and key presses, but they don’t do anything else yet.

Yep, that’s all *shame*. I suspect I’ll have better luck tomorrow.

Alliance Headquarters
Stardate 2310.11526541942353

P.S.: Neither the gibberish in the report nor the numbers in the signature are random. A copy of the latest build will be sent to the first person to correctly guess what either of them actually mean in a comment on this blog.