Thruster placing/rotate, toolbar UI

main
Cameron Murphy Reikes 2 years ago
parent 4d147e77de
commit 547727c7f3

@ -367,6 +367,30 @@ void des_grid(char **in, struct Grid *g, struct GameState *gs)
} }
} }
void ser_inputframe(char **out, struct InputFrame *i)
{
ser_V2(out, i->movement);
ser_bool(out, i->inhabit);
ser_V2(out, i->build);
ser_bool(out, i->dobuild);
ser_int(out, i->build_type);
ser_int(out, i->build_rotation);
ser_int(out, i->grid_index);
}
void des_inputframe(char **in, struct InputFrame *i)
{
des_V2(in, &i->movement);
des_bool(in, &i->inhabit);
des_V2(in, &i->build);
des_bool(in, &i->dobuild);
des_int(in, (int *)&i->build_type);
des_int(in, (int *)&i->build_rotation);
des_int(in, &i->grid_index);
}
void ser_player(char **out, struct Player *p) void ser_player(char **out, struct Player *p)
{ {
ser_bool(out, p->connected); ser_bool(out, p->connected);
@ -378,13 +402,7 @@ void ser_player(char **out, struct Player *p)
ser_float(out, p->spice_taken_away); ser_float(out, p->spice_taken_away);
ser_float(out, p->goldness); ser_float(out, p->goldness);
// input ser_inputframe(out, &p->input);
ser_V2(out, p->movement);
ser_bool(out, p->inhabit);
ser_V2(out, p->build);
ser_bool(out, p->dobuild);
ser_int(out, p->grid_index);
} }
} }
@ -399,13 +417,7 @@ void des_player(char **in, struct Player *p, struct GameState *gs)
des_float(in, &p->spice_taken_away); des_float(in, &p->spice_taken_away);
des_float(in, &p->goldness); des_float(in, &p->goldness);
// input des_inputframe(in, &p->input);
des_V2(in, &p->movement);
des_bool(in, &p->inhabit);
des_V2(in, &p->build);
des_bool(in, &p->dobuild);
des_int(in, &p->grid_index);
} }
} }
@ -586,9 +598,9 @@ void process(struct GameState *gs, float dt)
p->currently_inhabiting_index = -1; p->currently_inhabiting_index = -1;
} }
if (p->inhabit) if (p->input.inhabit)
{ {
p->inhabit = false; // "handle" the input p->input.inhabit = false; // "handle" the input
if (p->currently_inhabiting_index == -1) if (p->currently_inhabiting_index == -1)
{ {
@ -644,15 +656,15 @@ void process(struct GameState *gs, float dt)
// process movement // process movement
{ {
// no cheating by making movement bigger than length 1 // no cheating by making movement bigger than length 1
if (V2length(p->movement) != 0.0f) if (V2length(p->input.movement) != 0.0f)
{ {
p->movement = V2scale(V2normalize(p->movement), clamp(V2length(p->movement), 0.0f, 1.0f)); p->input.movement = V2scale(V2normalize(p->input.movement), clamp(V2length(p->input.movement), 0.0f, 1.0f));
} }
if (p->currently_inhabiting_index == -1) if (p->currently_inhabiting_index == -1)
{ {
// @Robust make sure movement vector is normalized so player can't cheat // @Robust make sure movement vector is normalized so player can't cheat
p->vel = V2add(p->vel, V2scale(p->movement, dt * 0.5f)); p->vel = V2add(p->vel, V2scale(p->input.movement, dt * 0.5f));
p->spice_taken_away += dt * 0.15f * V2length(p->movement); p->spice_taken_away += dt * 0.15f * V2length(p->input.movement);
} }
else else
{ {
@ -664,9 +676,9 @@ void process(struct GameState *gs, float dt)
float thruster_spice_consumption = 0.0f; float thruster_spice_consumption = 0.0f;
{ {
V2 target_direction = {0}; V2 target_direction = {0};
if (V2length(p->movement) > 0.0f) if (V2length(p->input.movement) > 0.0f)
{ {
target_direction = V2normalize(p->movement); target_direction = V2normalize(p->input.movement);
} }
for (int ii = 0; ii < MAX_BOXES_PER_GRID; ii++) for (int ii = 0; ii < MAX_BOXES_PER_GRID; ii++)
{ {
@ -681,23 +693,23 @@ void process(struct GameState *gs, float dt)
} }
} }
// cpBodyApplyForceAtWorldPoint(g->body, v2_to_cp(V2scale(p->movement, 5.0f)), v2_to_cp(grid_com(g))); // cpBodyApplyForceAtWorldPoint(g->body, v2_to_cp(V2scale(p->input.movement, 5.0f)), v2_to_cp(grid_com(g)));
// bigger the ship, the more efficient the spice usage // bigger the ship, the more efficient the spice usage
p->spice_taken_away += dt * thruster_spice_consumption * THRUSTER_SPICE_PER_SECOND; p->spice_taken_away += dt * thruster_spice_consumption * THRUSTER_SPICE_PER_SECOND;
} }
p->pos = V2add(p->pos, V2scale(p->vel, dt)); p->pos = V2add(p->pos, V2scale(p->vel, dt));
} }
if (p->dobuild) if (p->input.dobuild)
{ {
p->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... p->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}; cpPointQueryInfo info = {0};
// @Robust make sure to query only against boxes... // @Robust make sure to query only against boxes...
V2 world_build = p->build; V2 world_build = p->input.build;
if (p->grid_index != -1) if (p->input.grid_index != -1)
{ {
world_build = grid_local_to_world(&gs->grids[p->grid_index], p->build); world_build = grid_local_to_world(&gs->grids[p->input.grid_index], p->input.build);
} }
cpShape *nearest = cpSpacePointQueryNearest(gs->space, v2_to_cp(world_build), 0.01f, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES), &info); cpShape *nearest = cpSpacePointQueryNearest(gs->space, v2_to_cp(world_build), 0.01f, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES), &info);
if (nearest != NULL) if (nearest != NULL)
@ -707,7 +719,7 @@ void process(struct GameState *gs, float dt)
grid_remove_box(gs->space, cur_grid, cur_box); grid_remove_box(gs->space, cur_grid, cur_box);
p->spice_taken_away -= 0.1f; p->spice_taken_away -= 0.1f;
} }
else if (p->grid_index == -1) else if (p->input.grid_index == -1)
{ {
// @Robust better memory mgmt // @Robust better memory mgmt
struct Grid *empty_grid = NULL; struct Grid *empty_grid = NULL;
@ -719,15 +731,18 @@ void process(struct GameState *gs, float dt)
break; break;
} }
} }
// @Robust cleanly fail when not enough grids
assert(empty_grid != NULL); assert(empty_grid != NULL);
p->spice_taken_away += 0.2f; p->spice_taken_away += 0.2f;
grid_new(empty_grid, gs, world_build); grid_new(empty_grid, gs, world_build);
box_new(&empty_grid->boxes[0], gs, empty_grid, (V2){0}); box_new(&empty_grid->boxes[0], gs, empty_grid, (V2){0});
empty_grid->boxes[0].type = p->input.build_type;
empty_grid->boxes[0].rotation = p->input.build_rotation;
cpBodySetVelocity(empty_grid->body, v2_to_cp(p->vel)); cpBodySetVelocity(empty_grid->body, v2_to_cp(p->vel));
} }
else else
{ {
struct Grid *g = &gs->grids[p->grid_index]; struct Grid *g = &gs->grids[p->input.grid_index];
struct Box *empty_box = NULL; struct Box *empty_box = NULL;
for (int ii = 0; ii < MAX_BOXES_PER_GRID; ii++) for (int ii = 0; ii < MAX_BOXES_PER_GRID; ii++)
@ -742,6 +757,8 @@ void process(struct GameState *gs, float dt)
assert(empty_box != NULL); assert(empty_box != NULL);
p->spice_taken_away += 0.1f; p->spice_taken_away += 0.1f;
box_new(empty_box, gs, g, grid_world_to_local(g, world_build)); box_new(empty_box, gs, g, grid_world_to_local(g, world_build));
empty_box->type = p->input.build_type;
empty_box->rotation = p->input.build_rotation;
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

163
main.c

@ -39,8 +39,37 @@ static ENetHost *client;
static ENetPeer *peer; static ENetPeer *peer;
static float zoom_target = 300.0f; static float zoom_target = 300.0f;
static float zoom = 300.0f; static float zoom = 300.0f;
static sg_image image_hullpiece; static sg_image image_itemframe;
static sg_image image_thruster; static sg_image image_itemframe_selected;
static int cur_editing_boxtype = -1;
static int cur_editing_rotation = 0;
static struct BoxInfo
{
enum BoxType type;
const char *image_path;
sg_image image;
} boxes[] = { // if added to here will show up in toolbar, is placeable
{
.type = BoxHullpiece,
.image_path = "loaded/hullpiece.png",
},
{
.type = BoxThruster,
.image_path = "loaded/thruster.png",
}};
const int boxes_len = sizeof(boxes) / sizeof(*boxes);
struct BoxInfo boxinfo(enum BoxType type)
{
for (int i = 0; i < boxes_len; i++)
{
if (boxes[i].type == type)
return boxes[i];
}
Log("No box info found for type %d\n", type);
return (struct BoxInfo){0};
}
static sg_image load_image(const char *path) static sg_image load_image(const char *path)
{ {
@ -96,8 +125,12 @@ static void init(void)
// image loading // image loading
{ {
image_hullpiece = load_image("loaded/hullpiece.png"); for (int i = 0; i < boxes_len; i++)
image_thruster = load_image("loaded/thruster.png"); {
boxes[i].image = load_image(boxes[i].image_path);
}
image_itemframe = load_image("loaded/itemframe.png");
image_itemframe_selected = load_image("loaded/itemframe_selected.png");
} }
// socket initialization // socket initialization
@ -154,18 +187,7 @@ static void drawbox(V2 boxpos, float rot, float damage, enum BoxType type, enum
sgp_push_transform(); sgp_push_transform();
sgp_rotate_at(rot, boxpos.x, boxpos.y); sgp_rotate_at(rot, boxpos.x, boxpos.y);
switch(type) sgp_set_image(0, boxinfo(type).image);
{
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_rotate_at(rotangle(rotation), boxpos.x, boxpos.y); sgp_rotate_at(rotangle(rotation), boxpos.x, boxpos.y);
sgp_draw_textured_rect(boxpos.x - halfbox, boxpos.y - halfbox, BOX_SIZE, BOX_SIZE); sgp_draw_textured_rect(boxpos.x - halfbox, boxpos.y - halfbox, BOX_SIZE, BOX_SIZE);
sgp_reset_image(0); sgp_reset_image(0);
@ -202,6 +224,69 @@ static void draw_circle(V2 point, float radius)
sgp_draw_lines(lines, POINTS); sgp_draw_lines(lines, POINTS);
} }
static void ui(bool draw, float width, float height)
{
// draw spice bar
if (draw && myplayer != -1)
{
sgp_set_color(0.5f, 0.5f, 0.5f, 1.0f);
float margin = width * 0.1;
float bar_width = width - margin * 2.0f;
sgp_draw_filled_rect(margin, 80.0f, bar_width, 30.0f);
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
sgp_draw_filled_rect(margin, 80.0f, bar_width * (1.0f - gs.players[myplayer].spice_taken_away), 30.0f);
}
// draw item toolbar
{
int itemframe_width = sg_query_image_info(image_itemframe).width * 2.0f;
int itemframe_height = sg_query_image_info(image_itemframe).height * 2.0f;
int total_width = itemframe_width * boxes_len;
float item_width = itemframe_width * 0.75;
float item_height = itemframe_height * 0.75;
float item_offset_x = (itemframe_width - item_width) / 2.0f;
float item_offset_y = (itemframe_height - item_height) / 2.0f;
float x = width / 2.0 - total_width / 2.0;
float y = height - itemframe_height * 1.5;
for (int i = 0; i < boxes_len; i++)
{
if (has_point((AABB){
.x = x,
.y = y,
.width = itemframe_width,
.height = itemframe_height,
},
mouse_pos) &&
mouse_pressed)
{
// "handle" mouse pressed
mouse_pressed = false;
cur_editing_boxtype = i;
}
if (draw)
{
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
if (cur_editing_boxtype == i)
{
sgp_set_image(0, image_itemframe_selected);
}
else
{
sgp_set_image(0, image_itemframe);
}
sgp_draw_textured_rect(x, y, itemframe_width, itemframe_height);
sgp_set_image(0, boxinfo((enum BoxType)i).image);
sgp_draw_textured_rect(x + item_offset_x, y + item_offset_y, item_width, item_height);
sgp_reset_image(0);
}
x += itemframe_width;
}
}
}
static void frame(void) static void frame(void)
{ {
int width = sapp_width(), height = sapp_height(); int width = sapp_width(), height = sapp_height();
@ -269,6 +354,7 @@ static void frame(void)
} }
// gameplay // gameplay
ui(false, width, height); // handle events
V2 build_target_pos = {0}; V2 build_target_pos = {0};
float build_target_rotation = 0.0f; float build_target_rotation = 0.0f;
V2 camera_pos = {0}; V2 camera_pos = {0};
@ -356,6 +442,8 @@ static void frame(void)
cur_input_frame.grid_index = grid_index; cur_input_frame.grid_index = grid_index;
if (cur_input_frame.dobuild) if (cur_input_frame.dobuild)
{ {
cur_input_frame.build_type = cur_editing_boxtype;
cur_input_frame.build_rotation = cur_editing_rotation;
if (grid_index != -1) if (grid_index != -1)
{ {
cur_input_frame.build = grid_world_to_local(&gs.grids[cur_input_frame.grid_index], build_preview.pos); cur_input_frame.build = grid_world_to_local(&gs.grids[cur_input_frame.grid_index], build_preview.pos);
@ -410,28 +498,17 @@ static void frame(void)
sgp_set_color(0.1f, 0.1f, 0.1f, 1.0f); sgp_set_color(0.1f, 0.1f, 0.1f, 1.0f);
sgp_clear(); sgp_clear();
// draw spice bar
if (myplayer != -1)
{
sgp_set_color(0.5f, 0.5f, 0.5f, 1.0f);
float margin = width * 0.1;
float bar_width = width - margin * 2.0f;
sgp_draw_filled_rect(margin, 80.0f, bar_width, 30.0f);
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
sgp_draw_filled_rect(margin, 80.0f, bar_width * (1.0f - gs.players[myplayer].spice_taken_away), 30.0f);
}
// sokol drawing library draw in world space // sokol drawing library draw in world space
// world space coordinates are +Y up, -Y down. Like normal cartesian coords // world space coordinates are +Y up, -Y down. Like normal cartesian coords
{ {
sgp_push_transform();
sgp_translate(width / 2, height / 2); sgp_translate(width / 2, height / 2);
sgp_scale_at(zoom, -zoom, 0.0f, 0.0f); sgp_scale_at(zoom, -zoom, 0.0f, 0.0f);
// camera go to player // camera go to player
sgp_translate(-camera_pos.x, -camera_pos.y); sgp_translate(-camera_pos.x, -camera_pos.y);
}
// hand reached limit circle
if (myplayer != -1) if (myplayer != -1)
{ {
static float hand_reach_alpha = 1.0f; static float hand_reach_alpha = 1.0f;
@ -461,9 +538,9 @@ static void frame(void)
} }
// building preview // building preview
{ if(cur_editing_boxtype != -1){
sgp_set_color(0.5f, 0.5f, 0.5f, (sin(time * 9.0f) + 1.0) / 3.0f + 0.2); sgp_set_color(0.5f, 0.5f, 0.5f, (sin(time * 9.0f) + 1.0) / 3.0f + 0.2);
drawbox(build_preview.pos, build_preview.grid_rotation, 0.0f, BoxHullpiece, Right); drawbox(build_preview.pos, build_preview.grid_rotation, 0.0f, cur_editing_boxtype, cur_editing_rotation);
} }
// grids // grids
@ -478,8 +555,10 @@ static void frame(void)
struct Box *b = &g->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);
// debug draw force vectors for thrusters // debug draw force vectors for thrusters
if (false){ if (false)
if(b->type == BoxThruster) { {
if (b->type == BoxThruster)
{
dbg_rect(box_pos(b)); dbg_rect(box_pos(b));
dbg_line(box_pos(b), V2add(box_pos(b), V2scale(thruster_force(b), -1.0f))); dbg_line(box_pos(b), V2add(box_pos(b), V2scale(thruster_force(b), -1.0f)));
} }
@ -527,6 +606,12 @@ static void frame(void)
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f); sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
dbg_drawall(); dbg_drawall();
sgp_pop_transform();
}
// UI drawn in screen space
ui(true, width, height);
sg_pass_action pass_action = {0}; sg_pass_action pass_action = {0};
sg_begin_default_pass(&pass_action, width, height); sg_begin_default_pass(&pass_action, width, height);
sgp_flush(); sgp_flush();
@ -553,6 +638,18 @@ void event(const sapp_event *e)
{ {
mouse_frozen = !mouse_frozen; mouse_frozen = !mouse_frozen;
} }
if (e->key_code == SAPP_KEYCODE_R)
{
cur_editing_rotation += 1;
cur_editing_rotation %= RotationLast;
}
int key_num = e->key_code - SAPP_KEYCODE_0;
int target_box = key_num - 1;
if(target_box < BoxLast)
{
cur_editing_boxtype = target_box;
}
if (!mouse_frozen) if (!mouse_frozen)
{ {
keydown[e->key_code] = true; keydown[e->key_code] = true;

@ -157,20 +157,22 @@ void server(void *data)
if(received.inputs[i].tick <= latest_tick) if(received.inputs[i].tick <= latest_tick)
continue; // don't reprocess inputs already processed continue; // don't reprocess inputs already processed
struct InputFrame cur_input = received.inputs[i]; struct InputFrame cur_input = received.inputs[i];
gs.players[player_slot].movement = cur_input.movement; gs.players[player_slot].input.movement = cur_input.movement;
gs.players[player_slot].grid_index = cur_input.grid_index; gs.players[player_slot].input.grid_index = cur_input.grid_index;
// for these "event" inputs, only modify the game state if the event is true. // for these "event" inputs, only modify the current input if the event is true.
// while processing the gamestate, will mark it as false once processed. This // while processing the gamestate, will mark it as false once processed. This
// prevents setting the event input to false before it's been processed. // prevents setting the event input to false before it's been processed.
if (cur_input.inhabit) if (cur_input.inhabit)
{ {
gs.players[player_slot].inhabit = cur_input.inhabit; gs.players[player_slot].input.inhabit = cur_input.inhabit;
} }
if (cur_input.dobuild) if (cur_input.dobuild)
{ {
gs.players[player_slot].build = cur_input.build; gs.players[player_slot].input.build = cur_input.build;
gs.players[player_slot].dobuild = cur_input.dobuild; 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;
} }
} }
player_to_latest_tick_processed[player_slot] = received.inputs[0].tick; player_to_latest_tick_processed[player_slot] = received.inputs[0].tick;

@ -8,8 +8,8 @@
#define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2f #define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2f
#define MAX_BOXES_PER_GRID 32 #define MAX_BOXES_PER_GRID 32
#define BOX_MASS 1.0f #define BOX_MASS 1.0f
#define THRUSTER_FORCE 2.0f #define THRUSTER_FORCE 4.0f
#define THRUSTER_SPICE_PER_SECOND 0.1f #define THRUSTER_SPICE_PER_SECOND 0.02f
#define TIMESTEP (1.0f / 60.0f) // not required to simulate at this, but this defines what tick the game is on #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 TIME_BETWEEN_INPUT_PACKETS (1.0f / 20.0f)
@ -63,6 +63,36 @@ typedef sgp_point P2;
fprintf(stdout, "%s:%d | ", __FILE__, __LINE__); \ fprintf(stdout, "%s:%d | ", __FILE__, __LINE__); \
fprintf(stdout, __VA_ARGS__) fprintf(stdout, __VA_ARGS__)
enum BoxType
{
BoxHullpiece,
BoxThruster,
BoxLast
};
enum Rotation
{
Right,
Down,
Left,
Up,
RotationLast,
};
struct InputFrame
{
uint64_t tick;
V2 movement;
bool inhabit;
// if grid_index != -1, this is in local coordinates to the grid
V2 build;
bool dobuild;
enum BoxType build_type;
enum Rotation build_rotation;
int grid_index;
};
// gotta update the serialization functions when this changes // gotta update the serialization functions when this changes
struct GameState struct GameState
{ {
@ -86,13 +116,7 @@ struct GameState
// input // input
// @Cleanup make this a frameinput struct instead of copying over all the fields like this // @Cleanup make this a frameinput struct instead of copying over all the fields like this
V2 movement; // can be at maximum length 1.0 struct InputFrame input;
bool inhabit;
// if grid_index != -1, this is in local coordinates to the grid
V2 build; // @Robust this is messy, clean up?
bool dobuild;
int grid_index;
} players[MAX_PLAYERS]; } players[MAX_PLAYERS];
// if body or shape is null, then that grid/box has been freed // if body or shape is null, then that grid/box has been freed
@ -104,18 +128,8 @@ struct GameState
struct Box struct Box
{ {
enum BoxType enum BoxType type;
{ enum Rotation rotation;
BoxHullpiece,
BoxThruster,
} type;
enum Rotation
{
Right,
Down,
Left,
Up,
} rotation;
// thruster // thruster
float thrust; // must be between 0 and 1 float thrust; // must be between 0 and 1
@ -160,17 +174,7 @@ struct ServerToClient
struct ClientToServer struct ClientToServer
{ {
struct InputFrame struct InputFrame inputs[INPUT_BUFFER];
{
uint64_t tick;
V2 movement;
bool inhabit;
// if grid_index != -1, this is in local coordinates to the grid
V2 build;
bool dobuild;
int grid_index;
} inputs[INPUT_BUFFER];
}; };
// server // server
@ -218,6 +222,15 @@ void dbg_rect(V2 center);
// all the math is static so that it can be defined in each compilation unit its included in // all the math is static so that it can be defined in each compilation unit its included in
typedef struct AABB
{
float x, y, width, height;
} AABB;
static bool has_point(AABB aabb, V2 point)
{
return point.x > aabb.x && point.x < aabb.x + aabb.width && point.y > aabb.y && point.y < aabb.y + aabb.height;
}
static V2 V2add(V2 a, V2 b) static V2 V2add(V2 a, V2 b)
{ {

Loading…
Cancel
Save