Merge block fixes. Holy fuck!

main
Cameron Murphy Reikes 2 years ago
parent a7232b0831
commit 51b5c1494e

Binary file not shown.

@ -485,14 +485,13 @@ void box_create(GameState *gs, Entity *new_box, Entity *grid, V2 pos)
box_add_to_boxes(gs, grid, new_box);
}
V2 box_compass_vector(Entity *box)
{
assert(box->is_box);
V2 to_return = (V2){.x = 1.0f, .y = 0.0f};
to_return = V2rotate(to_return, rotangle(box->compass_rotation));
return to_return;
}
@ -593,12 +592,12 @@ static void grid_correct_for_holes(GameState *gs, struct Entity *grid)
}
Entity *newbox = get_entity(gs, box_in_direction);
if(newbox != NULL && newbox->box_type == BoxMerge && newbox->wants_disconnect && V2equal(V2scale(box_compass_vector(newbox), -1.0f), dir,0.01f))
if (newbox != NULL && newbox->box_type == BoxMerge && newbox->wants_disconnect && V2equal(V2scale(box_compass_vector(newbox), -1.0f), dir, 0.01f))
{
newbox = NULL;
}
if (newbox != NULL)
{
box_remove_from_boxes(gs, newbox);
@ -820,6 +819,7 @@ float box_rotation(Entity *box)
{
return (float)cpBodyGetAngle(cpShapeGetBody(box->shape));
}
V2 entity_pos(Entity *e)
{
if (e->is_box)
@ -1590,9 +1590,12 @@ bool client_to_server_deserialize(GameState *gs, struct ClientToServer *msg, uns
}
}
static THREADLOCAL Entity *grid_to_exclude = NULL;
static bool merge_filter(Entity *potential_merge)
{
return potential_merge->is_box && potential_merge->box_type == BoxMerge;
assert(grid_to_exclude != NULL);
assert(grid_to_exclude->is_grid);
return potential_merge->is_box && potential_merge->box_type == BoxMerge && box_grid(potential_merge) != grid_to_exclude;
}
static void cloaking_shield_callback_func(cpShape *shape, cpContactPointSet *points, void *data)
@ -1687,8 +1690,6 @@ static void do_explosion(GameState *gs, Entity *explosion, float dt)
cpBodyFree(tmpbody);
}
V2 box_facing_vector(Entity *box)
{
assert(box->is_box);
@ -1700,6 +1701,40 @@ V2 box_facing_vector(Entity *box)
return to_return;
}
enum CompassRotation facing_vector_to_compass(Entity *grid_to_transplant_to, Entity *grid_facing_vector_from, V2 facing_vector)
{
assert(grid_to_transplant_to->body != NULL);
assert(grid_to_transplant_to->is_grid);
V2 local_to_from = grid_world_to_local(grid_facing_vector_from, V2add(entity_pos(grid_facing_vector_from), facing_vector));
Log("local %f %f\n", local_to_from.x, local_to_from.y);
V2 from_target = V2add(entity_pos(grid_to_transplant_to), facing_vector);
V2 local_target = grid_world_to_local(grid_to_transplant_to, from_target);
V2 local_facing = local_target;
enum CompassRotation dirs[] = {
Right,
Left,
Up,
Down};
int smallest = -1;
float smallest_dist = INFINITY;
for (int i = 0; i < ARRLEN(dirs); i++)
{
V2 point = V2rotate((V2){.x = 1.0f}, rotangle(dirs[i]));
float dist = V2dist(point, local_facing);
if (dist < smallest_dist)
{
smallest_dist = dist;
smallest = i;
}
}
assert(smallest != -1);
return dirs[smallest];
}
V2 thruster_force(Entity *box)
{
return V2scale(box_facing_vector(box), -box->thrust * THRUSTER_FORCE);
@ -1797,11 +1832,14 @@ V2 box_vel(Entity *box)
void create_bomb_station(GameState *gs, V2 pos, enum BoxType platonic_type)
{
enum CompassRotation rot = Right;
#define BOX_AT_TYPE(grid, pos, type) \
{ \
Entity *box = new_entity(gs); \
box_create(gs, box, grid, pos); \
box->box_type = type; \
box->compass_rotation = rot; \
box->indestructible = indestructible; \
}
#define BOX_AT(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece)
@ -1842,14 +1880,7 @@ void create_bomb_station(GameState *gs, V2 pos, enum BoxType platonic_type)
void create_hard_shell_station(GameState *gs, V2 pos, enum BoxType platonic_type)
{
#define BOX_AT_TYPE(grid, pos, type) \
{ \
Entity *box = new_entity(gs); \
box_create(gs, box, grid, pos); \
box->box_type = type; \
box->indestructible = indestructible; \
}
#define BOX_AT(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece)
enum CompassRotation rot = Right;
bool indestructible = false;
Entity *grid = new_entity(gs);
@ -1884,6 +1915,40 @@ void create_initial_world(GameState *gs)
create_bomb_station(gs, (V2){-5.0f, 0.0f}, BoxExplosive);
create_bomb_station(gs, (V2){0.0f, 5.0f}, BoxGyroscope);
create_hard_shell_station(gs, (V2){-5.0f, 5.0f}, BoxCloaking);
bool indestructible = false;
float theta = deg2rad(65.0f);
V2 from = (V2){BOX_SIZE * 4.0f, -1};
enum CompassRotation rot = Right;
{
Entity *grid = new_entity(gs);
grid_create(gs, grid);
entity_set_pos(grid, V2add(from, V2rotate((V2){.x = -BOX_SIZE * 9.0f}, theta)));
cpBodySetAngle(grid->body, theta + PI);
entity_ensure_in_orbit(grid);
rot = Left;
BOX_AT_TYPE(grid, ((V2){0.0f, 0.0f}), BoxMerge);
BOX_AT(grid, ((V2){0.0f, -BOX_SIZE}));
BOX_AT_TYPE(grid, ((V2){BOX_SIZE, 0.0f}), BoxMerge);
}
{
Entity *grid = new_entity(gs);
grid_create(gs, grid);
entity_set_pos(grid, from);
cpBodySetAngle(grid->body, theta);
entity_ensure_in_orbit(grid);
rot = Left;
BOX_AT_TYPE(grid, ((V2){-BOX_SIZE, 0.0f}), BoxMerge);
rot = Down;
BOX_AT_TYPE(grid, ((V2){0.0f, 0.0f}), BoxMerge);
rot = Up;
BOX_AT_TYPE(grid, ((V2){0.0f, BOX_SIZE}), BoxMerge);
cpBodySetVelocity(grid->body, v2_to_cp(V2rotate((V2){-0.4f, 0.0f}, theta)));
}
#else
create_bomb_station(gs, (V2){-50.0f, 0.0f}, BoxExplosive);
create_hard_shell_station(gs, (V2){0.0f, 100.0f}, BoxGyroscope);
@ -2015,7 +2080,6 @@ void process(GameState *gs, float dt)
assert(potential_seat->exists);
assert(potential_seat->is_box);
assert(potential_seat->box_type == BoxMerge);
potential_seat->wants_disconnect = false;
}
if (potential_seat->box_type == BoxCockpit || potential_seat->box_type == BoxMedbay) // @Robust check by feature flag instead of box type
{
@ -2286,28 +2350,62 @@ void process(GameState *gs, float dt)
if (e->box_type == BoxMerge)
{
Entity *from_merge = e;
assert(from_merge != NULL);
grid_to_exclude = box_grid(from_merge);
Entity *other_merge = closest_box_to_point_in_radius(gs, entity_pos(from_merge), MERGE_MAX_DIST, merge_filter);
if (box_grid(from_merge) != box_grid(other_merge))
if (other_merge == NULL && from_merge->wants_disconnect)
from_merge->wants_disconnect = false;
if (!from_merge->wants_disconnect && other_merge != NULL && !other_merge->wants_disconnect)
{
assert(box_grid(from_merge) != box_grid(other_merge));
Entity *from_grid = box_grid(from_merge);
Entity *other_grid = box_grid(other_merge);
// the merges are near eachother, but are they facing eachother...
bool from_facing_other = V2dot(box_facing_vector(from_merge), V2normalize(V2sub(entity_pos(other_merge), entity_pos(from_merge)))) > 0.8f;
bool other_facing_from = V2dot(box_facing_vector(other_merge), V2normalize(V2sub(entity_pos(from_merge), entity_pos(other_merge)))) > 0.8f;
if (from_facing_other && other_facing_from)
// using this stuff to detect if when the other grid's boxes are snapped, they'll be snapped
// to be next to the from merge box
V2 actual_new_pos = grid_snapped_box_pos(from_grid, entity_pos(other_merge));
V2 needed_new_pos = V2add(entity_pos(from_merge), V2scale(box_facing_vector(from_merge), BOX_SIZE));
if (from_facing_other && other_facing_from && V2equal(needed_new_pos, actual_new_pos, 0.01f))
{
// do the merge
V2 facing_vector_needed = V2scale(box_facing_vector(from_merge), -1.0f);
V2 current_facing_vector = box_facing_vector(other_merge);
float angle_diff = V2anglediff(current_facing_vector, facing_vector_needed);
if(angle_diff == FLT_MIN)
angle_diff = 0.0f;
assert(!isnan(angle_diff));
cpBodySetAngle(other_grid->body, cpBodyGetAngle(other_grid->body) + angle_diff);
V2 moved_because_angle_change = V2sub(needed_new_pos, entity_pos(other_merge));
cpBodySetPosition(other_grid->body, v2_to_cp(V2add(entity_pos(other_grid), moved_because_angle_change)));
// V2 snap_movement_vect = V2sub(actual_new_pos, entity_pos(other_merge));
V2 snap_movement_vect = (V2){0};
Entity *cur = get_entity(gs, other_grid->boxes);
Entity *new_grid = box_grid(from_merge);
Entity *old_grid = box_grid(other_merge);
Entity *cur = get_entity(gs, old_grid->boxes);
old_grid->boxes = (EntityID){0};
other_grid->boxes = (EntityID){0};
while (cur != NULL)
{
Entity *next = get_entity(gs, cur->next_box);
V2 world = entity_pos(cur);
box_create(gs, cur, new_grid, grid_world_to_local(new_grid, grid_snapped_box_pos(new_grid, world))); // destroys next/prev fields on cur
enum CompassRotation new_rotation = facing_vector_to_compass(from_grid, other_grid, box_facing_vector(cur));
cur->compass_rotation = new_rotation;
V2 new_cur_pos = grid_snapped_box_pos(from_grid, V2add(snap_movement_vect, world));
box_create(gs, cur, from_grid, grid_world_to_local(from_grid, new_cur_pos)); // destroys next/prev fields on cur
assert(box_grid(cur) == box_grid(from_merge));
cur = next;
}
entity_destroy(gs, old_grid);
entity_destroy(gs, other_grid);
}
}
}

@ -6,7 +6,7 @@
#define MAX_PLAYERS 16
#define MAX_ENTITIES 1024 * 25
#define BOX_SIZE 0.25f
#define MERGE_MAX_DIST 0.35f
#define MERGE_MAX_DIST (BOX_SIZE / 2.0f + 0.01f)
#define PLAYER_SIZE ((V2){.x = BOX_SIZE, .y = BOX_SIZE})
#define PLAYER_MASS 0.5f
#define PLAYER_JETPACK_FORCE 1.5f
@ -17,10 +17,10 @@
#define MISSILE_MASS 1.0f
// how many missiles grown per second
#define MISSILE_DAMAGE_THRESHOLD 0.2f
#define MISSILE_CHARGE_RATE 0.5f
#define MISSILE_CHARGE_RATE 0.5f
// centered on the sprite
#define MISSILE_SPRITE_SIZE ((V2){.x = BOX_SIZE, .y = BOX_SIZE})
#define MISSILE_COLLIDER_SIZE ((V2){.x = BOX_SIZE*0.5f, .y = BOX_SIZE*0.5f})
#define MISSILE_COLLIDER_SIZE ((V2){.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
@ -38,7 +38,7 @@
#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
@ -65,10 +65,11 @@
// 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)
@ -156,7 +157,7 @@ typedef sgp_point P2;
enum BoxType
{
BoxInvalid, // zero initialized box is invalid!
BoxInvalid, // zero initialized box is invalid!
BoxHullpiece,
BoxThruster,
BoxBattery,
@ -236,13 +237,12 @@ typedef struct Entity
float 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
@ -265,7 +265,7 @@ typedef struct Entity
// missile
bool is_missile;
float time_burned_for; // until MISSILE_BURN_TIME
// grids
bool is_grid;
float total_energy_capacity;
@ -274,47 +274,46 @@ typedef struct Entity
// boxes
bool is_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
float 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
// 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!
float 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
float 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!
// scanner only stuff!
EntityID currently_scanning;
float currently_scanning_progress; // when 1.0, scans it!
float 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_speed; // not serialized, cosmetic
float scanner_head_rotate;
V2 platonic_nearest_direction; // normalized
V2 platonic_nearest_direction; // normalized
float platonic_detection_strength; // from zero to one
} Entity;
typedef struct Player
{
bool connected;
@ -336,9 +335,9 @@ typedef struct GameState
V2 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!
bool server_side_computing; // some things only the server should know and calculate, like platonic locations
// Entity arena
@ -415,14 +414,14 @@ void initialize(struct GameState *gs, void *entity_arena, size_t entity_arena_si
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*));
Entity *closest_box_to_point_in_radius(struct GameState *gs, V2 point, float radius, bool (*filter_func)(Entity *));
uint64_t tick(struct GameState *gs);
// 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);
@ -564,6 +563,14 @@ static V2 V2sub(V2 a, V2 b)
};
}
static float sign(float f)
{
if (f >= 0.0f)
return 1.0f;
else
return -1.0f;
}
static bool V2equal(V2 a, V2 b, float eps)
{
return V2length(V2sub(a, b)) < eps;
@ -593,6 +600,14 @@ static inline float clamp(float f, float minimum, float maximum)
return f;
}
static float V2anglediff(V2 a, V2 b)
{
float acos_input = V2dot(a, b) / (V2length(a) * V2length(b));
acos_input = clamp(acos_input, -1.0f, 1.0f);
assert(acos_input >= -1.0f && acos_input <= 1.0f);
return acosf(acos_input) * sign(V2dot(a, b));
}
static float fract(float f)
{
return f - floorf(f);
@ -711,10 +726,24 @@ 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 }
static float deg2rad(float deg)
{
return (deg / 360.0f) * 2.0f * PI;
}
#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)

Loading…
Cancel
Save