Accessibility

Table of Contents

Insult Dueler: Building a Flash game on Adobe AIR

Creating a worthy opponent

Artificial intelligence for games is never about creating a realistic replica of what a user would do. It's about creating the illusion of a realistic replica of what a user would do. As simply as possible. In this case, the computer needs to be smart enough to occasionally spit the correct comeback at the user. But it also needs to be dumb enough to mess up. Otherwise the user would never be able to win, which doesn't sound like a very fun game to me.

For insults, this is easy enough—pick a random insult, remember its index (just like _lastUserChoiceSelected) and display the text on the screen. For comebacks it's only slightly trickier:

var shouldOpponentBeCorrect:Boolean = _(_random(0,2) == 0);
		    
if(shouldOpponentBeCorrect)
{
_lastOpponentChoiceSelected = _lastUserChoiceSelected;
_mc.opponentTxt_mc.glow_mc.txt.text = Constants.COMEBACKS[_lastOpponentChoiceSelected];
}
else
{
_lastOpponentChoiceSelected = -1;
_mc.opponentTxt_mc.glow_mc.txt.text = Constants.GENERIC_WRONG_COMEBACKS[_random(0,
Constants.GENERIC_WRONG_COMEBACKS.length-1)];
}
		    
var t:Timer = new Timer(_mc.opponentTxt_mc.glow_mc.txt.text.length * Constants.READ_SPEED);
t.addEventListener(TimerEvent.TIMER,_onOpponentTimer,false,0,true);
t.start();

_random() is a utility function I created to pick a random number between a minimum and maximum range. In this case, the minimum is 0, the maximum is 2. shouldOpponentBeCorrect is a Boolean set to true only if that random number is 0. In other words, roughly 33% of the time the computer will answer with the correct insult. Then I display the text on the screen for the opponent the exact same way I did for the user. That's all. Artificial Intelligence indeed.

A winner is you.

Suppose the gamer has chosen "You fight like a dairy farmer." I know that translates to Insult #7 in my array, counting from 0. I have this saved as _lastUserChoiceSelected. I know the computer has just selected a comeback, the index of which I have stored as _lastOpponentChoiceSelected.

Scoring is as simple as this:

// award 1 point to whoever won the round
if(_lastOpponentChoiceSelected == _lastUserChoiceSelected)
{
if(!_knownComebacksObject["key_" + _lastOpponentChoiceSelected.toString()] && !Constants.CHARACTER_DATA[_opponentKey].BOSS)
{
_knownComebacks.push(_lastOpponentChoiceSelected); // now I can use this comeback!
_knownComebacksObject["key_" + _lastOpponentChoiceSelected.toString()] = true;
dispatchEvent(new DatabaseSaveEvent(DatabaseSaveEvent.SAVE,_lastOpponentChoiceSelected,
Constants.TABLE_COMEBACKS));    // that's it. Main will ask the Database to save the comeback.
}
_score--;
}
else _score++;
		    
_userMC.gotoAndPlay("PARRY");
_opponentMC.gotoAndPlay("PARRY");

Add to _score if the user won the round, subtract if the opponent did. In addition, if the user lost the round but didn't know the comeback (which I can check from _knownComebackObjects), add that comeback to the _knownComebacks array. Now the user can use that comeback.

The next round is the same flow reversed:

  1. Opponent selects a random insult. Text displays, _lastOpponentChoiceSelected is saved.
  2. Choices animate in. Once in, populate the choices movie clip with LibraryItem_GameOptions, this time with the text of all known comebacks.
  3. User selects. The choices movie clip animates out. Now print the user's text on the screen.
  4. Compare scoring. Display the two characters "PARRY" animation.
  5. Repeat until score >= 2 (user wins) || score <= -2 (user loses).
  6. Save all that stuff to the hard drive. The user, content with the thrill of victory (or the frustrating of defeat) closes the game. 10 minutes later, they need to quench the thirst for cheesy 8 bit sword fighting once more. The user opens the game up again, which reads the XML file and SQL data written out to their hard drive earlier and picks up right where they left off.

Detecting Adobe AIR

Detecting Adobe AIR is essential for several reasons. First, a Flash app assumes it's been built for the browser. If you try to use an Adobe AIR method when not in Adobe AIR, your application will break. At Big Spaceship I built a simple utility class which I distribute called Environment. In it, I have a static function that returns a Boolean: true if this is in Adobe AIR, false if not:

public static function get IS_IN_AIR():Boolean {
return Capabilities.playerType == "Desktop";
};

IS_IN_AIR enables you to detect this pitfall at runtime, so your Adobe AIR methods only happen when needed.

In addition sometimes the flow of testing in the IDE is different than in final deployment. For example, in the IDE you can't create an XML file if it doesn't already exist. If you can detect that you're not in AIR, then you can program a way to bypass loading an XML file you know won't be there.