Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (526)
Games in Android Showcase (127)
games submitted by our members
Games in WIP (593)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1] 2
  ignore  |  Print  
  Scene2D, utter tedious  (Read 2103 times)
0 Members and 1 Guest are viewing this topic.
Offline Gibbo3771
« Posted 2014-04-17 09:40:59 »

Is it just me or is messing with Scene2D tedious as hell.

I have been coding a Space Invaders game for the past couple of days, using LibGDX and I have the game almost complete (death animation missing) so I decided to add a menu.

Now it would have 2 buttons, play and quit. Jesus, the amount of fields/variables required to just do that was horrible.

Then the skin, god annoying.

Don't get me wrong, I have used it to good effect in the past but it just feels like a tedious and ugly library when it comes to coding with it.

Anyone feel the same or just me?

I ended up just creating a simple UIHandler, a few buttons, a list to holds the buttons and a way to navigate them with the keys.

It took around 5-6 classes but all I have to do is this:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
UIHandler uiHandler = new UIHandler();

   TextButton play = new TextButton("PLAY", Gdx.graphics.getWidth() / 2,
         Gdx.graphics.getHeight() / 2, 300, 100, new ButtonCallback() {
           
            @Override
            public void onClick() {
               ((Game)Gdx.app.getApplicationListener()).setScreen(new GameScreen());
            }
         });
   
   TextButton exit = new TextButton("QUIT", Gdx.graphics.getWidth() / 2,
         Gdx.graphics.getHeight() / 2, 300, 100, new ButtonCallback() {
           
            @Override
            public void onClick() {
               Gdx.app.exit();
            }
         });

      list.addWidget(play);
      list.addWidget(exit);
      uiHandler.addWidget(list);


You can see where the similarities lie, the ui hander is like the Stage and the list is like the List in Scene2D, this was pretty fun to write, it might not look pretty but at least I am not fighting with a whole bunch of exceptions to get Scene2D to agree :p

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline mcela
« Reply #1 - Posted 2014-04-17 11:56:33 »

Can you perhaps show the rest of the code? It usually doesn't take me more than a few lines to create a basic menu.

Follow | [Twitter]
Projects | [Pixel Gang] [Bumpy]

{ @ /
Offline Gibbo3771
« Reply #2 - Posted 2014-04-17 11:59:39 »

Can you perhaps show the rest of the code? It usually doesn't take me more than a few lines to create a basic menu.

The rest of my code? That is pretty much it in terms of creating the menu, do you mean my actual back classes? If you mean Scene2D, I have no Scene2D code.

It is very similar to Scene2D except I don't have a skin crashing 6x before everything is put into it.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline seismic

Senior Devvie


Medals: 4
Projects: 1



« Reply #3 - Posted 2014-04-17 13:16:33 »

I don't have any of the problems with Scene2D (like exceptions).
IIRC there is an "old" skin file around and if you try to use that you'll likely get errors, because Scene2D received an update for the SelectBox or something.

Once you're using a current version of the default skin you're good to go.

Also you don't really need to use the skin if all you're doing is creating two buttons. The setup is quite slim without a skin too.
Offline opiop65

JGO Kernel


Medals: 159
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #4 - Posted 2014-04-17 13:18:33 »

I feel your pain... when I used LibGDX, Scene2D was the one part I always feared when creating games. Its just so bloated and unnecessary when creating a simple menu that consists of a few buttons. I ended up just writing my own GUI code which worked perfectly fine.

Offline Grunnt

JGO Kernel


Medals: 94
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #5 - Posted 2014-04-17 14:07:28 »

If you want to make a complex GUI with sliders, editable text areas, checkboxes and other stuff then homebrewing stuff just does not cut it and becomes a big waste of time. In my experience Scene2D UI is quite elegant and requires few lines of code to get going.

Also, I cant really see the complexity in Scene2D ui, it's actually very simple and is just a couple of lines more than you are using. Yeah setting up a skin is some work, but actually a lot easier than in for example TWL IMHO.

Offline TeamworkGuy2

Junior Devvie


Medals: 10



« Reply #6 - Posted 2014-04-17 14:49:50 »

:scratches-head: are we talking about the 23 lines of code in the OP because basic Swing GUIs I've worked have required 400-600 lines of code just to get a few buttons and text boxes aligned and properly resizing.
You can always make a utility class to create basic menus for you if your menus always consist of a few buttons in the same positions.
Offline trollwarrior1
« Reply #7 - Posted 2014-04-17 14:56:50 »

@teamworkbuy

Agreed.. I don't see how OP's code is messy. Yes it could use .addCallBack() so you don't have to add call back in the constructor, but other than that, the code seems rather clean.

Here is what you would start calling messy. Adding few buttons without functions is one thing. Its other thing to make everything work together. Here is a really small project I worked on for couple of days. It had like 5 buttons and 1 JList.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
frame = new JFrame("Leauge Of Legends Simple Bot");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setResizable(false);

      JPanel leftPanel = new JPanel(new GridBagLayout());
      GridBagConstraints lc = new GridBagConstraints();
      lc.insets.top = 2;
      lc.insets.bottom = 2;
      lc.gridy = 0;
      frame.add(leftPanel, BorderLayout.WEST);

      JPanel rightPanel = new JPanel(new GridBagLayout());
      frame.add(rightPanel, BorderLayout.EAST);
      GridBagConstraints rc = new GridBagConstraints();
      rc.insets.top = 2;
      rc.insets.bottom = 2;
      rc.gridy = 0;

      buttonImport = new JButton("Import configuration");
      buttonImport.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent arg0) {
            int val = fileChooser.showOpenDialog(frame);

            if (val == JFileChooser.APPROVE_OPTION) {
               File file = fileChooser.getSelectedFile();

               if (file.exists()) {

                  String name = file.getName();
                  String[] names = name.split("\\.");

                  if (names.length == 2 && names[1].equals("botconfig")) {

                     try {
                        ParserDevice device = new ParserDevice(new FileReader(file));

                        try {
                           final BotConfigData data = new BotConfigData(file);
                           data.load(device);

                           dataList.add(data);

                           SwingUtilities.invokeLater(new Runnable() {
                              public void run() {
                                 jlistmodel.addElement(data);
                              }
                           });
                        } catch (NullPointerException e) {
                           showError("Cannot parse file. It may be corrupt.");
                        }
                     } catch (IOException e) {
                        e.printStackTrace();
                     }

                  } else {
                     showError("Wrong file format.\nExpected format is .botconfig");
                  }

               } else {
                  showError("File doesn't exist.");
               }
            }
         }
      });
      leftPanel.add(buttonImport, lc);

      jlistmodel = new DefaultListModel<BotConfigData>();
      jlist = new JList<BotConfigData>(jlistmodel);
      jlist.addListSelectionListener(new ListSelectionListener() {
         public void valueChanged(ListSelectionEvent arg0) {
            if (jlist.getSelectedIndex() == -1) {
               currentConfigData = null;
               buttonStart.setEnabled(false);
               buttonRemove.setEnabled(false);
               return;
            }
            currentConfigData = dataList.get(jlist.getSelectedIndex());
            buttonStart.setEnabled(true);
            buttonRemove.setEnabled(true);
         }
      });

      JScrollPane scroller = new JScrollPane(jlist);
      scroller.setPreferredSize(new Dimension(250, 100));
      rightPanel.add(scroller, rc);

      lc.gridy++;
      buttonRemove = new JButton("Remove configuration");
      buttonRemove.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent arg0) {
         
            BotConfigData data = currentConfigData;
            int val = JOptionPane.showConfirmDialog(frame, "Are you sure you want to remove: "+data.toString()+" ?", "Confirm your selection", JOptionPane.YES_NO_OPTION);
           
            if(val==JOptionPane.OK_OPTION) {
               final int index = dataList.indexOf(currentConfigData);
               
               dataList.remove(index);
               
               SwingUtilities.invokeLater(new Runnable() {
                  public void run() {
                     jlistmodel.removeElementAt(index);
                  }
               });
               
               JOptionPane.showMessageDialog(frame, "Configuration "+data.toString()+" has been removed.");
            }
         }
      });
      buttonRemove.setEnabled(false);
      leftPanel.add(buttonRemove, lc);

      lc.gridy++;
      buttonStart = new JButton("Start bot");
      buttonStart.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
           
            SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                  buttonImport.setEnabled(false);
                  buttonRemove.setEnabled(false);
                  jlist.setEnabled(false);
                  buttonStart.setEnabled(false);
                  buttonStop.setEnabled(true);
               }
            });
           
            new Thread() {
               public void run() {
                  Bot.instance.botLogic.start(dataList.get(jlist.getSelectedIndex()));
               }
            }.start();
         }
      });
      buttonStart.setEnabled(false);
      leftPanel.add(buttonStart, lc);

      lc.gridy++;
      buttonStop = new JButton("Stop bot");
      buttonStop.setEnabled(false);
      buttonStop.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            stop();
            Bot.instance.botLogic.stop();
         }
      });
      leftPanel.add(buttonStop, lc);

      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);

      frame.addWindowListener(getClosingWindowListener());


And this is not all the code that has to do something with GUI..

This is what I call messy.

Some people probably find my code clean. It probably depends on your coding skill level.
Offline Gibbo3771
« Reply #8 - Posted 2014-04-17 15:58:01 »

The 23 lines of code in the OP is my code, not Scene2D.

If it was scene2d the lines would be doubled, to get the same thing.

Scene2D is great when it comes to a semi complex UI, but see trying to get something basic to work...annoying as shit imo.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline StumpyStrust
« Reply #9 - Posted 2014-04-17 19:22:02 »

I have run into many issues with scene2d's ui stuff. It is sad that I have to say to stay away from it. There are focus issues, rendering issues where things get cutoff oddly. I have tried multiple version of libgdx but they all have the same issues.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Jimmt
« League of Dukes »

JGO Kernel


Medals: 139
Projects: 4
Exp: 3 years



« Reply #10 - Posted 2014-04-17 20:09:16 »

Personally I've never had any problems with it, obviously you are always going to sacrifice simplicity for power. A menu with two buttons is pretty trivial:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
TextButton startButton = new TextButton("Start", getSkin());
      startButton.addListener(new ClickListener(){
         @Override
         public void clicked(InputEvent event, float x, float y){
            //blah
         }
      });
TextButton quitButton = new TextButton("Start", getSkin());
      quitButton.addListener(new ClickListener(){
         @Override
         public void clicked(InputEvent event, float x, float y){
            //blah
         }
      });
table.add(startButton);
table.row();
table.add(quitButton);
right?
About the same amount of code as you have, except you don't have to manually resize each element.
Offline StumpyStrust
« Reply #11 - Posted 2014-04-17 20:13:47 »

My issues is not that writing stuff is hard, but that it doesn't work right.

Offline Jimmt
« League of Dukes »

JGO Kernel


Medals: 139
Projects: 4
Exp: 3 years



« Reply #12 - Posted 2014-04-17 20:37:45 »

My issues is not that writing stuff is hard, but that it doesn't work right.
I was referring to the OP, but what are the specific problems?
Offline Gibbo3771
« Reply #13 - Posted 2014-04-17 21:53:50 »

Personally I've never had any problems with it, obviously you are always going to sacrifice simplicity for power. A menu with two buttons is pretty trivial:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
TextButton startButton = new TextButton("Start", getSkin());
      startButton.addListener(new ClickListener(){
         @Override
         public void clicked(InputEvent event, float x, float y){
            //blah
         }
      });
TextButton quitButton = new TextButton("Start", getSkin());
      quitButton.addListener(new ClickListener(){
         @Override
         public void clicked(InputEvent event, float x, float y){
            //blah
         }
      });
table.add(startButton);
table.row();
table.add(quitButton);
right?
About the same amount of code as you have, except you don't have to manually resize each element.


Except you are missing the creation and setup of tgr stage, the table, sizing both.

Them setting up the skin so that it has a font and drawables.

I don't have to manually resize my widgets, I added in 1 line if code that sizes it to the parent if you wish.

I dunno, I just can't seem to do well with it and the thought of it puts me right off.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline Jimmt
« League of Dukes »

JGO Kernel


Medals: 139
Projects: 4
Exp: 3 years



« Reply #14 - Posted 2014-04-17 22:04:25 »

1  
2  
3  
skin = new Skin(Gdx.files.internal("skin/uiskin.json"));
Table table = new Table(skin);
stage.addActor(table);        

Tedious?
Also scene2d actually uses less than one line to resize elements:
1  
table.add(button).fill();

But you can also set align, borders, manually set w/h, etc too. I think it's just the way of thought that's different, instead of thinking about each button and where it goes you think about the table as a whole.
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 273
Exp: 5 years



« Reply #15 - Posted 2014-04-17 22:07:55 »

Yeah for me scene2D.ui has been gloriously concise.
Now I should make a drag and drop ui builder for it...
Offline Jimmt
« League of Dukes »

JGO Kernel


Medals: 139
Projects: 4
Exp: 3 years



« Reply #16 - Posted 2014-04-17 22:10:43 »

Now I should make a drag and drop ui builder for it...
That'd actually be really good, show the cell boundaries and stuff...like the old table layout editor with descriptors
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 273
Exp: 5 years



« Reply #17 - Posted 2014-04-17 22:14:11 »

show the cell boundaries and stuff...

You mean like the debug mode? (Just with more info?)
Offline Jimmt
« League of Dukes »

JGO Kernel


Medals: 139
Projects: 4
Exp: 3 years



« Reply #18 - Posted 2014-04-17 22:17:40 »

show the cell boundaries and stuff...

You mean like the debug mode? (Just with more info?)
Yeah basically, easiest way to determine why some component isn't going where you want it to.
Offline BurntPizza

« JGO Bitwise Duke »


Medals: 273
Exp: 5 years



« Reply #19 - Posted 2014-04-17 22:21:33 »

In the hypothetical editor: right-click component in debug mode to bring up a pop-up stacktrace of alignment functions taking place on it.
Could be interesting, might actually do it. Not now though, the rest of my spring break is devoted to this damn CPU emulator I'm writing.
Offline atombrot
« Reply #20 - Posted 2014-04-17 22:45:00 »

Having worked with some other (some of them "homebrew") ui libraries I have to say I love scene2d and scene2d.ui. Sometimes the ui creation and handling code can become a bit messy, but you can make your code a lot cleaner by using factories to create the ui elements you are using...

The backbone of my UI's is the table actor/widget. If you know what the table is capable of and are a bit creative you can achieve pretty much very fast. Also it's not bad to already have some nice widgets ready... Would not wan't to implement the scrollpane or textboxes myself Wink

Check the readme on the github page of the table layout: https://github.com/EsotericSoftware/tablelayout
Offline Memoryz

Senior Newbie


Exp: 2 years



« Reply #21 - Posted 2014-04-18 02:00:00 »

I dislike the skin system as well.

I'm still trying to figure out how to end up getting fonts to upscale properly (or generate at a proper resolution so that it shows nicely on the screen).
Offline StumpyStrust
« Reply #22 - Posted 2014-04-18 04:16:45 »

Once you have a skin it is rather easy. It is getting that setup that is hard. Libgdx comes with a default skin you can use.

Offline Grunnt

JGO Kernel


Medals: 94
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #23 - Posted 2014-04-18 08:00:33 »

Except you are missing the creation and setup of tgr stage, the table, sizing both.

Them setting up the skin so that it has a font and drawables.

Neither of which are present in your code example. I suppose your UI library doesnt magically render itself or create that "list" variable out of thin air?

I felt precisely the same about pretty much any UI library I used and baked my own UI code. But after some time you really need some extra functionality like additional types of widgets, or an easy way to restyle the whole thing. Some time later you end up with an unmanageable inconsistent and buggy mess which eats up half your game development time. Then compare the homebrew solution to something like the Scene2D UI and you get a sensible comparison.

Offline Grunnt

JGO Kernel


Medals: 94
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #24 - Posted 2014-04-18 08:02:24 »

I'm still trying to figure out how to end up getting fonts to upscale properly (or generate at a proper resolution so that it shows nicely on the screen).

Thats an issue with font rendering and has nothing to do with Scene2D. Just generate an upscaled version of your font.

Offline Nate

« JGO Bitwise Duke »


Medals: 158
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #25 - Posted 2014-04-18 08:36:07 »

OP, since your problem is "fighting with a whole bunch of exceptions to get Scene2D to agree" I assume you haven't taken the effort to understand the API. Presenting your opinion so crudely (scene2d is "annoying as shit") isn't going to lead to productive discussion. Complain until you are blue in the face, but don't be surprised that I don't care about you or your problems. If your goal is solely to complain, remember that scene2d is provided to you at considerable effort for free. If you don't like it, don't use it.

StumpyStrust, AFAIK scene2d works correctly. It has been in use in many complex apps for years, so it would quickly become apparent if something didn't work. A UI library has a number of problems that are difficult to solve in a nice way, such as drawing, responsive layout and event routing. scene2d has a solid approach to these, but it may not be easily apparent why it works the way it does. I'm happy to discuss what you find doesn't work, but in a different thread.

Offline Gibbo3771
« Reply #26 - Posted 2014-04-18 09:27:39 »

OP, since your problem is "fighting with a whole bunch of exceptions to get Scene2D to agree" I assume you haven't taken the effort to understand the API. Presenting your opinion so crudely (scene2d is "annoying as shit") isn't going to lead to productive discussion. Complain until you are blue in the face, but don't be surprised that I don't care about you or your problems. If your goal is solely to complain, remember that scene2d is provided to you at considerable effort for free. If you don't like it, don't use it.

Sorry I will reword, setting up a skin is annoying, I find it quite over whelming it you want something simple. I have read the documentation, I get it. I can easily setup a basic ui with it.

Just because I find it tedious does not mean I don't know how to work it.

I never complained about the functionality, nor at any point did I say I never appreciated the free use of the library.

I find your reply pretty aggressive, for no reason at all.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline Grunnt

JGO Kernel


Medals: 94
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #27 - Posted 2014-04-18 09:47:48 »

Yeah well in the end, Scene2D is a good library, its used by many to good effect, made by volunteers who know what they are doing. Considering that, its just not so nice to use words like "utter tedious", "tedious as hell", "that was horrible", "god annoying", "tedious and ugly library". It would be sad if a post like this would put newbies off using a good library for no good reason.

Offline Gibbo3771
« Reply #28 - Posted 2014-04-18 10:24:22 »

Yeah well in the end, Scene2D is a good library, its used by many to good effect, made by volunteers who know what they are doing. Considering that, its just not so nice to use words like "utter tedious", "tedious as hell", "that was horrible", "god annoying", "tedious and ugly library". It would be sad if a post like this would put newbies off using a good library for no good reason.

Fair enough I could have chosen my words better, I would never discourage someone to to use this library!

I never said it was ugly, I said it can make your code look ugly, especially if your just making something simple.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline Nate

« JGO Bitwise Duke »


Medals: 158
Projects: 4
Exp: 14 years


Esoteric Software


« Reply #29 - Posted 2014-04-18 23:58:52 »

Maybe my post was harsh. I felt your criticism was quite intense with no hint of an attempt at being productive.

Skin does a couple things:

1) Makes for a centralized place to find resources (colors, fonts, widget styles, etc).
2) Makes it convenient to convert textures, regions, etc to a drawable, tinted drawable, etc.
3) Makes it easier to reuse the resources in subsequent apps by specifying the skin as JSON.

If you are making a simple UI and don't need #3, you probably don't want to spend the extra effort to describe your skin as JSON.

This class creates a UI with simple code and no Skin JSON:
https://github.com/libgdx/libgdx/blob/master/tests/gdx-tests/src/com/badlogic/gdx/tests/UISimpleTest.java#L37
Most of the code is configuring how the button looks (what texture regions it uses). I don't see what the big deal is.

Pages: [1] 2
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

toopeicgaming1999 (73 views)
2014-11-26 15:22:04

toopeicgaming1999 (63 views)
2014-11-26 15:20:36

toopeicgaming1999 (15 views)
2014-11-26 15:20:08

SHC (29 views)
2014-11-25 12:00:59

SHC (27 views)
2014-11-25 11:53:45

Norakomi (32 views)
2014-11-25 11:26:43

Gibbo3771 (27 views)
2014-11-24 19:59:16

trollwarrior1 (40 views)
2014-11-22 12:13:56

xFryIx (78 views)
2014-11-13 12:34:49

digdugdiggy (57 views)
2014-11-12 21:11:50
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!