@ -1,7 +1,9 @@
# pragma once
# pragma once
# include "ipsettings.h"
# define MAX_PLAYERS 16
# define MAX_PLAYERS 16
# define MAX_ENTITIES 1024*25
# define MAX_ENTITIES 1024 * 25
# define BOX_SIZE 0.25f
# define BOX_SIZE 0.25f
# define PLAYER_SIZE ((V2){.x = BOX_SIZE, .y = BOX_SIZE})
# define PLAYER_SIZE ((V2){.x = BOX_SIZE, .y = BOX_SIZE})
# define PLAYER_MASS 0.5f
# define PLAYER_MASS 0.5f
@ -17,14 +19,18 @@
# define THRUSTER_ENERGY_USED_PER_SECOND 0.005f
# define THRUSTER_ENERGY_USED_PER_SECOND 0.005f
# define VISION_RADIUS 12.0f
# define VISION_RADIUS 12.0f
# define MAX_SERVER_TO_CLIENT 1024 * 512 // maximum size of serialized gamestate buffer
# define MAX_SERVER_TO_CLIENT 1024 * 512 // maximum size of serialized gamestate buffer
# define MAX_CLIENT_TO_SERVER 1024 * 10 // maximum size of serialized inputs and mic data
# define MAX_CLIENT_TO_SERVER 1024 * 10 // maximum size of serialized inputs and mic data
# define SUN_RADIUS 10.0f
# define SUN_RADIUS 10.0f
# define INSTANT_DEATH_DISTANCE_FROM_SUN 2000.0f
# define INSTANT_DEATH_DISTANCE_FROM_SUN 2000.0f
# define SUN_POS ((V2){50.0f,0.0f})
# define SUN_POS ((V2){50.0f, 0.0f})
# ifdef NO_GRAVITY
# define SUN_GRAVITY_STRENGTH 0.1f
# else
# define SUN_GRAVITY_STRENGTH (9.0e2f)
# define SUN_GRAVITY_STRENGTH (9.0e2f)
# endif
# define SOLAR_ENERGY_PER_SECOND 0.02f
# define SOLAR_ENERGY_PER_SECOND 0.02f
# define DAMAGE_TO_PLAYER_PER_BLOCK 0.1f
# define DAMAGE_TO_PLAYER_PER_BLOCK 0.1f
# define BATTERY_CAPACITY DAMAGE_TO_PLAYER_PER_BLOCK *0.7f
# define BATTERY_CAPACITY DAMAGE_TO_PLAYER_PER_BLOCK * 0.7f
# define PLAYER_ENERGY_RECHARGE_PER_SECOND 0.1f
# define PLAYER_ENERGY_RECHARGE_PER_SECOND 0.1f
# define EXPLOSION_TIME 0.5f
# define EXPLOSION_TIME 0.5f
# define EXPLOSION_PUSH_STRENGTH 5.0f
# define EXPLOSION_PUSH_STRENGTH 5.0f
@ -35,18 +41,27 @@
# define TIME_BETWEEN_WORLD_SAVE 30.0f
# define TIME_BETWEEN_WORLD_SAVE 30.0f
// VOIP
// VOIP
# define VOIP_PACKET_BUFFER_SIZE 15 // audio. Must be bigger than 2
# define VOIP_PACKET_BUFFER_SIZE 15 // audio. Must be bigger than 2
# define VOIP_EXPECTED_FRAME_COUNT 480
# define VOIP_EXPECTED_FRAME_COUNT 480
# define VOIP_SAMPLE_RATE 48000
# define VOIP_SAMPLE_RATE 48000
# define VOIP_TIME_PER_PACKET 1.0f / ((float)(VOIP_SAMPLE_RATE / VOIP_EXPECTED_FRAME_COUNT)) // in seconds
# define VOIP_TIME_PER_PACKET ( 1.0f / ((float)(VOIP_SAMPLE_RATE / VOIP_EXPECTED_FRAME_COUNT) )) // in seconds
# define VOIP_PACKET_MAX_SIZE 4000
# define VOIP_PACKET_MAX_SIZE 4000
# define VOIP_DISTANCE_WHEN_CANT_HEAR (VISION_RADIUS *0.8f)
# define VOIP_DISTANCE_WHEN_CANT_HEAR (VISION_RADIUS * 0.8f)
# define MAX_REPREDICTION_TIME (TIMESTEP * 50.0f)
# define TIME_BETWEEN_SEND_GAMESTATE (1.0f / 20.0f)
# define TIME_BETWEEN_SEND_GAMESTATE (1.0f / 20.0f)
# define TIME_BETWEEN_INPUT_PACKETS (1.0f / 20.0f)
# define TIME_BETWEEN_INPUT_PACKETS (1.0f / 20.0f)
# define TIMESTEP (1.0f / 60.0f) // server required to simulate at this, defines what tick the game is on
# define TIMESTEP (1.0f / 60.0f) // server required to simulate at this, defines what tick the game is on
# define SERVER_PORT 2551
# define SERVER_PORT 2551
# define INPUT_BUFFER 6
# define LOCAL_INPUT_QUEUE_MAX 90 // please god let you not have more than 90 frames of game latency
# define INPUT_QUEUE_MAX 15
// cross platform threadlocal variables
# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
# define THREADLOCAL __declspec(thread)
# else
# define THREADLOCAL __thread
# endif
// must make this header and set the target address, just #define SERVER_ADDRESS "127.0.0.1"
// must make this header and set the target address, just #define SERVER_ADDRESS "127.0.0.1"
# include "ipsettings.h" // don't leak IP!
# include "ipsettings.h" // don't leak IP!
@ -72,7 +87,7 @@ void sgp_set_color(float, float, float, float);
// somehow automatically or easily cast to floats
// somehow automatically or easily cast to floats
typedef struct sgp_vec2
typedef struct sgp_vec2
{
{
float x , y ;
float x , y ;
} sgp_vec2 ;
} sgp_vec2 ;
typedef sgp_vec2 sgp_point ;
typedef sgp_vec2 sgp_point ;
@ -85,8 +100,8 @@ typedef void cpBody;
typedef void cpShape ;
typedef void cpShape ;
# endif
# endif
# include <stdbool.h>
# include "queue.h"
# include "queue.h"
# include <stdbool.h>
# ifndef OPUS_TYPES_H
# ifndef OPUS_TYPES_H
typedef int opus_int32 ;
typedef int opus_int32 ;
@ -103,509 +118,512 @@ typedef int opus_int32;
typedef sgp_vec2 V2 ;
typedef sgp_vec2 V2 ;
typedef sgp_point P2 ;
typedef sgp_point P2 ;
# define Log(...){ \
# define Log(...) \
{ \
fprintf ( stdout , " %s:%d | " , __FILE__ , __LINE__ ) ; \
fprintf ( stdout , " %s:%d | " , __FILE__ , __LINE__ ) ; \
fprintf ( stdout , __VA_ARGS__ ) ; }
fprintf ( stdout , __VA_ARGS__ ) ; \
}
enum BoxType
enum BoxType
{
{
BoxHullpiece ,
BoxHullpiece ,
BoxThruster ,
BoxThruster ,
BoxBattery ,
BoxBattery ,
BoxCockpit ,
BoxCockpit ,
BoxMedbay ,
BoxMedbay ,
BoxSolarPanel ,
BoxSolarPanel ,
BoxExplosive ,
BoxExplosive ,
BoxLast ,
BoxLast ,
} ;
} ;
enum CompassRotation
enum CompassRotation
{
{
Right ,
Right ,
Down ,
Down ,
Left ,
Left ,
Up ,
Up ,
RotationLast ,
RotationLast ,
} ;
} ;
enum Squad
enum Squad
{
{
SquadNone ,
SquadNone ,
SquadRed ,
SquadRed ,
SquadGreen ,
SquadGreen ,
SquadBlue ,
SquadBlue ,
SquadPurple ,
SquadPurple ,
SquadLast ,
SquadLast ,
} ;
} ;
// when generation is 0, invalid ID
// when generation is 0, invalid ID
typedef struct EntityID
typedef struct EntityID
{
{
unsigned int generation ; // VERY IMPORTANT if 0 then EntityID points to nothing, generation >= 1
unsigned int generation ; // VERY IMPORTANT if 0 then EntityID points to nothing, generation >= 1
unsigned int index ; // index into the entity arena
unsigned int index ; // index into the entity arena
} EntityID ;
} EntityID ;
static bool entityids_same ( EntityID a , EntityID b )
static bool entityids_same ( EntityID a , EntityID b )
{
{
return ( a . generation = = b . generation ) & & ( a . index = = b . index ) ;
return ( a . generation = = b . generation ) & & ( a . index = = b . index ) ;
}
}
// when updated, must update serialization, comparison in main.c, and the server
// when updated, must update serialization, comparison in main.c, and the server
// on input received processing function
// on input received processing function
typedef struct InputFrame
typedef struct InputFrame
{
{
uint64_t tick ;
uint64_t tick ;
size_t id ; // each input has unique, incrementing, I.D, so server doesn't double process inputs. Inputs to server should be ordered from 0-max like biggest id-smallest. This is done so if packet loss server still processes input
V2 movement ;
V2 movement ;
int take_over_squad ; // -1 means not taking over any squad
int take_over_squad ; // -1 means not taking over any squad
bool accept_cur_squad_invite ;
bool accept_cur_squad_invite ;
bool reject_cur_squad_invite ;
bool reject_cur_squad_invite ;
EntityID invite_this_player ; // null means inviting nobody! @Robust make it so just sends interact pos input, and server processes who to invite. This depends on client side prediction + proper input processing at the right tick.
EntityID invite_this_player ; // null means inviting nobody! @Robust make it so just sends interact pos input, and server processes who to invite. This depends on client side prediction + proper input processing at the right tick.
bool seat_action ;
bool seat_action ;
EntityID seat_to_inhabit ;
V2 hand_pos ; // local to player transationally but not rotationally
V2 hand_pos ; // local to player transationally but not rotationally unless field below is not null, then it's local to that grid
// @BeforeShip bounds check on the hand_pos so that players can't reach across the entire map
// @BeforeShip bounds check on the hand_pos so that players can't reach across the entire map
EntityID grid_hand_pos_local_to ; // when not null, hand_pos is local to this grid. this prevents bug where at high speeds the built block is in the wrong place on the selected grid
bool dobuild ;
bool dobuild ;
enum BoxType build_type ;
enum BoxType build_type ;
enum CompassRotation build_rotation ;
enum CompassRotation build_rotation ;
} InputFrame ;
} InputFrame ;
typedef struct Entity
typedef struct Entity
{
{
bool exists ;
bool exists ;
EntityID next_free_entity ;
EntityID next_free_entity ;
unsigned int generation ;
unsigned int generation ;
bool no_save_to_disk ; // stuff generated later on, like player's bodies or space stations that respawn.
bool no_save_to_disk ; // stuff generated later on, like player's bodies or space stations that respawn.
float damage ; // used by box and player
float damage ; // used by box and player
cpBody * body ; // used by grid, player, and box
cpBody * body ; // used by grid, player, and box
cpShape * shape ; // must be a box so shape_size can be set appropriately, and serialized
cpShape * shape ; // must be a box so shape_size can be set appropriately, and serialized
// for serializing the shape
// for serializing the shape
// @Robust remove shape_parent_entity from this struct, use the shape's body to figure out
// @Robust remove shape_parent_entity from this struct, use the shape's body to figure out
// what the shape's parent entity is
// what the shape's parent entity is
EntityID shape_parent_entity ; // can't be zero if shape is nonzero
EntityID shape_parent_entity ; // can't be zero if shape is nonzero
V2 shape_size ;
V2 shape_size ;
// player
// player
bool is_player ;
bool is_player ;
enum Squad presenting_squad ;
enum Squad presenting_squad ;
EntityID currently_inside_of_box ;
EntityID currently_inside_of_box ;
enum Squad squad_invited_to ; // if squad none, then no squad invite
enum Squad squad_invited_to ; // if squad none, then no squad invite
float goldness ; // how much the player is a winner
float goldness ; // how much the player is a winner
// explosion
// explosion
bool is_explosion ;
bool is_explosion ;
V2 explosion_pos ;
V2 explosion_pos ;
V2 explosion_vel ;
V2 explosion_vel ;
float explosion_progresss ; // in seconds
float explosion_progresss ; // in seconds
// grids
// grids
bool is_grid ;
bool is_grid ;
float total_energy_capacity ;
float total_energy_capacity ;
EntityID boxes ;
EntityID boxes ;
// boxes
// boxes
bool is_box ;
bool is_box ;
bool always_visible ; // always serialized to the player
bool always_visible ; // always serialized to the player
enum BoxType box_type ;
enum BoxType box_type ;
bool is_explosion_unlock ;
bool is_explosion_unlock ;
EntityID next_box ;
EntityID next_box ;
EntityID prev_box ; // doubly linked so can remove in middle of chain
EntityID prev_box ; // doubly linked so can remove in middle of chain
enum CompassRotation compass_rotation ;
enum CompassRotation compass_rotation ;
bool indestructible ;
bool indestructible ;
float wanted_thrust ; // the thrust command applied to the thruster
float wanted_thrust ; // the thrust command applied to the thruster
float thrust ; // the actual thrust it can provide based on energy sources in the grid
float thrust ; // the actual thrust it can provide based on energy sources in the grid
float energy_used ; // battery
float energy_used ; // battery
float sun_amount ; // solar panel, between 0 and 1
float sun_amount ; // solar panel, between 0 and 1
EntityID player_who_is_inside_of_me ;
EntityID player_who_is_inside_of_me ;
} Entity ;
} Entity ;
typedef struct Player
typedef struct Player
{
{
bool connected ;
bool connected ;
bool unlocked_bombs ;
bool unlocked_bombs ;
enum Squad squad ;
enum Squad squad ;
EntityID entity ;
EntityID entity ;
EntityID last_used_medbay ;
EntityID last_used_medbay ;
InputFrame input ;
InputFrame input ;
} Player ;
} Player ;
// gotta update the serialization functions when this changes
// gotta update the serialization functions when this changes
typedef struct GameState
typedef struct GameState
{
{
cpSpace * space ;
cpSpace * space ;
double time ;
double time ; // @Robust separate tick integer not prone to precision issues
V2 goldpos ;
V2 goldpos ;
Player players [ MAX_PLAYERS ] ;
Player players [ MAX_PLAYERS ] ;
EntityID cur_spacestation ;
EntityID cur_spacestation ;
// Entity arena
// Entity arena
// ent:ity pointers can't move around because of how the physics engine handles user data.
// ent:ity pointers can't move around because of how the physics engine handles user data.
// if you really need this, potentially refactor to store entity IDs instead of pointers
// if you really need this, potentially refactor to store entity IDs instead of pointers
// in the shapes and bodies of chipmunk. Would require editing the library I think
// in the shapes and bodies of chipmunk. Would require editing the library I think
Entity * entities ;
Entity * entities ;
unsigned int max_entities ; // maximum number of entities possible in the entities list
unsigned int max_entities ; // maximum number of entities possible in the entities list
unsigned int cur_next_entity ; // next entity to pass on request of a new entity if the free list is empty
unsigned int cur_next_entity ; // next entity to pass on request of a new entity if the free list is empty
EntityID free_list ;
EntityID free_list ;
} GameState ;
} GameState ;
# define PLAYERS_ITER(players, cur) for(Player * cur = players; cur < players+MAX_PLAYERS; cur++) if(cur->connected)
# define PLAYERS_ITER(players, cur) \
for ( Player * cur = players ; cur < players + MAX_PLAYERS ; cur + + ) \
if ( cur - > connected )
# define PI 3.14159f
# define PI 3.14159f
# define TAU (PI *2.0f)
# define TAU (PI * 2.0f)
// returns in radians
// returns in radians
static float rotangle ( enum CompassRotation rot )
static float rotangle ( enum CompassRotation rot )
{
{
switch ( rot )
switch ( rot )
{
{
case Right :
case Right :
return 0.0f ;
return 0.0f ;
break ;
break ;
case Down :
case Down :
return - PI / 2.0f ;
return - PI / 2.0f ;
break ;
break ;
case Left :
case Left :
return - PI ;
return - PI ;
break ;
break ;
case Up :
case Up :
return - 3.0f * PI / 2.0f ;
return - 3.0f * PI / 2.0f ;
break ;
break ;
default :
default :
Log ( " Unknown rotation %d \n " , rot ) ;
Log ( " Unknown rotation %d \n " , rot ) ;
return - 0.0f ;
return - 0.0f ;
break ;
break ;
}
}
}
}
typedef struct OpusPacket {
typedef struct OpusPacket
opus_int32 length ;
{
char data [ VOIP_PACKET_MAX_SIZE ] ;
opus_int32 length ;
char data [ VOIP_PACKET_MAX_SIZE ] ;
} OpusPacket ;
} OpusPacket ;
typedef struct ServerToClient
typedef struct ServerToClient
{
{
struct GameState * cur_gs ;
struct GameState * cur_gs ;
Queue * playback_buffer;
Queue * audio_ playback_buffer;
int your_player ;
int your_player ;
} ServerToClient ;
} ServerToClient ;
typedef struct ClientToServer
typedef struct ClientToServer
{
{
Queue * mic_data ; // on serialize, flushes this of packets. On deserialize, fills it
Queue * mic_data ; // on serialize, flushes this of packets. On deserialize, fills it
InputFrame inputs [ INPUT_BUFFER ] ;
Queue * input_data ; // does not flush on serialize! must be in order of tick
} ClientToServer ;
} ClientToServer ;
# define DeferLoop(start, end) \
# define DeferLoop(start, end) \
for ( int _i_ = ( ( start ) , 0 ) ; _i_ = = 0 ; _i_ + = 1 , ( end ) )
for ( int _i_ = ( ( start ) , 0 ) ; _i_ = = 0 ; _i_ + = 1 , ( end ) )
// server
// server
void server ( void * info ) ; // data parameter required from thread api...
void server ( void * info ) ; // data parameter required from thread api...
// gamestate
// gamestate
EntityID create_spacestation ( GameState * gs ) ;
EntityID create_spacestation ( GameState * gs ) ;
void initialize ( struct GameState * gs , void * entity_arena , size_t entity_arena_size ) ;
void initialize ( struct GameState * gs , void * entity_arena , size_t entity_arena_size ) ;
void destroy ( struct GameState * gs ) ;
void destroy ( struct GameState * gs ) ;
void process ( struct GameState * gs , float dt ) ; // does in place
void process_fixed_timestep ( GameState * gs ) ;
Entity * closest_to_point_in_radius ( struct GameState * gs , V2 point , float radius ) ;
void process ( struct GameState * gs , float dt ) ; // does in place
uint64_t tick ( struct GameState * gs ) ;
Entity * closest_to_point_in_radius ( struct GameState * gs , V2 point , float radius ) ;
uint64_t tick ( struct GameState * gs ) ;
// all of these return if successful or not
// all of these return if successful or not
bool server_to_client_serialize ( struct ServerToClient * msg , char * bytes , size_t * out_len , size_t max_len , Entity * for_this_player , bool to_disk ) ;
bool server_to_client_serialize ( struct ServerToClient * msg , char * bytes , size_t * out_len , size_t max_len , Entity * for_this_player , bool to_disk ) ;
bool server_to_client_deserialize ( struct ServerToClient * msg , char * bytes , size_t max_len , bool from_disk ) ;
bool server_to_client_deserialize ( struct ServerToClient * msg , char * bytes , size_t max_len , bool from_disk ) ;
bool client_to_server_deserialize ( GameState * gs , struct ClientToServer * msg , char * bytes , size_t max_len ) ;
bool client_to_server_deserialize ( GameState * gs , struct ClientToServer * msg , char * bytes , size_t max_len ) ;
bool client_to_server_serialize ( GameState * gs , struct ClientToServer * msg , char * bytes , size_t * out_len , size_t max_len ) ;
bool client_to_server_serialize ( GameState * gs , struct ClientToServer * msg , char * bytes , size_t * out_len , size_t max_len ) ;
// entities
// entities
Entity * get_entity ( struct GameState * gs , EntityID id ) ;
Entity * get_entity ( struct GameState * gs , EntityID id ) ;
Entity * new_entity ( struct GameState * gs ) ;
Entity * new_entity ( struct GameState * gs ) ;
EntityID get_id ( struct GameState * gs , Entity * e ) ;
EntityID get_id ( struct GameState * gs , Entity * e ) ;
V2 entity_pos ( Entity * e ) ;
V2 entity_pos ( Entity * e ) ;
void entity_set_rotation ( Entity * e , float rot ) ;
void entity_set_rotation ( Entity * e , float rot ) ;
void entity_set_pos ( Entity * e , V2 pos ) ;
void entity_set_pos ( Entity * e , V2 pos ) ;
float entity_rotation ( Entity * e ) ;
float entity_rotation ( Entity * e ) ;
void entity_ensure_in_orbit ( Entity * e ) ;
void entity_ensure_in_orbit ( Entity * e ) ;
void entity_destroy ( GameState * gs , Entity * e ) ;
void entity_destroy ( GameState * gs , Entity * e ) ;
# define BOX_CHAIN_ITER(gs, cur, starting_box) for (Entity *cur = get_entity(gs, starting_box); cur != NULL; cur = get_entity(gs, cur->next_box))
# define BOX_CHAIN_ITER(gs, cur, starting_box) for (Entity *cur = get_entity(gs, starting_box); cur != NULL; cur = get_entity(gs, cur->next_box))
# define BOXES_ITER(gs, cur, grid_entity_ptr) BOX_CHAIN_ITER(gs, cur, (grid_entity_ptr)->boxes)
# define BOXES_ITER(gs, cur, grid_entity_ptr) BOX_CHAIN_ITER(gs, cur, (grid_entity_ptr)->boxes)
// grid
// grid
void grid_create ( struct GameState * gs , Entity * e ) ;
void grid_create ( struct GameState * gs , Entity * e ) ;
void box_create ( struct GameState * gs , Entity * new_box , Entity * grid , V2 pos ) ;
void box_create ( struct GameState * gs , Entity * new_box , Entity * grid , V2 pos ) ;
V2 grid_com ( Entity * grid ) ;
V2 grid_com ( Entity * grid ) ;
V2 grid_vel ( Entity * grid ) ;
V2 grid_vel ( Entity * grid ) ;
V2 box_vel ( Entity * box ) ;
V2 box_vel ( Entity * box ) ;
V2 grid_local_to_world ( Entity * grid , V2 local ) ;
V2 grid_local_to_world ( Entity * grid , V2 local ) ;
V2 grid_world_to_local ( Entity * grid , V2 world ) ;
V2 grid_world_to_local ( Entity * grid , V2 world ) ;
V2 grid_snapped_box_pos ( Entity * grid , V2 world ) ; // returns the snapped pos in world coords
V2 grid_snapped_box_pos ( Entity * grid , V2 world ) ; // returns the snapped pos in world coords
float entity_angular_velocity ( Entity * grid ) ;
float entity_angular_velocity ( Entity * grid ) ;
V2 entity_shape_pos ( Entity * box ) ;
V2 entity_shape_pos ( Entity * box ) ;
float box_rotation ( Entity * box ) ;
float box_rotation ( Entity * box ) ;
// thruster
// thruster
V2 box_facing_vector ( Entity * box ) ;
V2 box_facing_vector ( Entity * box ) ;
V2 thruster_force ( Entity * box ) ;
V2 thruster_force ( Entity * box ) ;
// debug draw
// debug draw
void dbg_drawall ( ) ;
void dbg_drawall ( ) ;
void dbg_line ( V2 from , V2 to ) ;
void dbg_line ( V2 from , V2 to ) ;
void dbg_rect ( V2 center ) ;
void dbg_rect ( V2 center ) ;
typedef struct ServerThreadInfo {
typedef struct ServerThreadInfo
ma_mutex info_mutex ;
{
const char * world_save ;
ma_mutex info_mutex ;
bool should_quit ;
const char * world_save ;
bool should_quit ;
} ServerThreadInfo ;
} ServerThreadInfo ;
// all the math is static so that it can be defined in each compilation unit its included in
// all the math is static so that it can be defined in each compilation unit its included in
typedef struct AABB
typedef struct AABB
{
{
float x , y , width , height ;
float x , y , width , height ;
} AABB ;
} AABB ;
static AABB centered_at ( V2 point , V2 size )
static AABB centered_at ( V2 point , V2 size )
{
{
return ( AABB )
return ( AABB ) {
{
. x = point . x - size . x / 2.0f ,
. x = point . x - size . x / 2.0f ,
. y = point . y - size . y / 2.0f ,
. y = point . y - size . y / 2.0f ,
. width = size . x ,
. width = size . x ,
. height = size . y ,
. height = size . y ,
} ;
} ;
}
}
static bool has_point ( AABB aabb , V2 point )
static bool has_point ( AABB aabb , V2 point )
{
{
return point . x > aabb . x & & point . x < aabb . x + aabb . width & & point . y > aabb . y & & point . y < aabb . y + aabb . height ;
return point . x > aabb . x & & point . x < aabb . x + aabb . width & & point . y > aabb . y & & point . y < aabb . y + aabb . height ;
}
}
static V2 V2add ( V2 a , V2 b )
static V2 V2add ( V2 a , V2 b )
{
{
return ( V2 ) {
return ( V2 ) {
. x = a . x + b . x ,
. x = a . x + b . x ,
. y = a . y + b . y ,
. y = a . y + b . y ,
} ;
} ;
}
}
static V2 V2scale ( V2 a , float f )
static V2 V2scale ( V2 a , float f )
{
{
return ( V2 ) {
return ( V2 ) {
. x = a . x * f ,
. x = a . x * f ,
. y = a . y * f ,
. y = a . y * f ,
} ;
} ;
}
}
static float V2lengthsqr ( V2 v )
static float V2lengthsqr ( V2 v )
{
{
return v . x * v . x + v . y * v . y ;
return v . x * v . x + v . y * v . y ;
}
}
static float V2length ( V2 v )
static float V2length ( V2 v )
{
{
return sqrtf ( V2lengthsqr ( v ) ) ;
return sqrtf ( V2lengthsqr ( v ) ) ;
}
}
static V2 V2normalize ( V2 v )
static V2 V2normalize ( V2 v )
{
{
return V2scale ( v , 1.0f / V2length ( v ) ) ;
return V2scale ( v , 1.0f / V2length ( v ) ) ;
}
}
static float V2dot ( V2 a , V2 b )
static float V2dot ( V2 a , V2 b )
{
{
return a . x * b . x + a . y * b . y ;
return a . x * b . x + a . y * b . y ;
}
}
static float V2projectvalue ( V2 vec , V2 onto )
static float V2projectvalue ( V2 vec , V2 onto )
{
{
float length_onto = V2length ( onto ) ;
float length_onto = V2length ( onto ) ;
return V2dot ( vec , onto ) / ( length_onto * length_onto ) ;
return V2dot ( vec , onto ) / ( length_onto * length_onto ) ;
}
}
static V2 V2project ( V2 vec , V2 onto )
static V2 V2project ( V2 vec , V2 onto )
{
{
return V2scale ( onto , V2projectvalue ( vec , onto ) ) ;
return V2scale ( onto , V2projectvalue ( vec , onto ) ) ;
}
}
static V2 V2rotate ( V2 vec , float theta )
static V2 V2rotate ( V2 vec , float theta )
{
{
return ( V2 ) {
return ( V2 ) {
. x = vec . x * cosf ( theta ) - vec . y * sinf ( theta ) ,
. x = vec . x * cosf ( theta ) - vec . y * sinf ( theta ) ,
. y = vec . x * sinf ( theta ) + vec . y * cosf ( theta ) ,
. y = vec . x * sinf ( theta ) + vec . y * cosf ( theta ) ,
} ;
} ;
}
}
// also known as atan2
// also known as atan2
static float V2angle ( V2 vec )
static float V2angle ( V2 vec )
{
{
return atan2f ( vec . y , vec . x ) ;
return atan2f ( vec . y , vec . x ) ;
}
}
static V2 V2sub ( V2 a , V2 b )
static V2 V2sub ( V2 a , V2 b )
{
{
return ( V2 ) {
return ( V2 ) {
. x = a . x - b . x ,
. x = a . x - b . x ,
. y = a . y - b . y ,
. y = a . y - b . y ,
} ;
} ;
}
}
static bool V2equal ( V2 a , V2 b , float eps )
static bool V2equal ( V2 a , V2 b , float eps )
{
{
return V2length ( V2sub ( a , b ) ) < eps ;
return V2length ( V2sub ( a , b ) ) < eps ;
}
}
static inline float clamp01 ( float f )
static inline float clamp01 ( float f )
{
{
return fmaxf ( 0.0f , fminf ( f , 1.0f ) ) ;
return fmaxf ( 0.0f , fminf ( f , 1.0f ) ) ;
}
}
static float V2distsqr ( V2 from , V2 to )
static float V2distsqr ( V2 from , V2 to )
{
{
return V2lengthsqr ( V2sub ( to , from ) ) ;
return V2lengthsqr ( V2sub ( to , from ) ) ;
}
}
static float V2dist ( V2 from , V2 to )
static float V2dist ( V2 from , V2 to )
{
{
return sqrtf ( V2distsqr ( from , to ) ) ;
return sqrtf ( V2distsqr ( from , to ) ) ;
}
}
static inline float clamp ( float f , float minimum , float maximum )
static inline float clamp ( float f , float minimum , float maximum )
{
{
if ( f < minimum )
if ( f < minimum )
return minimum ;
return minimum ;
if ( f > maximum )
if ( f > maximum )
return maximum ;
return maximum ;
return f ;
return f ;
}
}
static float fract ( float f )
static float fract ( float f )
{
{
return f - floorf ( f ) ;
return f - floorf ( f ) ;
}
}
static float lerp ( float a , float b , float f )
static float lerp ( float a , float b , float f )
{
{
return a * ( 1.0f - f ) + ( b * f ) ;
return a * ( 1.0f - f ) + ( b * f ) ;
}
}
static float lerp_angle ( float p_from , float p_to , float p_weight ) {
static float lerp_angle ( float p_from , float p_to , float p_weight )
float difference = fmodf ( p_to - p_from , ( float ) TAU ) ;
{
float distance = fmodf ( 2.0f * difference , ( float ) TAU ) - difference ;
float difference = fmodf ( p_to - p_from , ( float ) TAU ) ;
return p_from + distance * p_weight ;
float distance = fmodf ( 2.0f * difference , ( float ) TAU ) - difference ;
return p_from + distance * p_weight ;
}
}
static V2 V2floor ( V2 p )
static V2 V2floor ( V2 p )
{
{
return ( V2 ) { floorf ( p . x ) , floorf ( p . y ) } ;
return ( V2 ) { floorf ( p . x ) , floorf ( p . y ) } ;
}
}
static V2 V2fract ( V2 p )
static V2 V2fract ( V2 p )
{
{
return ( V2 ) { fract ( p . x ) , fract ( p . y ) } ;
return ( V2 ) { fract ( p . x ) , fract ( p . y ) } ;
}
}
/*
/*
float noise ( V2 p )
float noise ( V2 p )
{
{
V2 id = V2floor ( p ) ;
V2 id = V2floor ( p ) ;
V2 f = V2fract ( p ) ;
V2 f = V2fract ( p ) ;
V2 u = V2dot ( f , f ) * ( 3.0f - 2.0f * f ) ;
V2 u = V2dot ( f , f ) * ( 3.0f - 2.0f * f ) ;
return mix ( mix ( random ( id + V2 ( 0.0 , 0.0 ) ) ,
return mix ( mix ( random ( id + V2 ( 0.0 , 0.0 ) ) ,
random ( id + V2 ( 1.0 , 0.0 ) ) , u . x ) ,
random ( id + V2 ( 1.0 , 0.0 ) ) , u . x ) ,
mix ( random ( id + V2 ( 0.0 , 1.0 ) ) ,
mix ( random ( id + V2 ( 0.0 , 1.0 ) ) ,
random ( id + V2 ( 1.0 , 1.0 ) ) , u . x ) ,
random ( id + V2 ( 1.0 , 1.0 ) ) , u . x ) ,
u . y ) ;
u . y ) ;
}
}
float fbm ( V2 p )
float fbm ( V2 p )
{
{
float f = 0.0 ;
float f = 0.0 ;
float gat = 0.0 ;
float gat = 0.0 ;
for ( float octave = 0. ; octave < 5. ; + + octave )
for ( float octave = 0. ; octave < 5. ; + + octave )
{
{
float la = pow ( 2.0 , octave ) ;
float la = pow ( 2.0 , octave ) ;
float ga = pow ( 0.5 , octave + 1. ) ;
float ga = pow ( 0.5 , octave + 1. ) ;
f + = ga * noise ( la * p ) ;
f + = ga * noise ( la * p ) ;
gat + = ga ;
gat + = ga ;
}
}
f = f / gat ;
f = f / gat ;
return f ;
return f ;
}
}
*/
*/
static V2 V2lerp ( V2 a , V2 b , float factor )
static V2 V2lerp ( V2 a , V2 b , float factor )
{
{
V2 to_return = { 0 } ;
V2 to_return = { 0 } ;
to_return . x = lerp ( a . x , b . x , factor ) ;
to_return . x = lerp ( a . x , b . x , factor ) ;
to_return . y = lerp ( a . y , b . y , factor ) ;
to_return . y = lerp ( a . y , b . y , factor ) ;
return to_return ;
return to_return ;
}
}
// for random generation
// for random generation
static float hash11 ( float p )
static float hash11 ( float p )
{
{
p = fract ( p * .1031f ) ;
p = fract ( p * .1031f ) ;
p * = p + 33.33f ;
p * = p + 33.33f ;
p * = p + p ;
p * = p + p ;
return fract ( p ) ;
return fract ( p ) ;
}
}
typedef struct Color
typedef struct Color
{
{
float r , g , b , a ;
float r , g , b , a ;
} Color ;
} Color ;
static Color colhex ( int r , int g , int b )
static Color colhex ( int r , int g , int b )
{
{
return ( Color ) {
return ( Color ) {
. r = ( float ) r / 255.0f ,
. r = ( float ) r / 255.0f ,
. g = ( float ) g / 255.0f ,
. g = ( float ) g / 255.0f ,
. b = ( float ) b / 255.0f ,
. b = ( float ) b / 255.0f ,
. a = 1.0f ,
. a = 1.0f ,
} ;
} ;
}
}
static Color colhexcode ( int hexcode )
static Color colhexcode ( int hexcode )
{
{
// 0x020509;
// 0x020509;
int r = ( hexcode > > 16 ) & 0xFF ;
int r = ( hexcode > > 16 ) & 0xFF ;
int g = ( hexcode > > 8 ) & 0xFF ;
int g = ( hexcode > > 8 ) & 0xFF ;
int b = ( hexcode > > 0 ) & 0xFF ;
int b = ( hexcode > > 0 ) & 0xFF ;
return colhex ( r , g , b ) ;
return colhex ( r , g , b ) ;
}
}
static Color Collerp ( Color a , Color b , float factor )
static Color Collerp ( Color a , Color b , float factor )
{
{
Color to_return = { 0 } ;
Color to_return = { 0 } ;
to_return . r = lerp ( a . r , b . r , factor ) ;
to_return . r = lerp ( a . r , b . r , factor ) ;
to_return . g = lerp ( a . g , b . g , factor ) ;
to_return . g = lerp ( a . g , b . g , factor ) ;
to_return . b = lerp ( a . b , b . b , factor ) ;
to_return . b = lerp ( a . b , b . b , factor ) ;
to_return . a = lerp ( a . a , b . a , factor ) ;
to_return . a = lerp ( a . a , b . a , factor ) ;
return to_return ;
return to_return ;
}
}
static void set_color ( Color c )
static void set_color ( Color c )
{
{
sgp_set_color ( c . r , c . g , c . b , c . a ) ;
sgp_set_color ( c . r , c . g , c . b , c . a ) ;
}
}
# define WHITE \
# define WHITE \
( Color ) { . r = 1.0f , . g = 1.0f , . b = 1.0f , . a = 1.0f }
( Color ) { . r = 1.0f , . g = 1.0f , . b = 1.0f , . a = 1.0f }
# define RED \
# define RED \
( Color ) { . r = 1.0f , . g = 0.0f , . b = 0.0f , . a = 1.0f }
( Color ) { . r = 1.0f , . g = 0.0f , . b = 0.0f , . a = 1.0f }
# define GOLD colhex(255, 215, 0)
# define GOLD colhex(255, 215, 0)