diff --git a/gamestate.c b/gamestate.c index c483775..64c79cd 100644 --- a/gamestate.c +++ b/gamestate.c @@ -217,7 +217,8 @@ float grid_angular_velocity(struct Grid *grid) V2 box_pos(struct Box *box) { struct Grid *g = (struct Grid *)cpBodyGetUserData(cpShapeGetBody(box->shape)); - return V2add(grid_pos(g), cp_to_v2(cpShapeGetCenterOfGravity(box->shape))); + V2 local_pos = cp_to_v2(cpShapeGetCenterOfGravity(box->shape)); + return V2add(grid_pos(g), V2rotate(local_pos, grid_rotation(g))); } float box_rotation(struct Box *box) { @@ -319,6 +320,10 @@ void ser_grid(char **out, struct Grid *g) 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].damage); } } @@ -353,6 +358,10 @@ void des_grid(char **in, struct Grid *g, struct GameState *gs) 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].damage); } } @@ -529,6 +538,22 @@ static float hash11(float p) return fract(p); } +V2 thruster_direction(struct Box *box) +{ + assert(box->type == BoxThruster); + V2 to_return = (V2){.x = 1.0f, .y = 0.0f}; + + to_return = V2rotate(to_return, rotangle(box->rotation)); + to_return = V2rotate(to_return, box_rotation(box)); + + return to_return; +} + +V2 thruster_force(struct Box *box) +{ + return V2scale(thruster_direction(box), -box->thrust * THRUSTER_FORCE); +} + uint64_t tick(struct GameState *gs) { return (uint64_t)floor(gs->time / ((double)TIMESTEP)); @@ -618,6 +643,11 @@ void process(struct GameState *gs, float dt) // process movement { + // no cheating by making movement bigger than length 1 + if (V2length(p->movement) != 0.0f) + { + p->movement = V2scale(V2normalize(p->movement), clamp(V2length(p->movement), 0.0f, 1.0f)); + } if (p->currently_inhabiting_index == -1) { // @Robust make sure movement vector is normalized so player can't cheat @@ -628,10 +658,32 @@ void process(struct GameState *gs, float dt) { struct Grid *g = &gs->grids[p->currently_inhabiting_index]; V2 target_new_pos = V2lerp(p->pos, grid_com(g), dt * 20.0f); - p->vel = V2scale(V2sub(target_new_pos, p->pos), 1.0f / dt); - cpBodyApplyForceAtWorldPoint(g->body, v2_to_cp(V2scale(p->movement, 5.0f)), v2_to_cp(grid_com(g))); + p->vel = V2scale(V2sub(target_new_pos, p->pos), 1.0f / dt); // set vel correctly so newly built grids have the correct velocity copied from it + + // set thruster forces from movement + float thruster_spice_consumption = 0.0f; + { + V2 target_direction = {0}; + if (V2length(p->movement) > 0.0f) + { + target_direction = V2normalize(p->movement); + } + for (int ii = 0; ii < MAX_BOXES_PER_GRID; ii++) + { + SKIPNULL(g->boxes[ii].shape); + if (g->boxes[ii].type != BoxThruster) + continue; + + float wanted_thrust = -V2dot(target_direction, thruster_direction(&g->boxes[ii])); + wanted_thrust = clamp01(wanted_thrust); + thruster_spice_consumption += wanted_thrust; + g->boxes[ii].thrust = wanted_thrust; + } + } + + // cpBodyApplyForceAtWorldPoint(g->body, v2_to_cp(V2scale(p->movement, 5.0f)), v2_to_cp(grid_com(g))); // bigger the ship, the more efficient the spice usage - p->spice_taken_away += dt * 0.15f / (cpBodyGetMass(g->body) * 2.0f) * V2length(p->movement); + p->spice_taken_away += dt * thruster_spice_consumption * THRUSTER_SPICE_PER_SECOND; } p->pos = V2add(p->pos, V2scale(p->vel, dt)); } @@ -702,5 +754,19 @@ void process(struct GameState *gs, float dt) p->spice_taken_away = clamp01(p->spice_taken_away); } + // add thrust from thruster blocks + for (int i = 0; i < MAX_GRIDS; i++) + { + SKIPNULL(gs->grids[i].body); + for (int ii = 0; ii < MAX_BOXES_PER_GRID; ii++) + { + SKIPNULL(gs->grids[i].boxes[ii].shape); + if (gs->grids[i].boxes[ii].type == BoxThruster) + { + cpBodyApplyForceAtWorldPoint(gs->grids[i].body, v2_to_cp(thruster_force(&gs->grids[i].boxes[ii])), v2_to_cp(box_pos(&gs->grids[i].boxes[ii]))); + } + } + } + cpSpaceStep(gs->space, dt); } \ No newline at end of file diff --git a/loaded/box.png b/loaded/box.png deleted file mode 100644 index 2a8820d..0000000 Binary files a/loaded/box.png and /dev/null differ diff --git a/loaded/hullpiece.png b/loaded/hullpiece.png new file mode 100644 index 0000000..920b032 Binary files /dev/null and b/loaded/hullpiece.png differ diff --git a/loaded/thruster.png b/loaded/thruster.png new file mode 100644 index 0000000..f1c69ce Binary files /dev/null and b/loaded/thruster.png differ diff --git a/main.c b/main.c index 9508a69..e09097c 100644 --- a/main.c +++ b/main.c @@ -39,8 +39,39 @@ static ENetHost *client; static ENetPeer *peer; static float zoom_target = 300.0f; static float zoom = 300.0f; -static sg_image image_box; +static sg_image image_hullpiece; +static sg_image image_thruster; +static sg_image load_image(const char *path) +{ + sg_image to_return = sg_alloc_image(); + + int x = 0; + int y = 0; + int comp = 0; + const int desired_channels = 4; + stbi_set_flip_vertically_on_load(true); + stbi_uc *image_data = stbi_load(path, &x, &y, &comp, desired_channels); + if (!image_data) + { + fprintf(stderr, "Failed to load image: %s\n", stbi_failure_reason()); + exit(-1); + } + sg_init_image(to_return, &(sg_image_desc){ + .width = x, + .height = y, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + .data.subimage[0][0] = { + .ptr = image_data, + .size = (size_t)(x * y * desired_channels), + }}); + + stbi_image_free(image_data); + + return to_return; +} static void init(void) { // @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 @@ -65,30 +96,8 @@ static void init(void) // image loading { - image_box = sg_alloc_image(); - - int x = 0; - int y = 0; - int comp = 0; - const int desired_channels = 4; - stbi_uc *image_data = stbi_load("loaded/box.png", &x, &y, &comp, desired_channels); - if (!image_data) - { - fprintf(stderr, "Failed to load image: %s\n", stbi_failure_reason()); - exit(-1); - } - sg_init_image(image_box, &(sg_image_desc){ - .width = x, - .height = y, - .pixel_format = SG_PIXELFORMAT_RGBA8, - .min_filter = SG_FILTER_NEAREST, - .mag_filter = SG_FILTER_NEAREST, - .data.subimage[0][0] = { - .ptr = image_data, - .size = (size_t)(x * y * desired_channels), - }}); - - stbi_image_free(image_data); + image_hullpiece = load_image("loaded/hullpiece.png"); + image_thruster = load_image("loaded/thruster.png"); } // socket initialization @@ -139,20 +148,26 @@ static void init(void) } } -static void drawbox(V2 gridpos, float rot, V2 bpos, float damage, bool offset_from_grid) +static void drawbox(V2 boxpos, float rot, float damage, enum BoxType type, enum Rotation rotation) { float halfbox = BOX_SIZE / 2.0f; sgp_push_transform(); - if (offset_from_grid) - { - sgp_rotate_at(rot, gridpos.x, gridpos.y); - } - else + sgp_rotate_at(rot, boxpos.x, boxpos.y); + + switch(type) { - sgp_rotate_at(rot, bpos.x, bpos.y); + case BoxHullpiece: + sgp_set_image(0, image_hullpiece); + break; + case BoxThruster: + sgp_set_image(0, image_thruster); + break; + default: + Log("Unknown image for box type of type %d\n", type); + break; } - sgp_set_image(0, image_box); - sgp_draw_textured_rect(bpos.x - halfbox, bpos.y -halfbox, BOX_SIZE, BOX_SIZE); + sgp_rotate_at( rotangle(rotation),boxpos.x,boxpos.y); + sgp_draw_textured_rect(boxpos.x - halfbox, boxpos.y - halfbox, BOX_SIZE, BOX_SIZE); sgp_reset_image(0); /*sgp_draw_line(bpos.x - halfbox, bpos.y - halfbox, bpos.x - halfbox, bpos.y + halfbox); // left @@ -165,7 +180,7 @@ static void drawbox(V2 gridpos, float rot, V2 bpos, float damage, bool offset_fr if (damage > 0.0f) { sgp_set_color(0.5f, 0.1f, 0.1f, damage); - sgp_draw_filled_rect(bpos.x - halfbox, bpos.y - halfbox, BOX_SIZE, BOX_SIZE); + sgp_draw_filled_rect(boxpos.x - halfbox, boxpos.y - halfbox, BOX_SIZE, BOX_SIZE); } sgp_pop_transform(); @@ -438,7 +453,6 @@ static void frame(void) float halfbox = BOX_SIZE / 2.0f; - // mouse if (mouse_frozen) { @@ -449,7 +463,7 @@ static void frame(void) // building preview { sgp_set_color(0.5f, 0.5f, 0.5f, (sin(time * 9.0f) + 1.0) / 3.0f + 0.2); - drawbox(build_preview.grid_pos, build_preview.grid_rotation, build_preview.pos, 0.0f, false); + drawbox(build_preview.pos, build_preview.grid_rotation, 0.0f, BoxHullpiece, Right); } // grids @@ -463,7 +477,14 @@ static void frame(void) SKIPNULL(g->boxes[ii].shape); struct Box *b = &g->boxes[ii]; sgp_set_color(0.5f, 0.5f, 0.5f, 1.0f); - drawbox(grid_pos(g), grid_rotation(g), box_pos(b), b->damage, true); + // debug draw force vectors for thrusters + if (false){ + if(b->type == BoxThruster) { + dbg_rect(box_pos(b)); + dbg_line(box_pos(b), V2add(box_pos(b), V2scale(thruster_force(b), -1.0f))); + } + } + drawbox(box_pos(b), grid_rotation(g), b->damage, b->type, b->rotation); } sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f); V2 vel = grid_vel(&gs.grids[i]); @@ -499,7 +520,6 @@ static void frame(void) // sgp_draw_line(grid_pos(p->grid).x, grid_pos(p->grid).y, to.x, to.y); } - // gold target set_color(GOLD); sgp_draw_filled_rect(gs.goldpos.x, gs.goldpos.y, 0.1f, 0.1f); diff --git a/server.c b/server.c index df62796..3acbe9f 100644 --- a/server.c +++ b/server.c @@ -22,6 +22,11 @@ void server(void *data) box_new(&gs.grids[0].boxes[1], &gs, &gs.grids[0], (V2){0, BOX_SIZE}); box_new(&gs.grids[0].boxes[2], &gs, &gs.grids[0], (V2){0, BOX_SIZE*2.0}); box_new(&gs.grids[0].boxes[3], &gs, &gs.grids[0], (V2){BOX_SIZE, BOX_SIZE*2.0}); + gs.grids[0].boxes[3].type = BoxThruster; + gs.grids[0].boxes[3].rotation = Right; + box_new(&gs.grids[0].boxes[4], &gs, &gs.grids[0], (V2){0, BOX_SIZE*3.0}); + gs.grids[0].boxes[4].type = BoxThruster; + gs.grids[0].boxes[4].rotation = Up; grid_new(&gs.grids[1], &gs, (V2){.x = -BOX_SIZE*1.5, .y = 0.0}); box_new(&gs.grids[1].boxes[0], &gs, &gs.grids[1], (V2){0}); diff --git a/types.h b/types.h index 47a427f..c385240 100644 --- a/types.h +++ b/types.h @@ -8,6 +8,9 @@ #define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2f #define MAX_BOXES_PER_GRID 32 #define BOX_MASS 1.0f +#define THRUSTER_FORCE 2.0f +#define THRUSTER_SPICE_PER_SECOND 0.1f + #define TIMESTEP (1.0f / 60.0f) // not required to simulate at this, but this defines what tick the game is on #define TIME_BETWEEN_INPUT_PACKETS (1.0f / 20.0f) #define SERVER_PORT 2551 @@ -19,6 +22,7 @@ // @Robust remove this include somehow, needed for sqrt and cos #include #include // tick is unsigned integer +#include // logging on errors for functions // including headers from headers bad #ifndef SOKOL_GP_INCLUDED @@ -80,7 +84,9 @@ struct GameState float goldness; // how much the player is a winner // input - V2 movement; + // @Cleanup make this a frameinput struct instead of copying over all the fields like this + + V2 movement; // can be at maximum length 1.0 bool inhabit; // if grid_index != -1, this is in local coordinates to the grid @@ -98,12 +104,54 @@ struct GameState struct Box { + enum BoxType + { + BoxHullpiece, + BoxThruster, + } type; + enum Rotation + { + Right, + Down, + Left, + Up, + } rotation; + + // thruster + float thrust; // must be between 0 and 1 + 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]; }; +#define PI 3.14159f + +// returns in radians +static float rotangle(enum Rotation rot) +{ + switch(rot) + { + case Right: + return 0.0f; + break; + case Down: + return -PI/2.0f; + break; + case Left: + return -PI; + break; + case Up: + return -3.0f * PI/2.0f; + break; + default: + Log("Unknown rotation %d\n", rot); + return -0.0f; + break; + } +} + struct ServerToClient { struct GameState *cur_gs; @@ -154,6 +202,10 @@ void box_new(struct Box *to_modify, struct GameState *gs, struct Grid *grid, V2 V2 box_pos(struct Box *box); float box_rotation(struct Box *box); +// thruster +V2 thruster_direction(struct Box *box); +V2 thruster_force(struct Box *box); + // debug draw void dbg_drawall(); void dbg_line(V2 from, V2 to); @@ -166,7 +218,6 @@ void dbg_rect(V2 center); // all the math is static so that it can be defined in each compilation unit its included in -#define PI 3.14159f static V2 V2add(V2 a, V2 b) { @@ -244,9 +295,9 @@ static inline float clamp01(float f) static inline float clamp(float f, float minimum, float maximum) { - if(f < minimum) + if (f < minimum) return minimum; - if(f > maximum) + if (f > maximum) return maximum; return f; }