Ghost that can inhabit!

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

@ -36,6 +36,20 @@
"chipmunk_private.h": "c", "chipmunk_private.h": "c",
"chipmunk.h": "c", "chipmunk.h": "c",
"cpbody.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 #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) void __assert(bool cond, const char *file, int line, const char *cond_string)
{ {
if (!cond) 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) #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, static cpVect v2_to_cp(V2 v)
// gamestate its own portable submodule. If need to link to other stuff document here: {
// - debug.c for debug drawing return cpv(v.x, v.y);
// - chipmunk }
void initialize(struct GameState *gs) void initialize(struct GameState *gs)
{ {
gs->space = cpSpaceNew(); gs->space = cpSpaceNew();
}
void destroy(struct GameState *gs)
{
for (int i = 0; i < MAX_PLAYERS; i++) 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); cpSpaceDestroy(gs->space);
gs->space = NULL; 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); assert(gs->space != NULL);
float halfbox = BOX_SIZE / 2.0f; 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); cpBody *body = cpSpaceAddBody(gs->space, cpBodyNew(0.0, 0.0)); // zeros for mass/moment of inertia means automatically calculated from its collision shapes
cpSpaceAddShape(gs->space, shape);
cpBodySetPosition(body, v2_to_cp(pos)); cpBodySetPosition(body, v2_to_cp(pos));
return (struct Box){ struct Grid to_return = (struct Grid){
.body = body, .body = body,
.shape = shape,
}; };
}
void box_destroy(struct Box *box) // box_new(gs, &to_return, (V2){0});
{
cpShapeFree(box->shape);
cpBodyFree(box->body);
box->shape = NULL;
box->body = NULL;
}
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) \ #define memwrite(out, variable) \
@ -149,20 +214,29 @@ void des_V2(char **in, V2 *v)
des_float(in, &v->y); 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! // grid must not be null, dummy!
assert(b->body != NULL); assert(g->body != NULL);
ser_V2(out, box_pos(*b));
ser_V2(out, box_vel(*b)); ser_V2(out, grid_pos(g));
ser_float(out, box_rotation(*b)); ser_V2(out, grid_vel(g));
ser_float(out, box_angular_velocity(*b)); 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 // 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 pos = {0};
V2 vel = {0}; V2 vel = {0};
float rot = 0.0f; 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, &rot);
des_float(in, &angular_vel); des_float(in, &angular_vel);
*b = box_new(gs, pos); *g = grid_new(gs, pos);
cpBodySetVelocity(b->body, v2_to_cp(vel)); cpBodySetVelocity(g->body, v2_to_cp(vel));
cpBodySetAngle(b->body, rot); cpBodySetAngle(g->body, rot);
cpBodySetAngularVelocity(b->body, angular_vel); 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) 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); ser_bool(out, p->connected);
if (p->connected) if (p->connected)
{ {
ser_box(out, &p->box); ser_int(out, p->currently_inhabiting_index);
ser_V2(out, p->input); 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); des_bool(in, &p->connected);
if (p->connected) if (p->connected)
{ {
des_box(in, &p->box, gs); des_int(in, &p->currently_inhabiting_index);
des_V2(in, &p->input); 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; char *original_bytes = bytes;
ser_int(&bytes, msg->your_player); ser_int(&bytes, msg->your_player);
LEN_CHECK();
for (int i = 0; i < MAX_PLAYERS; i++) 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 // @Robust invalid message on num boxes bigger than max boxes
ser_int(&bytes, gs->num_boxes); ser_int(&bytes, gs->num_grids);
LEN_CHECK(); 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(); LEN_CHECK();
} }
@ -236,6 +327,7 @@ void from_bytes(struct ServerToClient *msg, char *bytes, int max_len)
char *original_bytes = bytes; char *original_bytes = bytes;
// destroy and free all chipmunk // destroy and free all chipmunk
destroy(gs); destroy(gs);
initialize(gs); initialize(gs);
@ -248,12 +340,12 @@ void from_bytes(struct ServerToClient *msg, char *bytes, int max_len)
LEN_CHECK(); LEN_CHECK();
} }
des_int(&bytes, &gs->num_boxes); des_int(&bytes, &gs->num_grids);
LEN_CHECK(); 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(); LEN_CHECK();
} }
} }
@ -268,7 +360,56 @@ void process(struct GameState *gs, float dt)
struct Player *p = &gs->players[i]; struct Player *p = &gs->players[i];
if (!p->connected) if (!p->connected)
continue; 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); cpSpaceStep(gs->space, dt);

@ -18,13 +18,19 @@ static struct GameState gs = {0};
static int myplayer = -1; static int myplayer = -1;
static bool mouse_down = false; static bool mouse_down = false;
static bool keydown[SAPP_KEYCODE_MENU] = {0}; 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 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 ENetHost *client;
static ENetPeer *peer; static ENetPeer *peer;
void init(void) 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); initialize(&gs);
@ -97,6 +103,15 @@ static void frame(void)
int width = sapp_width(), height = sapp_height(); int width = sapp_width(), height = sapp_height();
float ratio = width / (float)height; float ratio = width / (float)height;
float time = sapp_frame_count() * sapp_frame_duration(); 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 // networking
{ {
@ -150,12 +165,14 @@ static void frame(void)
.x = (float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A], .x = (float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A],
.y = (float)keydown[SAPP_KEYCODE_S] - (float)keydown[SAPP_KEYCODE_W], .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); ENetPacket *packet = enet_packet_create((void *)&curmsg, sizeof(curmsg), ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
enet_peer_send(peer, 0, packet); enet_peer_send(peer, 0, packet);
// @BeforeShip client side prediction and rollback to previous server authoritative state, then replay inputs // @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()); process(&gs, (float)sapp_frame_duration());
} }
@ -164,6 +181,7 @@ static void frame(void)
sgp_begin(width, height); sgp_begin(width, height);
sgp_viewport(0, 0, width, height); sgp_viewport(0, 0, width, height);
sgp_project(0.0f, width, 0.0f, height); sgp_project(0.0f, width, 0.0f, height);
sgp_set_blend_mode(SGP_BLENDMODE_BLEND);
// Draw background color // Draw background color
sgp_set_color(0.1f, 0.1f, 0.1f, 1.0f); sgp_set_color(0.1f, 0.1f, 0.1f, 1.0f);
@ -176,7 +194,7 @@ static void frame(void)
// camera go to player // camera go to player
if (myplayer != -1) if (myplayer != -1)
{ {
V2 pos = box_pos(gs.players[myplayer].box); V2 pos = gs.players[myplayer].pos;
sgp_translate(-pos.x, -pos.y); sgp_translate(-pos.x, -pos.y);
} }
@ -200,39 +218,48 @@ static void frame(void)
struct Player *p = &gs.players[i]; struct Player *p = &gs.players[i];
if (!p->connected) if (!p->connected)
continue; 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_push_transform();
sgp_rotate_at(box_rotation(p->box), box_pos(p->box).x, box_pos(p->box).y); float psize = 0.1f;
V2 bpos = box_pos(p->box); sgp_draw_filled_rect(p->pos.x - psize / 2.0f, p->pos.y - psize / 2.0f, psize, psize);
sgp_draw_filled_rect(box_pos(p->box).x - halfbox, box_pos(p->box).y - halfbox, BOX_SIZE, BOX_SIZE);
sgp_pop_transform(); 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); // sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f);
V2 vel = box_vel(p->box); // V2 vel = grid_vel(p->grid);
V2 to = V2add(box_pos(p->box), vel); // V2 to = V2add(grid_pos(p->grid), vel);
sgp_draw_line(box_pos(p->box).x, box_pos(p->box).y, to.x, to.y); // 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_set_color(0.5f, 0.5f, 0.5f, 1.0f);
sgp_push_transform(); sgp_push_transform();
sgp_rotate_at(box_rotation(gs.boxes[i]), box_pos(gs.boxes[i]).x, box_pos(gs.boxes[i]).y); sgp_rotate_at(box_rotation(b), grid_pos(g).x, grid_pos(g).y);
V2 bpos = box_pos(gs.boxes[i]); 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); // 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); // 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); // 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); // bottom
sgp_draw_line(bpos.x - halfbox, bpos.y - halfbox, bpos.x + halfbox, bpos.y + halfbox); // diagonal 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_pop_transform();
}
sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f); sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f);
V2 vel = box_vel(gs.boxes[i]); V2 vel = grid_vel(&gs.grids[i]);
V2 to = V2add(box_pos(gs.boxes[i]), vel); V2 to = V2add(grid_com(g), vel);
sgp_draw_line(box_pos(gs.boxes[i]).x, box_pos(gs.boxes[i]).y, to.x, to.y); 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: case SAPP_EVENTTYPE_KEY_DOWN:
keydown[e->key_code] = true; 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; break;
case SAPP_EVENTTYPE_KEY_UP: case SAPP_EVENTTYPE_KEY_UP:
keydown[e->key_code] = false; keydown[e->key_code] = false;
keypressed[e->key_code].pressed = false;
keypressed[e->key_code].frame = 0;
break; break;
case SAPP_EVENTTYPE_MOUSE_DOWN: case SAPP_EVENTTYPE_MOUSE_DOWN:
if (e->mouse_button == SAPP_MOUSEBUTTON_LEFT) if (e->mouse_button == SAPP_MOUSEBUTTON_LEFT)

@ -17,22 +17,29 @@ void server(void *data)
initialize(&gs); initialize(&gs);
// box haven // box haven
if (true) if(true)
{ {
gs.boxes[0] = box_new(&gs, (V2){.x = 0.75f, .y = 0.0}); gs.grids[0] = grid_new(&gs, (V2){.x = 0.75f, .y = 0.0});
gs.boxes[1] = box_new(&gs, (V2){.x = 0.75f, .y = 0.5f}); gs.grids[0].boxes[0] = box_new(&gs, &gs.grids[0], (V2){0});
gs.boxes[2] = box_new(&gs, (V2){.x = 0.75f, .y = 1.0f}); gs.grids[0].boxes[1] = box_new(&gs, &gs.grids[0], (V2){0, 0.5f});
gs.boxes[3] = box_new(&gs, (V2){.x = -0.75f, .y = 0.0}); gs.grids[0].boxes[2] = box_new(&gs, &gs.grids[0], (V2){0, 1.0f});
gs.boxes[4] = box_new(&gs, (V2){.x = -0.75f, .y = 0.5f}); gs.grids[0].boxes[3] = box_new(&gs, &gs.grids[0], (V2){0.5f, 1.0f});
gs.boxes[5] = box_new(&gs, (V2){.x = -0.75f, .y = 1.0f}); gs.grids[0].num_boxes = 4;
gs.num_boxes = 6;
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) if (false)
{ {
gs.boxes[0] = box_new(&gs, (V2){.x = 0.75f, .y = 0.0}); gs.grids[0] = grid_new(&gs, (V2){.x = 0.75f, .y = 0.0});
gs.num_boxes = 1; 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) if (enet_initialize() != 0)
@ -100,10 +107,11 @@ void server(void *data)
else else
{ {
event.peer->data = (void *)player_slot; event.peer->data = (void *)player_slot;
gs.players[player_slot].box = box_new(&gs, (V2){ reset_player(&gs.players[player_slot]);
.x = 0.0f, // gs.players[player_slot].box = box_new(&gs, (V2){
.y = 1.0f * (float)player_slot, // .x = 0.0f,
}); // .y = 1.0f * (float)player_slot,
// });
gs.players[player_slot].connected = true; gs.players[player_slot].connected = true;
} }
@ -123,7 +131,8 @@ void server(void *data)
struct ClientToServer received = {0}; struct ClientToServer received = {0};
memcpy(&received, event.packet->data, length); memcpy(&received, event.packet->data, length);
int64_t player_slot = (int64_t)event.peer->data; 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. */ /* 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; int player_index = (int64_t)event.peer->data;
Log("%" PRId64 " disconnected player index %d.\n", (int64_t)event.peer->data, player_index); Log("%" PRId64 " disconnected player index %d.\n", (int64_t)event.peer->data, player_index);
gs.players[player_index].connected = false; gs.players[player_index].connected = false;
box_destroy(&gs.players[player_index].box); // box_destroy(&gs.players[player_index].box);
event.peer->data = NULL; event.peer->data = NULL;
} }
} }

@ -2,7 +2,8 @@
#define MAX_PLAYERS 4 #define MAX_PLAYERS 4
#define BOX_SIZE 0.5f #define BOX_SIZE 0.5f
#define MAX_BOXES 128 #define MAX_GRIDS 16
#define MAX_BOXES_PER_GRID 16
#define BOX_MASS 1.0f #define BOX_MASS 1.0f
// @Robust remove this include somehow, needed for sqrt and cos // @Robust remove this include somehow, needed for sqrt and cos
@ -11,6 +12,8 @@
// including headers from headers bad // including headers from headers bad
#ifndef SOKOL_GP_INCLUDED #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 typedef struct sgp_vec2
{ {
float x, y; float x, y;
@ -24,10 +27,6 @@ typedef sgp_vec2 sgp_point;
typedef void cpSpace; typedef void cpSpace;
typedef void cpBody; typedef void cpBody;
typedef void cpShape; typedef void cpShape;
extern void cpShapeFree(cpShape *);
extern void cpBodyFree(cpBody *);
extern void cpSpaceFree(cpSpace *);
#endif #endif
#include <stdbool.h> #include <stdbool.h>
@ -47,24 +46,34 @@ typedef sgp_point P2;
fprintf(stdout, "%s:%d | ", __FILE__, __LINE__); \ fprintf(stdout, "%s:%d | ", __FILE__, __LINE__); \
fprintf(stdout, __VA_ARGS__) fprintf(stdout, __VA_ARGS__)
struct Box
{
cpBody *body;
cpShape *shape;
};
// gotta update the serialization functions when this changes // gotta update the serialization functions when this changes
struct GameState struct GameState
{ {
cpSpace *space; cpSpace *space;
struct Player struct Player
{ {
struct Box box; int currently_inhabiting_index; // is equal to -1 when not inhabiting a grid
bool connected; bool connected;
V2 input; V2 pos;
V2 vel;
// input
V2 movement;
bool inhabit;
} players[MAX_PLAYERS]; } 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; 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 struct ServerToClient
@ -75,11 +84,12 @@ struct ServerToClient
struct ClientToServer struct ClientToServer
{ {
V2 input; V2 movement;
bool inhabit;
}; };
// server // server
void server(void *data); void server(void *data); // data parameter required from thread api...
// gamestate // gamestate
void initialize(struct GameState *gs); // must do this to place boxes into it and process 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 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); void from_bytes(struct ServerToClient *gs, char *bytes, int max_len);
// box // player
struct Box box_new(struct GameState *gs, V2 pos); void reset_player(struct Player *p);
void box_destroy(struct Box * box);
V2 box_pos(struct Box box); // grid
V2 box_vel(struct Box box); struct Grid grid_new(struct GameState *gs, V2 pos);
float box_rotation(struct Box box); void grid_destroy(struct Grid *grid);
float box_angular_velocity(struct Box box); 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 // debug draw
void dbg_drawall(); void dbg_drawall();
@ -168,3 +185,17 @@ static V2 V2sub(V2 a, V2 b)
.y = a.y - b.y, .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