Sunday, November 15, 2009
Still working on Ionic. It's taking a lot longer than I originally planned to be honest, it's grown into quite a monster.

Because of this our blog posts have dropped off the past couple of months ( Not even a Halloween post this year, which I think is a first for us), where we've not had much to either release or show. There is a cronusX post mortem to be done, but since my host got hacked ( The host was a cheap one which was just to be a dumping ground for our games, it's not the main host on which both this blog and the site ( Along with the client area ) sit ) none of the development pages are online, so until we sort something permanent out there I've got to hold fire.

Speaking of cronusX, the cock who obviously doesn't like me on NG is still voting it down every day. It's about 3.55 from 3.81. Let's wish him luck with blamming it. Prick.

Anyway back to Ionic. After this post is submitted I'm back on adding the last turret type, which will give us 9 in total. Hopefully I can use a lot of existing code, so most of the time will be spent adding the help text for it ( There's a lot of text in this game. It's set up so you don't have to read any of it if you don't want to, but you'll get a better insight if you do ) and then testing it against various attack waves for balance.

We're getting close to the final push now. In terms of gameplay it's only really come together the past couple of weeks, which has been the main reason I've not been posting about it more here or releasing lots of builds like we did with cronusX. It's actually enjoyable to play now, to the point that it's disappointing when you complete the limited number of attack waves that are in there at present.
A lot of the presentation has been added as I've gone, which has taken longer ( I've still not drawn one baddie ship yet, which I'm really dreading ) but it means it won't be a case of the game being finished and then having to do another love pass on it.

The milestone of it being a "proper" game was hit last week sometime, so there is a game complete sequence and a game over one. It's all coming together.

As way of a filler, I've attached the dreadnought as it is in the game, here's the HMS Ionic

More news and grabs as and when.

Squize.

Sunday, November 15, 2009 12:44:12 PM (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Wednesday, November 04, 2009
I've got a bug in Ionic. It's with the little remote drones that collect the dropped guineas ( I thought guineas made a nice change to "Creds", plus I'd already done the spinning G graphic for the never to be released Orbs game I started on. The power-source you have to protect in the middle of your dreadnought in Ionic also funnily enough looks just like the orb from Orbs. No point wasting those pixels ).

Anyway, the drones are called "Chrysus". It's from Greek mythology, and he ( It ? ) was the spirit of gold. Naming things is always an important aspect of development for me, it helps settle things in my mind, it sets a tone, and I like that name ( Even though I had to check the spelling lots of times when first getting used to it. And edit the blog just now where I've spelt it wrong. Classy ).
It says what it is nice and succinctly.

Why am I even talking about this ? Yes partly 'cause our posts have dropped off recently, we're just having a boring phase where everything we do can just as easily be condensed down to a 140 words and spat out over twitter ( That's so cheap linking to our twitter account I know ) but partly because all the in-game help is done now.

All finished.

The snag is, no where in the docs do I explain the Chrysus.

The game is quite word heavy, the detail is there if you want it. I've not explained the drones / guineas as I like to credit the end player with the intelligence to figure things out ( Unless of course you're that first poster type on Kongregate. Anyone who can post a comment within minutes of a game being uploaded is someone whose not actually taken the time to play the game they're so keen to say sucks. First posters, you may think you're the Billy Bollocks for getting there first, but I think it's fair to say the majority of developers think you're a cock. And ignore your comments anyway. Well done for "winning" though, good work on that ), and get that "Ah, I get it" moment that is the reward of sticking with something.

So no mention of the word Chrysus anywhere in the game. Bit of an over sight ? Possibly. But I'd have to name it anyway, it needs a class name, and now it's our little secret. Think of reading the blog as buying the limited edition of the game, you're getting the inside scoop that other people miss.

And yes, right now I regret having a little green drone flying out of hatch to collect dropped coins instead of just adding them straight to your bank account like in other tower defense games. A drone with no name and a big fat bug.

Squize.

Wednesday, November 04, 2009 12:54:10 AM (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, October 23, 2009
Managed to knock up a logo I'm happy with last night, then added a bit of RGB Bumb Mapping, like so

ionic_logoGrab.jpg

Which looks really pretty when moving, and adds some much needed colour to a pretty drab game. In terms of cpu use it's pretty costly, but with only buttons on the title screen I should be able to get way with it.

The actual theory behind how to do the bump mapping was taken from the always excellent unitzeroone, then just ported to as3 and given a couple of tweaks ( The ADD blendmode is your mate here ).

Getting ever closer to it being a proper game.

Squize.

Friday, October 23, 2009 9:45:48 AM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, October 22, 2009
In between knocking out a Pac Man clone in a couple of days ( Not sure when that's been released, I'll be sure to pimp it when it's out there ), finishing off DAC and still working on the huge never ending Nat-Geo project, I've managed to squeeze some work in on Ionic.

ionic_machineGunGrab.jpg

If you've been following the few and far between updates you'll see that there are proper gun turrets in there now, those being of the machine gun variety. I've also nailed down the HUD look & feel, which took a lot longer than I planned.
In essence I'm trying to convey that you're looking at the battle from your monitor ( Possibly on the dreadnought itself, or remotely. I've not decided yet as I've not figured out how to blow up such a huge sprite ) so all the HUD overlays can be CGI looking. I'm trying to make it not look too much like cronusX but still feel like it's in the same universe. It needs that "Not done in Flash" feel.

ionic_shieldGrab.jpg

Lot's of things in this grab. We've got the lazer cannon turrets in there, which pack quite a punch but take ages to reload until you've upgraded them to their max. Also newly added today is the shield. That protects any turret in it's sphere of influence, reducing the baddies shot power to a quarter of what it should be.
Also on here we've got the "Slot menu", a quick way to amend your turret.

Slyly peeking around the corner is your 2nd in command, who pops up to relay information to you, such as when a new upgrade or weapon is available. As the game has a resource management aspect to it, you don't get all the goodies at once, you only start with 3 weapons and no upgrades. The more resources you put into R&D the quicker the new toys become available, but that's at the expense of the other areas of the ship you have to control.
And yes, the 2nd in command does look just like an ODST, placeholder atm.



The UX side of things have had a lot of amends the past couple of days thanks to the guys on the board, and it's feeling a lot more accessible now. There are a couple of annoying bugs, as always, but it's all going in the right direction.
I think next up will be the front-end, which means I've got to figure out what play modes there will be ( Feel free to offer up ideas ) and put the always heart breakingly nasty "More games" button placeholder. For the actual logo I'm thinking 3D text with a sexy bump mapping effect, which should be fun to actually get done.

Once the front end is in place I can actually make it a real game, with a start to play and a Game Over to look at, which is always a great milestone. From there it will be a case of drawing lots of sprites, finishing off the UI and throwing lots of data at it for the attack waves and all the in-game help / info.

Still tons to do, but it's all getting there.

Squize.

Thursday, October 22, 2009 12:15:54 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, October 21, 2009
AWESOME! Our new great game, Destroy all Cars, is live! WOW!

dac_liveGrab1.jpg


Play this GREAT physics based smash 'em up right now! Featuring 25 levels of AWESOMENESS with 12 selectable cars to smash. This is super fun, if you're 8 or 88!

dac_liveGrab2.jpg

Unlock all the liveries for each car as you SMASH through the levels! Go on, create MAYHEM on 4 wheels!

Squize.

* Worst. Blog. Post. Ever.

 | 
Wednesday, October 21, 2009 12:45:38 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, October 15, 2009
I've done this effect to death, used it again in the upcoming DAC ( Whose launch has been delayed due to having to add achievements, luckily someone else is doing that ) so I figured it's time to put it out to pasture and share it around.

'cause we've not been posting as often here, I thought I'd treat everyone to some nice grabs to break up the code slightly.

Basically we want to create a simple gradient rectangle movieclip, like so

swatch_grab.png

And we end up with something looking like this,

gradient_grab.png

A simple rectangle that's slightly bigger than your stage, that fades out to one side. It needs to be bigger than your stage 'cause we want all the gradient part with the alpha blending to be off-screen when we first apply it as a mask.

Next up, the boring old code bit.

package Classes {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.display.Stage;
    
    import gs.TweenLite;
    
    public class Transition {

//---------------------------------------------------------------------------------------
// Assets
//---------------------------------------------------------------------------------------
        [Embed("../_assets/assets.swf",symbol="transitionMaskMC")]
        private var transitionMaskMC:Class;

//---------------------------------------------------------------------------------------
// Properties
//---------------------------------------------------------------------------------------
        private var playField:Sprite;

        private var grabBitmapData:BitmapData;
        private var grabBitmap:Bitmap;

        private var transitionMask:Sprite;
            
//------------------------------------------------
// System
//------------------------------------------------
        private var main:Main;
        private var mainMovie:DisplayObject;
        private var stage:Stage;

        private var callBack:Function;
        
//---------------------------------------------------------------------------------------
//Constructor
//---------------------------------------------------------------------------------------
        public function Transition(){
            main=Main.getInstance();
            mainMovie=main.getMainMovie();
            stage=main.getStage();

            grabBitmapData=new BitmapData(640,480,false,0);
            grabBitmap=new Bitmap(grabBitmapData);
            grabBitmap.cacheAsBitmap=true;
            
            transitionMask=new transitionMaskMC();
            transitionMask.cacheAsBitmap=true;
        }
        
//---------------------------------------------------------------------------------------
// Public
//---------------------------------------------------------------------------------------
        public function toString():String {
            return "Transition";
        }        

//---------------------------------------------------------------------------------------
        public function grabScreen(callBackArg:Function):void{
            callBack=callBackArg;

            if(playField==null){
                playField=main.getInitObj().getPlayField().transition;
            }

            grabBitmapData.draw(stage);
            transitionMask.x=-160;
            grabBitmap.mask=transitionMask;
            playField.addChild(grabBitmap);
            playField.addChild(transitionMask);

            callBackArg();

            TweenLite.to(transitionMask, 1.5, {x:640, delay:0.2,onComplete:houseKeeping});

        }

//---------------------------------------------------------------------------------------
        public function houseKeeping():void{
            playField.removeChild(transitionMask);
            playField.removeChild(grabBitmap);
        }

//---------------------------------------------------------------------------------------
    }
}

There's really no way to make code look interesting in a blog is there ?

            grabBitmapData=new BitmapData(640,480,false,0);
            grabBitmap=new Bitmap(grabBitmapData);
            grabBitmap.cacheAsBitmap=true;
            
            transitionMask=new transitionMaskMC();
            transitionMask.cacheAsBitmap=true;

Here in our constructor we're just creating a bitmap which we're going to use when we take a snap shot of the whole screen, and create our transisitonMask ( ie, the gradient mc we've just made ) instance.
The key thing is to turn cacheAsBitmap=true on both, otherwise the gradient mask won't work, it'll just be a boring old normal gradient free mask.

            grabBitmapData.draw(stage);
            transitionMask.x=-160;
            grabBitmap.mask=transitionMask;
            playField.addChild(grabBitmap);
            playField.addChild(transitionMask);

            callBackArg();

            TweenLite.to(transitionMask, 1.5, {x:640, delay:0.2,onComplete:houseKeeping});

This is the guts of what we're doing. Firstly use the always sexy draw() to grab the whole screen, then position the mask so our alpha blended bit is off screen. We want the whole snap shot bitmap to be masked normally at first.
We then simply add both the bitmap and mask to the screen. You'll notice the playField reference there. What I do, as I'm a layer loving kinda guy, is create a simple class that creates all the holders for all the things in a game at the depths I want. It's like codey version of using layers that way. So for your transition "layer", that should be on top of everything else ( You could also add it directly to the stage, same thing, I'm just paranoid that I'll need a layer above the transition for some reason ).
We've got the bitmap, the mask, the bitmap is now masked so all we need to do is cheat, and use the very lovely TweenLite. That just slides our mask all the way across the screen, in effect removing our mask, but with the nice alpha blending on the edge it makes it appear soft. Kinda like in Star Wars.

Notice the callback and the slight delay in the tween in there ? What we do is call the transition method, and then jump back to our main code where we're setting up the screen "beneath" the transition, so it wipes away to reveal our next screen.

[Update: Destroy all Cars is now ( Finally ) live, so you can see it action and see if you want to go to the effort of using it yourself ]

Squize.

Thursday, October 15, 2009 11:57:14 AM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, October 07, 2009
Played with Pixel Bender yet ? For Ionic ( My TD game which is taking an age to finish ) I wanted to use a similar effect to the transition in cronusX, the rgb split / tv interference thing. In the transition it didn't matter how long it took, but to use it in-game with God knows how many sprites along with collisions running it's got to be a lot lot quicker. Yeah you know where this is going.

ionic_rgbGrab.jpg

After firing up the Pixel Bender toolkit and swearing and looking through docs and examples for an hour or so, I kinda got it, so I thought I'd share what little I know. Just to warn you, this is going to be code heavy and no example to even look at, dry reading ahead. Maybe open up some porn in another tab and just flick between the two when your eyes start glazing over.

<languageVersion : 1.0;>

kernel RGBDistortion
< namespace : "RGB Distort";
vendor : "www.gamingyourway.com";
version : 1;
description : "Pixel Bender version of RGB distort";
>
{
  input image3 src;
  output pixel3 dst;

  parameter float rOffset
<
   minValue:float(-50.0);
   maxValue:float(50.0);
   defaultValue:float(0.0);
>;

  parameter float gOffset
<
   minValue:float(-50.0);
   maxValue:float(50.0);
   defaultValue:float(0.0);
>;

  parameter float bOffset
<
   minValue:float(-50.0);
   maxValue:float(50.0);
   defaultValue:float(0.0);
>;
  void

  evaluatePixel()
  {
    float2 outCoords=outCoord();

    float2 redPos=outCoords;
    redPos[0]+=rOffset;
    float3 inputColorR = sample(src,redPos);
    dst.r = inputColorR.r;

    float2 greenPos=outCoords;
    greenPos[0]+=gOffset;
    float3 inputColorG = sample(src,greenPos);
    dst.g = inputColorG.g;

    float2 bluePos=outCoords;
    bluePos[0]+=bOffset;
    float3 inputColorB = sample(src,bluePos);
    dst.b = inputColorB.b;
  }
}

I'll try and break it down quickly in my usual lazy not really explaining things style. At the start is simple metadata, nothing you can't figure out there.

   input image3 src;
   output pixel3 dst;


A bit more interesting. input is the image you're going to send to the kernal, image3 being the datatype ( Because we don't want to worry about the alpha channel the datatype is image3 ( RGB ), if you want alpha it would be image4 ( ARGB )), the output is the bitmap we're going to plot the result to, which is a pixel3 datatype ( Which is the same as image3 ).

  parameter float rOffset
<
   minValue:float(-50.0);
   maxValue:float(50.0);
   defaultValue:float(0.0);
>;

This how you set up an argument to pass to the kernal. It's a float and refers to the red channel offset when we jiggle things about. The min/max and default values are more for the toolkit itself. When you run it it'll give you a slider to play with, and those are the values for the slider ( It'll make sense when you copy the code into the toolkit. Also you can drop a description in there as more metadata, but this is for Flash so we don't really need it ).

   evaluatePixel()

This is our main loop. Everything in this method is run on each and every pixel in the image.

    float2 outCoords=outCoord();

The outCoord() method returns this pixels x/y as two floating point numbers, in the form of an array ( Kinda ). We just store that in a var out of habit and speed.

    float2 redPos=outCoords;
    redPos[0]+=rOffset;
    float3 inputColorR = sample(src,redPos);
    dst.r = inputColorR.r;

redPos is our x,y position. We then add the new x position to redPos.x ( redPos[0] ), with the new x position being our parameter from above.
inputColorR is quite a chunky little line. sample grabs the pixel data at ( inputSource, position ), in effect we're setting the float3 ( RGB ) variable inputColorR = sourceImage[x+our offset][y]
After doing that, we make this pixel in the r(ed channel ) in our destination this colour. Basically we're doing this:

var rgb=sourceBitmap.getPixel(x+offset,y);
destBitmap.setPixel(x,y,rgb);

( But just on the red channel ).

The rest of the kernal is just more of the same, just for the other two channels.

After that pick the Export Filter for Flash Player option and we're ready to get back to normal coding.

package Classes {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.MovieClip;
    import flash.display.Shader;
    import flash.display.ShaderJob;
    import flash.display.ShaderParameter;
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.events.ShaderEvent;
    import flash.filters.ShaderFilter;
    
    public class Transition {
        
//---------------------------------------------------------------------------------------
// Assets
//---------------------------------------------------------------------------------------
        [Embed(source="_shaders/distortion.pbj", mimeType="application/octet-stream")]
        private var shader_distortPBJ:Class;

        [Embed("/_assets/assets.swf",symbol="staticMC")]
        private var staticMC:Class;

//---------------------------------------------------------------------------------------
// Properties
//---------------------------------------------------------------------------------------
        private var playField:Sprite;

        private var shaderBitmapData:BitmapData;
        private var grabBitmapData:BitmapData;
        private var grabBitmap:Bitmap;

        private var shader_distort:Shader;
        private var shaderFilter_distort:ShaderFilter;
        
        private var distortCnt:int;
        private var rSin:Number;

        private var staticEffect:Sprite;
        private var staticAnim:MovieClip;
                
//------------------------------------------------
// System
//------------------------------------------------
        private var main:Main;
        private var mainMovie:DisplayObject;
        private var stage:Stage;

//---------------------------------------------------------------------------------------
//Constructor
//---------------------------------------------------------------------------------------
        public function Transition(){
            main=Main.getInstance();
            mainMovie=main.getMainMovie();
            stage=main.getStage();

//Let's make our bitmaps where we're going to plot our data too
            shaderBitmapData=new BitmapData(700,380,false,0);
            
            grabBitmapData=new BitmapData(700,380,true,0);
            grabBitmap=new Bitmap(grabBitmapData);
            grabBitmap.alpha=0.4;
//Create our shader, sexy
            shader_distort=new Shader(new shader_distortPBJ());
            shader_distort.data.src.input=shaderBitmapData;

            staticEffect=new staticMC();
            staticAnim=staticEffect["anim"];
            staticAnim.gotoAndStop(1);
        }

//---------------------------------------------------------------------------------------
// Public
//---------------------------------------------------------------------------------------
        public function toString():String {
            return "Transition";
        }        

//---------------------------------------------------------------------------------------
        public function init():void{
            playField=main.getInitObj().getPlayfield().transitionPlayField;
            rSin=0;
        }
        
//---------------------------------------------------------------------------------------
        public function distortInit():void{
            if(grabBitmap.parent!=null){
                return;    
            }

            distortCnt=0;
            shaderBitmapData.draw(stage);
            playField.addChild(grabBitmap);

            staticEffect.height=int(Math.random()*100)+10;
            staticEffect.y=int(Math.random()*300)+50;
            staticAnim.gotoAndPlay(1);
            playField.addChild(staticEffect);
            
            startJob();
        }

//---------------------------------------------------------------------------------------
// Private
//---------------------------------------------------------------------------------------
        private function startJob():void{
            rSin++;
            rSin++;
            ShaderParameter(shader_distort.data.rOffset).value=[Math.sin(rSin)*12];
            ShaderParameter(shader_distort.data.gOffset).value=[Math.cos(rSin)*12];
            ShaderParameter(shader_distort.data.bOffset).value=[Math.sin(-rSin)*12];
            
            var job:ShaderJob = new ShaderJob(shader_distort,grabBitmapData);
            job.addEventListener(ShaderEvent.COMPLETE,shaderCompleted);
            job.start();
        }
        
//---------------------------------------------------------------------------------------
        private function shaderCompleted(e:ShaderEvent):void{
            if(++distortCnt==6){
                staticAnim.gotoAndStop(1);
                playField.removeChild(staticEffect);
                playField.removeChild(grabBitmap);
                return;
            }

            staticEffect.height=int(Math.random()*100)+10;
            staticEffect.y=int(Math.random()*300)+50;

            startJob();
        }

//---------------------------------------------------------------------------------------
    }
}

Sigh, that's a ton of boring code to look at, let's just pick out the fun stuff ( Equating as3 to fun, fuck me that's tragic ).

        [Embed(source="_shaders/distortion.pbj", mimeType="application/octet-stream")]
        private var shader_distortPBJ:Class;

That's how to embed the kernal.

//Let's make our bitmaps where we're going to plot our data too
            shaderBitmapData=new BitmapData(700,380,false,0);
            
            grabBitmapData=new BitmapData(700,380,true,0);
            grabBitmap=new Bitmap(grabBitmapData);
            grabBitmap.alpha=0.4;

What we're going to do is grab the screen when we want to run the effect, but we just want it faint ( alpha=0.4 ). shaderBitmapData is our source, with grabBitmapData being our dest. You can use the same bitmap for both, but Flash has to create a temp working copy, so it's slower.

//Create our shader, sexy
            shader_distort=new Shader(new shader_distortPBJ());
            shader_distort.data.src.input=shaderBitmapData;

Finally we're creating our actual shader, with the second line defining our source bitmap, which can do here as it won't change.

The distortInit() just grabs the screen, resets any vars we need and sends up the static effect ( Which has nothing to do with the pixel bender stuff, so we're going to ignore that ). Once that's all done we call the startJob() method which does the magic.

        private function startJob():void{
            rSin++;
            rSin++;
            ShaderParameter(shader_distort.data.rOffset).value=[Math.sin(rSin)*12];
            ShaderParameter(shader_distort.data.gOffset).value=[Math.cos(rSin)*12];
            ShaderParameter(shader_distort.data.bOffset).value=[Math.sin(-rSin)*12];
            
            var job:ShaderJob = new ShaderJob(shader_distort,grabBitmapData);
            job.addEventListener(ShaderEvent.COMPLETE,shaderCompleted);
            job.start();
        }

K, so we're using sin and cos to create our r,g and b position offsets, nothing new there. The ShaderParameter is how we change our parameter values in the kernal. You can hit the values directly, and they will be modified, but it doesn't actually do anything. I lost over an hour to that before finding out about ShaderParameter. Hopefully those lines should make sense.

Next up, we create a ShaderJob. We're doing this as opposed to the adding it as a filter approach, as this is the async magic that we've gone to all this effort for ( Runs on it's own thread etc. etc. Let's face it, that's the only thing we all know about PB ). The ShaderJob just wants to know which kernal we're using, and the destination bitmap.
Finally we just add a listener to that, and call the start() method, and it'll go and do it's thing and won't affect our other code. There's no waiting around here.

        private function shaderCompleted(e:ShaderEvent):void{
            if(++distortCnt==6){
                staticAnim.gotoAndStop(1);
                playField.removeChild(staticEffect);
                playField.removeChild(grabBitmap);
                return;
            }

            staticEffect.height=int(Math.random()*100)+10;
            staticEffect.y=int(Math.random()*300)+50;

            startJob();
        }

We're just been recursive bad boys here. Have we run the effect 6 times ? Yes so we're done, clear everything up, nope, so call the startJob() method again.

Hardly an indepth technical breakdown, but hopefully it's a starting point. This was a fair few hours of swearing for me, but I'm old so new things take longer to make sense, so it shouldn't take you more than a couple of hours to get PB doing cool things.

Squize.

Wednesday, October 07, 2009 9:29:09 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Monday, October 05, 2009
I was going to post about my experiments with Pixel Bender the other night, but I guess this tiny bit of news warrants more of a mention first.

Don't look at the picture below, don't! Now, what would be the coolest platform you can imagine having your as3 games running on ?

iphone_home.gif
You looked at the picture first and spoiled it didn't you ?

So before the net that talks about Flash is awash with the news, and it's all we ever hear about again, get your fill here: Flash Platform Extends to the iPhone

Squize.

Monday, October 05, 2009 7:43:10 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [3]  |  Trackback