diff --git a/gamestate.c b/gamestate.c index 9e1f598..f1b4806 100644 --- a/gamestate.c +++ b/gamestate.c @@ -229,6 +229,12 @@ void grid_create(GameState* gs, Entity* e) create_body(gs, e); } +void entity_set_rotation(Entity* e, float rot) +{ + assert(e->body != NULL); + cpBodySetAngle(e->body, rot); +} + void entity_set_pos(Entity* e, V2 pos) { assert(e->is_grid); @@ -676,7 +682,7 @@ void ser_entityid(SerState* ser, EntityID* id) SER_VAR(&id->index); } -void ser_inputframe(SerState* ser, struct InputFrame* i) +void ser_inputframe(SerState* ser, InputFrame* i) { SER_VAR(&i->tick); SER_VAR(&i->movement); @@ -684,11 +690,11 @@ void ser_inputframe(SerState* ser, struct InputFrame* i) SER_VAR(&i->seat_action); ser_entityid(ser, &i->seat_to_inhabit); SER_VAR(&i->hand_pos); + ser_entityid(ser, &i->grid_hand_pos_local_to); SER_VAR(&i->dobuild); SER_VAR(&i->build_type); SER_VAR(&i->build_rotation); - ser_entityid(ser, &i->grid_to_build_on); } void ser_player(SerState* ser, Player* p) @@ -962,6 +968,18 @@ uint64_t tick(GameState* gs) return (uint64_t)floor(gs->time / ((double)TIMESTEP)); } +V2 get_world_hand_pos(GameState* gs, InputFrame* input, Entity* player) +{ + Entity* potential_grid = get_entity(gs, input->grid_hand_pos_local_to); + if (potential_grid != NULL) + { + return grid_local_to_world(potential_grid, input->hand_pos); + } + else { + return input->hand_pos; + } +} + void process(GameState* gs, float dt) { assert(gs->space != NULL); @@ -993,6 +1011,7 @@ void process(GameState* gs, float dt) } #if 1 + V2 world_hand_pos = get_world_hand_pos(gs, &player->input, p); if (player->input.seat_action) { player->input.seat_action = false; // "handle" the input @@ -1000,7 +1019,7 @@ void process(GameState* gs, float dt) if (the_seat == NULL) // not piloting any seat { cpPointQueryInfo query_info = { 0 }; - cpShape* result = cpSpacePointQueryNearest(gs->space, v2_to_cp(player->input.hand_pos), 0.1f, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, BOXES), &query_info); + cpShape* result = cpSpacePointQueryNearest(gs->space, v2_to_cp(world_hand_pos), 0.1f, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, BOXES), &query_info); if (result != NULL) { Entity* potential_seat = cp_shape_entity(result); @@ -1013,7 +1032,8 @@ void process(GameState* gs, float dt) } else { - Log("No ship above player at point %f %f\n", player->input.hand_pos.x, player->input.hand_pos.y); + + Log("No ship above player at point %f %f\n", world_hand_pos.x, world_hand_pos.y); } } else @@ -1075,10 +1095,10 @@ void process(GameState* gs, float dt) player->input.dobuild = false; // handle the input. if didn't do this, after destruction of hovered box, would try to build on its grid with grid_index... cpPointQueryInfo info = { 0 }; - V2 world_build = player->input.hand_pos; + V2 world_build = world_hand_pos; // @Robust sanitize this input so player can't build on any grid in the world - Entity* target_grid = get_entity(gs, player->input.grid_to_build_on); + Entity* target_grid = get_entity(gs, player->input.grid_hand_pos_local_to); cpShape* nearest = cpSpacePointQueryNearest(gs->space, v2_to_cp(world_build), 0.01f, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, BOXES), &info); if (nearest != NULL) { diff --git a/main.c b/main.c index 9e7cd2a..337c284 100644 --- a/main.c +++ b/main.c @@ -420,7 +420,7 @@ frame(void) V2 grid_pos; float grid_rotation; } build_preview = { 0 }; - V2 hand_pos = { 0 }; + V2 hand_pos = { 0 }; // in local space of grid when hovering over a grid bool hand_at_arms_length = false; { // interpolate zoom @@ -439,6 +439,7 @@ frame(void) // calculate build preview stuff EntityID grid_to_build_on = (EntityID){ 0 }; + V2 possibly_local_hand_pos = (V2){ 0 }; if (myentity() != NULL) { hand_pos = V2sub(world_mouse_pos, entity_pos(myentity())); float hand_len = V2length(hand_pos); @@ -452,6 +453,7 @@ frame(void) hand_pos = V2scale(V2normalize(hand_pos), hand_len); hand_pos = V2add(hand_pos, entity_pos(myentity())); + possibly_local_hand_pos = hand_pos; Entity* placing_grid = closest_to_point_in_radius(&gs, hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP); if (placing_grid == NULL) { build_preview = (struct BuildPreviewInfo){ @@ -462,6 +464,7 @@ frame(void) else { grid_to_build_on = get_id(&gs, placing_grid); hand_pos = grid_snapped_box_pos(placing_grid, hand_pos); + possibly_local_hand_pos = grid_world_to_local(placing_grid, hand_pos); build_preview = (struct BuildPreviewInfo){ .grid_pos = entity_pos(placing_grid), .grid_rotation = entity_rotation(placing_grid), }; @@ -474,7 +477,7 @@ frame(void) // every frame static size_t last_frame_id = 0; - struct InputFrame cur_input_frame = { 0 }; + InputFrame cur_input_frame = { 0 }; cur_input_frame.id = last_frame_id; V2 input = (V2){ .x = (float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A], @@ -484,15 +487,16 @@ frame(void) input = V2normalize(input); cur_input_frame.movement = input; cur_input_frame.seat_action = keypressed[SAPP_KEYCODE_G].pressed; - cur_input_frame.hand_pos = hand_pos; + cur_input_frame.grid_hand_pos_local_to = grid_to_build_on; + cur_input_frame.hand_pos = possibly_local_hand_pos; + if (mouse_pressed && cur_editing_boxtype != -1) { cur_input_frame.dobuild = mouse_pressed; cur_input_frame.build_type = cur_editing_boxtype; cur_input_frame.build_rotation = cur_editing_rotation; - cur_input_frame.grid_to_build_on = grid_to_build_on; } - struct InputFrame latest = client_to_server.inputs[0]; + InputFrame latest = client_to_server.inputs[0]; // @Robust split this into separate lines and be very careful about testing for inequality bool input_differs = false; @@ -505,12 +509,12 @@ frame(void) input_differs = input_differs || cur_input_frame.dobuild != latest.dobuild; input_differs = input_differs || cur_input_frame.build_type != latest.build_type; input_differs = input_differs || cur_input_frame.build_rotation != latest.build_rotation; - input_differs = input_differs || !entityids_same(cur_input_frame.grid_to_build_on, latest.grid_to_build_on); + input_differs = input_differs || !entityids_same(cur_input_frame.grid_hand_pos_local_to, latest.grid_hand_pos_local_to); if (input_differs) { - struct InputFrame last_frame = client_to_server.inputs[0]; + InputFrame last_frame = client_to_server.inputs[0]; for (int i = 0; i < INPUT_BUFFER - 1; i++) { - struct InputFrame last_last_frame = last_frame; + InputFrame last_last_frame = last_frame; last_frame = client_to_server.inputs[i + 1]; client_to_server.inputs[i + 1] = last_last_frame; } @@ -518,7 +522,6 @@ frame(void) client_to_server.inputs[0] = cur_input_frame; last_frame_id += 1; } - dbg_rect(client_to_server.inputs[0].hand_pos); static double last_input_sent_time = 0.0; if (fabs(last_input_sent_time - time) > TIME_BETWEEN_INPUT_PACKETS) { @@ -545,7 +548,8 @@ frame(void) sgp_set_blend_mode(SGP_BLENDMODE_BLEND); // Draw background color - sgp_set_color(0.1f, 0.1f, 0.1f, 1.0f); + set_color(colhexcode(0x020509)); + // sgp_set_color(0.1f, 0.1f, 0.1f, 1.0f); sgp_clear(); // sokol drawing library draw in world space diff --git a/server.c b/server.c index b0f4da1..1d56ac5 100644 --- a/server.c +++ b/server.c @@ -1,3 +1,4 @@ +#include // initializing bodies #include "types.h" #include "sokol_time.h" #include @@ -5,6 +6,7 @@ #include // int64 printing #include + // started in a thread from host void server(void* data) { @@ -26,7 +28,24 @@ void server(void* data) entity_set_pos(grid, (V2) { -BOX_SIZE * 2, 0.0f }); Entity* box = new_entity(&gs); box_create(&gs, box, grid, (V2) { 0 }); + } + // rotation test + if (false) + { + Entity* grid = new_entity(&gs); + grid_create(&gs, grid); + entity_set_pos(grid, (V2) { -BOX_SIZE * 2, 0.0f }); + entity_set_rotation(grid, PI / 1.7f); + cpBodySetVelocity(grid->body, cpv(-0.1, 0.0)); + cpBodySetAngularVelocity(grid->body, 1.0f); + +#define BOX_AT(pos) { Entity* box = new_entity(&gs); box_create(&gs, box, grid, pos); } + BOX_AT(((V2) { 0 })); + BOX_AT(((V2) { BOX_SIZE, 0 })); + BOX_AT(((V2) { 2.0*BOX_SIZE, 0 })); + BOX_AT(((V2) { 2.0*BOX_SIZE, BOX_SIZE })); + BOX_AT(((V2) { 0.0*BOX_SIZE, -BOX_SIZE })); } if (enet_initialize() != 0) @@ -133,7 +152,7 @@ void server(void* data) continue; if (received.inputs[i].id <= latest_id) continue; // don't reprocess inputs already processed - struct InputFrame cur_input = received.inputs[i]; + InputFrame cur_input = received.inputs[i]; gs.players[player_slot].input.movement = cur_input.movement; gs.players[player_slot].input.hand_pos = cur_input.hand_pos; @@ -143,10 +162,11 @@ void server(void* data) if (cur_input.seat_action) { gs.players[player_slot].input.seat_action = cur_input.seat_action; + gs.players[player_slot].input.grid_hand_pos_local_to = cur_input.grid_hand_pos_local_to; } if (cur_input.dobuild) { - gs.players[player_slot].input.grid_to_build_on = cur_input.grid_to_build_on; + gs.players[player_slot].input.grid_hand_pos_local_to = cur_input.grid_hand_pos_local_to; gs.players[player_slot].input.dobuild = cur_input.dobuild; gs.players[player_slot].input.build_type = cur_input.build_type; gs.players[player_slot].input.build_rotation = cur_input.build_rotation; diff --git a/types.h b/types.h index cdce4e8..603d539 100644 --- a/types.h +++ b/types.h @@ -98,7 +98,7 @@ static bool entityids_same(EntityID a, EntityID b) // when updated, must update serialization, AND comparison // function in main.c -struct InputFrame +typedef struct { uint64_t tick; size_t id; // each input has unique, incrementing, I.D, so server doesn't double process inputs. Inputs to server should be ordered from 0-max like biggest id-smallest. This is done so if packet loss server still processes input @@ -106,13 +106,13 @@ struct InputFrame bool seat_action; EntityID seat_to_inhabit; - V2 hand_pos; // world coords, world star! + V2 hand_pos; + EntityID grid_hand_pos_local_to; // when not null, hand_pos is local to this grid. this prevents bug where bool dobuild; enum BoxType build_type; enum CompassRotation build_rotation; - EntityID grid_to_build_on; -}; +} InputFrame; typedef struct Entity { @@ -158,7 +158,7 @@ typedef struct Player { bool connected; EntityID entity; - struct InputFrame input; + InputFrame input; } Player; // gotta update the serialization functions when this changes typedef struct GameState @@ -215,7 +215,7 @@ typedef struct ServerToClient struct ClientToServer { - struct InputFrame inputs[INPUT_BUFFER]; + InputFrame inputs[INPUT_BUFFER]; }; // server @@ -235,6 +235,7 @@ Entity* get_entity(struct GameState* gs, EntityID id); Entity* new_entity(struct GameState* gs); EntityID get_id(struct GameState* gs, Entity* e); V2 entity_pos(Entity* e); +void entity_set_rotation(Entity* e, float rot); void entity_set_pos(Entity* e, V2 pos); float entity_rotation(Entity* e); #define BOX_CHAIN_ITER(gs, cur, starting_box) for (Entity *cur = get_entity(gs, starting_box); cur != NULL; cur = get_entity(gs, cur->next_box)) @@ -409,6 +410,15 @@ static Color colhex(int r, int g, int b) }; } +static Color colhexcode(int hexcode) +{ + // 0x020509; + int r = (hexcode >> 16) & 0xFF; + int g = (hexcode >> 8) & 0xFF; + int b = (hexcode >> 0) & 0xFF; + return colhex(r, g, b); +} + static Color Collerp(Color a, Color b, float factor) { Color to_return = { 0 };