Implemented gun and jetpack functionality. Player tile frame changes depending on facing dir and jump/jetpack state. Skip drawing tile 0, instead drawing fullscreen black BG rect.

This commit is contained in:
slipyx 2018-07-20 01:40:51 -04:00
parent e91c1f170d
commit 40dd8023d5
6 changed files with 190 additions and 34 deletions

68
game.c
View File

@ -22,6 +22,7 @@ static void G_InitAssets( SDL_Renderer* r ) {
Util_LoadLevels();
g_assets = malloc( sizeof(game_assets_t) );
memset( g_assets, 0, sizeof(game_assets_t) );
// copy all levels loaded from exe by util
for ( int i = 0; i < NUM_EXE_LEVELS; ++i )
@ -41,6 +42,10 @@ static void G_InitAssets( SDL_Renderer* r ) {
// tile surfaces should be converted as textures inside g_assets now
Util_FreeTileSurfaces();
// sfx
//g_assets->sfx[0] = S_LoadChunk( "res/jumpd8.wav" ); //Mix_LoadWAV_RW( rw, 1 );
//g_assets->sfx[1] = S_LoadChunk( "res/pickupd8.wav" ); //Mix_LoadWAV_RW( rw, 1 );
}
// poll input
@ -61,6 +66,10 @@ static void G_CheckInput() {
if ( key == SDLK_AC_BACK ) { gs->quit = 1; }
// jump event
if ( key == SDLK_UP || key == SDLK_z ) { gs->ps.try_jump = 1; }
// fire
if ( key == SDLK_LCTRL || key == SDLK_x ) { gs->ps.try_fire = 1; }
// jetpack
if ( key == SDLK_LALT || key == SDLK_c ) { gs->ps.try_jetpack = 1; }
// level select
if ( key >= SDLK_0 && key <= SDLK_9 ) {
gs->current_level = key - SDLK_0;
@ -78,7 +87,8 @@ static void G_CheckInput() {
// attempt dave movement by setting try_ flags
if ( keys[SDL_SCANCODE_RIGHT] ) gs->ps.try_right = 1;
if ( keys[SDL_SCANCODE_LEFT] ) gs->ps.try_left = 1;
//if ( keys[SDL_SCANCODE_UP] ) gs->ps.try_jump = 1;
if ( keys[SDL_SCANCODE_DOWN] ) gs->ps.try_down = 1;
if ( keys[SDL_SCANCODE_UP] ) gs->ps.try_up = 1;
}
// clear all try input flags at end of frame
@ -98,6 +108,8 @@ static void G_Update() {
P_UpdateCollision();
// pickups
P_PickupItem();
// player bullet
P_UpdateBullet();
// verify input try flags
P_VerifyInput();
// apply player movement
@ -114,7 +126,9 @@ static void G_Update() {
// draw level at current view
void Draw_World( SDL_Renderer* r ) {
SDL_Rect dst;
SDL_Rect dst = {0,0,320,200};
// solid BG fill
SDL_RenderCopy( r, g_assets->tile_tx[0], NULL, &dst );
// draw level view 20x10 tiles at 16x16px
for ( int j = 0; j < 10; ++j ) {
dst.y = j * TILE_SIZE;
@ -122,6 +136,7 @@ void Draw_World( SDL_Renderer* r ) {
for ( int i = 0; i < 20; ++i ) {
dst.x = i * TILE_SIZE;
uint8_t til = gs->levels[gs->current_level].tiles[j * 100 + gs->view_x + i];
if ( til == 0 ) continue;
SDL_RenderCopy( r, g_assets->tile_tx[til], NULL, &dst );
}
}
@ -133,19 +148,46 @@ void Draw_Player( SDL_Renderer* r ) {
// relative to view
dst.x = gs->ps.px - gs->view_x * TILE_SIZE;
dst.y = gs->ps.py;
// tile 56 neutral; 20x16px
uint8_t til = 53;
// tile 56 neutral, 53 right, 57 left 20x16px
uint8_t til = 56;
dst.w = 20; dst.h = 16;
// jetpack tile
if ( gs->ps.do_jetpack )
til = gs->ps.last_dir >= 0 ? 77 : 80;
// grounded walk tile
else if ( gs->ps.on_ground ) {
til = gs->ps.last_dir >= 0 ? 53 : 57;
}
// jump tile
else if ( gs->ps.do_jump || !gs->ps.on_ground )
til = gs->ps.last_dir >= 0 ? 67 : 68;
// render
// grounded debug
if ( gs->ps.on_ground ) {
SDL_SetRenderDrawColor( r, 255, 0, 255, 255 );
SDL_RenderDrawRect( r, &dst );
SDL_RenderDrawLine( r, dst.x, dst.y+dst.h, dst.x+dst.w, dst.y+dst.h );
}
SDL_RenderCopy( r, g_assets->tile_tx[til], NULL, &dst );
}
// draw player bullet
void Draw_Bullet( SDL_Renderer* r ) {
SDL_Rect dst;
if ( gs->ps.bullet_px && gs->ps.bullet_py ) {
// relative to view
dst.x = gs->ps.bullet_px - gs->view_x * TILE_SIZE;
dst.y = gs->ps.bullet_py;
// tile 127 right, 128 left
uint8_t til = gs->ps.bullet_dir > 0 ? 127 : 128;
dst.w = 12; dst.h = 3;
// render
SDL_RenderCopy( r, g_assets->tile_tx[til], NULL, &dst );
}
}
// main drawing routine
static void G_Draw( SDL_Renderer* r ) {
// clear backbuffer
@ -154,6 +196,7 @@ static void G_Draw( SDL_Renderer* r ) {
Draw_World( r );
Draw_Player( r );
Draw_Bullet( r );
// flip buffers
SDL_RenderPresent( r );
@ -173,8 +216,12 @@ static void emloop( void* p ) {
int main( int argc, char** argv ) {
// initialize SDL
if ( SDL_Init( SDL_INIT_VIDEO ) )
if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO ) )
SDL_Log( "SDL Init error: %s\n", SDL_GetError() );
// mixer
//int res = Mix_OpenAudio( 22050, MIX_DEFAULT_FORMAT, 2, 1024 );
//if ( res < 0 ) fprintf( stderr, "Mix_OpenAudio error: %s\n", Mix_GetError() );
//Mix_AllocateChannels( 8 );
// create window and renderer
int winw = 1280, winh = 720;
@ -212,10 +259,13 @@ int main( int argc, char** argv ) {
// set state for first level
W_StartLevel();
// start bg music
//Mix_PlayMusic( g_assets->mus, -1 );
// main loop
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop_arg( emloop, renderer, 1000 / FRAME_DELAY, 1 );
//emscripten_set_main_loop_timing( EM_TIMING_RAF, 2 ); // vsync/2
#else
while ( gs->quit == 0 ) {
// fixed timestep
@ -235,6 +285,11 @@ int main( int argc, char** argv ) {
SDL_DestroyTexture( g_assets->tile_tx[i] );
free( g_assets );
free( gs );
// free audio
//for ( int i = 0; i < sizeof(g_assets->sfx) / sizeof(g_assets->sfx[0]) )
//Mix_FreeChunk( g_assets->sfx[i] );
//Mix_FreeMusic( g_assets->mus );
//Mix_CloseAudio();
// cleanup SDL
SDL_DestroyRenderer( renderer );
@ -248,3 +303,4 @@ int main( int argc, char** argv ) {
return EXIT_SUCCESS;
}

9
game.h
View File

@ -2,6 +2,7 @@
#define _LMDAVE_H
#include <SDL.h>
//#include <SDL/SDL_mixer.h>
// for dealing with resources in EXE
#include "util/util.h"
@ -19,6 +20,10 @@ typedef struct {
// player state
player_state_t ps;
// enemy bullet
uint16_t ebullet_px, ebullet_py;
int8_t ebullet_dir;
level_t levels[NUM_EXE_LEVELS]; // copied from exe util's GetLevel
} game_state_t;
@ -26,6 +31,10 @@ typedef struct {
typedef struct {
// tiles as textures converted from util's tile surfaces
SDL_Texture* tile_tx[NUM_EXE_TILES];
// sfx
//Mix_Chunk* sfx[2];
// music
//Mix_Music* mus;
} game_assets_t;
// level tile size in pixels

104
player.c
View File

@ -1,7 +1,12 @@
#include "game.h"
#include "world.h"
// sound channels
#define S_CHAN_JUMP 0
#define S_CHAN_PICKUP 1
extern game_state_t* gs;
extern game_assets_t* g_assets;
// sets player position to current level's player start
void P_Spawn() {
@ -14,6 +19,13 @@ void P_Spawn() {
gs->ps.jump_timer = 0;
gs->ps.on_ground = 0;
gs->ps.do_jump = 0;
// reset direction and bullet
gs->ps.last_dir = 0;
gs->ps.bullet_px = 0;
gs->ps.bullet_py = 0;
gs->ps.do_fire = 0;
gs->ps.do_jetpack = 0;
gs->ps.do_up = 0; gs->ps.do_down = 0;
// hardcoded player starts
switch ( gs->current_level ) {
@ -51,27 +63,46 @@ void P_PickupItem() {
gs->levels[gs->current_level].tiles[ty * 100 + tx] = 0;
gs->ps.check_pickup_x = 0;
gs->ps.check_pickup_y = 0;
// sfx
//Mix_PlayChannel( S_CHAN_PICKUP, g_assets->sfx[1], 0 );
}
// update collision point clear flags
void P_UpdateCollision() {
// 8 points of collision; relative to top left of tile 56 neutral frame (20x16)
// 0, 1 = top left, top right
gs->ps.col_point[0] = W_IsClear( gs->ps.px + 4, gs->ps.py - 0 );
gs->ps.col_point[1] = W_IsClear( gs->ps.px + 10, gs->ps.py - 0 );
gs->ps.col_point[0] = W_IsClear( gs->ps.px + 4, gs->ps.py - 0, 1 );
gs->ps.col_point[1] = W_IsClear( gs->ps.px + 10, gs->ps.py - 0, 1 );
// 2, 3 = right edge
gs->ps.col_point[2] = W_IsClear( gs->ps.px + 12, gs->ps.py + 2 );
gs->ps.col_point[3] = W_IsClear( gs->ps.px + 12, gs->ps.py + 14 );
gs->ps.col_point[2] = W_IsClear( gs->ps.px + 12, gs->ps.py + 2, 1 );
gs->ps.col_point[3] = W_IsClear( gs->ps.px + 12, gs->ps.py + 14, 1 );
// 4, 5 = bottom edge
gs->ps.col_point[4] = W_IsClear( gs->ps.px + 10, gs->ps.py + 16 );
gs->ps.col_point[5] = W_IsClear( gs->ps.px + 4, gs->ps.py + 16 );
gs->ps.col_point[4] = W_IsClear( gs->ps.px + 10, gs->ps.py + 16, 1 );
gs->ps.col_point[5] = W_IsClear( gs->ps.px + 4, gs->ps.py + 16, 1 );
// 6, 7 = left edge
gs->ps.col_point[6] = W_IsClear( gs->ps.px + 2, gs->ps.py + 14 );
gs->ps.col_point[7] = W_IsClear( gs->ps.px + 2, gs->ps.py + 2 );
gs->ps.col_point[6] = W_IsClear( gs->ps.px + 2, gs->ps.py + 14, 1 );
gs->ps.col_point[7] = W_IsClear( gs->ps.px + 2, gs->ps.py + 2, 1 );
// update on_ground flag if a bottom point (4,5) is not clear
gs->ps.on_ground = (!gs->ps.col_point[4] || !gs->ps.col_point[5]);
}
// update bullet state
void P_UpdateBullet() {
// skip if no bullet in world
if ( !gs->ps.bullet_px || !gs->ps.bullet_py ) return;
gs->ps.bullet_px += gs->ps.bullet_dir * 4;
// collision
if ( !W_IsClear( gs->ps.bullet_px, gs->ps.bullet_py, 0 ) )
gs->ps.bullet_px = gs->ps.bullet_py = 0;
// off-screen
uint8_t tx = gs->ps.bullet_px / TILE_SIZE;
if ( tx - gs->view_x < 0 || tx - gs->view_x > 20 )
gs->ps.bullet_px = gs->ps.bullet_py = 0;
}
// validate input whose try flags were set
void P_VerifyInput() {
// right; col points 2, 3
@ -83,13 +114,33 @@ void P_VerifyInput() {
gs->ps.do_left = 1;
}
// jump; on_ground and col points 0, 1
if ( gs->ps.try_jump && gs->ps.on_ground && !gs->ps.do_jump
if ( gs->ps.try_jump && gs->ps.on_ground && !gs->ps.do_jump && !gs->ps.do_jetpack
&& (gs->ps.col_point[0] && gs->ps.col_point[1]) ) {
gs->ps.do_jump = 1;
}
// reset jump timer if contact a ground while still "jumping"
if ( gs->ps.try_jump && gs->ps.on_ground && gs->ps.jump_timer )
gs->ps.jump_timer = 0;
// fire if have gun and no bullet in world
if ( gs->ps.try_fire && gs->ps.gun && !gs->ps.bullet_px && !gs->ps.bullet_py ) {
gs->ps.do_fire = 1;
}
// jetpack toggle
if ( gs->ps.try_jetpack && gs->ps.jetpack ) {
gs->ps.do_jetpack = !gs->ps.do_jetpack;
// stop jump
if ( gs->ps.do_jetpack ) {
gs->ps.do_jump = 0; gs->ps.jump_timer = 0;
}
}
// down if bottom is clear and jetpack
if ( gs->ps.try_down && gs->ps.do_jetpack && gs->ps.col_point[4] && gs->ps.col_point[5] ) {
gs->ps.do_down = 1;
}
// up if top is clear and jetpack
if ( gs->ps.try_up && gs->ps.do_jetpack && gs->ps.col_point[0] && gs->ps.col_point[1] ) {
gs->ps.do_up = 1;
}
}
// apply validated player movement
@ -101,15 +152,28 @@ void P_Move() {
if ( gs->ps.do_right ) {
gs->ps.px += 2;
gs->ps.last_dir = 1;
gs->ps.do_right = 0;
}
if ( gs->ps.do_left ) {
gs->ps.px -= 2;
gs->ps.last_dir = -1;
gs->ps.do_left = 0;
}
if ( gs->ps.do_jump ) {
if ( !gs->ps.jump_timer )
// up and down
if ( gs->ps.do_up ) {
gs->ps.py -= 2;
gs->ps.do_up = 0;
} else if ( gs->ps.do_down ) {
gs->ps.py += 2;
gs->ps.do_down = 0;
// jump
} else if ( gs->ps.do_jump ) {
if ( !gs->ps.jump_timer ) {
gs->ps.jump_timer = 25;
//gs->ps.last_dir = 0;
//Mix_PlayChannel( S_CHAN_JUMP, g_assets->sfx[0], 0 );
}
if ( gs->ps.col_point[0] && gs->ps.col_point[1] ) {
if ( gs->ps.jump_timer > 12 )
@ -124,13 +188,27 @@ void P_Move() {
if ( !gs->ps.jump_timer )
gs->ps.do_jump = 0;
}
// fire
if ( gs->ps.do_fire ) {
gs->ps.bullet_dir = gs->ps.last_dir;
// default right
if ( !gs->ps.bullet_dir ) gs->ps.bullet_dir = 1;
// start bullet in "front" of player
if ( gs->ps.bullet_dir == 1 )
gs->ps.bullet_px = gs->ps.px + 18;
if ( gs->ps.bullet_dir == -1 )
gs->ps.bullet_px = gs->ps.px - 8;
// halfway down player
gs->ps.bullet_py = gs->ps.py + 8;
gs->ps.do_fire = 0;
}
}
// apply gravity to player
void P_ApplyGravity() {
if ( !gs->ps.do_jump && !gs->ps.on_ground ) {
if ( !gs->ps.do_jump && !gs->ps.on_ground && !gs->ps.do_jetpack ) {
// check below sprite
if ( W_IsClear( gs->ps.px + 4, gs->ps.py + 17 ) && W_IsClear( gs->ps.px + 10, gs->ps.py + 17 ) )
if ( W_IsClear( gs->ps.px + 4, gs->ps.py + 17, 0 ) && W_IsClear( gs->ps.px + 10, gs->ps.py + 17, 0 ) )
gs->ps.py += 2;
else { // align to tile
uint8_t not_align = gs->ps.py % TILE_SIZE;

View File

@ -10,6 +10,8 @@ typedef struct {
uint8_t lives;
// on ground flag
uint8_t on_ground;
// last facing direction
int8_t last_dir;
// input flags
uint8_t try_right;
@ -34,6 +36,9 @@ typedef struct {
uint8_t check_door;
// item flags; jetpack is also fuel count
uint8_t trophy, gun, jetpack;
// bullet
uint16_t bullet_px, bullet_py;
int8_t bullet_dir;
// collision point clear flags; 1 = clear
uint8_t col_point[8];
@ -42,6 +47,7 @@ typedef struct {
void P_Spawn();
void P_PickupItem();
void P_UpdateCollision();
void P_UpdateBullet();
void P_VerifyInput();
void P_Move();
void P_ApplyGravity();

35
world.c
View File

@ -8,6 +8,10 @@ extern game_state_t* gs;
void W_StartLevel() {
P_Spawn();
// reset monster bullet
gs->ebullet_px = 0;
gs->ebullet_py = 0;
// reset items
gs->ps.gun = 0;
gs->ps.jetpack = 0;
@ -21,8 +25,8 @@ void W_ResetLevel() {
W_StartLevel();
}
// returns 1 if passed pixel point is not within a solid tile
uint8_t W_IsClear( uint16_t px, uint16_t py ) {
// returns 1 if passed pixel point is not within a solid tile, is_player non zero to execute functionality
uint8_t W_IsClear( uint16_t px, uint16_t py, uint8_t is_player ) {
uint8_t tx, ty; // tile pos
uint8_t til; // tile index
@ -37,20 +41,23 @@ uint8_t W_IsClear( uint16_t px, uint16_t py ) {
if ( til >= 21 && til <= 24 ) return 0;
if ( til >= 29 && til <= 30 ) return 0;
// kill tiles
if ( til == 6 || til == 25 || til == 36 ) {
P_Spawn();
}
// player collision functionality
if ( is_player ) {
// kill tiles
if ( til == 6 || til == 25 || til == 36 ) {
P_Spawn();
}
// pickups
if ( til == 10 || til == 4 || til == 20 || (til >= 47 && til <= 52) ) {
gs->ps.check_pickup_x = tx;
gs->ps.check_pickup_y = ty;
}
// pickups
if ( til == 10 || til == 4 || til == 20 || (til >= 47 && til <= 52) ) {
gs->ps.check_pickup_x = tx;
gs->ps.check_pickup_y = ty;
}
// door
if ( til == 2 ) {
gs->ps.check_door = 1;
// door
if ( til == 2 ) {
gs->ps.check_door = 1;
}
}
return 1;

View File

@ -4,7 +4,7 @@
void W_StartLevel();
void W_ResetLevel();
uint8_t W_IsClear( uint16_t px, uint16_t py );
uint8_t W_IsClear( uint16_t px, uint16_t py, uint8_t is_player );
void W_Update();
void W_ScrollView();