@ -19,24 +19,24 @@
# include <string.h> // errno error message on file open
# include <string.h> // errno error message on file open
# include <inttypes.h>
# include <inttypes.h>
static struct GameState gs = { 0 } ;
static struct GameState gs = { 0 } ;
static int myplayer = - 1 ;
static int myplayer = - 1 ;
static bool right_mouse_down = false ;
static bool right_mouse_down = false ;
static bool keydown [ SAPP_KEYCODE_MENU ] = { 0 } ;
static bool keydown [ SAPP_KEYCODE_MENU ] = { 0 } ;
typedef struct KeyPressed
typedef struct KeyPressed
{
{
bool pressed ;
bool pressed ;
uint64_t frame ;
uint64_t frame ;
} KeyPressed ;
} KeyPressed ;
static KeyPressed keypressed [ SAPP_KEYCODE_MENU ] = { 0 } ;
static KeyPressed keypressed [ SAPP_KEYCODE_MENU ] = { 0 } ;
static V2 mouse_pos = { 0 } ;
static V2 mouse_pos = { 0 } ;
static bool mouse_pressed = false ;
static bool mouse_pressed = false ;
static uint64_t mouse_pressed_frame = 0 ;
static uint64_t mouse_pressed_frame = 0 ;
static bool mouse_frozen = false ; // @BeforeShip make this debug only thing
static bool mouse_frozen = false ; // @BeforeShip make this debug only thing
static float funval = 0.0f ; // easy to play with value controlled by left mouse button when held down @BeforeShip remove on release builds
static float funval = 0.0f ; // easy to play with value controlled by left mouse button when held down @BeforeShip remove on release builds
static struct ClientToServer client_to_server = { 0 } ; // buffer of inputs
static struct ClientToServer client_to_server = { 0 } ; // buffer of inputs
static ENetHost * client ;
static ENetHost * client ;
static ENetPeer * peer ;
static ENetPeer * peer ;
static float zoom_target = 300.0f ;
static float zoom_target = 300.0f ;
static float zoom = 300.0f ;
static float zoom = 300.0f ;
static sg_image image_itemframe ;
static sg_image image_itemframe ;
@ -49,7 +49,7 @@ static int cur_editing_rotation = 0;
static struct BoxInfo
static struct BoxInfo
{
{
enum BoxType type ;
enum BoxType type ;
const char * image_path ;
const char * image_path ;
sg_image image ;
sg_image image ;
} boxes [ ] = { // if added to here will show up in toolbar, is placeable
} boxes [ ] = { // if added to here will show up in toolbar, is placeable
{
{
@ -63,7 +63,7 @@ static struct BoxInfo
{
{
. type = BoxBattery ,
. type = BoxBattery ,
. image_path = " loaded/battery.png " ,
. image_path = " loaded/battery.png " ,
} } ;
} } ;
const int boxes_len = sizeof ( boxes ) / sizeof ( * boxes ) ;
const int boxes_len = sizeof ( boxes ) / sizeof ( * boxes ) ;
struct BoxInfo boxinfo ( enum BoxType type )
struct BoxInfo boxinfo ( enum BoxType type )
@ -74,10 +74,10 @@ struct BoxInfo boxinfo(enum BoxType type)
return boxes [ i ] ;
return boxes [ i ] ;
}
}
Log ( " No box info found for type %d \n " , type ) ;
Log ( " No box info found for type %d \n " , type ) ;
return ( struct BoxInfo ) { 0 } ;
return ( struct BoxInfo ) { 0 } ;
}
}
static sg_image load_image ( const char * path )
static sg_image load_image ( const char * path )
{
{
sg_image to_return = sg_alloc_image ( ) ;
sg_image to_return = sg_alloc_image ( ) ;
@ -86,7 +86,7 @@ static sg_image load_image(const char *path)
int comp = 0 ;
int comp = 0 ;
const int desired_channels = 4 ;
const int desired_channels = 4 ;
stbi_set_flip_vertically_on_load ( true ) ;
stbi_set_flip_vertically_on_load ( true ) ;
stbi_uc * image_data = stbi_load ( path , & x , & y , & comp , desired_channels ) ;
stbi_uc * image_data = stbi_load ( path , & x , & y , & comp , desired_channels ) ;
if ( ! image_data )
if ( ! image_data )
{
{
fprintf ( stderr , " Failed to load image: %s \n " , stbi_failure_reason ( ) ) ;
fprintf ( stderr , " Failed to load image: %s \n " , stbi_failure_reason ( ) ) ;
@ -111,10 +111,10 @@ static void init(void)
{
{
// @BeforeShip make all fprintf into logging to file, warning dialog grids on failure instead of exit(-1), replace the macros in sokol with this as well, like assert
// @BeforeShip make all fprintf into logging to file, warning dialog grids on failure instead of exit(-1), replace the macros in sokol with this as well, like assert
Entity * entity_data = malloc ( sizeof * entity_data * MAX_ENTITIES ) ;
Entity * entity_data = malloc ( sizeof * entity_data * MAX_ENTITIES ) ;
initialize ( & gs , entity_data , sizeof * entity_data * MAX_ENTITIES ) ;
initialize ( & gs , entity_data , sizeof * entity_data * MAX_ENTITIES ) ;
sg_desc sgdesc = { . context = sapp_sgcontext ( ) } ;
sg_desc sgdesc = { . context = sapp_sgcontext ( ) } ;
sg_setup ( & sgdesc ) ;
sg_setup ( & sgdesc ) ;
if ( ! sg_isvalid ( ) )
if ( ! sg_isvalid ( ) )
{
{
@ -122,7 +122,7 @@ static void init(void)
exit ( - 1 ) ;
exit ( - 1 ) ;
}
}
sgp_desc sgpdesc = { 0 } ;
sgp_desc sgpdesc = { 0 } ;
sgp_setup ( & sgpdesc ) ;
sgp_setup ( & sgpdesc ) ;
if ( ! sgp_is_valid ( ) )
if ( ! sgp_is_valid ( ) )
{
{
@ -208,7 +208,7 @@ static void draw_texture_rectangle_centered(V2 center, V2 width_height)
static void draw_texture_centered ( V2 center , float size )
static void draw_texture_centered ( V2 center , float size )
{
{
draw_texture_rectangle_centered ( center , ( V2 ) { size , size } ) ;
draw_texture_rectangle_centered ( center , ( V2 ) { size , size } ) ;
}
}
static void draw_circle ( V2 point , float radius )
static void draw_circle ( V2 point , float radius )
@ -219,19 +219,19 @@ static void draw_circle(V2 point, float radius)
{
{
float progress = ( float ) i / ( float ) POINTS ;
float progress = ( float ) i / ( float ) POINTS ;
float next_progress = ( float ) ( i + 1 ) / ( float ) POINTS ;
float next_progress = ( float ) ( i + 1 ) / ( float ) POINTS ;
lines [ i ] . a = ( V2 ) { . x = cos ( progress * 2.0f * PI ) * radius , . y = sin ( progress * 2.0f * PI ) * radius } ;
lines [ i ] . a = ( V2 ) { . x = cos ( progress * 2.0f * PI ) * radius , . y = sin ( progress * 2.0f * PI ) * radius } ;
lines [ i ] . b = ( V2 ) { . x = cos ( next_progress * 2.0f * PI ) * radius , . y = sin ( next_progress * 2.0f * PI ) * radius } ;
lines [ i ] . b = ( V2 ) { . x = cos ( next_progress * 2.0f * PI ) * radius , . y = sin ( next_progress * 2.0f * PI ) * radius } ;
lines [ i ] . a = V2add ( lines [ i ] . a , point ) ;
lines [ i ] . a = V2add ( lines [ i ] . a , point ) ;
lines [ i ] . b = V2add ( lines [ i ] . b , point ) ;
lines [ i ] . b = V2add ( lines [ i ] . b , point ) ;
}
}
sgp_draw_lines ( lines , POINTS ) ;
sgp_draw_lines ( lines , POINTS ) ;
}
}
static Entity * myentity ( )
static Entity * myentity ( )
{
{
if ( myplayer = = - 1 )
if ( myplayer = = - 1 )
return NULL ;
return NULL ;
Entity * to_return = get_entity ( & gs , gs . players [ myplayer ] . entity ) ;
Entity * to_return = get_entity ( & gs , gs . players [ myplayer ] . entity ) ;
if ( to_return ! = NULL )
if ( to_return ! = NULL )
assert ( to_return - > is_player ) ;
assert ( to_return - > is_player ) ;
return to_return ;
return to_return ;
@ -278,7 +278,7 @@ static void ui(bool draw, float dt, float width, float height)
float y = height - itemframe_height * 1.5 ;
float y = height - itemframe_height * 1.5 ;
for ( int i = 0 ; i < boxes_len ; i + + )
for ( int i = 0 ; i < boxes_len ; i + + )
{
{
if ( has_point ( ( AABB ) {
if ( has_point ( ( AABB ) {
. x = x ,
. x = x ,
. y = y ,
. y = y ,
. width = itemframe_width ,
. width = itemframe_width ,
@ -346,9 +346,13 @@ static void frame(void)
switch ( event . type )
switch ( event . type )
{
{
case ENET_EVENT_TYPE_CONNECT :
case ENET_EVENT_TYPE_CONNECT :
{
Log ( " New client from host %x \n " , event . peer - > address . host ) ;
Log ( " New client from host %x \n " , event . peer - > address . host ) ;
break ;
break ;
}
case ENET_EVENT_TYPE_RECEIVE :
case ENET_EVENT_TYPE_RECEIVE :
{
// @Robust @BeforeShip use some kind of serialization strategy that checks for out of bounds
// @Robust @BeforeShip use some kind of serialization strategy that checks for out of bounds
// and other validation instead of just casting to a struct
// and other validation instead of just casting to a struct
// "Alignment of structure members can be different even among different compilers on the same platform, let alone different platforms."
// "Alignment of structure members can be different even among different compilers on the same platform, let alone different platforms."
@ -361,11 +365,16 @@ static void frame(void)
myplayer = msg . your_player ;
myplayer = msg . your_player ;
enet_packet_destroy ( event . packet ) ;
enet_packet_destroy ( event . packet ) ;
break ;
break ;
}
case ENET_EVENT_TYPE_DISCONNECT :
case ENET_EVENT_TYPE_DISCONNECT :
{
fprintf ( stderr , " Disconnected from server \n " ) ;
fprintf ( stderr , " Disconnected from server \n " ) ;
exit ( - 1 ) ;
exit ( - 1 ) ;
break ;
break ;
}
}
}
}
}
else if ( enet_status = = 0 )
else if ( enet_status = = 0 )
{
{
@ -381,16 +390,16 @@ static void frame(void)
// gameplay
// gameplay
ui ( false , dt , width , height ) ; // handle events
ui ( false , dt , width , height ) ; // handle events
V2 build_target_pos = { 0 } ;
V2 build_target_pos = { 0 } ;
float build_target_rotation = 0.0f ;
float build_target_rotation = 0.0f ;
static V2 camera_pos = { 0 } ; // keeps camera at same position after player death
static V2 camera_pos = { 0 } ; // keeps camera at same position after player death
V2 world_mouse_pos = mouse_pos ;
V2 world_mouse_pos = mouse_pos ;
struct BuildPreviewInfo
struct BuildPreviewInfo
{
{
V2 grid_pos ;
V2 grid_pos ;
float grid_rotation ;
float grid_rotation ;
V2 pos ;
V2 pos ;
} build_preview = { 0 } ;
} build_preview = { 0 } ;
bool hand_at_arms_length = false ;
bool hand_at_arms_length = false ;
{
{
// interpolate zoom
// interpolate zoom
@ -403,14 +412,14 @@ static void frame(void)
camera_pos = entity_pos ( myentity ( ) ) ;
camera_pos = entity_pos ( myentity ( ) ) ;
}
}
world_mouse_pos = V2sub ( world_mouse_pos , ( V2 ) { . x = width / 2.0f , . y = height / 2.0f } ) ;
world_mouse_pos = V2sub ( world_mouse_pos , ( V2 ) { . x = width / 2.0f , . y = height / 2.0f } ) ;
world_mouse_pos . x / = zoom ;
world_mouse_pos . x / = zoom ;
world_mouse_pos . y / = - zoom ;
world_mouse_pos . y / = - zoom ;
world_mouse_pos = V2add ( world_mouse_pos , ( V2 ) { . x = camera_pos . x , . y = camera_pos . y } ) ;
world_mouse_pos = V2add ( world_mouse_pos , ( V2 ) { . x = camera_pos . x , . y = camera_pos . y } ) ;
}
}
// calculate build preview stuff
// calculate build preview stuff
EntityID grid_to_build_on = ( EntityID ) { 0 } ;
EntityID grid_to_build_on = ( EntityID ) { 0 } ;
if ( myentity ( ) ! = NULL )
if ( myentity ( ) ! = NULL )
{
{
V2 hand_pos = V2sub ( world_mouse_pos , entity_pos ( myentity ( ) ) ) ;
V2 hand_pos = V2sub ( world_mouse_pos , entity_pos ( myentity ( ) ) ) ;
@ -427,7 +436,7 @@ static void frame(void)
hand_pos = V2scale ( V2normalize ( hand_pos ) , hand_len ) ;
hand_pos = V2scale ( V2normalize ( hand_pos ) , hand_len ) ;
hand_pos = V2add ( hand_pos , entity_pos ( myentity ( ) ) ) ;
hand_pos = V2add ( hand_pos , entity_pos ( myentity ( ) ) ) ;
Entity * placing_grid = closest_to_point_in_radius ( & gs , hand_pos , BUILD_BOX_SNAP_DIST_TO_SHIP ) ;
Entity * placing_grid = closest_to_point_in_radius ( & gs , hand_pos , BUILD_BOX_SNAP_DIST_TO_SHIP ) ;
if ( placing_grid = = NULL )
if ( placing_grid = = NULL )
{
{
build_preview = ( struct BuildPreviewInfo ) {
build_preview = ( struct BuildPreviewInfo ) {
@ -443,7 +452,7 @@ static void frame(void)
build_preview = ( struct BuildPreviewInfo ) {
build_preview = ( struct BuildPreviewInfo ) {
. grid_pos = entity_pos ( placing_grid ) ,
. grid_pos = entity_pos ( placing_grid ) ,
. grid_rotation = entity_rotation ( placing_grid ) ,
. grid_rotation = entity_rotation ( placing_grid ) ,
. pos = pos } ;
. pos = pos } ;
}
}
}
}
@ -451,7 +460,7 @@ static void frame(void)
{
{
// @Robust accumulate total time and send input at rate like 20 hz, not every frame
// @Robust accumulate total time and send input at rate like 20 hz, not every frame
struct InputFrame cur_input_frame = { 0 } ;
struct InputFrame cur_input_frame = { 0 } ;
V2 input = ( V2 ) {
V2 input = ( V2 ) {
. x = ( float ) keydown [ SAPP_KEYCODE_D ] - ( float ) keydown [ SAPP_KEYCODE_A ] ,
. x = ( float ) keydown [ SAPP_KEYCODE_D ] - ( float ) keydown [ SAPP_KEYCODE_A ] ,
. y = ( float ) keydown [ SAPP_KEYCODE_W ] - ( float ) keydown [ SAPP_KEYCODE_S ] ,
. y = ( float ) keydown [ SAPP_KEYCODE_W ] - ( float ) keydown [ SAPP_KEYCODE_S ] ,
@ -464,7 +473,7 @@ static void frame(void)
cur_input_frame . build_type = cur_editing_boxtype ;
cur_input_frame . build_type = cur_editing_boxtype ;
cur_input_frame . build_rotation = cur_editing_rotation ;
cur_input_frame . build_rotation = cur_editing_rotation ;
cur_input_frame . grid_to_build_on = grid_to_build_on ;
cur_input_frame . grid_to_build_on = grid_to_build_on ;
Entity * grid = get_entity ( & gs , grid_to_build_on ) ;
Entity * grid = get_entity ( & gs , grid_to_build_on ) ;
if ( grid ! = NULL )
if ( grid ! = NULL )
{
{
cur_input_frame . build = grid_world_to_local ( grid , build_preview . pos ) ;
cur_input_frame . build = grid_world_to_local ( grid , build_preview . pos ) ;
@ -496,7 +505,7 @@ static void frame(void)
static double last_input_sent_time = 0.0 ;
static double last_input_sent_time = 0.0 ;
if ( fabs ( last_input_sent_time - time ) > TIME_BETWEEN_INPUT_PACKETS )
if ( fabs ( last_input_sent_time - time ) > TIME_BETWEEN_INPUT_PACKETS )
{
{
ENetPacket * packet = enet_packet_create ( ( void * ) & client_to_server , sizeof ( client_to_server ) , ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT ) ;
ENetPacket * packet = enet_packet_create ( ( void * ) & client_to_server , sizeof ( client_to_server ) , ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT ) ;
enet_peer_send ( peer , 0 , packet ) ;
enet_peer_send ( peer , 0 , packet ) ;
last_input_sent_time = time ;
last_input_sent_time = time ;
}
}
@ -520,7 +529,7 @@ static void frame(void)
// sokol drawing library draw in world space
// sokol drawing library draw in world space
// world space coordinates are +Y up, -Y down. Like normal cartesian coords
// world space coordinates are +Y up, -Y down. Like normal cartesian coords
transform_scope {
transform_scope {
sgp_translate ( width / 2 , height / 2 ) ;
sgp_translate ( width / 2 , height / 2 ) ;
sgp_scale_at ( zoom , - zoom , 0.0f , 0.0f ) ;
sgp_scale_at ( zoom , - zoom , 0.0f , 0.0f ) ;
@ -574,17 +583,17 @@ static void frame(void)
for ( int i = 0 ; i < gs . cur_next_entity ; i + + )
for ( int i = 0 ; i < gs . cur_next_entity ; i + + )
{
{
Entity * e = & gs . entities [ i ] ;
Entity * e = & gs . entities [ i ] ;
if ( ! e - > exists )
if ( ! e - > exists )
continue ;
continue ;
if ( e - > is_grid ) // grid drawing
if ( e - > is_grid ) // grid drawing
{
{
Entity * g = e ;
Entity * g = e ;
BOXES_ITER ( & gs , b , g )
BOXES_ITER ( & gs , b , g )
{
{
sgp_set_color ( 1.0f , 1.0f , 1.0f , 1.0f ) ;
sgp_set_color ( 1.0f , 1.0f , 1.0f , 1.0f ) ;
// debug draw force vectors for thrusters
// debug draw force vectors for thrusters
#if 0
#if 0
{
{
if ( b - > type = = BoxThruster )
if ( b - > type = = BoxThruster )
{
{
@ -592,7 +601,7 @@ static void frame(void)
dbg_line ( box_pos ( b ) , V2add ( box_pos ( b ) , V2scale ( thruster_force ( b ) , - 1.0f ) ) ) ;
dbg_line ( box_pos ( b ) , V2add ( box_pos ( b ) , V2scale ( thruster_force ( b ) , - 1.0f ) ) ) ;
}
}
}
}
# endif
# endif
if ( b - > box_type = = BoxBattery )
if ( b - > box_type = = BoxBattery )
{
{
float cur_alpha = sgp_get_color ( ) . a ;
float cur_alpha = sgp_get_color ( ) . a ;
@ -653,14 +662,14 @@ static void frame(void)
// player
// player
for ( int i = 0 ; i < MAX_PLAYERS ; i + + )
for ( int i = 0 ; i < MAX_PLAYERS ; i + + )
{
{
struct Player * player = & gs . players [ i ] ;
struct Player * player = & gs . players [ i ] ;
if ( ! player - > connected )
if ( ! player - > connected )
continue ;
continue ;
Entity * p = get_entity ( & gs , player - > entity ) ;
Entity * p = get_entity ( & gs , player - > entity ) ;
if ( p = = NULL )
if ( p = = NULL )
continue ;
continue ;
static float opacities [ MAX_PLAYERS ] = { 1.0f } ;
static float opacities [ MAX_PLAYERS ] = { 1.0f } ;
static V2 positions [ MAX_PLAYERS ] = { 0 } ;
static V2 positions [ MAX_PLAYERS ] = { 0 } ;
opacities [ i ] = lerp ( opacities [ i ] , p ! = NULL ? 1.0f : 0.1f , dt * 7.0f ) ;
opacities [ i ] = lerp ( opacities [ i ] , p ! = NULL ? 1.0f : 0.1f , dt * 7.0f ) ;
Color col_to_draw = Collerp ( WHITE , GOLD , p - > goldness ) ;
Color col_to_draw = Collerp ( WHITE , GOLD , p - > goldness ) ;
col_to_draw . a = opacities [ i ] ;
col_to_draw . a = opacities [ i ] ;
@ -689,7 +698,7 @@ static void frame(void)
// UI drawn in screen space
// UI drawn in screen space
ui ( true , dt , width , height ) ;
ui ( true , dt , width , height ) ;
sg_pass_action pass_action = { 0 } ;
sg_pass_action pass_action = { 0 } ;
sg_begin_default_pass ( & pass_action , width , height ) ;
sg_begin_default_pass ( & pass_action , width , height ) ;
sgp_flush ( ) ;
sgp_flush ( ) ;
sgp_end ( ) ;
sgp_end ( ) ;
@ -706,7 +715,7 @@ void cleanup(void)
enet_deinitialize ( ) ;
enet_deinitialize ( ) ;
}
}
void event ( const sapp_event * e )
void event ( const sapp_event * e )
{
{
switch ( e - > type )
switch ( e - > type )
{
{
@ -775,7 +784,7 @@ void event(const sapp_event *e)
case SAPP_EVENTTYPE_MOUSE_MOVE :
case SAPP_EVENTTYPE_MOUSE_MOVE :
if ( ! mouse_frozen )
if ( ! mouse_frozen )
{
{
mouse_pos = ( V2 ) { . x = e - > mouse_x , . y = e - > mouse_y } ;
mouse_pos = ( V2 ) { . x = e - > mouse_x , . y = e - > mouse_y } ;
}
}
if ( right_mouse_down )
if ( right_mouse_down )
{
{
@ -786,14 +795,14 @@ void event(const sapp_event *e)
}
}
}
}
sapp_desc sokol_main ( int argc , char * argv [ ] )
sapp_desc sokol_main ( int argc , char * argv [ ] )
{
{
if ( argc > 1 )
if ( argc > 1 )
{
{
_beginthread ( server , 0 , NULL ) ;
_beginthread ( server , 0 , NULL ) ;
}
}
( void ) argv ;
( void ) argv ;
return ( sapp_desc ) {
return ( sapp_desc ) {
. init_cb = init ,
. init_cb = init ,
. frame_cb = frame ,
. frame_cb = frame ,
. cleanup_cb = cleanup ,
. cleanup_cb = cleanup ,