Add thruster block and thruster thrust

main
Cameron Murphy Reikes 2 years ago
parent a90b5331de
commit 4d147e77de

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1019 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

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

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

@ -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 <math.h>
#include <stdint.h> // tick is unsigned integer
#include <stdio.h> // 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;
}

Loading…
Cancel
Save