From 3e8c289dc1e053836617662e054d04ba83363eee Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Sat, 22 Oct 2022 00:01:37 -0700 Subject: [PATCH] Ghost that can inhabit! --- .vscode/settings.json | 16 ++- gamestate.c | 265 ++++++++++++++++++++++++++++++++---------- main.c | 90 +++++++++----- server.c | 45 ++++--- types.h | 79 +++++++++---- 5 files changed, 362 insertions(+), 133 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index bcaafff..01610c5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -36,6 +36,20 @@ "chipmunk_private.h": "c", "chipmunk.h": "c", "cpbody.h": "c", - "chipmunk_structs.h": "c" + "chipmunk_structs.h": "c", + "xloctime": "c", + "iterator": "c", + "vector": "c", + "xlocmon": "c", + "xlocnum": "c", + "xutility": "c", + "functional": "c", + "memory": "c", + "mutex": "c", + "streambuf": "c", + "type_traits": "c", + "xmemory": "c", + "xtr1common": "c", + "xtree": "c" } } \ No newline at end of file diff --git a/gamestate.c b/gamestate.c index 612dd45..c513576 100644 --- a/gamestate.c +++ b/gamestate.c @@ -3,6 +3,13 @@ #include // assert logging +// do not use any global variables to process gamestate + +// super try not to depend on external libraries like enet or sokol to keep build process simple, +// gamestate its own portable submodule. If need to link to other stuff document here: +// - debug.c for debug drawing +// - chipmunk + void __assert(bool cond, const char *file, int line, const char *cond_string) { if (!cond) @@ -13,82 +20,140 @@ void __assert(bool cond, const char *file, int line, const char *cond_string) #define assert(condition) __assert(condition, __FILE__, __LINE__, #condition) -// do not use any global variables to process gamestate +static V2 cp_to_v2(cpVect v) +{ + return (V2){.x = v.x, .y = v.y}; +} -// super try not to depend on external libraries like enet or sokol to keep build process simple, -// gamestate its own portable submodule. If need to link to other stuff document here: -// - debug.c for debug drawing -// - chipmunk +static cpVect v2_to_cp(V2 v) +{ + return cpv(v.x, v.y); +} void initialize(struct GameState *gs) { gs->space = cpSpaceNew(); -} -void destroy(struct GameState *gs) -{ for (int i = 0; i < MAX_PLAYERS; i++) { - box_destroy(&gs->players[i].box); + reset_player(&gs->players[i]); } - for (int i = 0; i < gs->num_boxes; i++) +} +void destroy(struct GameState *gs) +{ + for (int i = 0; i < gs->num_grids; i++) { - box_destroy(&gs->boxes[i]); + grid_destroy(&gs->grids[i]); } - gs->num_boxes = 0; + gs->num_grids = 0; + cpSpaceDestroy(gs->space); gs->space = NULL; } -static V2 cp_to_v2(cpVect v) +void reset_player(struct Player *p) { - return (V2){.x = v.x, .y = v.y}; + *p = (struct Player){0}; + p->currently_inhabiting_index = -1; } -static cpVect v2_to_cp(V2 v) +struct Box box_new(struct GameState *gs, struct Grid *grid, V2 pos) { - return cpv(v.x, v.y); + + float halfbox = BOX_SIZE / 2.0f; + cpBB box = cpBBNew(-halfbox + pos.x, -halfbox + pos.y, halfbox + pos.x, halfbox + pos.y); + cpVect verts[4] = { + cpv(box.r, box.b), + cpv(box.r, box.t), + cpv(box.l, box.t), + cpv(box.l, box.b), + }; + + struct Box to_return = (struct Box){ + .shape = (cpShape *)cpPolyShapeInitRaw(cpPolyShapeAlloc(), grid->body, 4, verts, 0.0f), // this cast is done in chipmunk, not sure why it works + }; + + // assumed to be grid in inhabit code as well + cpShapeSetUserData(to_return.shape, (void *)grid); + cpShapeSetMass(to_return.shape, BOX_MASS); + cpSpaceAddShape(gs->space, to_return.shape); + + // update the center of mass (can't believe this isn't done for me...) + // float total_mass = 0.0f; + // float total_moment_of_inertia = 0.0f; + // V2 total_pos = {0}; + // for (int i = 0; i < grid->num_boxes; i++) + // { + // cpShape *cur_shape = grid->boxes[i].shape; + // total_mass += cpShapeGetMass(cur_shape); + // total_moment_of_inertia += cpShapeGetMoment(cur_shape); + // total_pos = V2add(total_pos, V2scale(cp_to_v2(cpShapeGetCenterOfGravity(cur_shape)), cpShapeGetMass(cur_shape))); + // } + // total_pos = V2scale(total_pos, 1.0f / total_mass); + + + // @Robust I think moment of inertia calculation is wrong? https://chipmunk-physics.net/forum/viewtopic.php?t=2566 + + return to_return; } -struct Box box_new(struct GameState *gs, V2 pos) +struct Grid grid_new(struct GameState *gs, V2 pos) { assert(gs->space != NULL); float halfbox = BOX_SIZE / 2.0f; - cpBody *body = cpSpaceAddBody(gs->space, cpBodyNew(BOX_MASS, cpMomentForBox(BOX_MASS, BOX_SIZE, BOX_SIZE))); - cpShape *shape = cpBoxShapeNew(body, BOX_SIZE, BOX_SIZE, 0.0f); - cpSpaceAddShape(gs->space, shape); + + cpBody *body = cpSpaceAddBody(gs->space, cpBodyNew(0.0, 0.0)); // zeros for mass/moment of inertia means automatically calculated from its collision shapes cpBodySetPosition(body, v2_to_cp(pos)); - return (struct Box){ + struct Grid to_return = (struct Grid){ .body = body, - .shape = shape, }; -} -void box_destroy(struct Box *box) -{ - cpShapeFree(box->shape); - cpBodyFree(box->body); - box->shape = NULL; - box->body = NULL; -} + // box_new(gs, &to_return, (V2){0}); + return to_return; +} +void grid_destroy(struct Grid *grid) +{ + for (int ii = 0; ii < grid->num_boxes; ii++) + { + cpShapeFree(grid->boxes[ii].shape); + grid->boxes[ii].shape = NULL; + } + grid->num_boxes = 0; -V2 box_pos(struct Box box) + cpBodyFree(grid->body); + grid->body = NULL; +} +// center of mass, not the literal position +V2 grid_com(struct Grid *grid) +{ + return cp_to_v2(cpBodyLocalToWorld(grid->body, cpBodyGetCenterOfGravity(grid->body))); +} +V2 grid_pos(struct Grid *grid) +{ + return cp_to_v2(cpBodyGetPosition(grid->body)); +} +V2 grid_vel(struct Grid *grid) +{ + return cp_to_v2(cpBodyGetVelocity(grid->body)); +} +float grid_rotation(struct Grid *grid) { - return cp_to_v2(cpBodyGetPosition(box.body)); + return cpBodyGetAngle(grid->body); } -V2 box_vel(struct Box box) +float grid_angular_velocity(struct Grid *grid) { - return cp_to_v2(cpBodyGetVelocity(box.body)); + return cpBodyGetAngularVelocity(grid->body); } -float box_rotation(struct Box box) +V2 box_pos(struct Box *box) { - return cpBodyGetAngle(box.body); + struct Grid *g = (struct Grid *)cpShapeGetUserData(box->shape); + return V2add(grid_pos(g), cp_to_v2(cpShapeGetCenterOfGravity(box->shape))); } -float box_angular_velocity(struct Box box) +float box_rotation(struct Box *box) { - return cpBodyGetAngularVelocity(box.body); + return cpBodyGetAngle(cpShapeGetBody(box->shape)); } #define memwrite(out, variable) \ @@ -149,20 +214,29 @@ void des_V2(char **in, V2 *v) des_float(in, &v->y); } -void ser_box(char **out, struct Box *b) +void ser_grid(char **out, struct Grid *g) { - // box must not be null, dummy! - assert(b->body != NULL); - ser_V2(out, box_pos(*b)); - ser_V2(out, box_vel(*b)); - ser_float(out, box_rotation(*b)); - ser_float(out, box_angular_velocity(*b)); + // grid must not be null, dummy! + assert(g->body != NULL); + + ser_V2(out, grid_pos(g)); + ser_V2(out, grid_vel(g)); + ser_float(out, grid_rotation(g)); + ser_float(out, grid_angular_velocity(g)); + + ser_int(out, g->num_boxes); + for (int i = 0; i < g->num_boxes; i++) + { + ser_V2(out, cp_to_v2(cpShapeGetCenterOfGravity(g->boxes[i].shape))); + ser_float(out, g->boxes[i].damage); + } } // takes gamestate as argument to place box in the gamestates space -void des_box(char **in, struct Box *b, struct GameState *gs) +void des_grid(char **in, struct Grid *g, struct GameState *gs) { - assert(b->body == NULL); // destroy the box before deserializing into it + assert(g->body == NULL); // destroy the grid before deserializing into it + V2 pos = {0}; V2 vel = {0}; float rot = 0.0f; @@ -173,10 +247,20 @@ void des_box(char **in, struct Box *b, struct GameState *gs) des_float(in, &rot); des_float(in, &angular_vel); - *b = box_new(gs, pos); - cpBodySetVelocity(b->body, v2_to_cp(vel)); - cpBodySetAngle(b->body, rot); - cpBodySetAngularVelocity(b->body, angular_vel); + *g = grid_new(gs, pos); + cpBodySetVelocity(g->body, v2_to_cp(vel)); + cpBodySetAngle(g->body, rot); + cpBodySetAngularVelocity(g->body, angular_vel); + + des_int(in, &g->num_boxes); + + for (int i = 0; i < g->num_boxes; i++) + { + V2 pos = {0}; + des_V2(in, &pos); + g->boxes[i] = box_new(gs, g, pos); + des_float(in, &g->boxes[i].damage); + } } void ser_player(char **out, struct Player *p) @@ -184,8 +268,11 @@ void ser_player(char **out, struct Player *p) ser_bool(out, p->connected); if (p->connected) { - ser_box(out, &p->box); - ser_V2(out, p->input); + ser_int(out, p->currently_inhabiting_index); + ser_V2(out, p->pos); + ser_V2(out, p->vel); + ser_V2(out, p->movement); + ser_bool(out, p->inhabit); } } @@ -194,8 +281,11 @@ void des_player(char **in, struct Player *p, struct GameState *gs) des_bool(in, &p->connected); if (p->connected) { - des_box(in, &p->box, gs); - des_V2(in, &p->input); + des_int(in, &p->currently_inhabiting_index); + des_V2(in, &p->pos); + des_V2(in, &p->vel); + des_V2(in, &p->movement); + des_bool(in, &p->inhabit); } } @@ -210,6 +300,7 @@ void into_bytes(struct ServerToClient *msg, char *bytes, int *out_len, int max_l char *original_bytes = bytes; ser_int(&bytes, msg->your_player); + LEN_CHECK(); for (int i = 0; i < MAX_PLAYERS; i++) { @@ -218,12 +309,12 @@ void into_bytes(struct ServerToClient *msg, char *bytes, int *out_len, int max_l } // @Robust invalid message on num boxes bigger than max boxes - ser_int(&bytes, gs->num_boxes); + ser_int(&bytes, gs->num_grids); LEN_CHECK(); - for (int i = 0; i < gs->num_boxes; i++) + for (int i = 0; i < gs->num_grids; i++) { - ser_box(&bytes, &gs->boxes[i]); + ser_grid(&bytes, &gs->grids[i]); LEN_CHECK(); } @@ -236,6 +327,7 @@ void from_bytes(struct ServerToClient *msg, char *bytes, int max_len) char *original_bytes = bytes; // destroy and free all chipmunk + destroy(gs); initialize(gs); @@ -248,12 +340,12 @@ void from_bytes(struct ServerToClient *msg, char *bytes, int max_len) LEN_CHECK(); } - des_int(&bytes, &gs->num_boxes); + des_int(&bytes, &gs->num_grids); LEN_CHECK(); - for (int i = 0; i < gs->num_boxes; i++) + for (int i = 0; i < gs->num_grids; i++) { - des_box(&bytes, &gs->boxes[i], gs); + des_grid(&bytes, &gs->grids[i], gs); LEN_CHECK(); } } @@ -268,7 +360,56 @@ void process(struct GameState *gs, float dt) struct Player *p = &gs->players[i]; if (!p->connected) continue; - cpBodyApplyForceAtWorldPoint(p->box.body, v2_to_cp(V2scale(p->input, 5.0f)), v2_to_cp(box_pos(p->box))); + + if (p->inhabit) + { + p->inhabit = false; // "handle" the input + if (p->currently_inhabiting_index == -1) + { + + // @Robust mask to only ship boxes of things the player can inhabit + cpPointQueryInfo query_info = {0}; + cpShape *result = cpSpacePointQueryNearest(gs->space, v2_to_cp(p->pos), 0.1, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES), &query_info); + if (result != NULL) + { + struct Grid *g = (struct Grid *)cpShapeGetUserData(result); + for (int ii = 0; ii < gs->num_grids; ii++) + { + if (&gs->grids[ii] == g) + { + p->currently_inhabiting_index = ii; + break; + } + } + if (p->currently_inhabiting_index == -1) + { + Log("Couldn't find ship to inhabit even though point collision returned something\n"); + } + } + else + { + Log("No ship above player at point %f %f\n", p->pos.x, p->pos.y); + } + } + else + { + p->vel = grid_vel(&gs->grids[p->currently_inhabiting_index]); + p->currently_inhabiting_index = -1; + } + } + + if (p->currently_inhabiting_index == -1) + { + p->vel = V2lerp(p->vel, p->movement, dt * 5.0f); + p->pos = V2add(p->pos, V2scale(p->vel, dt)); + } + else + { + struct Grid *g = &gs->grids[p->currently_inhabiting_index]; + p->pos = V2lerp(p->pos, grid_com(g), dt * 20.0f); + cpBodyApplyForceAtWorldPoint(g->body, v2_to_cp(V2scale(p->movement, 5.0f)), v2_to_cp(grid_com(g))); + } + // cpBodyApplyForceAtWorldPoint(p->box.body, v2_to_cp(V2scale(p->input, 5.0f)), v2_to_cp(box_pos(p->box))); } cpSpaceStep(gs->space, dt); diff --git a/main.c b/main.c index 09c0565..0902305 100644 --- a/main.c +++ b/main.c @@ -18,13 +18,19 @@ static struct GameState gs = {0}; static int myplayer = -1; static bool mouse_down = false; static bool keydown[SAPP_KEYCODE_MENU] = {0}; +typedef struct KeyPressed +{ + bool pressed; + uint64_t frame; +} KeyPressed; +static KeyPressed keypressed[SAPP_KEYCODE_MENU] = {0}; static float funval = 0.0f; // easy to play with value controlled by left mouse button when held down @BeforeShip remove on release builds static ENetHost *client; static ENetPeer *peer; void init(void) { - // @BeforeShip make all fprintf into logging to file, warning dialog boxes 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 initialize(&gs); @@ -97,6 +103,15 @@ static void frame(void) int width = sapp_width(), height = sapp_height(); float ratio = width / (float)height; float time = sapp_frame_count() * sapp_frame_duration(); + float dt = sapp_frame_duration(); + + for (int i = 0; i < SAPP_KEYCODE_MENU; i++) + { + if (keypressed[i].frame < sapp_frame_count()) + { + keypressed[i].pressed = false; + } + } // networking { @@ -150,12 +165,14 @@ static void frame(void) .x = (float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A], .y = (float)keydown[SAPP_KEYCODE_S] - (float)keydown[SAPP_KEYCODE_W], }; - curmsg.input = input; + curmsg.movement = input; + curmsg.inhabit = keypressed[SAPP_KEYCODE_G].pressed; ENetPacket *packet = enet_packet_create((void *)&curmsg, sizeof(curmsg), ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT); enet_peer_send(peer, 0, packet); // @BeforeShip client side prediction and rollback to previous server authoritative state, then replay inputs + // no need to store copies of game state, just player input frame to frame. Then know how many frames ago the server game state arrived, it's that easy! process(&gs, (float)sapp_frame_duration()); } @@ -164,6 +181,7 @@ static void frame(void) sgp_begin(width, height); sgp_viewport(0, 0, width, height); sgp_project(0.0f, width, 0.0f, height); + sgp_set_blend_mode(SGP_BLENDMODE_BLEND); // Draw background color sgp_set_color(0.1f, 0.1f, 0.1f, 1.0f); @@ -176,7 +194,7 @@ static void frame(void) // camera go to player if (myplayer != -1) { - V2 pos = box_pos(gs.players[myplayer].box); + V2 pos = gs.players[myplayer].pos; sgp_translate(-pos.x, -pos.y); } @@ -200,39 +218,48 @@ static void frame(void) struct Player *p = &gs.players[i]; if (!p->connected) continue; - sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f); + static float opacities[MAX_PLAYERS] = {1.0f}; + opacities[i] = lerp(opacities[i], p->currently_inhabiting_index == -1 ? 1.0f : 0.1f, dt*7.0f); + sgp_set_color(1.0f, 1.0f, 1.0f, opacities[i]); sgp_push_transform(); - sgp_rotate_at(box_rotation(p->box), box_pos(p->box).x, box_pos(p->box).y); - V2 bpos = box_pos(p->box); - sgp_draw_filled_rect(box_pos(p->box).x - halfbox, box_pos(p->box).y - halfbox, BOX_SIZE, BOX_SIZE); + float psize = 0.1f; + sgp_draw_filled_rect(p->pos.x - psize / 2.0f, p->pos.y - psize / 2.0f, psize, psize); sgp_pop_transform(); + // sgp_rotate_at(grid_rotation(p->grid), grid_pos(p->grid).x, grid_pos(p->grid).y); + // V2 bpos = grid_pos(p->grid); + // sgp_draw_filled_rect(grid_pos(p->grid).x - halfbox, grid_pos(p->grid).y - halfbox, BOX_SIZE, BOX_SIZE); + // sgp_pop_transform(); - sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f); - V2 vel = box_vel(p->box); - V2 to = V2add(box_pos(p->box), vel); - sgp_draw_line(box_pos(p->box).x, box_pos(p->box).y, to.x, to.y); + // sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f); + // V2 vel = grid_vel(p->grid); + // V2 to = V2add(grid_pos(p->grid), vel); + // sgp_draw_line(grid_pos(p->grid).x, grid_pos(p->grid).y, to.x, to.y); } - // boxes + // grids { - for (int i = 0; i < gs.num_boxes; i++) + for (int i = 0; i < gs.num_grids; i++) { - sgp_set_color(0.5f, 0.5f, 0.5f, 1.0f); - sgp_push_transform(); - sgp_rotate_at(box_rotation(gs.boxes[i]), box_pos(gs.boxes[i]).x, box_pos(gs.boxes[i]).y); - V2 bpos = box_pos(gs.boxes[i]); - sgp_draw_line(bpos.x - halfbox, bpos.y - halfbox, bpos.x - halfbox, bpos.y + halfbox); // left - sgp_draw_line(bpos.x - halfbox, bpos.y - halfbox, bpos.x + halfbox, bpos.y - halfbox); // top - sgp_draw_line(bpos.x + halfbox, bpos.y - halfbox, bpos.x + halfbox, bpos.y + halfbox); // right - sgp_draw_line(bpos.x - halfbox, bpos.y + halfbox, bpos.x + halfbox, bpos.y + halfbox); // bottom - sgp_draw_line(bpos.x - halfbox, bpos.y - halfbox, bpos.x + halfbox, bpos.y + halfbox); // diagonal - // sgp_draw_filled_rect(box_pos(gs.boxes[i]).x - halfbox, box_pos(gs.boxes[i]).y - halfbox, BOX_SIZE, BOX_SIZE); - sgp_pop_transform(); - + struct Grid *g = &gs.grids[i]; + for (int ii = 0; ii < g->num_boxes; ii++) + { + struct Box *b = &g->boxes[ii]; + sgp_set_color(0.5f, 0.5f, 0.5f, 1.0f); + sgp_push_transform(); + sgp_rotate_at(box_rotation(b), grid_pos(g).x, grid_pos(g).y); + V2 bpos = box_pos(b); + sgp_draw_line(bpos.x - halfbox, bpos.y - halfbox, bpos.x - halfbox, bpos.y + halfbox); // left + sgp_draw_line(bpos.x - halfbox, bpos.y - halfbox, bpos.x + halfbox, bpos.y - halfbox); // top + sgp_draw_line(bpos.x + halfbox, bpos.y - halfbox, bpos.x + halfbox, bpos.y + halfbox); // right + sgp_draw_line(bpos.x - halfbox, bpos.y + halfbox, bpos.x + halfbox, bpos.y + halfbox); // bottom + sgp_draw_line(bpos.x - halfbox, bpos.y - halfbox, bpos.x + halfbox, bpos.y + halfbox); // diagonal + // sgp_draw_filled_rect(box_pos(b).x - halfbox, box_pos(b).y - halfbox, BOX_SIZE, BOX_SIZE); + sgp_pop_transform(); + } sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f); - V2 vel = box_vel(gs.boxes[i]); - V2 to = V2add(box_pos(gs.boxes[i]), vel); - sgp_draw_line(box_pos(gs.boxes[i]).x, box_pos(gs.boxes[i]).y, to.x, to.y); + V2 vel = grid_vel(&gs.grids[i]); + V2 to = V2add(grid_com(g), vel); + sgp_draw_line(grid_com(g).x, grid_com(g).y, to.x, to.y); } } @@ -267,9 +294,16 @@ void event(const sapp_event *e) { case SAPP_EVENTTYPE_KEY_DOWN: keydown[e->key_code] = true; + if (keypressed[e->key_code].frame == 0) + { + keypressed[e->key_code].pressed = true; + keypressed[e->key_code].frame = e->frame_count; + } break; case SAPP_EVENTTYPE_KEY_UP: keydown[e->key_code] = false; + keypressed[e->key_code].pressed = false; + keypressed[e->key_code].frame = 0; break; case SAPP_EVENTTYPE_MOUSE_DOWN: if (e->mouse_button == SAPP_MOUSEBUTTON_LEFT) diff --git a/server.c b/server.c index 4147939..346aa42 100644 --- a/server.c +++ b/server.c @@ -17,22 +17,29 @@ void server(void *data) initialize(&gs); // box haven - if (true) + if(true) { - gs.boxes[0] = box_new(&gs, (V2){.x = 0.75f, .y = 0.0}); - gs.boxes[1] = box_new(&gs, (V2){.x = 0.75f, .y = 0.5f}); - gs.boxes[2] = box_new(&gs, (V2){.x = 0.75f, .y = 1.0f}); - gs.boxes[3] = box_new(&gs, (V2){.x = -0.75f, .y = 0.0}); - gs.boxes[4] = box_new(&gs, (V2){.x = -0.75f, .y = 0.5f}); - gs.boxes[5] = box_new(&gs, (V2){.x = -0.75f, .y = 1.0f}); - gs.num_boxes = 6; + gs.grids[0] = grid_new(&gs, (V2){.x = 0.75f, .y = 0.0}); + gs.grids[0].boxes[0] = box_new(&gs, &gs.grids[0], (V2){0}); + gs.grids[0].boxes[1] = box_new(&gs, &gs.grids[0], (V2){0, 0.5f}); + gs.grids[0].boxes[2] = box_new(&gs, &gs.grids[0], (V2){0, 1.0f}); + gs.grids[0].boxes[3] = box_new(&gs, &gs.grids[0], (V2){0.5f, 1.0f}); + gs.grids[0].num_boxes = 4; + + gs.grids[1] = grid_new(&gs, (V2){.x = -0.75f, .y = 0.0}); + gs.grids[1].boxes[0] = box_new(&gs, &gs.grids[1], (V2){0}); + gs.grids[1].num_boxes = 1; + + gs.num_grids = 2; } - - // one box + + // one box policy if (false) { - gs.boxes[0] = box_new(&gs, (V2){.x = 0.75f, .y = 0.0}); - gs.num_boxes = 1; + gs.grids[0] = grid_new(&gs, (V2){.x = 0.75f, .y = 0.0}); + gs.grids[0].boxes[0] = box_new(&gs, &gs.grids[0], (V2){0}); + gs.num_grids = 1; + gs.grids[0].num_boxes = 1; } if (enet_initialize() != 0) @@ -100,10 +107,11 @@ void server(void *data) else { event.peer->data = (void *)player_slot; - gs.players[player_slot].box = box_new(&gs, (V2){ - .x = 0.0f, - .y = 1.0f * (float)player_slot, - }); + reset_player(&gs.players[player_slot]); + // gs.players[player_slot].box = box_new(&gs, (V2){ + // .x = 0.0f, + // .y = 1.0f * (float)player_slot, + // }); gs.players[player_slot].connected = true; } @@ -123,7 +131,8 @@ void server(void *data) struct ClientToServer received = {0}; memcpy(&received, event.packet->data, length); int64_t player_slot = (int64_t)event.peer->data; - gs.players[player_slot].input = received.input; + gs.players[player_slot].movement = received.movement; + gs.players[player_slot].inhabit = received.inhabit; } /* Clean up the packet now that we're done using it. */ @@ -135,7 +144,7 @@ void server(void *data) int player_index = (int64_t)event.peer->data; Log("%" PRId64 " disconnected player index %d.\n", (int64_t)event.peer->data, player_index); gs.players[player_index].connected = false; - box_destroy(&gs.players[player_index].box); + // box_destroy(&gs.players[player_index].box); event.peer->data = NULL; } } diff --git a/types.h b/types.h index 85b630b..d38c515 100644 --- a/types.h +++ b/types.h @@ -2,7 +2,8 @@ #define MAX_PLAYERS 4 #define BOX_SIZE 0.5f -#define MAX_BOXES 128 +#define MAX_GRIDS 16 +#define MAX_BOXES_PER_GRID 16 #define BOX_MASS 1.0f // @Robust remove this include somehow, needed for sqrt and cos @@ -11,6 +12,8 @@ // including headers from headers bad #ifndef SOKOL_GP_INCLUDED +// @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; @@ -24,10 +27,6 @@ typedef sgp_vec2 sgp_point; typedef void cpSpace; typedef void cpBody; typedef void cpShape; - -extern void cpShapeFree(cpShape *); -extern void cpBodyFree(cpBody *); -extern void cpSpaceFree(cpSpace *); #endif #include @@ -47,24 +46,34 @@ typedef sgp_point P2; fprintf(stdout, "%s:%d | ", __FILE__, __LINE__); \ fprintf(stdout, __VA_ARGS__) -struct Box -{ - cpBody *body; - cpShape *shape; -}; - // gotta update the serialization functions when this changes struct GameState { cpSpace *space; struct Player { - struct Box box; + int currently_inhabiting_index; // is equal to -1 when not inhabiting a grid bool connected; - V2 input; + V2 pos; + V2 vel; + + // input + V2 movement; + bool inhabit; } players[MAX_PLAYERS]; - int num_boxes; - struct Box boxes[MAX_BOXES]; + int num_grids; + // important that this memory does not move around, each box shape in it has a pointer to its grid struct, stored in the box's shapes user_data + struct Grid + { + cpBody *body; + + int num_boxes; + struct Box + { + cpShape *shape; + float damage; + } boxes[MAX_BOXES_PER_GRID]; // @Robust this needs to be dynamically allocated, huge disparity in how many blocks a body can have... + } grids[MAX_GRIDS]; }; struct ServerToClient @@ -75,11 +84,12 @@ struct ServerToClient struct ClientToServer { - V2 input; + V2 movement; + bool inhabit; }; // server -void server(void *data); +void server(void *data); // data parameter required from thread api... // gamestate void initialize(struct GameState *gs); // must do this to place boxes into it and process @@ -88,13 +98,20 @@ void process(struct GameState *gs, float dt); // does in place void into_bytes(struct ServerToClient *gs, char *out_bytes, int *out_len, int max_len); void from_bytes(struct ServerToClient *gs, char *bytes, int max_len); -// box -struct Box box_new(struct GameState *gs, V2 pos); -void box_destroy(struct Box * box); -V2 box_pos(struct Box box); -V2 box_vel(struct Box box); -float box_rotation(struct Box box); -float box_angular_velocity(struct Box box); +// player +void reset_player(struct Player *p); + +// grid +struct Grid grid_new(struct GameState *gs, V2 pos); +void grid_destroy(struct Grid *grid); +V2 grid_com(struct Grid *grid); +V2 grid_pos(struct Grid *grid); +V2 grid_vel(struct Grid *grid); +float grid_rotation(struct Grid *grid); +float grid_angular_velocity(struct Grid *grid); +struct Box box_new(struct GameState *gs, struct Grid *grid, V2 pos); +V2 box_pos(struct Box *box); +float box_rotation(struct Box *box); // debug draw void dbg_drawall(); @@ -167,4 +184,18 @@ static V2 V2sub(V2 a, V2 b) .x = a.x - b.x, .y = a.y - b.y, }; +} + +static float lerp(float a, float b, float f) +{ + return a * (1.0f - f) + (b * f); +} + +static V2 V2lerp(V2 a, V2 b, float factor) +{ + 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; } \ No newline at end of file