/* * game.c * * Game book-keeping * * Copyright (c) 2008 Thomas White * * This file is part of Thrust3D - a silly game * * Thrust3D 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 3 of the License, or * (at your option) any later version. * * Thrust3D 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 Thrust3D. If not, see . * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "types.h" #include "model.h" #include "game.h" #include "render.h" #include "utils.h" #include "audio.h" #define MAX_OBJECTS 100 /* Maximum indicies of the rooms - remember they start at zero */ #define MAX_ROOM_X 1 #define MAX_ROOM_Y 2 #define MAX_ROOM_Z 4 static Room *room_new() { Room *r; r = malloc(sizeof(Room)); if ( r == NULL ) return NULL; r->objects = malloc(MAX_OBJECTS*sizeof(ModelInstance *)); r->num_objects = 0; r->num_connected = 0; r->num_lights = 0; return r; } static ModelInstance *room_add_object(Room *room, ModelContext *ctx, const char *name, GLfloat x, GLfloat y, GLfloat z, ObjectAttrib attribs, RenderContext *render) { if ( room->num_objects == MAX_OBJECTS ) return NULL; room->objects[room->num_objects] = model_instance_new(ctx, name, render); if ( room->objects[room->num_objects] != NULL ) { room->objects[room->num_objects]->x = x; room->objects[room->num_objects]->y = y; room->objects[room->num_objects]->z = z; room->objects[room->num_objects]->attribs = attribs; } room->num_objects++; return room->objects[room->num_objects-1]; } static Room *room_load(int rx, int ry, int rz, ModelContext *models, RenderContext *render) { Room *r; char tmp[64]; FILE *fh; snprintf(tmp, 63, "%s/rooms/%02i-%02i-%02i", DATADIR, rx, ry, rz); fh = fopen(tmp, "r"); if ( fh == NULL ) { fprintf(stderr, "Couldn't load room '%s'\n", tmp); return NULL; } r = room_new(); if ( r == NULL ) return NULL; r->rx = rx; r->ry = ry; r->rz = rz; r->comment = NULL; while ( !feof(fh) ) { int i; char line[1024]; GLfloat x, y, z; int rx, ry, rz; fgets(line, 1023, fh); if ( line[0] == '#' ) { continue; } for ( i=0; i= strlen(line) ) continue; line[i] = '\0'; /* "line" is now the model name */ for ( i=strlen(line)+1; iconnected[r->num_connected].rx = rx; r->connected[r->num_connected].ry = ry; r->connected[r->num_connected].rz = rz; r->num_connected++; } else if ( (strcmp(line, "light") == 0) && (sscanf(line+i, "%f %f %f", &x, &y, &z) == 3) ) { r->lights[r->num_lights].x = x; r->lights[r->num_lights].y = y; r->lights[r->num_lights].z = z; r->num_lights++; } else if ( sscanf(line+i, "%f %f %f", &x, &y, &z) == 3 ) { room_add_object(r, models, line, x, y, z, OBJ_NONE, render); } else if ( strcmp(line, "comment") == 0 ) { r->comment = strdup(line+i); } } fclose(fh); return r; } Room *game_find_room(Game *game, int rx, int ry, int rz) { int i; for ( i=0; inum_rooms; i++ ) { if ( ( game->rooms[i]->rx == rx ) && ( game->rooms[i]->ry == ry ) && ( game->rooms[i]->rz == rz ) ) { return game->rooms[i]; } } return NULL; } static void game_delete_room(Game *game, int idx) { int i; Room *room; room = game->rooms[idx]; for ( i=0; inum_objects; i++ ) { free(room->objects[i]); } free(room->objects); free(room->comment); free(room); /* Shift the list up one place */ for ( i=idx; inum_rooms-1; i++ ) { game->rooms[i] = game->rooms[i+1]; } game->num_rooms--; } static int room_can_be_seen(Game *game, int rx, int ry, int rz) { int crx, cry, crz; crx = game->cur_room_x; cry = game->cur_room_y; crz = game->cur_room_z; if ( (crx==0) && (cry==0) && (crz==0) && (rx==0) && (ry==2) && (rz==4) ) return 0; return 1; } static void game_load_all_connected(Room *room, Game *game) { int i; if ( room->checked_this_time ) return; room->checked_this_time = 1; room->needed_this_time = 1; for ( i=0; inum_connected; i++ ) { Room *con; int rx, ry, rz; rx = room->connected[i].rx; ry = room->connected[i].ry; rz = room->connected[i].rz; if ( !room_can_be_seen(game, rx, ry, rz) ) continue; con = game_find_room(game, rx, ry, rz); if ( con == NULL ) { if ( game->debug ) printf("GM: Loading %2i %2i %2i\n", rx, ry, rz); con = room_load(rx, ry, rz, game->models, game->render); con->checked_this_time = 0; /* Add the new room to the list */ game->rooms[game->num_rooms] = con; game->num_rooms++; } /* Recurse */ game_load_all_connected(con, game); } } /* Load the current room and all rooms causally connected */ static void game_load_all_relevant(Game *game) { int i; Room *room; /* Go down the current list of rooms, setting 'needed_this_time' to 0 */ for ( i=0; inum_rooms; i++ ) { game->rooms[i]->needed_this_time = 0; game->rooms[i]->checked_this_time = 0; } /* Is the current room in the list? Load it if not */ if ( game->debug ) printf("GM: Current: %2i %2i %2i\n", game->cur_room_x, game->cur_room_y, game->cur_room_z); room = game_find_room(game, game->cur_room_x, game->cur_room_y, game->cur_room_z); if ( room == NULL ) { room = room_load(game->cur_room_x, game->cur_room_y, game->cur_room_z, game->models, game->render); if ( room == NULL ) { /* This room couldn't be loaded. */ if ( game->debug ) printf("GM: Couldn't load the current room. Giving up.\n"); return; } room->checked_this_time = 0; game->rooms[game->num_rooms] = room; game->num_rooms++; } game_load_all_connected(room, game); /* Remove any rooms left in the list which are no longer needed */ for ( i=0; inum_rooms; i++ ) { if ( !game->rooms[i]->needed_this_time ) { if ( game->debug ) printf("GM: %2i %2i %2i is no longer needed\n", game->rooms[i]->rx, game->rooms[i]->ry, game->rooms[i]->rz); game_delete_room(game, i); } } } /* Create a new "game" structure */ Game *game_new(int width, int height, GameOptions gameopts) { Game *g; g = malloc(sizeof(Game)); if ( g == NULL ) return NULL; g->debug = gameopts.game_debug; g->thrusting = 0; g->turn_left = 0; g->turn_right = 0; g->forward = 0; g->reverse = 0; g->tlast = 0; g->cur_room_x = 0; g->cur_room_y = 2; g->cur_room_z = 4; g->num_rooms = 0; g->view_angle = deg2rad(+20.0); g->view_yaw = 0.0; g->view_dist = 5.0; g->paused = 0; g->pause_rel = 1; g->frames = 0; g->fps = 0; g->query_this_frame = 1; g->time_render = 30000; g->time_physics = 0; g->fuel = 1.0; g->radiation = 0.1; g->platform_rel_x = 0.0; g->platform_rel_y = 0.0; g->time_of_landing_event = -1500.0; /* Force the platform recharge ripple to be 'bright' */ /* Renderer setup */ g->render = render_setup(width, height, gameopts.disable_vbos, gameopts.disable_fbos, gameopts.disable_shaders); if ( g->render == NULL ) { fprintf(stderr, "Couldn't initialise renderer\n"); free(g); return NULL; } /* Note: render_setup() initialises GLEW, which must be done before loading models. */ /* Audio setup */ g->audio = audio_setup(gameopts.audio_debug, gameopts.no_music); if ( g->audio == NULL ) { fprintf(stderr, "Couldn't initialise audio\n"); } /* Load models */ g->models = model_init(); if ( g->models == NULL ) { fprintf(stderr, "Couldn't create model context\n"); render_shutdown(g->render); free(g); return NULL; } game_load_all_relevant(g); /* Initialise the craft */ g->lander = model_instance_new(g->models, "lander", g->render); g->lander->x = 0.0; g->lander->y = 0.0; g->lander->z = -4.93; g->lander->landed = 1; g->lander->recharging = 1; g->lander->yaw = deg2rad(210.0); g->lander->attribs = OBJ_GRAVITY; return g; } void game_shutdown(Game *game) { render_shutdown(game->render); audio_shutdown(game->audio); free(game); } /* Check if the player needs to be moved to another room */ void game_check_handoff(Game *game) { /* x-axis handoff */ if ( game->lander->x > 5.0 ) { if ( game->cur_room_x >= MAX_ROOM_X ) { game->lander->x = 5.0; game->lander->vx = 0.0; } else { game->lander->x -= 10.0; game->cur_room_x += 1; game_load_all_relevant(game); } } if ( game->lander->x < -5.0 ) { if ( game->cur_room_x <= 0 ) { game->lander->x = -5.0; game->lander->vx = 0.0; } else { game->lander->x += 10.0; game->cur_room_x -= 1; game_load_all_relevant(game); } } /* y-axis handoff */ if ( game->lander->y > 5.0 ) { if ( game->cur_room_y >= MAX_ROOM_Y ) { game->lander->y = 5.0; game->lander->vy = 0.0; } else { game->lander->y -= 10.0; game->cur_room_y += 1; game_load_all_relevant(game); } } if ( game->lander->y < -5.0 ) { if ( game->cur_room_y <= 0 ) { game->lander->y = -5.0; game->lander->vy = 0.0; } else { game->lander->y += 10.0; game->cur_room_y -= 1; game_load_all_relevant(game); } } /* z-axis handoff */ if ( game->lander->z > 5.0 ) { if ( game->cur_room_z >= MAX_ROOM_Z ) { game->lander->z = 5.0; game->lander->vz = 0.0; } else { game->lander->z -= 10.0; game->cur_room_z += 1; game_load_all_relevant(game); } } if ( game->lander->z < -5.0 ) { if ( game->cur_room_z <= 0 ) { game->lander->z = -5.0; game->lander->vz = 0.0; } else { game->lander->z += 10.0; game->cur_room_z -= 1; game_load_all_relevant(game); } } } void game_pause(Game *game) { if ( game->pause_rel == 0 ) return; if ( game->paused ) { game->paused = 0; audio_unpause(game->audio); game->pause_rel = 0; } else { game->paused = 1; audio_pause(game->audio); game->pause_rel = 0; } }