@ -1,15 +1,30 @@
# pragma once
# include " ip settings.h"
# include " build settings.h"
# define MAX_BOX_TYPES 64
# define ZOOM_MIN 0.25f
# define ZOOM_MAX 1500.0f
# define MAX_PLAYERS 16
# define MAX_SUNS 8
# define MAX_ENTITIES 1024 * 25
# define BOX_SIZE 0.25f
# define PLAYER_SIZE ((V2){.x = BOX_SIZE, .y = BOX_SIZE})
# define MERGE_MAX_DIST (BOX_SIZE / 2.0f + 0.01f)
# define PLAYER_SIZE ((cpVect){.x = BOX_SIZE, .y = BOX_SIZE})
# define PLAYER_MASS 0.5f
# define PLAYER_JETPACK_FORCE 1.5 f
# define PLAYER_JETPACK_FORCE 2.0 f
# define PLAYER_JETPACK_TORQUE 0.05f
# define MISSILE_RANGE 4.0f
# define MISSILE_BURN_TIME 1.5f
# define MISSILE_ARM_TIME 0.5f
# define MISSILE_BURN_FORCE 4.0f
# define MISSILE_MASS 1.0f
// how many missiles grown per second
# define MISSILE_DAMAGE_THRESHOLD 0.2f
# define MISSILE_CHARGE_RATE 0.5f
// centered on the sprite
# define MISSILE_SPRITE_SIZE ((cpVect){.x = BOX_SIZE, .y = BOX_SIZE})
# define MISSILE_COLLIDER_SIZE ((cpVect){.x = BOX_SIZE * 0.5f, .y = BOX_SIZE * 0.5f})
// #define PLAYER_JETPACK_FORCE 20.0f
// distance at which things become geostationary and no more solar power!
# define PLAYER_JETPACK_ROTATION_ENERGY_PER_SECOND 0.2f
@ -22,47 +37,47 @@
# define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2f
# define BOX_MASS 1.0f
# define COLLISION_DAMAGE_SCALING 0.15f
# define THRUSTER_FORCE 1 2.0f
# define THRUSTER_FORCE 24 .0f
# define THRUSTER_ENERGY_USED_PER_SECOND 0.005f
# define GYROSCOPE_ENERGY_USED_PER_SECOND 0.005f
# define GYROSCOPE_TORQUE 0.5f
# define CLOAKING_ENERGY_USE 0.1f
# define CLOAKING_PANEL_SIZE BOX_SIZE *3.0f
# define CLOAKING_PANEL_SIZE BOX_SIZE * 3.0f
# define VISION_RADIUS 12.0f
# 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 SUN_RADIUS 10.0f
# define SUN_NO_MORE_ELECTRICITY_OR_GRAVITY 200.0f
# define INSTANT_DEATH_DISTANCE_FROM_SUN 2000.0f
# define SUN_POS ((V2){50.0f, 0.0f})
# ifdef NO_GRAVITY
# define SUN_GRAVITY_STRENGTH 0.0f
# else
# define SUN_GRAVITY_STRENGTH (11.0e2f)
# endif
# define GRAVITY_CONSTANT 0.1f
# define GRAVITY_SMALLEST 0.01f // used to determine when gravity is clamped to 0.0f
# define INSTANT_DEATH_DISTANCE_FROM_CENTER 4000.0f
# define SOLAR_ENERGY_PER_SECOND 0.09f
# define DAMAGE_TO_PLAYER_PER_BLOCK 0.1f
# define BATTERY_CAPACITY 1.5f
# define PLAYER_ENERGY_RECHARGE_PER_SECOND 0.2 f
# define PLAYER_ENERGY_RECHARGE_PER_SECOND 0.2
# define EXPLOSION_TIME 0.5f
# define EXPLOSION_PUSH_STRENGTH 5.0f
# define EXPLOSION_DAMAGE_PER_SEC 10.0f
# define EXPLOSION_RADIUS 1.0f
# define EXPLOSION_DAMAGE_THRESHOLD 0.2f // how much damage until it explodes
# define GOLD_UNLOCK_RADIUS 1.0f
# define TIME_BETWEEN_WORLD_SAVE 30.0f
# define MISSILE_EXPLOSION_PUSH 2.5f
# define MISSILE_EXPLOSION_RADIUS 0.4f
# define BOMB_EXPLOSION_PUSH 5.0f
# define BOMB_EXPLOSION_RADIUS 1.0f
// VOIP
# define VOIP_PACKET_BUFFER_SIZE 15 // audio. Must be bigger than 2
# define VOIP_EXPECTED_FRAME_COUNT 480
# define VOIP_SAMPLE_RATE 48000
# define VOIP_EXPECTED_FRAME_COUNT 240
# define VOIP_SAMPLE_RATE (48000 / 2)
// in seconds
# define VOIP_TIME_PER_PACKET (1.0f / ((float)((float)VOIP_SAMPLE_RATE / VOIP_EXPECTED_FRAME_COUNT)))
# define VOIP_TIME_PER_PACKET (1.0f / ((float)((float)VOIP_SAMPLE_RATE / VOIP_EXPECTED_FRAME_COUNT)))
# define VOIP_PACKET_MAX_SIZE 4000
# define VOIP_DISTANCE_WHEN_CANT_HEAR (VISION_RADIUS * 0.8f)
// multiplayer
# define MAX_REPREDICTION_TIME (TIMESTEP * 50.0f)
# define TICKS_BEHIND_DO_SNAP 6 // when this many ticks behind, instead of dilating time SNAP to the healthy ticks ahead
# define MAX_MS_SPENT_REPREDICTING 30.0f
# define TIME_BETWEEN_SEND_GAMESTATE (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
@ -80,36 +95,23 @@
# define THREADLOCAL __thread
# endif
// must make this header and set the target address, just #define SERVER_ADDRESS "127.0.0.1"
# include "ipsettings.h" // don't leak IP!
# define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
# include "miniaudio.h" // @Robust BAD. using miniaudio mutex construct for server thread synchronization. AWFUL!
# include "cpVect.h" // offers vector functions and types for the structs
// @Robust remove this include somehow, needed for sqrt and cos
# include <math.h>
# include <stdint.h> // tick is unsigned integer
# include <stdio.h> // logging on errors for functions
// defined in gamestate.c. Janky
# ifndef assert
# define assert(condition) __flight_assert(condition, __FILE__, __LINE__, #condition)
# endif
// including headers from headers bad
# ifndef SOKOL_GP_INCLUDED
void sgp_set_color ( float , float , float , float ) ;
// @Robust use double precision for all vectors, when passed back to sokol
// somehow automatically or easily cast to floats
typedef struct sgp_vec2
{
float x , y ;
} sgp_vec2 ;
typedef sgp_vec2 sgp_point ;
# endif
# ifndef CHIPMUNK_H
typedef void cpSpace ;
@ -132,9 +134,6 @@ typedef int opus_int32;
# endif
typedef sgp_vec2 V2 ;
typedef sgp_point P2 ;
# define Log(...) \
{ \
fprintf ( stdout , " %s:%d | " , __FILE__ , __LINE__ ) ; \
@ -143,7 +142,7 @@ typedef sgp_point P2;
enum BoxType
{
BoxInvalid , // zero initialized box is invalid!
BoxInvalid , // zero initialized box is invalid!
BoxHullpiece ,
BoxThruster ,
BoxBattery ,
@ -154,6 +153,8 @@ enum BoxType
BoxScanner ,
BoxGyroscope ,
BoxCloaking ,
BoxMissileLauncher ,
BoxMerge ,
BoxLast ,
} ;
@ -183,7 +184,7 @@ typedef struct EntityID
unsigned int index ; // index into the entity arena
} EntityID ;
static bool entityids_same ( EntityID a , EntityID b )
static inline bool entityids_same ( EntityID a , EntityID b )
{
return ( a . generation = = b . generation ) & & ( a . index = = b . index ) ;
}
@ -193,8 +194,8 @@ static bool entityids_same(EntityID a, EntityID b)
typedef struct InputFrame
{
uint64_t tick ;
V2 movement ;
float rotation ;
cpVect movement ;
double rotation ;
int take_over_squad ; // -1 means not taking over any squad
bool accept_cur_squad_invite ;
@ -202,7 +203,7 @@ typedef struct InputFrame
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 ;
V2 hand_pos ; // local to player transationally but not rotationally
cpVect hand_pos ; // local to player transationally but not rotationally
// @BeforeShip bounds check on the hand_pos so that players can't reach across the entire map
bool dobuild ;
@ -218,79 +219,95 @@ typedef struct Entity
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
double damage ; // used by box and player
cpBody * body ; // used by grid, player, and box
cpShape * shape ; // must be a box so shape_size can be set appropriately, and serialized
// players and boxes can be cloaked
// If this is within 2 timesteps of the current game time, the entity is invisible.
// If this is within 2 timesteps of the current game time, the entity is invisible.
double time_was_last_cloaked ;
enum Squad last_cloaked_by_squad ;
// for serializing the shape
// @Robust remove shape_parent_entity from this struct, use the shape's body to figure out
// what the shape's parent entity is
EntityID shape_parent_entity ; // can't be zero if shape is nonzero
V2 shape_size ;
cpVect shape_size ;
// player
bool is_player ;
enum Squad present ing_squad; // also controls what the player can see, because of cloaking!
enum Squad own ing_squad; // also controls what the player can see, because of cloaking!
EntityID currently_inside_of_box ;
enum Squad squad_invited_to ; // if squad none, then no squad invite
float goldness ; // how much the player is a winner
double goldness ; // how much the player is a winner
// explosion
bool is_explosion ;
V2 explosion_pos ;
V2 explosion_vel ;
float explosion_progresss ; // in seconds
cpVect explosion_pos ;
cpVect explosion_vel ;
double explosion_progress ; // in seconds
double explosion_push_strength ;
double explosion_radius ;
// sun
bool is_sun ;
cpVect sun_vel ;
cpVect sun_pos ;
double sun_mass ;
double sun_radius ;
// missile
bool is_missile ;
double time_burned_for ; // until MISSILE_BURN_TIME. Before MISSILE_ARM_TIME cannot explode
// grids
bool is_grid ;
float total_energy_capacity ;
double total_energy_capacity ;
EntityID boxes ;
// boxes
bool is_box ;
enum Squad owning_squad ; // which squad owns this box
enum BoxType box_type ;
bool is_platonic ; // can't be destroyed, unaffected by physical forces
bool is_platonic ; // can't be destroyed, unaffected by physical forces
bool always_visible ; // always serialized to the player. @Robust check if not used
EntityID next_box ; // for the grid!
EntityID prev_box ; // doubly linked so can remove in middle of chain
EntityID next_box ; // for the grid!
EntityID prev_box ; // doubly linked so can remove in middle of chain
enum CompassRotation compass_rotation ;
bool indestructible ;
// merger
bool wants_disconnect ; // don't serialized, termporary value not used across frames
// missile launcher
double missile_construction_charge ;
// used by medbay and cockpit
EntityID player_who_is_inside_of_me ;
EntityID player_who_is_inside_of_me ;
// only serialized when box_type is thruster or gyroscope, used for both. Thrust
// can mean rotation thrust!
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
double wanted_thrust ; // the thrust command applied to the thruster
double thrust ; // the actual thrust it can provide based on energy sources in the grid
// only serialized when box_type is battery
float energy_used ; // battery, between 0 battery capacity. You have to look through code to figure out what that is! haha sucker!
double energy_used ; // battery, between 0 battery capacity. You have to look through code to figure out what that is! haha sucker!
// only serialized when box_type is solar panel
float sun_amount ; // solar panel, between 0 and 1
double sun_amount ; // solar panel, between 0 and 1
// cloaking only
float cloaking_power ; // 0.0 if unable to be used because no power, 1.0 if fully cloaking!
double cloaking_power ; // 0.0 if unable to be used because no power, 1.0 if fully cloaking!
// scanner only stuff!
EntityID currently_scanning ;
float currently_scanning_progress ; // when 1.0, scans it!
double currently_scanning_progress ; // when 1.0, scans it!
BOX_UNLOCKS_TYPE blueprints_learned ; // @Robust make this same type as blueprints
float scanner_head_rotate_speed ; // not serialized, cosmetic
float scanner_head_rotate ;
V2 platonic_nearest_direction ; // normalized
float platonic_detection_strength ; // from zero to one
double scanner_head_rotate_speed ; // not serialized, cosmetic
double scanner_head_rotate ;
cpVect platonic_nearest_direction ; // normalized
double platonic_detection_strength ; // from zero to one
} Entity ;
typedef struct Player
{
bool connected ;
@ -300,21 +317,35 @@ typedef struct Player
EntityID last_used_medbay ;
InputFrame input ;
} Player ;
// use i.sun to access the current sun's pointer
typedef struct SunIter
{
int i ;
Entity * sun ;
} SunIter ;
# define SUNS_ITER(gs_ptr) \
for ( SunIter i = { 0 } ; i . i < MAX_SUNS ; i . i + + ) \
if ( ( i . sun = get_entity ( gs_ptr , ( gs_ptr ) - > suns [ i . i ] ) ) ! = NULL )
// gotta update the serialization functions when this changes
typedef struct GameState
{
cpSpace * space ;
// @Robust for the integer tick, also store a float for how much time has been processed.
// Like a whole timestep then a float for subtimestep
double time ; // @Robust separate tick integer not prone to precision issues. Could be very large as is saved to disk!
// @Robust for the integer tick, also store a double for how much time has been processed.
// Like a whole timestep then a double for subtimestep
uint64_t tick ;
double subframe_time ;
V2 goldpos ;
cpVect goldpos ;
Player players [ MAX_PLAYERS ] ;
V2 platonic_positions [ MAX_BOX_TYPES ] ; // don't want to search over every entity to get the nearest platonic box!
EntityID suns [ MAX_SUNS ] ; // can't have holes in it for serialization
cpVect platonic_positions [ MAX_BOX_TYPES ] ; // don't want to search over every entity to get the nearest platonic box!
bool server_side_computing ; // some things only the server should know and calculate, like platonic locations
// Entity arena
@ -335,7 +366,7 @@ typedef struct GameState
# define TAU (PI * 2.0f)
// returns in radians
static float rotangle ( enum CompassRotation rot )
static inline double rotangle ( enum CompassRotation rot )
{
switch ( rot )
{
@ -390,51 +421,62 @@ void create_initial_world(GameState *gs);
void initialize ( struct GameState * gs , void * entity_arena , size_t entity_arena_size ) ;
void destroy ( struct GameState * gs ) ;
void process_fixed_timestep ( GameState * gs ) ;
void process ( struct GameState * gs , float dt ) ; // does in place
Entity * closest_box_to_point_in_radius ( struct GameState * gs , V2 point , float radius , bool ( * filter_func ) ( Entity * ) ) ;
// if is subframe, doesn't always increment the tick. When enough
// subframe time has been processed, increments the tick
void process ( struct GameState * gs , double dt ) ; // does in place
Entity * closest_box_to_point_in_radius ( struct GameState * gs , cpVect point , double radius , bool ( * filter_func ) ( Entity * ) ) ;
uint64_t tick ( struct GameState * gs ) ;
double elapsed_time ( GameState * gs ) ;
double sun_dist_no_gravity ( Entity * sun ) ;
// all of these return if successful or not
bool server_to_client_serialize ( struct ServerToClient * msg , unsigned 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 , unsigned char * bytes , size_t max_len , bool from_disk ) ;
bool client_to_server_deserialize ( GameState * gs , struct ClientToServer * msg , unsigned char * bytes , size_t max_len ) ;
bool client_to_server_serialize ( GameState * gs , struct ClientToServer * msg , unsigned char * bytes , size_t * out_len , size_t max_len ) ;
bool server_to_client_serialize ( struct ServerToClient * msg , unsigned 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 , unsigned char * bytes , size_t max_len , bool from_disk ) ;
bool client_to_server_deserialize ( GameState * gs , struct ClientToServer * msg , unsigned char * bytes , size_t max_len ) ;
bool client_to_server_serialize ( GameState * gs , struct ClientToServer * msg , unsigned char * bytes , size_t * out_len , size_t max_len ) ;
// entities
bool is_burning ( Entity * missile ) ;
Entity * get_entity ( struct GameState * gs , EntityID id ) ;
Entity * new_entity ( struct GameState * gs ) ;
EntityID get_id ( struct GameState * gs , Entity * e ) ;
V2 entity_pos ( Entity * e ) ;
void entity_set_rotation ( Entity * e , float rot ) ;
void entity_set_pos ( Entity * e , V2 pos ) ;
float entity_rotation ( Entity * e ) ;
void entity_ensure_in_orbit ( Entity * e ) ;
cpVect entity_pos ( Entity * e ) ;
void entity_set_rotation ( Entity * e , double rot ) ;
void entity_set_pos ( Entity * e , cpVect pos ) ;
double entity_rotation ( Entity * e ) ;
void entity_ensure_in_orbit ( 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 BOXES_ITER(gs, cur, grid_entity_ptr) BOX_CHAIN_ITER(gs, cur, (grid_entity_ptr)->boxes)
typedef struct LauncherTarget
{
bool target_found ;
double facing_angle ; // in global coords
} LauncherTarget ;
LauncherTarget missile_launcher_target ( GameState * gs , Entity * launcher ) ;
// grid
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 , cpVect pos ) ;
Entity * box_grid ( Entity * box ) ;
V2 grid_com ( Entity * grid ) ;
V2 grid_vel ( Entity * grid ) ;
V2 box_vel ( Entity * box ) ;
V2 grid_local_to_world ( Entity * grid , V2 local ) ;
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
float entity_angular_velocity ( Entity * grid ) ;
V2 entity_shape_pos ( Entity * box ) ;
float box_rotation ( Entity * box ) ;
cpVect grid_com ( Entity * grid ) ;
cpVect grid_vel ( Entity * grid ) ;
cpVect box_vel ( Entity * box ) ;
cpVect grid_local_to_world ( Entity * grid , cpVect local ) ;
cpVect grid_world_to_local ( Entity * grid , cpVect world ) ;
cpVect grid_snapped_box_pos ( Entity * grid , cpVect world ) ; // returns the snapped pos in world coords
double entity_angular_velocity ( Entity * grid ) ;
cpVect entity_shape_pos ( Entity * box ) ;
double box_rotation ( Entity * box ) ;
// thruster
V2 box_facing_vector ( Entity * box ) ;
V2 thruster_force ( Entity * box ) ;
cpVect box_facing_vector ( Entity * box ) ;
cpVect thruster_force ( Entity * box ) ;
// debug draw
void dbg_drawall ( ) ;
void dbg_line ( V2 from , V2 to ) ;
void dbg_rect ( V2 center ) ;
void dbg_line ( cpVect from , cpVect to ) ;
void dbg_rect ( cpVect center ) ;
typedef struct ServerThreadInfo
{
@ -446,10 +488,10 @@ typedef struct ServerThreadInfo
typedef struct AABB
{
float x , y , width , height ;
double x , y , width , height ;
} AABB ;
static AABB centered_at ( V2 point , V2 size )
static inline AABB centered_at ( cpVect point , cpVect size )
{
return ( AABB ) {
. x = point . x - size . x / 2.0f ,
@ -459,101 +501,46 @@ static AABB centered_at(V2 point, V2 size)
} ;
}
static bool has_point ( AABB aabb , V2 point )
static inline bool has_point ( AABB aabb , cpVect point )
{
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 inline double cpvprojectval ( cpVect vec , cpVect onto )
{
return ( V2 ) {
. x = a . x + b . x ,
. y = a . y + b . y ,
} ;
double length_onto = cpvlength ( onto ) ;
return cpvdot ( vec , onto ) / ( length_onto * length_onto ) ;
}
static V2 V2scale ( V2 a , float f )
// spins around by theta
static inline cpVect cpvspin ( cpVect vec , double theta )
{
return ( V2 ) {
. x = a . x * f ,
. y = a . y * f ,
} ;
}
static float V2lengthsqr ( V2 v )
{
return v . x * v . x + v . y * v . y ;
}
static float V2length ( V2 v )
{
return sqrtf ( V2lengthsqr ( v ) ) ;
}
static V2 V2normalize ( V2 v )
{
return V2scale ( v , 1.0f / V2length ( v ) ) ;
}
static float V2dot ( V2 a , V2 b )
{
return a . x * b . x + a . y * b . y ;
}
static float V2projectvalue ( V2 vec , V2 onto )
{
float length_onto = V2length ( onto ) ;
return V2dot ( vec , onto ) / ( length_onto * length_onto ) ;
}
static V2 V2project ( V2 vec , V2 onto )
{
return V2scale ( onto , V2projectvalue ( vec , onto ) ) ;
}
static V2 V2rotate ( V2 vec , float theta )
{
return ( V2 ) {
. x = vec . x * cosf ( theta ) - vec . y * sinf ( theta ) ,
. y = vec . x * sinf ( theta ) + vec . y * cosf ( theta ) ,
return ( cpVect ) {
. x = vec . x * cos ( theta ) - vec . y * sin ( theta ) ,
. y = vec . x * sin ( theta ) + vec . y * cos ( theta ) ,
} ;
}
// also known as atan2
static float V2angle ( V2 vec )
static inline double cpvangle ( cpVect vec )
{
return atan2 f ( vec . y , vec . x ) ;
return atan2 ( vec . y , vec . x ) ;
}
static V2 V2sub ( V2 a , V2 b )
static double sign ( double f )
{
return ( V2 ) {
. x = a . x - b . x ,
. y = a . y - b . y ,
} ;
}
static bool V2equal ( V2 a , V2 b , float eps )
{
return V2length ( V2sub ( a , b ) ) < eps ;
}
static inline float clamp01 ( float f )
{
return fmaxf ( 0.0f , fminf ( f , 1.0f ) ) ;
if ( f > = 0.0f )
return 1.0f ;
else
return - 1.0f ;
}
static float V2distsqr ( V2 from , V2 to )
static inline double clamp01 ( double f )
{
return V2lengthsqr( V2sub ( to , from ) ) ;
return fmax ( 0.0f , fmin ( f , 1.0f ) ) ;
}
static float V2dist ( V2 from , V2 to )
{
return sqrtf ( V2distsqr ( from , to ) ) ;
}
static inline float clamp ( float f , float minimum , float maximum )
static inline double clamp ( double f , double minimum , double maximum )
{
if ( f < minimum )
return minimum ;
@ -562,77 +549,43 @@ static inline float clamp(float f, float minimum, float maximum)
return f ;
}
static float fract ( float f )
{
return f - floorf ( f ) ;
}
static float lerp ( float a , float b , float f )
static inline double cpvanglediff ( cpVect a , cpVect b )
{
return a * ( 1.0f - f ) + ( b * f ) ;
double acos_input = cpvdot ( a , b ) / ( cpvlength ( a ) * cpvlength ( b ) ) ;
acos_input = clamp ( acos_input , - 1.0f , 1.0f ) ;
assert ( acos_input > = - 1.0f & & acos_input < = 1.0f ) ;
return acos ( acos_input ) * sign ( cpvdot ( a , b ) ) ;
}
static float lerp_angle ( float p_from , float p_to , float p_weight )
static inline double fract ( double f )
{
float difference = fmodf ( p_to - p_from , ( float ) TAU ) ;
float distance = fmodf ( 2.0f * difference , ( float ) TAU ) - difference ;
return p_from + distance * p_weight ;
return f - floor ( f ) ;
}
static V2 V2floor ( V2 p )
static inline double lerp ( double a , double b , double f )
{
return ( V2 ) { floorf ( p . x ) , floorf ( p . y ) } ;
return a * ( 1.0f - f ) + ( b * f ) ;
}
static V2 V2fract ( V2 p )
static inline double lerp_angle ( double p_from , double p_to , double p_weight )
{
return ( V2 ) { fract ( p . x ) , fract ( p . y ) } ;
}
/*
float noise ( V2 p )
{
V2 id = V2floor ( p ) ;
V2 f = V2fract ( p ) ;
V2 u = V2dot ( f , f ) * ( 3.0f - 2.0f * f ) ;
return mix ( mix ( random ( id + V2 ( 0.0 , 0.0 ) ) ,
random ( id + V2 ( 1.0 , 0.0 ) ) , u . x ) ,
mix ( random ( id + V2 ( 0.0 , 1.0 ) ) ,
random ( id + V2 ( 1.0 , 1.0 ) ) , u . x ) ,
u . y ) ;
double difference = fmod ( p_to - p_from , ( float ) TAU ) ;
double distance = fmod ( 2.0f * difference , ( float ) TAU ) - difference ;
return p_from + distance * p_weight ;
}
float fbm ( V2 p )
static inline cpVect cpvfloor ( cpVect p )
{
float f = 0.0 ;
float gat = 0.0 ;
for ( float octave = 0. ; octave < 5. ; + + octave )
{
float la = pow ( 2.0 , octave ) ;
float ga = pow ( 0.5 , octave + 1. ) ;
f + = ga * noise ( la * p ) ;
gat + = ga ;
}
f = f / gat ;
return f ;
return ( cpVect ) { floor ( p . x ) , floor ( p . y ) } ;
}
*/
static V2 V2lerp ( V2 a , V2 b , float factor )
static inline cpVect cpvfract ( cpVect p )
{
V2 to_return = { 0 } ;
to_return . x = lerp ( a . x , b . x , factor ) ;
to_return . y = lerp ( a . y , b . y , factor ) ;
return to_return ;
return ( cpVect ) { fract ( p . x ) , fract ( p . y ) } ;
}
// for random generation
static float hash11 ( float p )
static inline double hash11 ( double p )
{
p = fract ( p * .1031f ) ;
p * = p + 33.33f ;
@ -640,50 +593,7 @@ static float hash11(float p)
return fract ( p ) ;
}
typedef struct Color
{
float r , g , b , a ;
} Color ;
static Color colhex ( int r , int g , int b )
{
return ( Color ) {
. r = ( float ) r / 255.0f ,
. g = ( float ) g / 255.0f ,
. b = ( float ) b / 255.0f ,
. a = 1.0f ,
} ;
}
static Color colhexcode ( int hexcode )
static inline double deg2rad ( double deg )
{
// 0x020509;
int r = ( hexcode > > 16 ) & 0xFF ;
int g = ( hexcode > > 8 ) & 0xFF ;
int b = ( hexcode > > 0 ) & 0xFF ;
return colhex ( r , g , b ) ;
return ( deg / 360.0f ) * 2.0f * PI ;
}
static Color Collerp ( Color a , Color b , float factor )
{
Color to_return = { 0 } ;
to_return . r = lerp ( a . r , b . r , factor ) ;
to_return . g = lerp ( a . g , b . g , factor ) ;
to_return . b = lerp ( a . b , b . b , factor ) ;
to_return . a = lerp ( a . a , b . a , factor ) ;
return to_return ;
}
static void set_color ( Color c )
{
sgp_set_color ( c . r , c . g , c . b , c . a ) ;
}
# define WHITE \
( Color ) { . r = 1.0f , . g = 1.0f , . b = 1.0f , . a = 1.0f }
# define RED \
( Color ) { . r = 1.0f , . g = 0.0f , . b = 0.0f , . a = 1.0f }
# define BLUE \
( Color ) { . r = 0.0f , . g = 0.0f , . b = 1.0f , . a = 1.0f }
# define GOLD colhex(255, 215, 0)