Golden Sun Hacking Community
23, January, 2021, 09:05:41 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: Come give us a "hello" on our Discord server!
 
  Home   Forum   DC Wiki Help Search Calendar Downloads Login Register  
Pages: 1 2 [3] 4 5 ... 7   Go Down
  Print  
Author Topic: Open Golden Sun: Open source recreation of the Golden Sun engine  (Read 73267 times)
0 Members and 1 Guest are viewing this topic.
Rolina
The Fulminous Witch
Jupiter Clan

Template maker turned lurker

Alchemist
*

Coins: 3
Offline Offline

Gender: Female
I am: wondering if we can get our clan position changed...
Clan Position: Grand Overlady of Jupiter
Posts: 6049

« Reply #40 on: 13, January, 2015, 01:53:57 AM »

Do you have battle examples up yet?  I'd like to see the effect of the screen smoothing there, since the art style is a bit different.

Edit:  Also, been meaning to ask this.  For those of us who wish to add additional statistical values to the games, will we be able to increase the screen size to compensate for the larger menus we'll have to do (to, say, the resolution on a single screen of the DS for instance), or are you committed to the whole VBA-only thing?  Would you have a solution in place in such an instance, perhaps to allow for some menu scrolling so that we can get all the data to the player?
« Last Edit: 13, January, 2015, 01:57:02 AM by Rolina » Logged

View Profile WWW

Novice Member
*

Coins: 0
Offline Offline

Gender: Male
Posts: 33

« Reply #41 on: 13, January, 2015, 03:44:29 AM »

Do you have battle examples up yet?  I'd like to see the effect of the screen smoothing there, since the art style is a bit different.

Edit:  Also, been meaning to ask this.  For those of us who wish to add additional statistical values to the games, will we be able to increase the screen size to compensate for the larger menus we'll have to do (to, say, the resolution on a single screen of the DS for instance), or are you committed to the whole VBA-only thing?  Would you have a solution in place in such an instance, perhaps to allow for some menu scrolling so that we can get all the data to the player?

I'll be posting a video for the battle system hopefully within the next couple weeks.

For your second set of questions, here is what the engine looks like when it's run at an internal game resolution of 256x192 (Nintendo DS):

 

As you can see the menus are larger than normal. Also, you can see more of the map in the second image.

Essentially I can adjust the internal game resolution to whatever I want. Which basically will just show more map, and make more dead space in the menus.
Logged

View Profile WWW

New User


Coins: 0
Offline Offline

Posts: 1

« Reply #42 on: 13, January, 2015, 08:29:38 AM »

good
Logged

View Profile WWW
MaxiPower
Feed Me Coins!!
Venus Clan

Great Member
*

Coins: 20
Offline Offline

Gender: Male
Posts: 699

« Reply #43 on: 13, January, 2015, 08:33:16 PM »

Cant wait for the video. I need a time machine! :)
Logged
View Profile
Rolina
The Fulminous Witch
Jupiter Clan

Template maker turned lurker

Alchemist
*

Coins: 3
Offline Offline

Gender: Female
I am: wondering if we can get our clan position changed...
Clan Position: Grand Overlady of Jupiter
Posts: 6049

« Reply #44 on: 14, January, 2015, 12:51:56 AM »

Do you have battle examples up yet?  I'd like to see the effect of the screen smoothing there, since the art style is a bit different.

Edit:  Also, been meaning to ask this.  For those of us who wish to add additional statistical values to the games, will we be able to increase the screen size to compensate for the larger menus we'll have to do (to, say, the resolution on a single screen of the DS for instance), or are you committed to the whole VBA-only thing?  Would you have a solution in place in such an instance, perhaps to allow for some menu scrolling so that we can get all the data to the player?

I'll be posting a video for the battle system hopefully within the next couple weeks.

For your second set of questions, here is what the engine looks like when it's run at an internal game resolution of 256x192 (Nintendo DS):

 

As you can see the menus are larger than normal. Also, you can see more of the map in the second image.

Essentially I can adjust the internal game resolution to whatever I want. Which basically will just show more map, and make more dead space in the menus.

Awesome!  Thanks!  Being able to to generate more space is good for when we run out. 
Logged

View Profile WWW

Regular Member
**

Coins: 0
Offline Offline

Gender: Male
Posts: 109

« Reply #45 on: 14, January, 2015, 12:22:10 PM »

Maybe put a limit on it, could be interesting... some game engines calculate a scale factor based on the stage and viewport width and apply it in the sprites size.

----------------

Looking at that Atrius gif, it seems that Y position of the heroes don't change. The sprite texture and hierarchy change with X variation. The X speed variation at this animation seems to be cos/sinusoidal with zeros near the borders and peaks at the center. But I don't know about your resources, but I think that an easeInOut animation can substitute the cos/sin functions once you call it everytime you need to vary X.
« Last Edit: 14, January, 2015, 12:36:26 PM by jjppof » Logged

View Profile WWW
Atrius
Programmer Extraordinaire
Web Host

Fear my blades

Veteran Member
*

Coins: 100
Offline Offline

Gender: Male
Emblems: Website Founder
Clan Position: Creation God of Sol
Posts: 1767

« Reply #46 on: 15, January, 2015, 12:30:08 AM »

Th Y values change.  There's a lot going on there that may not be immediately obvious.

I've remembered more of how I programmed that.

The character's X and Y coordinates are completely different from the X and Y coordinates where their sprites are drawn on screen.  I'll call the sprite coordinates spriteX and spriteY.  As the camera rotates I don't change the values of X and Y, spriteX and spriteY get recalculated based on the camera's new position.  X and Y are located on a plane that extends into the screen, basically they're a location on the ground as it is visible on screen where 0, 0 is at the center.

To calculate spriteX and spriteY you need to perform a rotation on X and Y so:
spriteX = X  * cos(-cameraAngle) - Y * sin(-cameraAngle)
spriteY = Y * cos(-cameraAngle) + X * sin(-cameraAngle)

That's not all though, now spriteX and spriteY need to be translated to screen coordinates.  This also includese scaling the sprite based on spriteY which currently represents how far forward or back they are on the ground, rather than how far up or down they are on the screen.  Unfortunately this is where I have trouble remembering the specifics again, I think some of it was based on math from a raycasting engine...  In the end the fact is you'll be doing a simplified 3D to 2D coordinate conversion, hence raycasting engine math.


Remember, Golden Sun uses a 3D coordinate system for movement in towns/dungeons.  I doubt they took an easier route than what I've described to fake the battles which almost actually look 3D.
Logged

I'm shaking my head in general disapproval of everything
View Profile WWW

Regular Member
**

Coins: 0
Offline Offline

Gender: Male
Posts: 109

« Reply #47 on: 15, January, 2015, 01:39:53 AM »

Ok, I don't know how golden sun engine works. But this scene can be made in 2D using resources like scale, hierarchy, sprites texture change and xy coordinates. The effect will be the same and, maybe, done in an easier way. Depending how his engine treats the texture atlas, he only has to concern about the 'real' xy position of the char on the screen making animations become easier to be made.

--------- edit

To make this scene, you had to adapt to the golden sun base engine, but I don't think that's the way the programmers took. Perhaps they used a framework.
« Last Edit: 15, January, 2015, 01:51:45 AM by jjppof » Logged

View Profile WWW
Atrius
Programmer Extraordinaire
Web Host

Fear my blades

Veteran Member
*

Coins: 100
Offline Offline

Gender: Male
Emblems: Website Founder
Clan Position: Creation God of Sol
Posts: 1767

« Reply #48 on: 15, January, 2015, 03:20:29 AM »

The end result was made in 2D using resources like scale, hierarchy, sprites, and xy coordinates.  The complexity of the math is so that you can change some of the variables and it will still behave correctly.

If you use the real math behind the effect you want, and make it so all you have to do is move the camera and everything else behaves correctly it's a lot easier to create hundreds of attacks that can move the camera in different ways.

If you fake the math, it probably won't behave correctly and you may have to fake it a hundred different ways to make your hundred different attacks look right.


--edit--

I should also mention that the math may sound complex, but it ended up being less than a dozen lines of code to calculate the screen xy coordinates and scale of a single sprite.  It wasn't particularly hard on the processor either, each individual particle from the magic effect in the image I posted had to go through the same algorithm.  (Though, I think the real Golden Sun's particles were strictly 2D calculations)
« Last Edit: 15, January, 2015, 03:36:12 AM by Atrius » Logged

I'm shaking my head in general disapproval of everything
View Profile WWW

Regular Member
**

Coins: 0
Offline Offline

Gender: Male
Posts: 109

« Reply #49 on: 15, January, 2015, 11:13:19 AM »

Actually, with 2d calcs, you won't need to do hundreds of attacks since you create a standard, so you can pass just few parameters (or only the type of attack... physical, summon, psy... then the algorithm does the animation). That's how recent 2D engines do these things, like starling and cocos2d. They are told 'easy to use' cause they treat things in this way.

I know this math isn't complex, but once you can avoid, why not? And I don't think this is fake math, It's just another way to deal with this.

Of course, this things I'm talking is only possible if his engine gives support to it.
Logged

View Profile WWW
Daddy Poi's Oily Gorillas
Fox McCloud, the Hacking Doctor
Mercury Clan

Prodigy
*

Coins: 1002
Offline Offline

I am: certainly not a Gallant!
Clan Position: Head Gallant
Posts: 2493

« Reply #50 on: 15, January, 2015, 12:38:14 PM »

Um... Would it be worth having both methods?

I could see it useful for educational purposes, and so that people have more choices. (Including even altering the formula itself.) :)  (Assuming it's not as hard as it sounds, and the method differs enough for it to be worth considering.)

One version could be Golden Sun Original Method, the other version could be "WIP" whatever you want it to be.

---
Quote
To calculate spriteX and spriteY you need to perform a rotation on X and Y so:
spriteX = X  * cos(-cameraAngle) - Y * sin(-cameraAngle)
spriteY = Y * cos(-cameraAngle) + X * sin(-cameraAngle)
I remember seeing two angle values in RAM.... But you make it sound like there's just one?
« Last Edit: 15, January, 2015, 01:21:09 PM by Fox » Logged

Golden Sun Docs: Broken Seal - The Lost Age - Dark Dawn | Mario Sports Docs: Mario Golf & Mario Tennis | Misc. Docs
Refer to Yoshi's Lighthouse for any M&L hacking needs...

Sometimes I like to compare apples to oranges. (Figuratively) ... They are both fruits, but which one would you eat more? (If taken literally, I'd probably choose apples.)
Maybe it is over-analyzing, but it doesn't mean the information is useless.


The only GS Discord servers with significance are:
Golden Sun Hacking Community
GS Speedrunning
/r/Golden Sun
GS United Nations
Temple of Kraden

Can you believe how small the Golden Sun Community is?

2+2=5 Don't believe me? Those are rounded decimal numbers. Take that, flat earth theorists! :)
View Profile

Regular Member
**

Coins: 0
Offline Offline

Gender: Male
Posts: 109

« Reply #51 on: 18, January, 2015, 09:59:52 PM »

A suggestion would be use the Tiled Map Editor http://www.mapeditor.org/, or something like it. It's very simple to use and exports files like JSON, CSV, XML etc, that you can adapt your engine to use.

I searched at google images 'golden sun tileset', got this http://img.photobucket.com/albums/v48/psgels/psgels-Ruins-Exterior-01.png, set white as transparent color and easily made the image below.It will be very nice to build maps. And It's free! An example of JSON file is also attached.

* ruins.json (236.9 KB - downloaded 283 times.)
Logged

View Profile WWW
Luna_blade
The last member of the Luna clan
Luna Clan

Great Member
*

Coins: 28
Offline Offline

I am: Timeless. As in, i don't have much free time.
Posts: 543

« Reply #52 on: 14, February, 2015, 10:51:02 PM »

It's almost a month later. How's the project been going?
Logged

"Hear the sounds and melodies
Of rilets flowing down
They're the verlasting songs
Whispering all the time
As a warning that behind some rocks
There's a rigid grap even
Oreads fear the tread"
View Profile

Novice Member
*

Coins: 0
Offline Offline

Gender: Male
Posts: 33

« Reply #53 on: 25, February, 2015, 07:35:56 PM »

Nothing to report, sadly. I've been really busy.
Logged

View Profile WWW

Novice Member
*

Coins: 0
Offline Offline

Gender: Male
Posts: 33

« Reply #54 on: 02, March, 2015, 04:55:10 AM »

A video on the battle system progress (preliminary).
Logged

View Profile WWW

Regular Member
**

Coins: 0
Offline Offline

Gender: Male
Posts: 109

« Reply #55 on: 02, March, 2015, 11:46:16 AM »


youtube tag [ youtube] [/youtube]



And very nice!
Logged

View Profile WWW
MaxiPower
Feed Me Coins!!
Venus Clan

Great Member
*

Coins: 20
Offline Offline

Gender: Male
Posts: 699

« Reply #56 on: 02, March, 2015, 12:56:22 PM »

Great stuff man. Nailed it.
Logged
View Profile
Luna_blade
The last member of the Luna clan
Luna Clan

Great Member
*

Coins: 28
Offline Offline

I am: Timeless. As in, i don't have much free time.
Posts: 543

« Reply #57 on: 02, March, 2015, 03:10:40 PM »

That's a good start. It's also nice to see progress again.
Logged

"Hear the sounds and melodies
Of rilets flowing down
They're the verlasting songs
Whispering all the time
As a warning that behind some rocks
There's a rigid grap even
Oreads fear the tread"
View Profile

Novice Member
*

Coins: 0
Offline Offline

Gender: Male
Posts: 33

« Reply #58 on: 04, March, 2015, 08:08:49 PM »

Th Y values change.  There's a lot going on there that may not be immediately obvious.

I've remembered more of how I programmed that.

The character's X and Y coordinates are completely different from the X and Y coordinates where their sprites are drawn on screen.  I'll call the sprite coordinates spriteX and spriteY.  As the camera rotates I don't change the values of X and Y, spriteX and spriteY get recalculated based on the camera's new position.  X and Y are located on a plane that extends into the screen, basically they're a location on the ground as it is visible on screen where 0, 0 is at the center.

To calculate spriteX and spriteY you need to perform a rotation on X and Y so:
spriteX = X  * cos(-cameraAngle) - Y * sin(-cameraAngle)
spriteY = Y * cos(-cameraAngle) + X * sin(-cameraAngle)

That's not all though, now spriteX and spriteY need to be translated to screen coordinates.  This also includese scaling the sprite based on spriteY which currently represents how far forward or back they are on the ground, rather than how far up or down they are on the screen.  Unfortunately this is where I have trouble remembering the specifics again, I think some of it was based on math from a raycasting engine...  In the end the fact is you'll be doing a simplified 3D to 2D coordinate conversion, hence raycasting engine math.


Remember, Golden Sun uses a 3D coordinate system for movement in towns/dungeons.  I doubt they took an easier route than what I've described to fake the battles which almost actually look 3D.

It would definitely be useful to see what you built for that.
Logged

View Profile WWW
Atrius
Programmer Extraordinaire
Web Host

Fear my blades

Veteran Member
*

Coins: 100
Offline Offline

Gender: Male
Emblems: Website Founder
Clan Position: Creation God of Sol
Posts: 1767

« Reply #59 on: 13, March, 2015, 09:06:16 PM »

I responded to your PM, but I'll post it here too cause it's good information other people might want as well.


Hey Atrius,

When you have a chance, can you expand on this? Specifically how the sprites are positioned and how you are translating the recalculated coordinates back to screen coordinates?

Sorry I haven't been on in a while.

I'll go over it all, but I'm working it out in my head from what I can remember of the process so forgive me if I make any mistakes.

Quote
The character's X and Y coordinates are completely different from the X and Y coordinates where their sprites are drawn on screen.  I'll call the sprite coordinates spriteX and spriteY.  As the camera rotates I don't change the values of X and Y, spriteX and spriteY get recalculated based on the camera's new position.  X and Y are located on a plane that extends into the screen, basically they're a location on the ground as it is visible on screen where 0, 0 is at the center.

Perhaps a better way to explain the X, and Y coordinates are that they're as though you were looking down on the battle from directly above it.   



Quote
To calculate spriteX and spriteY you need to perform a rotation on X and Y so:
spriteX = X  * cos(-cameraAngle) - Y * sin(-cameraAngle)
spriteY = Y * cos(-cameraAngle) + X * sin(-cameraAngle)

That's not all though, now spriteX and spriteY need to be translated to screen coordinates.  This also includese scaling the sprite based on spriteY which currently represents how far forward or back they are on the ground, rather than how far up or down they are on the screen.

spriteX and spriteY still represent a top down view at this point, but it's been rotated so that the spriteY axis is the direction the camera is pointing, and the spriteX axis is perpendicular to it.  At this point you could use the spriteY values to sort your sprite drawing order.


Note that 0, 0 is still located at the center of the grid, this will be important later.


Now, for the next step it's important to know how perspective works.  There are two variables to consider when calculating how large the sprites should appear on screen: their distance from the camera, and the camera's field of view.  Since the camera, rather than just being a single point in space, can be considered a flat viewing plane parallel to the spriteX axis we can ignore spriteX when calculating distance to avoid weird fisheye distortion effects leaving us with just spriteY for the calculation.  The camera is NOT at the center of our coordinate system though so you'll have to use an offset to represent the camera's location on the spriteY axis. This offset is equal to the camera's distance from the center of the battlefield, so I'll call it "cameraDist"

distance = cameraDist - spriteY

Note that a negative distance value would mean the character is behind the camera, and should not be drawn at all.  On second thought, also include 0 in that.  So less than or equal to zero means they're not drawn, we wouldn't want to go dividing by zero later on.


Now, as for field of view and how it affects the scaling of the sprite on screen, it's all a matter of ratios.  Think of the field of view as a cone that extends out from the camera, the size of this cone increases at a fixed ratio (scaleRatio) as the distance increases.  The size a character appears to be on screen is proportional to the amount of that cone they fill, so their sprite's scale will be inverse to scaleRatio multiplied by their distance from the camera.

scale = defaultScale / (scaleRatio * distance)

When initialized defaultScale (normally 1) should be multiplied by (scaleRatio * cameraDist) replacing cameraDist with your camera's default/neutral distance if you want to make the camera distance variable.



scaleRatio is a fixed value as long as the Camera's field of view (cameraFOV) doesn't change.  If you take your field of view cone, and split it in half you get two lines with slopes of Sin(cameraFOV/2) so:

scaleRatio = 2 * Sin(cameraFOV/2)

You'll have to find a value you like for cameraFOV on your own, just make sure it's greater than zero and less than 180.  Around 60 would probably be a good starting point, and since it has a scaleRatio equal to 1 it would allow for more optimized code.



Now, this scale value isn't just the sprite scale, keep in mind that this is the scale of ALL measurements inside the camera's field of view at that distance.  Since our spriteX axis is parallel to the camera's viewing plane we can simply multiply spriteX by scale to get our on screen spriteX coordinate.  The camera is looking directly down what our spriteY axis currently is so it doesn't actually factor into our on screen spriteY coordinate at all.  So where do we get our on screen spriteY value?  Well, there's one axis left we haven't touched this entire time, the Z Axis!  Much like the spriteX axis, it's also parallel to the camera's viewing plane, albeit rotated 90 degrees around our current spriteY axis so a simple multiplication by scale will do for it as well.  If all of the characters are standing on the ground we can assume their Z coordinate is 0 which seems awfully boring, we'll have to move the camera up a bit by giving it it's own Z value (cameraZ, I don't know a good default value) to make things more interesting.  I don't know about you, but I like my positive Z values toward the sky, and negative Z's into the ground so that's how I'll arrange the calculation assuming screen coordinates are such that higher Y values are more toward the bottom of the screen.

spriteX = spriteX * scale
spriteY = (cameraZ - Z) * scale

I know I said these were the on screen coordinates, but there's still one little problem.  The 0,0 location hasn't moved from the center of the battlefield so add half of your screen's width to the x coordinate and half of it's height to the y coordinate to center the battlefield on screen.



Ideally sprites should be drawn in such a way that the spriteX, and spriteY coordinate we have now is at the center of the character's feet.
Logged

I'm shaking my head in general disapproval of everything
View Profile WWW
Pages: 1 2 [3] 4 5 ... 7   Go Up
  Print  
 
Jump to:  

Chatbox
15, November, 2020, 03:45:26 PM
Salanewt: We're more alive than the board implies; most discussion takes place over on the Discord server.
15, November, 2020, 03:45:06 PM
Salanewt: Haha, heya!
15, November, 2020, 02:07:50 PM
Frog: Nice to see this place is still alive...ish anyway xD
30, October, 2020, 11:10:23 PM
Salanewt: ... Actually, it's almost time for me to post an update on the forum. It's been coming along really nicely overall, and it was only a couple months or so ago that I added some new status effects to the game.
30, October, 2020, 11:08:41 PM
Salanewt: I need to get it done to start making progress on the parts of the AI Overhaul I want to start working on.
30, October, 2020, 11:08:14 PM
Salanewt: It's going well! I wanted to get more of my djinn reform done this week but I've been busy with other things sadly.
30, October, 2020, 09:58:14 PM
KyleRunner: Hi guys! How are you doing?
13, October, 2020, 11:19:21 AM
MaxiPower: first chapter of my golden sun inspired webtoon is now live. be cool if yas could check it out. https://www.webtoons.com/en/challenge/legacy-of-the-lost-dragoon/the-northern-reaches/viewer?title_no=508487&episode_no=6
31, August, 2020, 08:40:32 AM
Daddy Poi's Oily Gorillas: Being able to convert IPS files to text files to also supporting commenting, and convert back to IPS after changes.... sounds like a fun strategy? 
27, August, 2020, 10:37:30 PM
Salanewt: Hey, thanks! 
25, August, 2020, 12:59:22 AM
Foreclosure: Yo! Looking forward for this AI OVerhaul patch when it's finished :) :) 
19, August, 2020, 07:00:17 PM
Salanewt: That's awesome! I'm pretty good; been working on an AI Overhaul project and have made some nice progress in the past couple weeks; the goal for tonight is to add Deadbeard to GS2 and then I can move onto something new.
19, August, 2020, 01:14:56 PM
Awec: Ye not bad, just checked back in for updates on GS Reloaded really. Although now I'm looking back through some of my old forum posts, which is fun :D how are you?
19, August, 2020, 01:43:07 AM
Salanewt: It sure is! How have you been?
16, August, 2020, 09:40:57 AM
Awec: Yo nice to see this forum is still going
14, August, 2020, 09:06:44 PM
Salanewt: Hey! How goes it?
01, August, 2020, 01:31:53 PM
KyleRunner: Hello, people!
08, July, 2020, 08:57:11 PM
Salanewt: That awkward feeling when almost a quarter of a year goes by without anyone posting a message in this chatbox.
27, April, 2020, 07:54:10 PM
Daddy Poi's Oily Gorillas: I'm having a thought. So I know Atrius's editor is slow at saving sprites.... So I've been thinking.   I am considering another console program.  This time, for importing and exporting sprites/animations. Image files (for sprites), binary files (for animation data), and maybe binary or text files for Settings. Would certainly be cool if it works out. - Console programs are fun, when you can (at least temporarily) cheat out making a full interface editor for them. =P
25, April, 2020, 10:42:07 PM
Daddy Poi's Oily Gorillas: Tried to list those in order of importance, but anyway....
25, April, 2020, 10:37:59 PM
Daddy Poi's Oily Gorillas: GSHC, GS Speedrunning, /r/Golden Sun, GS United Nations, ToK.  Sixth one is border-line I'm not sure.
25, April, 2020, 08:00:21 PM
Daddy Poi's Oily Gorillas: (low as in <100)
25, April, 2020, 08:00:04 PM
Daddy Poi's Oily Gorillas: So while there are quite a few GS Communities out there. There are only up to maybe ~6 Discords worth even the consideration of joining, I think. Most of the rest of them have a fairly low member count. (Of which I can tell?)
14, April, 2020, 10:49:35 AM
Daddy Poi's Oily Gorillas: The Essential Age could be what it stands for?
13, April, 2020, 05:23:32 PM
Misery: Tea editor... that's a cute name for it
01, April, 2020, 02:42:48 AM
JupiterDjinn: Also my browser is sending two o f my messages srry.
01, April, 2020, 02:42:06 AM
JupiterDjinn: Just got the reply. Ok my question is: how can I get the tea editor on Android, because I already have an emulator. But no computer.
31, March, 2020, 07:18:29 PM
Salanewt: Sure thing!
31, March, 2020, 03:38:27 PM
JupiterDjinn: Can I have a question answered?
28, March, 2020, 12:50:13 AM
Salanewt: I want to shrink it down in size later, but yup!

Affiliates
Temple of Kraden Golden Sunrise
Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.096 seconds with 23 queries.