#define PLAYER_IS_OK 1 #define PLAYER_IS_DEAD 2 #define PLAYER_HAS_WON 3 #include #include void update_player_animation(struct level_struct *cur_level_state) { // Cycle through the player's faces cur_level_state->player_face++; if (cur_level_state->player_face == 4) { cur_level_state->player_face = 0; } } // player_action moves the player // It returns one of the three PLAYER_ define values ( top of this file ) int player_action( signed int xdiff, signed int ydiff, struct level_struct *currentlevel ) { // no movent so we just update the animation if (xdiff == 0 && ydiff == 0) { update_player_animation(currentlevel); return PLAYER_IS_OK; } // Find the tile the player moved into/onto using the player's // current position int newx = currentlevel->px + xdiff; int newy = currentlevel->py + ydiff; update_player_animation(currentlevel); // Check to make sure these directions are not solid ( ie walls ) // and also to see if they are interactive ( ie buttons ) char targetsquare = currentlevel->squares[newx][newy]; if ( targetsquare == T_WALL || targetsquare == T_YELL_DOOR_UP || targetsquare == T_BLUE_DOOR_UP || targetsquare == T_RED_DOOR_UP ) // remember '||' means 'OR' { // Cannot move, so do nothing return PLAYER_IS_OK; } else { // Move onto the new square currentlevel->px = newx; currentlevel->py = newy; // Check to see if the new square is interactive ( ie a button ) switch ( targetsquare ) { case T_BLUE_BUTT_UP: // Blue button // Change the button to be depressed currentlevel->squares[newx][newy] = T_BLUE_BUTT_DOWN; // Change all blue doors in the level to be unlocked level_replace_tiles( currentlevel, T_BLUE_DOOR_UP, T_BLUE_DOOR_DOWN ); return PLAYER_IS_OK; case T_YELL_BUTT_UP: // Yellow button // Change the button to be depressed currentlevel->squares[newx][newy] = T_YELL_BUTT_DOWN; // Change all blue doors in the level to be unlocked level_replace_tiles( currentlevel, T_YELL_DOOR_UP, T_YELL_DOOR_DOWN ); return PLAYER_IS_OK; case T_EXIT: // User is on the exit return PLAYER_HAS_WON; case T_SPIKE: // The daft player walked onto spikes return PLAYER_IS_DEAD; default: // This has to be here to stop the compiler complaining // Basically the 'default' is run if no other case is run return PLAYER_IS_OK; } } // Code should never reach this point // The return line here is just to make the compiler happy return 0; } int play_level(SDL_Renderer* renderer, struct level_struct *currentlevel) { struct timespec last_render; last_render.tv_sec = 0; last_render.tv_nsec = 0; // Loop until the user completes the level int player_status = PLAYER_IS_OK; while ( player_status == PLAYER_IS_OK ) { struct timespec start, end; clock_gettime(CLOCK_MONOTONIC_RAW, &start); double since_last = (start.tv_sec - last_render.tv_sec) * 1000000 + (double)(start.tv_nsec - last_render.tv_nsec) / 1000; double ms_since_last = (double)since_last/(double)1000; printf("ms since last %f\n", ms_since_last); if (last_render.tv_nsec == 0 || ms_since_last >= 16.5) { printf("fps: %f\n", 1000 / ms_since_last); render(renderer, currentlevel); } else { float sleep_ms = 16.5 - ms_since_last; printf("not yet. Sleep for %f ms\n", sleep_ms); if (sleep_ms > 0) { usleep(sleep_ms * 1000); } continue; } clock_gettime(CLOCK_MONOTONIC_RAW, &end); uint64_t delta_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000; double ms = (double)delta_us/(double)1000; printf("time taken %f\n", ms); last_render = end; SDL_Event userinput; int got_event = SDL_PollEvent(&userinput); // Player movement signed int x = 0; signed int y = 0; if ( userinput.type == SDL_KEYDOWN ) { switch ( userinput.key.keysym.sym ) { case SDLK_UP: x = 0; y = -1; break; case SDLK_DOWN: x = 0; y = 1; break; case SDLK_RIGHT: x = 1; y = 0; break; case SDLK_LEFT: x = -1; y = 0; break; case SDLK_q: exit(0); // Quit the game break; case SDLK_h: // View the help screen help(renderer); x = 0; y = 0; default: // The user hits any other key // ( do nothing ) x = 0; y = 0; break; } } // Now update the player state player_status = player_action(x, y, currentlevel); // there was no event so there is nothing else to do if (!got_event) { continue; } // Move all of the echidnas run_all_echidnaAI_onestep( currentlevel ); // And make sure we open/close red doors if any // of them are stepping on red levers check_echidna_on_switches( currentlevel ); // Check to see if the player has run into an echidna if ( check_echidna_proximity( currentlevel ) == 1 ) player_status = PLAYER_IS_DEAD; // Show a losing screen if applicable if (player_status == PLAYER_IS_DEAD) render_a_losingscreen(renderer, currentlevel); } // The code running play_level needs to know if too advance // to the next level or just repeat the current one (if the // player has died) return player_status; } void game_loop(SDL_Renderer* renderer) { struct level_struct currentlevel; level_load_resources(renderer, ¤tlevel ); // load pixmaps // Initialise initial player face currentlevel.player_face = 2; int level_number; // Level's number in progression of game, also used as the filename to load the level from // Run through each of the levels for ( level_number = 2; level_number <= 10; level_number++ ) { int level_state = PLAYER_IS_OK; while ( level_state != PLAYER_HAS_WON ) { // Load the level level_load( level_number, ¤tlevel ); // Set the player's new position currentlevel.px = currentlevel.sx; currentlevel.py = currentlevel.sy; // Main game loop level_state = play_level(renderer, ¤tlevel); } // When the game loop ends, then the user has successfully completed the level, and we can move on } // The user has now completed all of the game // Show them the winning screen SDL_RenderClear(renderer); SDL_RenderCopy(renderer, currentlevel.winning_screen, NULL, NULL); SDL_RenderPresent(renderer); SDL_Delay(20 * 1000); // 20 * 1000msecs = 20 seconds // Once this function ends, we return to the menu }