New Fun Blog – Scott Bilas

Take what you want, and leave the rest (just like your salad bar).

Archive for December, 2009

You Have Slain A Magic Mushroom!

without comments

Sword of FargoalI’m excited. The classic Sword of Fargoal has been remade for the iPhonepod platform!

Sword of Fargoal is an ancient Rogue-style game for the C-64, written by Jeff McCord in BASIC (I remember curiously walking through the source code) and published by the long-defunct Epyx, one of my favorite publishers from back in the day. When I was a kid I played the hell out of this game and I’m excited to see some game developers who are even bigger fans than me have gone and remade this awesome game.

Thud!

I’ve been playing Fargoal LLC’s remake on my iPhone (available here at the App Store for only five bucks!) and it’s clearly a labor of love. Most remakes of classics are disappointing. Someone writes an emulator and they just run the old game on new hardware. It’s 100% authentic and all, but that’s not necessarily a good thing.

You’re all excited to play the game you loved as a kid, and then, five minutes in you realize that..oh..this game is kind of frustrating, annoying even.. It’s not because the game is bad, it’s because the world of gaming has changed since then.

As an industry we’ve moved far past the primitive control techniques and UI’s of the past. We expect to have tutorials and hint screens, auto-save and context-sensitive UI gizmos that help us out.

Old games just didn’t have room for any of that in their 8K or whatever of ROM. While this gave some insane replayability, and created a small sub-industry of invisible ink cheat books, learn-by-dying is just a thing of the past. It’s no fun.

I’m happy to report that the developers of the new Sword of Fargoal did more than just slap some upgraded graphics on this game. They’ve added help, auto-save, and a great control scheme (moving diagonally on the C-64 joysticks was always a dicey affair). But my favorite is a great new feature called the “Smart button” that gives you context-sensitive actions, such as using a teleport spell right when the ceiling is about to collapse on you.

Fargoal LLC has big respect from me. I’m already in love with this game again and now I can bring it everywhere with me. All I need is some OpenFeint (or similar) integration to compete with my brother.

Anyway, go buy Sword of Fargoal immediately. Swoosh! Clang! Thud!!

Written by Scott

December 27th, 2009 at 12:00 pm

Posted in game review, iphone, ipod

Ultimate AS3 Fake Enums

with 11 comments

I just can’t leave this thing alone. This is hopefully the last revision I’m going to make to my fake AS3 enum class (here’s the original posting, then two updates). This is inspired by commenter Mikko Korpela, who asked about an iteration ability. Well, that sounds like a fun idea! Let’s fill in some holes and bring it up to par with the enum support in C#.

Sorry it took me so long to get around to this Miko, but I’ve had my Flex work on hold for a while. But recently I did some Flash work at PopCap and it got me back in the mood again.

New Enum Features

Here’s my Ultimate AS3 Fake Enum class, which has the following new features:

  • Further type safety. Only the enum can construct its own constants now, via enum constructor protection. This should prevent people using the constants incorrectly (e.g. someone is incorrectly new’ing them up instead of using MyEnum.MyConstant).
  • Every constant now gets a zero-based index that corresponds to the order it is created in the class. Access via the Index accessor on the constant. Useful in serialization code, for example.
  • Added a new GetConstants method that returns an array of all the constants, where each constant’s slot in the array corresponds to its Index.
  • Added a new ParseConstant method that does the reverse of toString(). Given a constant’s Name, return the constant. Case sensitivity optional and off by default for convenience.

…all without any extra burden placed on the derivative class! I’m always interested in solutions that keep client code as clean as possible.

I also adjusted the code to my current AS3 coding standards, which is nearly the same as Microsoft’s C# standards. Note that I’d normally use Vector.<Enum> instead of the generic Array class, but I haven’t upgraded my Flex to the newest SDK yet.

The Code

Without further ado, here’s the new Enum.as file. Stick it in a package if you wish, and drop it wherever you like. No system setup is required to make it work.

package
{
	import flash.utils.*;

	public /*abstract*/ class Enum
	{
		public function get Name()  :String { return _name; }
		public function get Index() :int    { return _index; }

		public /*override*/ function toString() :String { return Name; }

		public static function GetConstants(i_type :Class) :Array
		{
			var constants :EnumConstants = _enumDb[getQualifiedClassName(i_type)];
			if (constants == null)
				return null;

			// return a copy to prevent caller modifications
			return constants.ByIndex.slice();
		}

		public static function ParseConstant(
				i_type			:Class,
				i_constantName  :String,
				i_caseSensitive :Boolean = false) :Enum
		{
			var constants :EnumConstants = _enumDb[getQualifiedClassName(i_type)];
			if (constants == null)
				return null;

			var constant :Enum = constants.ByName[i_constantName.toLowerCase()];
			if (i_caseSensitive && (constant != null) && (i_constantName != constant.Name))
				return null;

			return constant;
		}

		/*-----------------------------------------------------------------*/

		/*protected*/ function Enum()
		{
			var typeName :String = getQualifiedClassName(this);

			// discourage people new'ing up constants on their own instead
			// of using the class constants
			if (_enumDb[typeName] != null)
			{
				throw new Error(
					"Enum constants can only be constructed as static consts " +
					"in their own enum class " + "(bad type='" + typeName + "')");
			}

			// if opening up a new type, alloc an array for its constants
			var constants :Array = _pendingDb[typeName];
			if (constants == null)
				_pendingDb[typeName] = constants = [];

			// record
			_index = constants.length;
			constants.push(this);
		}

		protected static function initEnum(i_type :Class) :void
		{
			var typeName :String = getQualifiedClassName(i_type);

			// can't call initEnum twice on same type (likely copy-paste bug)
			if (_enumDb[typeName] != null)
			{
				throw new Error(
					"Can't initialize enum twice (type='" + typeName + "')");
			}

			// no constant is technically ok, but it's probably a copy-paste bug
			var constants :Array = _pendingDb[typeName];
			if (constants == null)
			{
				throw new Error(
					"Can't have an enum without any constants (type='" +
					typeName + "')");
			}

			// process constants
			var type :XML = flash.utils.describeType(i_type);
			for each (var constant :XML in type.constant)
			{
				// this will fail to coerce if the type isn't inherited from Enum
				var enumConstant :Enum = i_type[constant.@name];

				// if the types don't match then probably have a copy-paste error.
				// this is really common so it's good to catch it here.
				var enumConstantType :* = Object(enumConstant).constructor;
				if (enumConstantType != i_type)
				{
					throw new Error(
						"Constant type '" + enumConstantType + "' " +
						"does not match its enum class '" + i_type + "'");
				}

				enumConstant._name = constant.@name;
			}

			// now seal it
			_pendingDb[typeName] = null;
			_enumDb[typeName] = new EnumConstants(constants);
		}

		private var _name :String = null;
		private var _index :int = -1;

		private static var _pendingDb :Object = {};	// typename -> [constants]
		private static var _enumDb	  :Object = {};	// typename -> EnumConstants
	}
}

// private support class
class EnumConstants
{
	public function EnumConstants(i_byIndex :Array)
	{
		ByIndex = i_byIndex;

		for (var i :int = 0; i < ByIndex.length; ++i)
		{
			var enumConstant :Enum = ByIndex[i];
			ByName[enumConstant.Name.toLowerCase()] = enumConstant;
		}
	}

	public var ByIndex :Array;
	public var ByName :Object = {};
}

The Samples

Here are a couple sample enums and some code to demonstrate the features.

Simple Enum: State

This is the simplest usage of the Enum class. All you have to do to make a fake enum is:

  1. Create a class that extends the Enum class. Tag it as final too so nobody inherits it (that would be weird).
  2. Put some static init code in there that just calls initEnum(MyTypeName). This will get run on demand, on the first usage of the enum by other code. It sets up the necessary lookup tables and attaches a Name and Index to each enum constant.
  3. Create a public static const for each enum constant you want, following the pattern in the sample.

Here’s the sample enum. Note that it includes a bug on purpose! (It’s clearly marked.)

package
{
	public final class State extends Enum
	{
		{initEnum(State);} // static ctor

		public static const Initializing :State = new State();
		public static const Connecting   :State = new State();
		public static const Loading      :State = new State();
		public static const Ready        :State = new State();

		// illegal; this will fail at runtime
		public static const Test		 :OtherEnum = new OtherEnum();
	}
}

And some test code to walk through the features…

for each (var state :State in Enum.GetConstants(State))
{
	trace(state.Index + ": " + state.Name);
}

trace("connecting (no case): " + Enum.ParseConstant(State, "connecting"));
trace("connecting (with case): " + Enum.ParseConstant(State, "connecting", true));

// illegal; gets a runtime exception
var someEnum :State = new State();

Which produces the following output:

0: Initializing
1: Connecting
2: Loading
3: Ready
connecting (no case): Connecting
connecting (with case): null
Error: Enum constants can only be constructed as static consts in their own enum class (bad type='State')
 ...

Advanced Enum: NetError

Let’s look at a more advanced sample, which I’m calling an “attributed” enum. Let’s say we have a NetError enum with constants for different types of failures on a network request. We want to be able to show a more user-friendly error, and perhaps classify it as a fatal error or not. We could have a table that associates extended attributes for enum constants with those constants, but why not just attach them to the constants directly?

In .NET I’d do this with attributes. Now, AS3 doesn’t support attributes like we need, so we’ll have to fake it. Given that we already have a fake enum constant, which is really an object underneath, we can attach whatever we like to it!

Take a look at the sample enum.

package
{
	public final class NetError extends Enum
	{
		{ initEnum(NetError); } // static ctor

	// Constants.

		public static const None			:NetError = new NetError(false, "no error");
		public static const CantConnect		:NetError = new NetError(false, "can't connect to the server, must be down?");
		public static const VersionMismatch	:NetError = new NetError(true,  "protocol versions different");
		public static const Unknown			:NetError = new NetError(true,  "unexpected error type");

	// Constant attributes.

		public function get IsFatal()	  :Boolean	{ return _isFatal; }
		public function get Description() :String	{ return _description; }		

	// Constant query.

		public static function GetConstants() :Array
			{ return Enum.GetConstants(NetError); }
		public static function ParseConstant(i_constantName :String, i_caseSensitive :Boolean = false) :NetError
			{ return NetError(Enum.ParseConstant(NetError, i_constantName, i_caseSensitive)); }

	// Private.

		/*private*/ function NetError(i_isFatal :Boolean, i_description :String)
			{ _isFatal = i_isFatal; _description = i_description; }
		private var _isFatal	 :Boolean;
		private var _description :String;
	}
}

Given a constant, perhaps returned from a function as an error code, or thrown as part of an exception, we can ask it for its friendlier name by accessing its Description member.

Also in this class note the helper functions GetConstants and ParseConstant that wrap the Enum class versions, automatically passing in the local type. This isn’t necessary, but is a nice convenience. It is a bit more code to copy-paste around when adding new enums, though. If only AS3 supported templates, we could have a TEnum base that does all this for us. Ah well.

Here is some test code to play with the features

for each (var netError :NetError in NetError.GetConstants())
{
	trace(
		netError.Index + ": " + netError.Name +
		" (" + netError.Description + ") " +
		(netError.IsFatal ? "[FATAL]" : ""));
}

Which produces the following output:

0: None (no error)
1: CantConnect (can't connect to the server, must be down?)
2: VersionMismatch (protocol versions different) [FATAL]
3: Unknown (unexpected error type) [FATAL]

Future?

I hope this is the last word on fake enums in Actionscript. Not because I don’t think anyone else can improve on it, but because I’m hoping that the next version of the language will have native support for enums. Then we can put all this fakery behind us and get on with our lives.

The enum posts are among the top hitters on my blog, and they are popular topics elsewhere on the web. I’m hoping that Adobe will strongly consider native enum support in their next Flash player.

Written by Scott

December 24th, 2009 at 2:00 pm

Posted in as3, enum, flex

Upcoming Perforce Feature: Shelving

with 3 comments

Perforce announced on their blog that they have a new feature in beta testing called “shelve”. Great!

In short, “shelving” is taking a pending changelist and publishing it to the server without checking it into the depot.

As I mentioned in a previous post, this is one of the top features from git that was making me seriously consider checking it out. I started implementing this myself in my p4.exe wrapper, but I never gave it enough priority to get much work done. I’m excited to know that Perforce are adding it themselves. This is the first new feature I’ve seen from them in years that is worth upgrading for!

The things that interest me most about this ability are:

  • Easy trading of in-progress work in a formal, server-supported way that is done out-of-band of the depot, so it cannot break the rest of the team. Good for code reviews, small tests, moving the same pending changes among multiple machines/clients, etc.
  • Easy, incremental backup of works in progress. This is hot.
  • Easy ability to temporarily put aside a local change in order to work on something else that collides (typically as an emergency). This eliminates the main reason I needed to use several P4 clients on the same machine.

I hope Crucible will have support for this great new feature after it’s released. That would eliminate most of the need for the tool I wrote.

I’m looking forward to seeing this new feature in 2010.

Written by Scott

December 22nd, 2009 at 1:37 pm

Posted in p4

Switch to our mobile site