Final Fantasy 13 – Initial Impressions

I picked up Final Fantasy 13 for the PS3 last night. Between trade-ins, trade-in deals and two $25 gift cards from E-Rewards surveys, I was able to pick up the game, strategy guide and put $5 down on Transformers: War for Cybertron and still have $8 left on my gift cards. Sweet deal!

After LOST, I threw the game in to check it out for a bit. I played 1 1/2 hours before bed. So far, I like it – a lot more than 12, which I sold back about 20 hours into the game. That said, I do have a couple of gripes – some of which, I’m sure are mostly due to it being early in the game.

For the most part, I’ve stayed away from game reviews of FF13. Like movie reviewers, I tend to have a hard time finding reviewers I agree with enough to totally trust. I also wanted to steer clear of spoilers. One of the few things I do recall from what I’ve read was the complaint that the dungeons were just straight lines. So far, that’s exactly the case here. Flipping through the strategy guide (which is supposedly spoiler-minimized), this does not look like it changes much through the course of the game. Not a horrible thing, but a little exploration never hurt.

I like the idea of the new battle system for the most part. I like the idea of queuing up commands and executing them in batches. So far, the playable characters only have two action slots. So far, I’ve only been able to control one character at a time, despite the fact that have been four NPCs that have joined me in the portions of the game (max: 2 at a time). Right now, it seems like trying to control the queues of more than one character at a time would get messy.

For the most part, I like the fact that the entire party’s health completely regenerates between battles automatically. It makes things much easier, and you don’t have to concentrate on health management so much. At the same time though, maybe it makes it a little too easy?

When I posted to Twitter about playing for a while last night, one of my friends responded “1.5 hours of FFXIII? So you watched the opening cutscene? :p” She was joking, I know – but historically, there’s a lot of truth to that. This time, I’d really estimate that only about half, maybe less of that was cut-scenes. Most of the cut-scenes were very short. Unlike a lot of the recent FF games, this one starts a lot more in media res. The game opens in the middle of a war zone. Almost no time is wasted on exposition. Instead, that gets shoved into the datalog menu. The cut scenes go over the most essential plot points, but all that stuff you probably don’t really need to know gets filed away for you to ignore or read later. Maybe I’m just used to my e-mail clients, but I really wish there was a “mark all as read” option. I’m OCD about the little “new!” indicator telling me that there’s new stuff to read or new items in my inventory, and I may not always want to read every bit – especially the bestiary. Furthering that point, how do I get rid of the indicator on items? It keeps telling me that phoenix downs are new – even though I’ve been running around with one for over an hour. Clicking X does nothing in the inventory screen.

Emily mentioned that her boss had been reading an article about how bad the voice acting is. Neither of us are really sure why Holly was reading this, as she doesn’t play video games at all, but whatever. Emily commented that she hated one of the male voices, but wasn’t watching the screen to tell me which one it was. I believe she was referring to Yuj (the blue haired kid). I’m still a little ambivalent about the whole idea of having voices in FF games. I didn’t really care much one way or another until that whole sequence in FF12 when Vaan had to run around yelling “I’m Basch fon Ronsenburg!” over and over for 30 minutes. Most of the voices seem reasonable enough so far – except for Vanille. Something about her voice just doesn’t seem right to me. It seems too squeaky/young for the model. Then again, the character seems very immature – much younger than the model indicates. Something that I noticed just before I quit for the night was that an accent snuck in in a few words. Obviously, I don’t know if this was intentional or not, but it seemed out of place as it was only on two or three words.

My final gripe is really more with how the PSN handles trophy updates to Facebook. My friends and I post our trophies so we can keep up with each other easily. The annoying thing is that the PSN displays hidden trophies on Facebook as “??? (???)”. Guesstimating, I’d say that 80% of the trophies in FF13 are hidden. That means that I’ve got about 25 “??? (???)” posts to come.

Despite my couple of gripes, it’s a fun game. The story seems much more bearable than FF12, and I like the character and creature designs.

Oh my god, that’s butter!

Today was our annual trip to the Strawberry Festival in Plant City, FL. It’s our annual “undo all our exercise day” where we eat all sorts of horribly delicious foods that should probably come with a free trip to the emergency room. Here are the major highlights. Click the thumbnails to view the larger versions. The more complete gallery is available here.

Our favorite new item was the Fried Jalapeno Cheddar. It had that perfect amount of bite and heat, and was nice and gooey.
jalapeno cheddar cheese

Emily is obsessed with cheese fries – but not just any cheese fries. They have to meet certain requirements. One of those is that they must use cheese sauce – not real melted cheese. Yes, she’s kind of weird. So, we had to walk around looking for the perfect cheese fries. Then we came across the buckets. Giant “x-treme” buckets of fries covered in neon orange cheese sauce. It took six people, but we finished them.

x-treme cheese fries in a bucket x-treme cheese fries - all gone

Last year, we had noticed a stand selling tacos. We had skipped it, but decided to take a look at their offerings this year. We joked that they might have deep fried tacos. Instead, we saw that they had “tacos in a bag”. They were basically just a frito pie in a bag.

taco in a bagtaco in a bag (frito pie)

The real highlight though, was the deep fried butter. Yes. Deep Fried Butter. I assume the butter is frozen (or at least chilled) and then battered and fried. Of course, the heat of the oil just still melts the butter inside the dough. The butter itself (at least here) is flavored with cinnamon. It was better than I had expected, but we didn’t even finish our order of 4 pieces. The real problem with eating it is that if you bite into the dough too far, you just pop the pocket and the melted butter squirts out everywhere. You have to either bite carfully, or just shove the whole thing into your mouth and eat it whole. Of course, doing that will probably just send your heart into immediate shock.

deep fried butter - insidespulling apart deep fried butterdeep fried butter - insides

Oh, there’s also this.

corn dog deepthroat

Ghost In The Machine

The big application I’ll be working on for my job will be extremely expansive and far too large and complex for all of the components to be embedded. To prepare for this, I started playing with the ModuleLoader class on Friday. I had a working understanding of it by the end of the day on Friday. My demo app wasn’t complete, but it was loading external modules into memory. I didn’t have time to continue experimenting until yesterday evening.

Instead of continuing with my tiny little playground app, I decided to start working on something a little more real world and expansive. My plan was to use Beau Scott’s example and re-write it my own way. That’s how I do a lot of my early learning of anything.  I went about writing up an example module and then went on to start writing the code to load it.

That’s when everything started getting weird.

My code wasn’t working. Surely, I just forgot to declare the var keyword (I can’t tell you how many times I forget to do that), or forgot an import statement or something. Everything seemed OK. I toggled back and forth comparing code against my original test file. Everything seemed fine. Nevertheless, I kept getting errors saying that moduleLoader.url was a read-only property. Huh? Double-checking the documentation, I confirmed that it was a read-write property – ignoring the fact that I had set the property in my original test file.

Knowing that I had a working example was what was confusing me the most. If I didn’t have any working code, I’d just chalk it up to my own bad code. I decided to copy and paste the working code into my new file. Surely, that would get me back on track. No. My working code suddenly no longer worked – but it continued working in my original test file.

At this point, I just went to bed – my mind racing trying to figure out what was wrong.

This morning, I made sure to clean the apps and rebuild them. No joy. That new file just wouldn’t work no matter what. I decided to copy the non-working code into a new file.

It worked.

I compared project settings. Nothing was different. My code was fine. There was nothing wrong with it. It seems that at some point, somehow, the Sunday night file just got corrupted.

I immediately flashed back to a JavaScript issue my friend Roger and I once ran into. It’s been so long (at least 10 years), that I no longer remember what we were working  on, but it didn’t work. A piece that we had done many times before just wasn’t working this time and we had no idea why.  After hours of chasing our tails, we decided to just re-write it. We re-wrote it word-for-word – and it worked. Now we were really confused. We set out comparing the code line by line – and discovered that the difference between them was a single extra white space character at the end of a line. But JavaScript isn’t white-space dependent, so surely that couldn’t be it, could it? We removed the extra space, saved and reloaded. It worked. We closed the files, and sought out beer.

Drag to Reorder Items

One of the major functions that I need for a couple of projects I’ll be working on is the ability to drag items in a list to reorder them. My wife sometimes uploads items to her site, EmilySculpts, but occasionally they’re not uploaded sequentially. It’d be nice to be able to drag the items in the gallery to re-order them.

For purposes of this demo, we’ll just be reordering objects in an ArrayCollection, but you could easily just update position indexes in a database based on the updated orders.

First, we’ll just set up a custom ItemRenderer called ImageRenderer that will display each of our images in a gallery.

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="120" height="140" horizontalAlign="center">
 <mx:Image source="images/{data.source}" />
 <mx:Label text="{data.name}"/>
</mx:VBox>

Next, set up some bindable variables we’ll use. galleryData is an ArrayCollection holding objects that will populate our images. output is just a string that we will use to track the values of our arrayCollection. hs is the HTTPService that we will use to load our external data. For this demo, I’ve just used a flat xml file. This could just as easily be any xml feed you generate dynamically .

[Bindable] private var galleryData:ArrayCollection ;
 [Bindable] private var output:String = '';
 private var hs:HTTPService = new HTTPService() ;

The last bit of our setup will be a simple TileList and TextArea. The TileList will use our custom ImageRenderer to render the data in galleryData.

<mx:TileList id="myTilelist"  width="650" height="200" itemRenderer="renderers.ImageRenderer" />
 <mx:TextArea text="{output}" width="200" height="400"/>

We’ll call a custom function called init() from the applicationComplete event of our application. Here, we set up our HTTPService along with a handler to handle it’s results. Finally, send the request for the data.

// use httpservice to get our external data
 hs.url = "data/gallery.xml" ;
 hs.addEventListener(ResultEvent.RESULT,hsResult) ;
 hs.send() ;

Set up the result handler function to populate galleryData. DataProvider ArrayCollections should be populated by addressing the individual items in the xml. In this case, gallery is the root node and image is the child that we want to populate the ArrayCollection with. Notice that I’ve set the dataProvider property via Actionscript. You could do this in the TileList MXML declaration just as easily. Finally, we set the value of output to a string returned by ObjectUtil.toString, which just does a  simple dump of an object’s properties and values.

private function hsResult(r:ResultEvent):void {
 // dataprovider arrays are set based on the objects you want to display
 galleryData = r.result.gallery.image;
 // set the dataprovider
 myTilelist.dataProvider = galleryData ;
 // display the array so we can make sure values are updating properly
 output = ObjectUtil.toString(galleryData.source) ;
 }

Running the application at this point would give you a basic gallery populated by xml with a TextArea displaying the values feeding galleryData. This would be just fine for the front end side of a gallery, but we want to be able to drag to reorder the images.

Enabling dragging is simple. Just set the dragEnabled property of the TileList to true. This will allow you to drag an item out of the TileList, but to allow the item to be dropped into the TileList, the dropEnabled property must also be set to true.

<mx:TileList id="myTilelist"  width="650" height="200" itemRenderer="renderers.ImageRenderer"  dragEnabled="true" dropEnabled="true" />

If you run the application now, you can drag the images to reorder them within the TileList. Watch the output display though – nothing is changing. Simply reordering the images within the TileList does nothing to our array, so we need to handle that next.

First, set the dragDrop event of the TileList to call a custom function that we’ll call reorderItems.

<mx:TileList id="myTilelist"  width="650" height="200" itemRenderer="renderers.ImageRenderer"  dragEnabled="true" dropEnabled="true" dragDrop="reorderItems(event)"/>

Finally, set up the reorderItems function. The DragEvent passed to the function has a currentIndex property, which in turn holds a selectedIndex value. This is the index position of the image that was dragged before it was dragged. So, if you click and drag the third image, de.currentIndex.selectedIndex would hold the value 2 (remember, arrays in Actionscript are zero-based). Store this value in a local variable called selectedIndex.

TileLists have a special built in method called calculateDropIndex which returns the index where an item was dropped. In other words, the new index position of our dragged and dropped image. Store this in a local variable called newPos.

In order to reorder the items in the ArrayCollection, we’ll grab a copy of the item that was dragged, insert it into the Array at the new index (newPos), and then delete the original from the original index (origPos). There is one “gotcha” though. If newPos is less than origPos, and you remove the item at origPos after the insert, you end up with duplicates of the image item. You need to remove the item from origPos + 1.

private function reorderItems(de:DragEvent):void {
 // selectedIndex holds the item that was clicked and dragged
 var origPos:int = de.currentTarget.selectedIndex ;
 // calculateDropIndex automatically figures out the position the item was dropped into
 var newPos:int = myTilelist.calculateDropIndex(de) ;
 // don't bother doing anything if it wasn't actually moved
 if (origPos != newPos) {
 // grab a copy of the item that was dragged
 var theItem:Object = galleryData[origPos] ;
 // insert the copy into the new position
 galleryData.addItemAt(theItem,newPos) ;
 if (newPos > origPos) {
 // if it's a higher index, ok to just remove the original
 galleryData.removeItemAt(origPos) ;
 } else {
 // if its a lower index, need to remove one up, otherwise you get a duplicate
 galleryData.removeItemAt(origPos + 1) ;
 }
 }
 // set the output data
 output = ObjectUtil.toString(galleryData.source) ;

 }

Now, if you run the application, you can drag & drop the images to reorder them, and the ArrayCollection values will be properly updated with the correct order. You can monitor this in the TextArea.

This technique could be used for reordering anything – not just gallery images. Reorder a schedule, or elements in a custom layout. The possibilities are endless. View the final application here.

Data Update Notifications

My first real project in Flex/AIR was to recreate an existing dashboard application. As it was a first version release, our primary focus was getting the essential functionality built allowing us to come back and work on some of the niceties and flourishes later. One of those a little pop-up notification that occurs when a datafeed is updated.

In ColdFusion or other languages, this would require storing the current data and comparing against the new data coming in. When I first started looking into how to do this in Actionscript, I remembered a tutorial on Lynda which discussed using asynctoken to update a database and only updating a datagrid with the updated row instead of polling it for the entire dataset. Unfortunately, my Lynda account had expired and I no longer had access to this tutorial. Regardless, from my digging, it seems that asynctoken wouldn’t really do what I was looking for anyways.

Digging through other articles and the documentation, I found the CollectionEvent event class. Using the CollectionEvent.COLLECTIONCHANGE event, I was able to identify when my array had been updated.

private function init():void {
	// set up a simple array collection
	arData = new ArrayCollection(
		[
			{FirstName:'Luke', LastName:'Skywalker'},
			{FirstName:'Han', LastName:'Solo'},
			{FirstName:'Leia', LastName:'Organa'}
		]
	) ;
}

// simple functions to edit the array
private function addName():void {
	arData.addItem({FirstName:'Ben',LastName:'Kenobi'}) ;
}
private function updateName():void {
	arData.setItemAt({FirstName:'Jaster',LastName:'Mereel'},1) ;
}

First we just set up a simple array collection with some basic data and some basic functions to change the data. For the sake of simplicity, I’ve just got one to add a row, and another to edit a row. Obviously, you could update your data via the method of your choice. In my final application, the data would be receiving updated data from a server.

// set an event listener on the array to identify changes to it
	arData.addEventListener(CollectionEvent.COLLECTION_CHANGE,dataChanged) ;

Now, we add an event listener to the init function to the array. By attaching it to the array, we’re listening for when that specific array is updated.

// event handler function
private function dataChanged(c:CollectionEvent):void {
	var m:String = "" ;
	// for now, we'll just handle replace/add
	if (c.kind == "replace") {
		// replacements have oldValue and newValue properties
		m =  c.kind + ': ' + c.items[0].oldValue.FirstName + ' ' + c.items[0].oldValue.LastName ;
	} else if (c.kind == "add") {
		// additions only carry the single value
		m =  c.kind + ': ' + c.items[0].FirstName + ' ' + c.items[0].LastName ;
	}
	// create a highlight box to display our alert
	var h:HighlightAlertBox = new HighlightAlertBox();
	h.message = m ;
	this.addChild(h) ;
	// set a listener to know when to remove the box
	MyDispatcher.Dispatcher.addEventListener(Countdown.COUNTDOWNCOMPLETE,removeHighlightBox) ;
}

Here we have the function that will handle the change notifications. The CollectionEvent items struct is a little different depending on the kind value. In the case of “add”, it simply holds the new values. In the case of “update”, it holds both the original and new values. This could obviously be leveraged for some really nice update notices.

This block also creates a custom class called HighlightAlertBox which is just an extended canvas class. After creating the HighlightAlertBox, we add an eventlistener to await a custom Countdown event to be dispatched.

Once created, the HighlightAlertBox creates a five second timer. Once it runs, it dispatched Countdown.COUNTDOWNCOMPLETE. The main application is listening for this event and just removes the HighlightAlertBox which dispatched the event.

Here is the final product. View Source to see it all put together. Click one of the buttons to update the array. The CollectionEvent listener recognizes that the array has been updated and creates a new HighlightAlertBox which has a lifespan of five seconds. Once the five seconds have elapsed, the HighlightAlertBox is automatically removed.

There is still work to be done here, but we’ve got a great basis to work from.

Hello world!

I’ve decided to start a regular blog to track my experience as I learn Flex/Actionscript. As much as I’ve hated Flash historically, who ever thought that I’d be willingly learning Actionscript – much less liking it?

Return top

ego:

Web programmer, digital photographer, asian pirate and a heartless bastard. Not necessarily in that order.

Like any good programmer, I am constantly learning, re-learning and re-writing code. Anything I post is what I feel the best method is at the time of posting based on what I know. If you know a better way, please feel free to let me know.