Gwynne's Blog

Sa souvraya niende misain ye

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

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.

, , , , , ,
July 6, 2010 at 12:29 pm

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

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.

, , , ,
June 28, 2010 at 10:56 am

Missions of the Reliant: Too late. Hang on! 1 Comment

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!

, , , , ,
May 20, 2010 at 7:24 pm

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

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

, , , , , , ,
May 4, 2010 at 2:18 am

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

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 <stdio.h>
#include <math.h>
#include <sys/time.h>

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;
}

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.

, , , , , ,
April 25, 2010 at 1:43 pm

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

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.

, , , , , , , ,
April 22, 2010 at 3:07 pm

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

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.

, , , , , , , , , , ,
April 14, 2010 at 1:27 pm

Missions of the Reliant: More progress 1 Comment

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 ;-).

, , , , ,
March 26, 2010 at 1:06 am

Algebraic simplification 2 Comments

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:

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.

, , , , , , ,
March 17, 2010 at 6:35 pm

Code funnies No Comments

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}

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.

, , , ,
March 11, 2010 at 7:04 pm

Missions of the Reliant: Warp drive online, Captain! 1 Comment

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!

, , , , , ,
March 8, 2010 at 2:48 pm

Missions of the Reliant: Untangling the strands No Comments

, , , , , , , , , , , , ,
February 27, 2010 at 2:18 am

Missions of the Reliant: Complications 3 Comments

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

, , , , , , , , , , ,
February 19, 2010 at 11:10 am

Missions of the Reliant: A report from Alliance HQ No Comments

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.

, , , , , , , ,
February 11, 2010 at 2:25 am

Missions of the Reliant 4 Comments

Those who have been using Macs for at least 14 years may or may not remember a space game for old Macs that went by the name “Missions of the Reliant”.

It was a really fun little game with a few missions in it, changable crew members in your ship, powerups for your ship, a nice big galaxy to hang around in, systems that took damage and could be repaired… if you’re thinking Rescue!, don’t, Missions was much better.

Anyway, like all the old Mac games, it’s long since nonfunctional on modern machines. But I wasn’t willing to settle for that, so I pulled up my e-mail and wrote a letter to Michael Rubin, the original author of Missions, asking if I might get the source code and take a crack at porting it to OS X.

His enthusiasm was beyond anything I could have hoped for. I’m very grateful to him for the opportunity he’s given me to bring a classic back to the Mac. I’ll be posting updates here regularly about my progress on the port.

Progress Report 1

Well, I’ve got the code, and I’ve looked it over. Ah, the old glory days of Pascal, inline A-traps, GWorlds, manual event handling… The Mac Toolbox did almost nothing for you; it was a true low-level interface to the OS, something I feel we’ve gotten away from in these days of Cocoa. Sure, OS X has the POSIX interfaces, but they’re a whole different world. Anyway, the code is a real trip back to olden times, and I love every minute of it.

First step was to create the Xcode project. That took about three hours.

Wait, what? Three hours? Well, once you figure setting up the project settings, the target settings, tweaking the things Xcode’s templates don’t get quite right, editing the pregenerated files to not have broken line breaks and incorrect heading comments, and writing the entire Info.plist for the application, that’s a lot of work! I had to look up Info.plist keys, UTI listings, sweep the original source code for the proper value of NSHumanReadableCopyright, and ask a question or two about semantics in the #macdev IRC channel.

Next step, I figure, is to sweep up all the original visual resources – strings, pictures, icons – and reorganize them out of old-style rsrc files into a modern application’s Resources folder. Yes, I’m aware you can still use resource files in OS X, but I feel if you’re going to do something, you might as well do it right!

After that, I have to take a little time out to brush up on my OpenGL 2D, it’s been awhile since I used it and I never did use it for anything this complex. Binding several dozens of textures to represent all the various sprites should be a fascinating undertaking.

I’m enjoying the hell out of myself *grin*. Thanks again, Michael!

, , , , , , , , , , , ,
February 7, 2010 at 10:41 pm