Grids with gaps in them no longer valid

main
Cameron Murphy Reikes 2 years ago
parent 0c53810aa5
commit 3c569fdb9f

@ -35,6 +35,23 @@ static struct Box *getbox(cpShape *shape)
return (struct Box *)cpShapeGetUserData(shape);
}
static struct Grid *find_empty_grid(struct GameState *gs)
{
// @Robust better memory mgmt
struct Grid *empty_grid = NULL;
for (int ii = 0; ii < MAX_GRIDS; ii++)
{
if (gs->grids[ii].body == NULL)
{
empty_grid = &gs->grids[ii];
break;
}
}
// @Robust cleanly fail when not enough grids
assert(empty_grid != NULL);
return empty_grid;
}
static int grid_num_boxes(struct Grid *g)
{
int to_return = 0;
@ -69,14 +86,187 @@ void grid_destroy(cpSpace *space, struct Grid *grid)
grid->body = NULL;
}
// removes boxe from grid, then ensures that the rule that grids must not have
// holes in them is applied.
// uses these forward declared serialization functions to duplicate a box
void ser_box(char **out, struct Box *b);
void des_box(char **in, struct Box *new_box, struct GameState *gs, struct Grid *g);
static void grid_remove_box(cpSpace *space, struct Grid *grid, struct Box *box)
{
box_destroy(space, box);
if (grid_num_boxes(grid) == 0)
struct GameState *gs = (struct GameState *)cpSpaceGetUserData(space);
int num_boxes = grid_num_boxes(grid);
if (num_boxes == 0)
{
grid_destroy(space, grid);
return;
}
if (num_boxes == 1)
return;
// could be a gap between boxes in the grid, separate into multiple grids
// goal: create list of "real grids" from this grid that have boxes which are
// ONLY connected horizontally and vertically. whichever one of these "real grids"
// has the most blocks stays the current grid, so
// if a player is inhabiting this ship it stays that ship.
// The other "real grids" are allocated as new grids
#define MAX_SEPARATE_GRIDS 8
struct Box *separate_grids[MAX_SEPARATE_GRIDS][MAX_BOXES_PER_GRID] = {0};
int cur_separate_grid_index = 0;
int processed_boxes = 0;
struct Box **biggest_separate_grid = separate_grids[0];
int biggest_separate_grid_length = 0;
// process all boxes into separate, but correctly connected, grids
while (processed_boxes < num_boxes)
{
// grab an unprocessed box, one not in separate_grids, to start the flood fill
struct Box *unprocessed = NULL;
for (int i = 0; i < MAX_BOXES_PER_GRID; i++)
{
SKIPNULL(grid->boxes[i].shape);
struct Box *cur = &grid->boxes[i];
bool cur_has_been_processed = false;
for (int sep_i = 0; sep_i < MAX_SEPARATE_GRIDS; sep_i++)
{
for (int sep_box_i = 0; sep_box_i < MAX_BOXES_PER_GRID; sep_box_i++)
{
if (cur == separate_grids[sep_i][sep_box_i])
{
cur_has_been_processed = true;
break;
}
}
if (cur_has_been_processed)
break;
}
if (!cur_has_been_processed)
{
unprocessed = cur;
break;
}
}
assert(unprocessed != NULL);
// flood fill from this unprocessed box, adding each result to a new separate grid
// https://en.wikipedia.org/wiki/Flood_fill
struct Box **cur_separate_grid = separate_grids[cur_separate_grid_index];
cur_separate_grid_index++;
int separate_grid_i = 0;
{
// queue stuff @Robust use factored datastructure
struct Box *Q[MAX_BOXES_PER_GRID] = {0};
int Q_i = 0;
Q[Q_i] = unprocessed;
Q_i++;
struct Box *N = NULL;
while (Q_i > 0)
{
N = Q[Q_i - 1];
Q_i--;
if (true) // if node "inside", this is always true
{
cur_separate_grid[separate_grid_i] = N;
separate_grid_i++;
processed_boxes++;
V2 cur_local_pos = box_local_pos(N);
const V2 dirs[] = {
(V2){.x = -1.0f, .y = 0.0f},
(V2){.x = 1.0f, .y = 0.0f},
(V2){.x = 0.0f, .y = 1.0f},
(V2){.x = 0.0f, .y = -1.0f},
};
int num_dirs = sizeof(dirs) / sizeof(*dirs);
for (int ii = 0; ii < num_dirs; ii++)
{
V2 dir = dirs[ii];
// @Robust faster method, not O(N^2), of getting the box
// in the direction currently needed
V2 wanted_local_pos = V2add(cur_local_pos, V2scale(dir, BOX_SIZE));
struct Box *box_in_direction = NULL;
for (int iii = 0; iii < MAX_BOXES_PER_GRID; iii++)
{
SKIPNULL(grid->boxes[iii].shape);
if (V2cmp(box_local_pos(&grid->boxes[iii]), wanted_local_pos, 0.01f))
{
box_in_direction = &grid->boxes[iii];
break;
}
}
if (box_in_direction != NULL)
{
// make sure not already added to the separate grid
bool already_in_separate_grid = false;
for (int sepgrid_i = 0; sepgrid_i < MAX_BOXES_PER_GRID; sepgrid_i++)
{
if (cur_separate_grid[sepgrid_i] == NULL)
break; // assumed to be end of the current separate grid list
if (cur_separate_grid[sepgrid_i] == box_in_direction)
{
already_in_separate_grid = true;
break;
}
}
if (!already_in_separate_grid)
{
Q[Q_i] = box_in_direction;
Q_i++;
}
}
}
}
}
}
if (separate_grid_i > biggest_separate_grid_length)
{
biggest_separate_grid_length = separate_grid_i;
biggest_separate_grid = cur_separate_grid;
}
}
float ang_vel_per_mass = cpBodyGetAngularVelocity(grid->body) / cpBodyGetMass(grid->body);
// create new grids for all lists of boxes except for the biggest one.
// delete the boxes out of the current grid as I pull boxes into separate ones
// which are no longer connected
for (int sepgrid_i = 0; sepgrid_i < MAX_SEPARATE_GRIDS; sepgrid_i++)
{
if (separate_grids[sepgrid_i] == biggest_separate_grid)
continue; // leave the boxes of the biggest separate untouched
struct Box **cur_separate_grid = separate_grids[sepgrid_i];
int cur_sepgrid_i = 0;
if (cur_separate_grid[cur_sepgrid_i] == NULL)
continue; // this separate grid is empty
struct Grid *new_grid = find_empty_grid(gs);
grid_new(new_grid, gs, grid_pos(grid)); // all grids have same pos but different center of mass (com)
cpBodySetAngle(new_grid->body, grid_rotation(grid));
cpBodySetVelocity(new_grid->body, cpBodyGetVelocity(grid->body));
int new_grid_box_i = 0;
while (cur_separate_grid[cur_sepgrid_i] != NULL)
{
char box_bytes[128];
char * cur = box_bytes;
// duplicate the box by serializing it then deserializing it
ser_box(&cur, cur_separate_grid[cur_sepgrid_i]);
box_destroy(space, cur_separate_grid[cur_sepgrid_i]);
cur = box_bytes;
des_box(&cur,&new_grid->boxes[new_grid_box_i],gs, new_grid);
cur_sepgrid_i++;
new_grid_box_i++;
}
cpBodySetAngularVelocity(new_grid->body, ang_vel_per_mass * cpBodyGetMass(new_grid->body));
}
cpBodySetAngularVelocity(grid->body, ang_vel_per_mass * cpBodyGetMass(grid->body));
}
static void postStepRemove(cpSpace *space, void *key, void *data)
@ -111,6 +301,7 @@ static cpBool on_damage(cpArbiter *arb, cpSpace *space, cpDataPointer userData)
void initialize(struct GameState *gs)
{
gs->space = cpSpaceNew();
cpSpaceSetUserData(gs->space, (cpDataPointer)gs); // needed in the handler
cpCollisionHandler *handler = cpSpaceAddCollisionHandler(gs->space, 0, 0); // @Robust limit collision type to just blocks that can be damaged
// handler->beginFunc = begin;
handler->postSolveFunc = on_damage;
@ -214,11 +405,17 @@ float grid_angular_velocity(struct Grid *grid)
{
return cpBodyGetAngularVelocity(grid->body);
}
struct Grid *box_grid(struct Box *box)
{
return (struct Grid *)cpBodyGetUserData(cpShapeGetBody(box->shape));
}
V2 box_local_pos(struct Box *box)
{
return cp_to_v2(cpShapeGetCenterOfGravity(box->shape));
}
V2 box_pos(struct Box *box)
{
struct Grid *g = (struct Grid *)cpBodyGetUserData(cpShapeGetBody(box->shape));
V2 local_pos = cp_to_v2(cpShapeGetCenterOfGravity(box->shape));
return V2add(grid_pos(g), V2rotate(local_pos, grid_rotation(g)));
return V2add(grid_pos(box_grid(box)), V2rotate(box_local_pos(box), grid_rotation(box_grid(box))));
}
float box_rotation(struct Box *box)
{
@ -303,6 +500,30 @@ void des_V2(char **in, V2 *v)
des_float(in, &v->y);
}
void ser_box(char **out, struct Box *b)
{
ser_V2(out, cp_to_v2(cpShapeGetCenterOfGravity(b->shape)));
ser_int(out, b->type); // @Robust separate enum serialization that checks for out of bounds enum
ser_int(out, b->rotation);
ser_float(out, b->thrust);
ser_float(out, b->energy_used);
ser_float(out, b->damage);
}
void des_box(char **in, struct Box *new_box, struct GameState *gs, struct Grid *g)
{
V2 pos = {0};
des_V2(in, &pos);
box_new(new_box, gs, g, pos);
des_int(in, (int *)&new_box->type);
des_int(in, (int *)&new_box->rotation);
des_float(in, &new_box->thrust);
des_float(in, &new_box->energy_used);
des_float(in, &new_box->damage);
}
void ser_grid(char **out, struct Grid *g)
{
// grid must not be null, dummy!
@ -321,13 +542,7 @@ void ser_grid(char **out, struct Grid *g)
ser_bool(out, exists);
if (exists)
{
ser_V2(out, cp_to_v2(cpShapeGetCenterOfGravity(g->boxes[i].shape)));
ser_int(out, g->boxes[i].type); // @Robust separate enum serialization that checks for out of bounds enum
ser_int(out, g->boxes[i].rotation);
ser_float(out, g->boxes[i].thrust);
ser_float(out, g->boxes[i].energy_used);
ser_float(out, g->boxes[i].damage);
ser_box(out, &g->boxes[i]);
}
}
}
@ -354,21 +569,14 @@ void des_grid(char **in, struct Grid *g, struct GameState *gs)
des_float(in, &g->total_energy_capacity);
// iterate over every box like this so box index is preserved on deserialization
for (int i = 0; i < MAX_BOXES_PER_GRID; i++)
{
bool exists = false;
des_bool(in, &exists);
if (exists)
{
V2 pos = {0};
des_V2(in, &pos);
box_new(&g->boxes[i], gs, g, pos);
des_int(in, (int *)&g->boxes[i].type);
des_int(in, (int *)&g->boxes[i].rotation);
des_float(in, &g->boxes[i].thrust);
des_float(in, &g->boxes[i].energy_used);
des_float(in, &g->boxes[i].damage);
des_box(in, &g->boxes[i], gs, g);
}
}
}
@ -691,7 +899,7 @@ void process(struct GameState *gs, float dt)
float needed_energy = wanted_thrust * THRUSTER_ENERGY_USED_PER_SECOND * dt;
energy_available -= needed_energy;
if(energy_available > 0.0f)
if (energy_available > 0.0f)
g->boxes[ii].thrust = wanted_thrust;
else
g->boxes[ii].thrust = 0.0f;
@ -724,18 +932,7 @@ void process(struct GameState *gs, float dt)
}
else if (p->input.grid_index == -1)
{
// @Robust better memory mgmt
struct Grid *empty_grid = NULL;
for (int ii = 0; ii < MAX_GRIDS; ii++)
{
if (gs->grids[ii].body == NULL)
{
empty_grid = &gs->grids[ii];
break;
}
}
// @Robust cleanly fail when not enough grids
assert(empty_grid != NULL);
struct Grid *empty_grid = find_empty_grid(gs);
p->spice_taken_away += 0.2f;
grid_new(empty_grid, gs, world_build);
box_new(&empty_grid->boxes[0], gs, empty_grid, (V2){0});
@ -820,7 +1017,7 @@ void process(struct GameState *gs, float dt)
}
gs->grids[i].total_energy_capacity = 0.0f;
for(int ii = 0; ii < batteries_len; ii++)
for (int ii = 0; ii < batteries_len; ii++)
{
gs->grids[i].total_energy_capacity += 1.0f - batteries[ii]->energy_used;
}

@ -131,7 +131,7 @@ struct GameState
struct Box
{
enum BoxType type;
enum Rotation rotation;
enum Rotation rotation; // @Robust rename to compass_rotation
// thruster
float thrust; // must be between 0 and 1
@ -208,6 +208,7 @@ V2 grid_snapped_box_pos(struct Grid *grid, V2 world); // returns the snapped pos
float grid_rotation(struct Grid *grid);
float grid_angular_velocity(struct Grid *grid);
void box_new(struct Box *to_modify, struct GameState *gs, struct Grid *grid, V2 pos);
V2 box_local_pos(struct Box *box);
V2 box_pos(struct Box *box);
float box_rotation(struct Box *box);

Loading…
Cancel
Save