[SpeedHack] A Slug's Life
Mark Oates

A Slug's Life

You're a Slug who's lost his shell. So you're now on a quest looking for a modest replacement. When you find things, you can use them to customize your character in the "Customize Your Character" room. Increase your strength, hydration, and attack power by killing ants and gaining experience.

Screenshots:

{"name":"608638","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/2\/22d7c513309121799a1e2d12541bd254.png","w":1001,"h":600,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/2\/22d7c513309121799a1e2d12541bd254"}608638
{"name":"608639","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/0\/60d69ba13aaaeb0d1a57cbdce4ee61b3.png","w":1001,"h":609,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/6\/0\/60d69ba13aaaeb0d1a57cbdce4ee61b3"}608639
{"name":"608640","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/7\/a78e38fa7bbb32f82b9e0f2d15946a5a.png","w":1001,"h":600,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/7\/a78e38fa7bbb32f82b9e0f2d15946a5a"}608640
{"name":"608641","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/6\/c6c97bd0449d2a3e870c71cbc5741b3f.png","w":1001,"h":609,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/6\/c6c97bd0449d2a3e870c71cbc5741b3f"}608641

Downloads:

Source
Windows Binary (including dlls)

About the Game:

  1. Controls are with a joystick. Move with the axis, buttons are Jump, Attack, and Strafe. You can also respawn to the beginning of the level.

  2. If you don't have a joystick, you can use Mouse+WASD, Space, and the Mouse Buttons.

  3. The game is unfinished, there are only 3 trinkets to find - I was hoping to add more... and... you can't die. (These were the last essential features that didn't make it in. ;) )

  4. There's no sound or music.

  5. This is my first 3D game, and there are some edge cases where you can slip through faces in the collision mesh. If you get stuck, you can respawn with the joystick's respawn button.

Compile Insructions:

I used Allegro 5.1.7, and I believe it will compile with the latest WIP (5.1.8) just fine.

Comment out line 464 in my_game_maps.cpp:

//home_door = door;

and comment out line 19 in program_master.h

//extern Door *home_door;

otherwise you'll get an unresolved extern and undefined reference, respectively.

(I made and submitted these changes at 11:59 but it didn't post in time. But you be the judge. :) )

To run the program, you'll need to place the "bitmaps" "models" and "fonts" folders inside a new folder called "data". The "data" folder should be in the same directory as the EXE.

Neil Roy

That's an interesting concept. Looks like some of the polygons are wound wrong (clockwise or counterclockwise making them see through). Also, there was no way to exit the game. I had to bring up my task manager. Still, looks like it will be fun if completed. :)

Edgar Reynaldo

It worked for a while and it was cool once I figured out the controls but then they went all crazy on me and I couldn't steer anymore with the mouse it just kept changing direction it was like an ai had taken over and I had no more control and I couldn't quit with escape.

GullRaDriel

Does it also works without a Joy ?

Dizzy Egg

Getting very annoyed whilst grinding, trying to kill the light blue ants! Little buggers! This is pretty cool,, worked smoothly here!

Mark Oates

you'll have to close the window with alt+f4.

Nitehaxor, could you post a screenshot?

Edgar, sometimes the strafe can get stuck, hit the button again to unstick it.

Arthur Kalliokoski

Your zipfile spewed files all over my home directory! >:(
Making a 'slug' subdir to unzip to didn't have a makefile, so I did

g++ *.cpp -o slugfest"

and I got more errors than you can shake a mollusc at, so I gave up.

Mark Oates

I'd be really interested to see what kind of compile errors you get, because I get none. :/ I try -Wall but then I get a bunch of errors for the allegro headers and I can't parse through them.

When compiling this program, I get no errors:

//#include <allegro5/allegro.h>
int main()
{
  return 0;
}

When compiling this program, I get the following spew:

#include <allegro5/allegro.h>
int main()
{
  return 0;
}

{"name":"608656","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/e\/fe82ae0e3ddb59ba8e97426d04fd70f4.jpg","w":960,"h":3543,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/e\/fe82ae0e3ddb59ba8e97426d04fd70f4"}608656

What are you compiling on?

Arthur Kalliokoski

g++ 4.8.2 on Linux. They're all compile errors, it never gets to the linking part. 1552 lines of errors.

[EDIT] I noticed the first error complained that I needed to add -std=c++11, but it didn't help.

SiegeLord

Wow, this was quite an experience to compile on Linux. My respect for MSVC has dropped to negative infinity. I really hope, Mark, that you didn't ignore any warnings it gave you, :P. I should note that I did not compile this with -Wall, so there are 1000's of other warnings yet to fix. This is mostly a 'minimal compillation without default warnings' patch.

Before I go through the changes I had to make, I should note that you should move the 'bitmaps', 'models' and 'fonts' into a 'data' subdirectory, like they are in the windows binary zip.

Now, let's look through some choice diffs:

-#include <allegro5\allegro.h>
-#include <allegro5\allegro_font.h>
+#include <allegro5/allegro.h>
+#include <allegro5/allegro_font.h>

Tsk tsk.

-  int screen_flags = NULL;
+  int screen_flags = 0;

This gives a warning in GCC... only pointers can be NULL.

-  std::cout << "[" __FUNCTION__ "] could not load \"" << identifier << "\"" << std::endl;
+  std::cout << "[" << __FUNCTION__ << "] could not load \"" << identifier << "\"" << std::endl;

__FUNCTION__ is not a string literal in GCC. It really is more portable to do what I did in this change... C string-literal concatenation is an abomination.

-  for (std::vector<Bin<T>::Record *>::iterator it=record.begin(); it!=record.end(); it++)
+  for (typename std::vector<Bin<T>::Record *>::iterator it=record.begin(); it!=record.end(); it++)

It's unbelievable that MSVC compiles this code in 2014. Explanation.

 void change_saturation(ALLEGRO_COLOR &color, float amount, float (*operation)(float op1, float op2))
 {
+  using std::min;
+  using std::max;
   float h, s, l, a=color.a;
   al_color_rgb_to_hsl(color.r, color.g, color.b, &h, &s, &l);
-  s = min(max(0, operation(s, amount)), 1.0);
+  s = min(max(0.0f, operation(s, amount)), 1.0f);
   color = al_color_hsl(h, s, l);
   color.a = a;
 }

I don't know which min/max MSVC was letting you use, but the ones GCC suggested didn't work with the original code. They had to be imported, and all their arguments had to be made explicitly the same (i.e. float s).

-static void adjust_image(ALLEGRO_BITMAP *bitmap, float amount,
+void adjust_image(ALLEGRO_BITMAP *bitmap, float amount,
   void (*filter_func)(ALLEGRO_COLOR &, float, float (*)(float, float)),
   float (*filter_func_op)(float op1, float op2))
 {

MSVC should have warned about this. This function is marked static, but is then used externally.

 AntEnemy::AntEnemy(Map *map, vec3d position, vec3d domain_min, vec3d domain_max)
   : Enemy(map, position, map->models["ant-03.obj"])
-  , domain(domain)
+  //~ , domain(domain)
 {

Nice uninitialized memory access :P. This invoked the copy constructor of domain which copied from the uninitialized domain. Kind of like doing:

int a;
a = a;

in C except in a more 'C++-shoot-yourself-in-the-foot' kind of way. I hope MSVC gave you a warning for this that you ignored, because otherwise this is unacceptable (I didn't check if GCC warned about this with -Wall, it didn't without it).

-  bool Entity2::update_player_collision(vec3d player_location);
+  bool update_player_collision(vec3d player_location);

This is not valid C++.

-int main(int argc, char *argv)
+int main(int argc, char **argv)

That's not a valid main function signature.

#SelectExpand
1-template<class T> 2-A_INLINE T random_element(std::vector<T> &elements) 3-{ 4- return elements[random_int(0, elements.size()+i)]; 5-} 6- 7-template<class T> 8-A_INLINE T random_element(T elements[], int size) 9-{ 10- return elements[random_int(0, size-1)]; 11-} 12+//~ template<class T> 13+//~ A_INLINE T random_element(std::vector<T> &elements) 14+//~ { 15+ //~ return elements[random_int(0, elements.size()+i)]; 16+//~ } 17+ 18+//~ template<class T> 19+//~ A_INLINE T random_element(T elements[], int size) 20+//~ { 21+ //~ return elements[random_int(0, size-1)]; 22+//~ }

Wow. MSVC apparently refuses to do even the most basic checking for templated functions. Note that there's no i defined anywhere in the first function.

 // trim from both ends
 A_INLINE std::string php::trim(std::string &s)
 {
-  return ltrim(rtrim(s));
+  std::string s1 = s;
+  std::string s2 = rtrim(s1);
+  std::string s3 = ltrim(s2);
+  return s3;
 }

Not sure how this could have compiled. Those trim functions trim the string in place, and the original code essentially tries to trim a bunch of rvalue references.

After all this was done, it ran fine. I actually tried it before on Windows, and many of the polygons were... weird looking. I actually suspect the Z-buffer wasn't set up correctly.

EDIT: And I did like it... the graphics style was very pleasant and I liked the jump-pads + "shells", hehe. Ignoring the code issues, I like this entry quite a bit.

Neil Roy

I done up a short video of my gameplay. I forgot to turn off my music I had randomly playing (it does sort of fit the game though :) ) and got a warning from youtube about copyrighted music, but it should still play... if not, I'll record it again...

video

You should be able to see some of the issues I was talking about anyhow.

Edit: And this was played on Windows 7 - 64bit with a GeForce GTX650 video card, up to date drivers.

Mark Oates

Well, I installed g++ 4.8.1 and have completely cleaned my codebase to -Wall with no errors or warnings (with a few exceptions reliant on dependencies that I'll do tomorrow.) It feels pretty good :) I don't know if there is any difference between compiling with g++ on Linux/Windows/Mac as long as you're not using any platform-specific code?

SiegeLord thank you so much for your diagnostic. In cleaning my code, my biggest sadness was that I had to take out the "override" keyword because I didn't want to require a -std=c++11. Other than that, it was a bunch of the stuff you mentioned, I ended up making a

#define ALLEGRO_FLAGS_EMPTY 0

for the NULL flags thing. I don't know where I picked that up, but I always thought that was the correct way to do it, it's littered throughout my code. The typename for the oops-not-a-member thing was interesting,

For me, yea, I used MSVC on its default warning level, and it gave me no errors or warnings on compile. I think it sucks at templates and only cares about them if you invoke them. I also think it doesn't compile function code unless you invoke it, or something like that.

Another one that came up was neat was the initialization list order. I knew that initialization order was based on declaration order in the header file during compile time (which at one point seemed a potential for problems), but I didn't think the order in the list would make any difference. Nevertheless the domain(domain) code was from earlier when a AABB3D domain was in the argument list, but I shaved it out and replaced it with the vec3d min, vec3d max but forgot to take it out from that init list - funny result. :D

Another one that I genuinely didn't know was the reference-passing-trim one.

There were also a few declared-but-unused variables in there. Thanks again so much. :)

Neil, thanks for the video. I witnessed that same problem when trying it on another computer (I tried 3 computers, only one demonstrated that behavior and it looked like an older computer so I prayed it wouldn't be an issue). Nevertheless, I realized at some point that I was doing something wrong (but getting away with it), when I had to the text I wanted in front before the text I wanted in back.

The geometry/faces was one thing I wasn't sure about. On my computer it shows both back and front faces, which I thought was strange and incorrect. But I had no time to investigate, only time to panic, code, and hope for the best. 8-) It's a point I need to figure out, but I'm not sure exactly where the fault lies. Right now I'm thinking it could be any combination of gl settings, model geometry, some contorted rendering technique I'm using, z-bufffer, my .obj loading, or blender exporting settings.

Throughout the process of making the game, I found more, right-er ways to do it. Blender became a nice little package for making cheap geometry, checking UV maps, flipping normals, baking light texture maps, all kinds of stuff.

I learned so much this SpeedHack. :D8-)

AMCerasoli

I have the same problem than Neil. But it's incredible that you came up with that 3D game is such short time using only Allegro.

Mark Oates

OK, I have strong evidence that the problem is related to the depth buffer not being correctly created or set.

Would you guys be willing to try these modified exes?

{"name":"608687","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/2\/320eaff41e1783d5da371dfaa128c380.png","w":539,"h":136,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/2\/320eaff41e1783d5da371dfaa128c380"}608687

Just drop them in the same directory and try them out.

On my computer, this seems to be the most reliable:

  al_set_new_display_option(ALLEGRO_DEPTH_SIZE, 16, ALLEGRO_SUGGEST);

In the original code, this is what I had

  al_set_new_display_option(ALLEGRO_DEPTH_SIZE, 256, ALLEGRO_SUGGEST);

Which probably just failed on some computers. This is my best guess, right now. :) If I comment out the line all together, then I have the same anomalous rendering on my computer that's in the video, and on some settings (1024*8, l0l) it has the same behavior - probably from failure to initialize.

AMCerasoli

Nope... The same problem here. :(

Neil Roy

I tried them all out, same result.

I am thinking, perhaps it is the order in which you are drawing them. The walls are longer than the ground. Perhaps you need to draw the walls first, then the ground, or the ground first, then the walls? I have a 3D terrain program where my tree trunks go into the terrain like these walls, but they are drawn after the ground is rendered. I do remember needing to draw things in a certain order, like my background before my trees or the transparent portions wouldn't look right etc... so it can matter.

I know the order you render the vertexes of polygons effects which way it is facing. There is an option, in OpenGL anyhow which will make it so both sides of a face is showing, maybe that was on by default on your system (driver settings perhaps?) but not on others. You should make it a point to set this specifcally to be certain. Have it display both sides would fix the problem, but I would personally rewind the polygons so the vertexes are rendered in the right order (clockwise I think?) you can even reset the order, I know in OpenGL you can anyhow. You can set it to your own preference of CW or CCW with something like glFrontFace(GL_CW) or glFrontFace(GL_CCW). I haven't worked with OpenGL in Allegro at all so this might be different, but it works when I compile it presently. ;)

There's also glEnable(GL_CULL_FACE) which enables culling the backfaces, that would be turned off to ensure they are drawn for a quick fix.

I am sure the default for the vertexes is clockwise (GL_CW) but I set it in my code anyhow to be certain. You know the saying... "never assume, it makes an ass out of u and me" ;)

Edgar Reynaldo

I enjoyed this entry, but as others have said, my walls are backwards and green, with no texture visible. And the controls went weird without using a joystick too. Is there an updated binary anywhere I could try?

Mark Oates

All I can say at this point is that it works correctly on some computers, but on others the depth buffer does not get setup correctly on others. If I had more time and more computers to test on I believe I could solve the issue, but for now just ... :-X pretend that it works correctly on your computer? :-[

Gideon Weems

I've really been wanting to play this, but it doesn't compile.

Edgar Reynaldo

I tried the exes that varied the depth buffer but none of them work. The walls are still backwards and green.

Bruce Perry

Hi!

Well it seems people are well on top of the coding feedback, and the only possible thing I add is: OH. MY. GOD. And you're telling me people actually use C++?! You should try D ;)

I liked the game. I had the z-buffer issues, but I coped.

I found the black bugs easy (though I felt as if I was eating them rather than fighting them), while I found all the other bugs just wouldn't die and maybe I didn't try enough times. A little more feedback to encourage me to keep hitting them would help clarify that one.

The biggest frustration, though, occurred when I collected the paper clip and then accidentally got rid of the text before I'd had a chance to read it. You might want to tweak that. A possibility is to allocate a special key (or button) for dismissing text, and display which key (or button) that is while the text is up. The same problem exists on the question marks and when your level goes up, although you can bring the text back up with the question marks so it doesn't matter so much there.

The joystick thing is interesting. While I do have a joystick, it's an old one that needs a game port and I can't currently use it, and even on my old computer it had such awful accuracy that I couldn't play with it. These days, I'm not sure there are even very many people with them. I do like the idea of them, it's just I don't think anyone has them. Advising people that it's a joystick game but they can get by with the 'alternative' controls is a bit odd - you run the risk of making your players feel a bit second-class.

On the joystick subject, there's another thing that sounded wrong. From what I've seen, joysticks have all sorts of numbers of buttons on them. But one of your messages says "press one of the three buttons on your joystick" or something like that. Were you under the impression that everyone had precisely three buttons on their joysticks?

Also on the subject of the alternative keys, you have to support the arrow keys. Not everyone here has their keyboard set to a layout where WASD makes sense. Gull probably has an AZERTY keyboard for example. And then there are people like me who simply have the mouse on the left and feel more natural using the arrow keys. Obviously anticipating every keyboard layout is impractical (and supporting custom controls may not always be time well spent in a SpeedHack), but a good compromise is to support both WASD and arrow keys; that's what I did.

Anyway, back to the game: I do like the idea of jumping around on platforms in 3D space. I first saw my brother doing it in Mario 64, but that game had other issues for me that stopped me playing it long enough to get to those bits. Thanks for giving me that experience in SpeedHack :)

Just one question: 'slug', not 'snail'? Here, slugs don't have shells. Is it a regional thing? Where are you from?

Mark Oates

Yea, one thing the game lacks is usability conveniences across the board, and it was a time-constraints thing. I had to make quick gameplay decisions to leave some features behind. The "polish" is what this game lacks:

  • Including arrow keys for navigation

  • Keyboard customization

  • Controller customization - "press BUTTON 1 on your joystick" instead of "press ITS_ONE_OF_THE_3_GAMEPLAY_BUTTONS on your joystick".

  • Shadow maps baked into the textures. Some of my early tests included shadow maps and the graphics looked quite stunning. I had to iterate so quickly during level design that I didn't have time to put that finishing touch, which, interestingly, makes the levels easier to navigate. Another problem I didn't realize until the end is that bigger textures have quite larger file sizes :-/.

  • The notification text when picking up an item would with a slight animation delay, all controls are halted, happy sound effect, then a note for a keypress.

  • The info box would animate text in on_enter(), and animate text out on_leave().

For those last two, I just did a keypress to dismiss the text - knowing it would cause problems like you mentioned.

Advising people that it's a joystick game but they can get by with the 'alternative' controls is a bit odd - you run the risk of making your players feel a bit second-class.

That's true. Both work equally, but the way I said it did make it seem like you mentioned.

Thank you for your feeeeeddabbbaaaachhhhkkk!

Quote:

Just one question: 'slug', not 'snail'? Here, slugs don't have shells. Is it a regional thing? Where are you from?

He was a snail and lost his shell, thus became a slug, and had to live a slug's life. :P

Arthur Kalliokoski

I bought a snail for racing, and he was too slow so I removed the shell, hoping it would make him faster. But it just made him sluggish... :-/

Bruce Perry

He was a snail and lost his shell, thus became a slug, and had to live a slug's life. :P

Perfect! I don't remember seeing that explanation in the in-game introduction ;)

Mark Oates

Oh it was there, you just had to... Uh... program it in. That's the Easter egg. ;D

Gideon Weems

But it just made him sluggish... :-/

:D

A Slug's Life is very impressive technically. I doubt many people could program something like this in three months, much less three days.

I like the story. This might sound silly, but I sympathize with that kind of situation. I think anyone can... In other words, I really wanted to get my shell back. Can you? I maxed out my level (36), but the ants in the Experience Room just kept coming. And I ate all of the bad guys in the bouncy level.

The bouncy level was my favorite, by the way, mainly because of the huge jump. That was fun. :)

Story time.

I forget who it was, but there was an Olympic long jumper who blew his first two chances in the qualifiers by stepping over the line. The judges called on him to make his third and final jump, but he couldn't move. The looming possibility of disqualification had paralyzed his legs.

His coach took him aside and said hey, with legs like yours, you've got room to spare. Let's draw a line in the sand half a foot in front of the real line. Pretend that's the real line. Now go jump.

The long jumper followed orders, taking off well before the starting line, and ended up qualifying. He even went on to win a medal.

Playing A Slug's Life reminded me of that story.

Thread #614297. Printed from Allegro.cc