Ghost that can inhabit!

main
Cameron Murphy Reikes 2 years ago
parent 21f9a5eb50
commit 3e8c289dc1

@ -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"
}
}

@ -3,6 +3,13 @@
#include <stdio.h> // 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(box.body));
return cp_to_v2(cpBodyGetPosition(grid->body));
}
V2 box_vel(struct Box box)
V2 grid_vel(struct Grid *grid)
{
return cp_to_v2(cpBodyGetVelocity(box.body));
return cp_to_v2(cpBodyGetVelocity(grid->body));
}
float box_rotation(struct Box box)
float grid_rotation(struct Grid *grid)
{
return cpBodyGetAngle(box.body);
return cpBodyGetAngle(grid->body);
}
float box_angular_velocity(struct Box box)
float grid_angular_velocity(struct Grid *grid)
{
return cpBodyGetAngularVelocity(box.body);
return cpBodyGetAngularVelocity(grid->body);
}
V2 box_pos(struct Box *box)
{
struct Grid *g = (struct Grid *)cpShapeGetUserData(box->shape);
return V2add(grid_pos(g), cp_to_v2(cpShapeGetCenterOfGravity(box->shape)));
}
float box_rotation(struct Box *box)
{
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);

@ -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_grids; i++)
{
for (int i = 0; i < gs.num_boxes; i++)
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(gs.boxes[i]), box_pos(gs.boxes[i]).x, box_pos(gs.boxes[i]).y);
V2 bpos = box_pos(gs.boxes[i]);
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(gs.boxes[i]).x - halfbox, box_pos(gs.boxes[i]).y - halfbox, BOX_SIZE, BOX_SIZE);
// 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)

@ -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;
}
}

@ -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 <stdbool.h>
@ -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_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 boxes[MAX_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();
@ -168,3 +185,17 @@ static V2 V2sub(V2 a, V2 b)
.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;
}
Loading…
Cancel
Save