/*
spacemaze - Fly a space ship through a maze and collect all the items.
Copyright (C) 2000, 2001, 2002 John Ericson
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2000-03-25 High / John Ericson john.ericson@home.se
*/
#include <stdlib.h>
#include <string.h>
#include <SDL.h>
#include <SDL_image.h>
#include "bool.h"
#include "sprite.h"
#include "campaign.h"
#include "level.h"
#include "defines.h"
#include "getfile.h"
#include "loadimg.h"
#include "gfx.h"
#include "fpstimer.h"
#include "BFont.h"
#ifdef _WIN32
#include "getopt.h"
#else /* UNIX */
#include <getopt.h>
#endif
void LoadGraphics(void);
/* Different default places to search for levels */
const char *levels_dirs[6] = {
"./",
"./levels/",
"/usr/share/games/spacemaze/levels/",
"/usr/local/share/games/spacemaze/levels/",
"/opt/spacemaze/levels/",
NULL
};
const char *campaigns_dirs[6] = {
"./",
"./campaigns/",
NULL
};
const char *gfx_dirs[3] = {
"./",
"./gfx/",
NULL
};
/* Video buffer */
SDL_Surface *screen;
Uint32 videoflags = SDL_SWSURFACE|SDL_HWPALETTE;
/*
SDL_Surface *image_spaceship,
*image_box;
*/
/* Structure to cache images */
typedef struct {
int used;
SDL_Surface *image;
} image_s;
image_s images[MAXIMAGES];
struct struct_ship {
float fx, /* Position */
fy;
int x,
y;
int w, /* Size */
h;
float oldx, /* Old position */
oldy;
float vx, /* Velocity */
vy;
float gasx, /* Gas */
gasy;
bool dead;
bool notmoving;
int color;
sprite sprite;
} ship;
static level_s level;
static campaign_s campaign;
unsigned int currentlevel = 0;
int borderwidth = 1;
//struct struct_drawrect drawrect[MAXDRAWRECT];
/* Draw level.
If level piece is bigger than the screen, no drawing will be made.
*/
void drawlevel(void) {
int i;
SDL_Rect rect;
for (i = 0; i <= level.size; i++) {
if (level.landscape[i].visible) {
rect.x = level.landscape[i].x;
rect.y = level.landscape[i].y;
rect.w = level.landscape[i].w;
rect.h = level.landscape[i].h;
SDL_FillRect(screen, &rect, level.landscape[i].color);
inputdrawrect(&rect);
}
}
}
void drawship(void)
{ drawsprite(&ship.sprite); }
void eraseship(void)
{ erasesprite(&ship.sprite); }
/*
void drawbox(int n)
{ drawsprite(&level.item[n].sprite); }
*/
void erasebox(int n) {
erasesprite(&level.item[n].sprite); }
/*
void drawboxes(void) {
int i;
for (i = 0; i <= level.numitems; i++)
if (level.item[i].type == LEVEL_OBJ_BOX)
if (level.item[i].visible)
drawbox(i);
}
*/
/* Check if two rectangular objects intersect.
For optimal collision detection, the smallest object
should be 1 and the larger 2.
*/
int detectcollision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) {
if ((x1 >= x2 && x1 < (x2 + w2)) ||
((x1 + w1) >= x2 && (x1 + w1) < (x2 + w2)))
if ((y1 >= y2 && y1 < (y2 + h2)) ||
((y1 + h1) >= y2 && (y1 + h1) < (y2 + h2)))
return 1;
return 0;
}
/* Load level */
void loadlevel(const char *file, level_s *level) {
char fullfile[512];
SDL_Color colors[256];
/* Check if player has played through the whole campaign */
if (strcmp(file, "") == 0) {
printf("You have played through the complete game now\n");
exit(0);
}
if (strcmp(file, "") != 0) {
/* Look through directories for the file to load */
if (!getfile(levels_dirs, file, fullfile)) {
fprintf(stderr, "Error couldn't find %s\n", file);
exit(1);
} else {
printf("%s exists\n", fullfile);
}
/* Load level */
if (LoadLevel(level, fullfile) != 0) {
fprintf(stderr, "Error parsing XML file %s\n", fullfile);
exit(1);
}
/* Check level version */
if (!strcmp(level->version, "1.0") == 0) {
fprintf(stderr, "Error unknown level version. This game can run version 1.0 levels\n");
exit(1);
}
printf("Loaded level %s name %s.\n", file, level->name);
} else {
fprintf(stderr, "Error no map to open, please use -m\n");
exit(1);
}
/* If the screen resolution differs set new videomode */
if (screen == NULL ||
level->sizew != screen->w ||
level->sizeh != screen->h) {
/* Set videomode */
if ((screen = SDL_SetVideoMode(level->sizew, level->sizeh,
8, videoflags)) == NULL) {
fprintf(stderr, "Couldn't initialize video mode: %s\n", SDL_GetError());
exit(1);
}
/* Report graphics settings */
printf("Set %dx%dx%d video mode\n",
screen->w, screen->h, screen->format->BitsPerPixel);
/***************/
/* Set palette */
/***************/
/* Hopefully the loading of pictures wont mess this palette up */
colors[0].r = colors[0].g = colors[0].b = 0;
colors[1].r = 200;colors[1].g = 0; colors[1].b = 0;
colors[2].r = 0; colors[2].g = 0; colors[2].b = 150;
colors[3].r = 200;colors[3].g = 200;colors[3].b = 0;
colors[4].r = 0; colors[4].g = 200;colors[4].b = 0;
SDL_SetColors(screen, colors, 0, 5); /* Sets a portion of the colormap
for the given 8-bit surface.
Last digit is how many colours to set.
*/
}
}
/* Start level */
void initlevel() {
SDL_Rect trect;
char caption[255] = CAPTION;
/* Set title and icon for the window */
strcat(caption, " - ");
strcat(caption, level.name);
SDL_WM_SetCaption(caption, NULL);
/* Initialize ship */
//ship.x = &ship.sprite.x;
//ship.y = &ship.sprite.y;
// TODO When I assign these to xf? It crapes out the value in h
// These should be assigned to fx.
ship.x = level.playerstart[level.numstartpos].x;
ship.y = level.playerstart[level.numstartpos].y;
ship.fx = (float)ship.x;
ship.fy = (float)ship.y;
ship.sprite.x = ship.x;
ship.sprite.y = ship.y;
ship.w = ship.sprite.image->w;
ship.h = ship.sprite.image->h;
ship.oldx = 100;
ship.oldy = 100;
ship.vx = 0;
ship.vy = 0;
ship.gasx = (float)0.1;
ship.gasy = (float)0.1;
ship.dead = false;
ship.notmoving = true;
//ship.color = 1;
/* Clear all stacked drawings */
cleardrawrect();
/* Draw borders */
// Draw boarder around the screen
trect.x = 0;
trect.y = 0;
trect.w = screen->w;
trect.h = screen->h;
SDL_FillRect(screen, &trect, 4);
trect.x = borderwidth;
trect.y = borderwidth;
trect.w = screen->w - 1 - borderwidth;
trect.h = screen->h - 1 - borderwidth;
SDL_FillRect(screen, &trect, 0);
LoadGraphics();
drawlevel();
drawship();
/* Locks the screen */
/*if (SDL_MUSTLOCK(screen))
SDL_LockSurface(screen);
*/
//SDL_UpdateRect(screen, 0, 0, 0, 0);
/* Unlocks the screen */
/*if (SDL_MUSTLOCK(screen))
SDL_UnlockSurface(screen);
*/
/* Redraw */
updatescreen();
}
/* Check if the ship picks up any boxes
*/
void shipgrabboxes(void) {
int i, i2, i3;
SDL_Rect rect;
bool alltaken = true;
/* Look thru items */
for (i = 0;i <= level.numitems;i++)
/* If ship collides with an existing box */
if (level.item[i].type == LEVEL_OBJ_BOX && level.item[i].visible)
if (detectcollision(level.item[i].x, level.item[i].y,
level.item[i].sprite.image->w, level.item[i].sprite.image->h,
ship.x, ship.y, ship.w, ship.h)) {
printf("Got box number %d\n", i);
level.item[i].visible = false;
/* If something should be activated */
if (level.item[i].activate)
/* Look thru landscape */
for (i2 = 0; i2 <= level.size; i2++)
if (level.landscape[i2].activate == level.item[i].activate) {
/* Change visible and crashable */
if (level.landscape[i2].visible)
level.landscape[i2].visible = false;
else
level.landscape[i2].visible = true;
if (level.landscape[i2].crashable)
level.landscape[i2].crashable = false;
else
level.landscape[i2].crashable = true;
/* If landscape is changed to unvisible redraw that area */
if (!level.landscape[i2].visible) {
level.landscape[i2].visible = false;
rect.x = level.landscape[i2].x;
rect.y = level.landscape[i2].y;
rect.w = level.landscape[i2].w;
rect.h = level.landscape[i2].h;
/* Erase landscape */
SDL_FillRect(screen, &rect, 0);
inputdrawrect(&rect);
}
}
/* Look if all items are taken */
for (i3 = 0; i3 <= level.numitems; i3++)
if (level.item[i3].type == LEVEL_OBJ_BOX)
if (level.item[i3].visible)
alltaken = false;
erasebox(i); /* Erase the picked up box */
drawlevel(); /* Redraw level */
/* All items are taken */
if (alltaken) {
printf("Congratulations! You have picked up all the boxes\n");
loadlevel(campaign.levels[currentlevel++].filename, &level);
initlevel();
}
}
}
/* See if the ship collides with any wall
*/
int shipcrash(void) {
int i;
for (i = 0; i <= level.size; i++)
if (level.landscape[i].crashable)
if (detectcollision(ship.x, ship.y, ship.w, ship.h,
level.landscape[i].x, level.landscape[i].y,
level.landscape[i].w, level.landscape[i].h)) {
printf("Ship crashed into a wall\n");
ship.dead = true;
return 1 ;
}
return 0;
}
void ImageCacheInit(image_s *images) {
int i;
for (i = 0; i < MAXIMAGES; i++, *images++) {
images->used = 0;
}
}
SDL_Surface *loadimgdata(const char *file, bool transparent) {
SDL_Surface *rtnsurface;
char fullfile[512];
if (!getfile(gfx_dirs, file, fullfile)) {
fprintf(stderr, "Error couldn't find %s\n", file);
exit(1);
} else {
printf("%s exists\n", fullfile);
}
/* Load graphics */
rtnsurface = loadimage(fullfile, transparent);
if (rtnsurface == NULL) {
fprintf(stderr, "Error couldn't load %s\n", fullfile);
exit(1);
}
return rtnsurface;
}
void LoadGraphics(void) {
int i;
// TODO level.numitems is wrong. It starts at 1, it should start at 0
for (i = 0;i <= level.numitems; i++) {
switch (level.item[i].type) {
case LEVEL_OBJ_BOX:
if (images[LEVEL_OBJ_BOX].used == 0) {
images[LEVEL_OBJ_BOX].image = loadimgdata("box.png", false);
if (images[LEVEL_OBJ_BOX].image != NULL)
printf("Loaded image box.png\n");
} else
printf("Reused image box.png\n");
/* Assign image */
level.item[i].sprite.image = images[level.item[i].type].image;
break;
case LEVEL_OBJ_LANDINGPLATE:
if (images[LEVEL_OBJ_LANDINGPLATE].used == 0) {
images[LEVEL_OBJ_LANDINGPLATE].image = loadimgdata("landingplate.png", false);
if (images[LEVEL_OBJ_LANDINGPLATE].image != NULL)
printf("Loaded image landingplate.png\n");
} else
printf("Reused image landingplate.png\n");
/* Assign image */
level.item[i].sprite.image = images[level.item[i].type].image;
/* Transform the coordinates so they are middle lower placed */
level.item[i].x -= (int)(level.item[i].sprite.image->w / 2);
level.item[i].y -= level.item[i].sprite.image->h - 1;
break;
}
/* Increase the usage to show how much this file is needed */
if (images[level.item[i].type].used != -1)
images[level.item[i].type].used++;
/* Assign cordinates to sprite */
level.item[i].sprite.x = level.item[i].x;
level.item[i].sprite.y = level.item[i].y;
}
}
/* Draw text on the screen
*/
void DrawText(unsigned int x, unsigned int y, char *text, ...) {
SDL_Rect rect;
PrintString(screen, x, y, text);
rect.x = x;
rect.y = y;
rect.w = TextWidth(text);
rect.h = FontHeight(GetCurrentFont());
inputdrawrect(&rect);
}
void drawlandscapeobjects(void) {
int i;
for (i = 0;i <= level.numitems; i++) {
switch (level.item[i].type) {
case LEVEL_OBJ_BOX:
if (!level.item[i].visible) break;
case LEVEL_OBJ_LANDINGPLATE:
drawsprite(&level.item[i].sprite);
break;
}
}
}
/* Show how to use the program
*/
void usage(void) {
printf("spacemaze 0.9 Copyright (C) 2000 John Ericson john.ericson@home.se\n");
printf("Usage: spacemaze [OPTIONS]...\n");
printf("\n");
printf(" -h display this help\n");
printf(" -f use fullscreen\n");
printf(" -m .. map to open\n");
printf(" -c display mouse cursor inside application window\n");
}
/* Main program
*/
int main(int argc, char *argv[]) {
SDL_Event event;
Uint8 *keys;
//int key;
//int numkeys;
bool done;
int i;
//int readx,
// ready;
int c;
char leveltoload[255] = DEFAULTLEVEL;
char campaigntoload[255] = DEFAULTCAMPAIGN;
int drawcursor = 0;
char file[512]; /* Stores complete path */
BFont_Info *Font1=NULL;
/**************************************/
/* Process the command line arguments */
/**************************************/
while (1) {
c = getopt(argc, argv, "fm:hck");
/* Missing argument on an option that requires a argument */
if (c == '?') exit(1);
/* End of command line */
if (c == EOF) break;
switch(c) {
case 'f':
videoflags |= SDL_FULLSCREEN;
break;
case 'm':
strcpy(leveltoload, optarg);
break;
case 'h':
usage();
exit(1);
break;
case 'c':
drawcursor = 1;
break;
case 'k':
strcpy(campaigntoload, optarg);
break;
default:
fprintf(stderr, "Error getopt returned character code 0%o\n", c);
}
}
/***********************/
/* Initialize hardware */
/***********************/
/* Initialize SDL */
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
exit(1);
}
/* Initialize graphich system */
initdrawrect(MAXDRAWRECT);
cleardrawrect();
/***************/
/* Load levels */
/***************/
/* Check if used has specified a specific level */
if (strcmp(leveltoload, "") == 0) {
/* Load a campaign */
/* Initialize campaign */
if (strcmp(campaigntoload, "") != 0) {
/* Look through directories for the file to load */
if (!getfile(campaigns_dirs, campaigntoload, file)) {
fprintf(stderr, "Error couldn't find campaign %s\n", campaigntoload);
exit(1);
} else {
printf("%s exists\n", file);
}
/* Load campaign */
if (LoadCampaign(&campaign, file) != 0) {
fprintf(stderr, "Error parsing XML file %s\n", file);
exit(1);
}
/* Check campaign version */
if (!strcmp(campaign.version, "1.0") == 0) {
fprintf(stderr, "Error unknown campaign version. This game can run version 1.0 campaigns\n");
exit(1);
}
printf("Loaded campaign %s name %s.\n", campaigntoload, campaign.name);
} else {
fprintf(stderr, "Error no campaign to open, please use -k\n");
exit(1);
}
} else {
/* Load a level */
/* Setup a dummy campaign for our custom level */
strcpy(campaign.name, "custom level");
campaign.numlevels = 1;
strcpy(campaign.levels->filename, leveltoload);
}
/* Initialize level */
loadlevel(campaign.levels[currentlevel++].filename, &level);
/* Initialize image cache */
ImageCacheInit((image_s *)&images);
/*****************/
/* Load graphics */
/*****************/
/* Ship */
images[OBJ_SPACESHIP].image = loadimgdata("spaceship.png", false);
images[OBJ_SPACESHIP].used = -1;
ship.sprite.image = images[OBJ_SPACESHIP].image;
//LoadGraphics();
/**************/
/* Load Fonts */
/**************/
/* Look through directories for the level to load */
if (!getfile(gfx_dirs, "topaz.png", file)) {
fprintf(stderr, "Error couldn't find font topaz.png\n");
exit(1);
} else {
printf("%s exists\n", file);
}
/* Load Font */
Font1 = LoadFont(file);
if (Font1 == NULL) {
fprintf(stderr, "Couldnt load %s\n", file);
exit(1);
}
/* Set the default font */
SetCurrentFont(Font1);
/*************************/
/* Misc program settings */
/*************************/
SDL_ShowCursor(drawcursor); /* Don't draw mouse cursor inside application window */
/************************************/
/* Set keyboard delays for gameplay */
/************************************/
/* Set keyboard delays.
Delay for when to start sending repeating KEYDOWN's
and the interval between them */
SDL_EnableKeyRepeat(1000, 200);
keys = SDL_GetKeyState(NULL);
/*******************/
/* The game begins */
/*******************/
initlevel();
/* Main loop */
done = false;
while (!done) {
/* Polls for currently pending events */
if (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
keys = SDL_GetKeyState(NULL);
break;
case SDL_QUIT: /* Quit request */
printf("Quit requested, quitting.\n");
exit(0);
break;
}
}
if (keys[SDLK_ESCAPE] == SDL_PRESSED) {
printf("Quit requested, quitting.\n");
exit(0);
}
if (keys[SDLK_UP] == SDL_PRESSED) {
ship.vy = ship.vy - ship.gasy;
ship.notmoving = false;
}
if (keys[SDLK_DOWN] == SDL_PRESSED) {
ship.vy = ship.vy + ship.gasy;
ship.notmoving = false;
}
if (keys[SDLK_LEFT] == SDL_PRESSED) {
ship.vx = ship.vx - ship.gasx;
ship.notmoving = false;
}
if (keys[SDLK_RIGHT] == SDL_PRESSED) {
ship.vx = ship.vx + ship.gasx;
ship.notmoving = false;
}
waitframe();
if (ship.dead)
ship.vx = ship.vy = 0;
/* Save old positions */
ship.oldx = (float)ship.x;
ship.oldy = (float)ship.y;
/* Locks the screen */
if (SDL_MUSTLOCK(screen))
if (SDL_LockSurface(screen) < 0)
continue;
/*
IMPORTANT!
No operating system or library calls should be made here
since critical system locks may be held during this time.
*/
/* Move the ship */
if (!ship.notmoving) {
eraseship();
ship.fx += ship.vx;
ship.fy += ship.vy;
ship.x = (int)ship.fx;
ship.y = (int)ship.fy;
/* Add gravity */
ship.vy += level.gravity;
/* Look if the spaceship hit any wall */
if ((ship.x + (int)ship.w) >= (screen->w - borderwidth)) {
ship.vx = ship.vx * -1;
ship.fx = (float)(screen->w - borderwidth - (int)ship.w);
} else if (ship.x <= (borderwidth)) {
ship.vx = ship.vx * -1;
ship.fx = (float)borderwidth + 1;
}
if ((ship.y + (int)ship.h) >= (screen->h - borderwidth)) {
ship.vy = ship.vy * -1;
ship.fy = (float)(screen->h - borderwidth - (int)ship.h);
} else if (ship.y <= (borderwidth)) {
ship.vy = ship.vy * -1;
ship.fy = (float)borderwidth + 1;
}
ship.x = (int)ship.fx;
ship.y = (int)ship.fy;
ship.sprite.x = ship.x;
ship.sprite.y = ship.y;
/* Look if the spaceship has landed on any landingplate */
for (i = 0;i <= level.numitems; i++) {
if (level.item[i].type == LEVEL_OBJ_LANDINGPLATE) {
if (detectcollision(ship.x, ship.y+ship.h, 0, 0,
level.item[i].x, level.item[i].y,
level.item[i].sprite.image->w, level.item[i].sprite.image->h) &&
detectcollision(ship.x+ship.w, ship.y+ship.h, 0, 0,
level.item[i].x, level.item[i].y,
level.item[i].sprite.image->w, level.item[i].sprite.image->h)) {
/* Ship landed successfully */
ship.notmoving = true;
ship.vx = 0;
ship.vy = 0;
ship.fy = (float)level.item[i].y - ship.h;
DrawText(10, 150, "You have landed");
}
}
}
ship.x = (int)ship.fx;
ship.y = (int)ship.fy;
ship.sprite.x = ship.x;
ship.sprite.y = ship.y;
if (shipcrash())
done = true;
shipgrabboxes();
drawship();
}
/* Dont let the ship get out of the screen */
/*
if (ship.x < 0) ship.x = 0;
if (ship.y < 0) ship.y = 0;
if (ship.x > (screen->w - ship.w)) ship.x = (screen->w - ship.w);
if (ship.y > (screen->h - ship.h)) ship.y = (screen->h - ship.h);
*/
//drawboxes();
drawlandscapeobjects();
//DrawText(10, 10, "This is a test");
/* Unlocks the screen */
if (SDL_MUSTLOCK(screen))
SDL_UnlockSurface(screen);
/* Redraw */
updatescreen();
} /* while */
/*******************/
/* Clear things up */
/*******************/
FreeFont(Font1);
/********/
/* Quit */
/********/
return 0; /* Success */
}