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:
parent
e91c1f170d
commit
40dd8023d5
68
game.c
68
game.c
|
@ -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
9
game.h
|
@ -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
104
player.c
|
@ -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;
|
||||
|
|
6
player.h
6
player.h
|
@ -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
35
world.c
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue