Added blueprint palette and item toolbar

main
Cameron Murphy Reikes 2 years ago
parent 0ce9ecad9d
commit 98d7dc146a

@ -73,22 +73,29 @@ Entity *get_entity(GameState *gs, EntityID id)
return to_return; return to_return;
} }
static uint64_t box_unlock_number(enum BoxType box) static BOX_UNLOCKS_TYPE box_unlock_number(enum BoxType box)
{ {
assert((uint64_t)box < 64); assert((BOX_UNLOCKS_TYPE)box < 64);
return (uint64_t)((uint64_t)1 << ((uint64_t)box)); return (BOX_UNLOCKS_TYPE)((BOX_UNLOCKS_TYPE)1 << ((BOX_UNLOCKS_TYPE)box));
}
static bool learned_boxes_has_box(BOX_UNLOCKS_TYPE learned, enum BoxType box)
{
return (learned & box_unlock_number(box)) > 0;
} }
void unlock_box(Player *player, enum BoxType box) void unlock_box(Player *player, enum BoxType box)
{ {
assert(box < MAX_BOX_TYPES); assert(box < MAX_BOX_TYPES);
assert(box != BoxInvalid);
player->box_unlocks |= box_unlock_number(box); player->box_unlocks |= box_unlock_number(box);
} }
bool box_unlocked(Player *player, enum BoxType box) bool box_unlocked(Player *player, enum BoxType box)
{ {
assert(box < MAX_BOX_TYPES); assert(box < MAX_BOX_TYPES);
return (player->box_unlocks & box_unlock_number(box)) > 0; if(box == BoxInvalid) return false;
return learned_boxes_has_box(player->box_unlocks, box);
} }
EntityID get_id(GameState *gs, Entity *e) EntityID get_id(GameState *gs, Entity *e)
@ -611,6 +618,8 @@ float entity_angular_velocity(Entity *grid)
} }
Entity *box_grid(Entity *box) Entity *box_grid(Entity *box)
{ {
if(box == NULL) return NULL;
assert(box->is_box);
return (Entity *)cpBodyGetUserData(cpShapeGetBody(box->shape)); return (Entity *)cpBodyGetUserData(cpShapeGetBody(box->shape));
} }
// in local space // in local space
@ -787,18 +796,6 @@ SerMaybeFailure ser_var(SerState *ser, char *var_pointer, size_t var_size, const
enum GameVersion enum GameVersion
{ {
VInitial, VInitial,
VAddedTest,
VAddedSerToDisk,
VRemovedTest,
VChangedVectorSerializing,
VAddedLastUsedMedbay,
VAddedSquads,
VAddedSquadInvites,
VRemovedTimeFromDiskSave, // did this to avoid wayy too big a time causing precision problems
VReallyRemovedTimeFromDiskSave, // apparently last one didn't work
VRemovedInsideOfMe,
VSwitchedToUnlocks,
VAddedPlatonic,
VMax, // this minus one will be the version used VMax, // this minus one will be the version used
}; };
@ -839,12 +836,9 @@ SerMaybeFailure ser_inputframe(SerState *ser, InputFrame *i)
SER_VAR(&i->take_over_squad); SER_VAR(&i->take_over_squad);
SER_ASSERT(i->take_over_squad >= 0 || i->take_over_squad == -1); SER_ASSERT(i->take_over_squad >= 0 || i->take_over_squad == -1);
SER_ASSERT(i->take_over_squad < SquadLast); SER_ASSERT(i->take_over_squad < SquadLast);
if (ser->version >= VAddedSquadInvites) SER_VAR(&i->accept_cur_squad_invite);
{ SER_VAR(&i->reject_cur_squad_invite);
SER_VAR(&i->accept_cur_squad_invite); SER_MAYBE_RETURN(ser_entityid(ser, &i->invite_this_player));
SER_VAR(&i->reject_cur_squad_invite);
SER_MAYBE_RETURN(ser_entityid(ser, &i->invite_this_player));
}
SER_VAR(&i->seat_action); SER_VAR(&i->seat_action);
SER_MAYBE_RETURN(ser_V2(ser, &i->hand_pos)); SER_MAYBE_RETURN(ser_V2(ser, &i->hand_pos));
@ -863,20 +857,11 @@ SerMaybeFailure ser_player(SerState *ser, Player *p)
SER_VAR(&p->connected); SER_VAR(&p->connected);
if (p->connected) if (p->connected)
{ {
if (ser->version >= VSwitchedToUnlocks) SER_VAR(&p->box_unlocks);
{
SER_VAR(&p->box_unlocks); SER_VAR(&p->squad);
}
else
{
bool throwaway;
SER_VAR_NAME(&throwaway, "&p->unlocked_bombs");
}
if (ser->version >= VAddedSquads)
SER_VAR(&p->squad);
SER_MAYBE_RETURN(ser_entityid(ser, &p->entity)); SER_MAYBE_RETURN(ser_entityid(ser, &p->entity));
if (ser->version >= VAddedLastUsedMedbay) SER_MAYBE_RETURN(ser_entityid(ser, &p->last_used_medbay));
SER_MAYBE_RETURN(ser_entityid(ser, &p->last_used_medbay));
SER_MAYBE_RETURN(ser_inputframe(ser, &p->input)); SER_MAYBE_RETURN(ser_inputframe(ser, &p->input));
} }
@ -889,10 +874,6 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
SER_VAR(&e->generation); SER_VAR(&e->generation);
SER_VAR(&e->damage); SER_VAR(&e->damage);
int test;
if (ser->version < VRemovedTest && ser->version >= VAddedTest)
SER_VAR(&test);
bool has_body = ser->serializing && e->body != NULL; bool has_body = ser->serializing && e->body != NULL;
SER_VAR(&has_body); SER_VAR(&has_body);
@ -922,14 +903,7 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
V2 shape_pos; V2 shape_pos;
if (ser->serializing) if (ser->serializing)
shape_pos = entity_shape_pos(e); shape_pos = entity_shape_pos(e);
if (ser->version < VChangedVectorSerializing) SER_MAYBE_RETURN(ser_V2(ser, &shape_pos));
{
SER_VAR(&shape_pos);
}
else
{
SER_MAYBE_RETURN(ser_V2(ser, &shape_pos));
}
float shape_mass; float shape_mass;
if (ser->serializing) if (ser->serializing)
@ -958,10 +932,8 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
SER_ASSERT(e->no_save_to_disk); SER_ASSERT(e->no_save_to_disk);
SER_MAYBE_RETURN(ser_entityid(ser, &e->currently_inside_of_box)); SER_MAYBE_RETURN(ser_entityid(ser, &e->currently_inside_of_box));
if (ser->version >= VAddedSquads) SER_VAR(&e->presenting_squad);
SER_VAR(&e->presenting_squad); SER_VAR(&e->squad_invited_to);
if (ser->version >= VAddedSquadInvites)
SER_VAR(&e->squad_invited_to);
SER_VAR(&e->goldness); SER_VAR(&e->goldness);
} }
@ -984,37 +956,39 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
if (e->is_box) if (e->is_box)
{ {
SER_VAR(&e->box_type); SER_VAR(&e->box_type);
SER_VAR(&e->is_platonic);
SER_VAR(&e->always_visible); SER_VAR(&e->always_visible);
if (ser->version >= VAddedPlatonic)
SER_VAR(&e->is_platonic);
if (ser->version <= VSwitchedToUnlocks)
{
bool throwaway;
SER_VAR_NAME(&throwaway, "&e->is_explosion_unlock");
}
SER_MAYBE_RETURN(ser_entityid(ser, &e->next_box)); SER_MAYBE_RETURN(ser_entityid(ser, &e->next_box));
SER_MAYBE_RETURN(ser_entityid(ser, &e->prev_box)); SER_MAYBE_RETURN(ser_entityid(ser, &e->prev_box));
SER_VAR(&e->compass_rotation); SER_VAR(&e->compass_rotation);
SER_VAR(&e->indestructible); SER_VAR(&e->indestructible);
SER_VAR(&e->thrust); switch (e->box_type)
SER_VAR(&e->wanted_thrust);
SER_VAR(&e->energy_used);
SER_VAR(&e->sun_amount);
if (ser->version >= VAddedPlatonic)
{ {
case BoxMedbay:
case BoxCockpit:
if (!ser->save_or_load_from_disk)
SER_MAYBE_RETURN(ser_entityid(ser, &e->player_who_is_inside_of_me));
break;
case BoxThruster:
SER_VAR(&e->thrust);
SER_VAR(&e->wanted_thrust);
break;
case BoxBattery:
SER_VAR(&e->energy_used);
break;
case BoxSolarPanel:
SER_VAR(&e->sun_amount);
break;
case BoxScanner:
SER_MAYBE_RETURN(ser_entityid(ser, &e->currently_scanning));
SER_VAR(&e->currently_scanning_progress);
SER_VAR(&e->blueprints_learned);
SER_VAR(&e->scanner_head_rotate); SER_VAR(&e->scanner_head_rotate);
SER_VAR(&e->platonic_nearest_direction); SER_VAR(&e->platonic_nearest_direction);
SER_VAR(&e->platonic_detection_strength); SER_VAR(&e->platonic_detection_strength);
} break;
default:
if (ser->version >= VRemovedInsideOfMe && ser->save_or_load_from_disk) break;
{
}
else
{
SER_MAYBE_RETURN(ser_entityid(ser, &e->player_who_is_inside_of_me));
} }
} }
@ -1094,13 +1068,7 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s)
SER_ASSERT(cur_next_entity <= ser->max_entity_index); SER_ASSERT(cur_next_entity <= ser->max_entity_index);
SER_VAR(&s->your_player); SER_VAR(&s->your_player);
if (ser->version >= VReallyRemovedTimeFromDiskSave && ser->save_or_load_from_disk) SER_VAR(&gs->time);
{
}
else
{
SER_VAR(&gs->time);
}
SER_MAYBE_RETURN(ser_V2(ser, &gs->goldpos)); SER_MAYBE_RETURN(ser_V2(ser, &gs->goldpos));
@ -1389,10 +1357,15 @@ bool client_to_server_deserialize(GameState *gs, struct ClientToServer *msg, uns
// has to be global var because can only get this information // has to be global var because can only get this information
static THREADLOCAL cpShape *closest_to_point_in_radius_result = NULL; static THREADLOCAL cpShape *closest_to_point_in_radius_result = NULL;
static THREADLOCAL float closest_to_point_in_radius_result_largest_dist = 0.0f; static THREADLOCAL float closest_to_point_in_radius_result_largest_dist = 0.0f;
static THREADLOCAL bool (*closest_to_point_in_radius_filter_func)(Entity *);
static void closest_point_callback_func(cpShape *shape, cpContactPointSet *points, void *data) static void closest_point_callback_func(cpShape *shape, cpContactPointSet *points, void *data)
{ {
assert(points->count == 1); assert(points->count == 1);
if (!cp_shape_entity(shape)->is_box) Entity *e = cp_shape_entity(shape);
if (!e->is_box)
return;
if (closest_to_point_in_radius_filter_func != NULL && !closest_to_point_in_radius_filter_func(e))
return; return;
float dist = V2length(cp_to_v2(cpvsub(points->points[0].pointA, points->points[0].pointB))); float dist = V2length(cp_to_v2(cpvsub(points->points[0].pointA, points->points[0].pointB)));
// float dist = -points->points[0].distance; // float dist = -points->points[0].distance;
@ -1403,11 +1376,13 @@ static void closest_point_callback_func(cpShape *shape, cpContactPointSet *point
} }
} }
Entity *closest_to_point_in_radius(GameState *gs, V2 point, float radius) // filter func null means everything is ok, if it's not null and returns false, that means
// exclude it from the selection. This returns the closest box entity!
Entity *closest_box_to_point_in_radius(struct GameState *gs, V2 point, float radius, bool (*filter_func)(Entity *))
{ {
closest_to_point_in_radius_result = NULL; closest_to_point_in_radius_result = NULL;
closest_to_point_in_radius_result_largest_dist = 0.0f; closest_to_point_in_radius_result_largest_dist = 0.0f;
closest_to_point_in_radius_filter_func = filter_func;
cpBody *tmpbody = cpBodyNew(0.0f, 0.0f); cpBody *tmpbody = cpBodyNew(0.0f, 0.0f);
cpShape *circle = cpCircleShapeNew(tmpbody, radius, v2_to_cp(point)); cpShape *circle = cpCircleShapeNew(tmpbody, radius, v2_to_cp(point));
cpSpaceShapeQuery(gs->space, circle, closest_point_callback_func, NULL); cpSpaceShapeQuery(gs->space, circle, closest_point_callback_func, NULL);
@ -1418,12 +1393,22 @@ Entity *closest_to_point_in_radius(GameState *gs, V2 point, float radius)
if (closest_to_point_in_radius_result != NULL) if (closest_to_point_in_radius_result != NULL)
{ {
// @Robust query here for only boxes that are part of ships, could get nasty... // @Robust query here for only boxes that are part of ships, could get nasty...
return cp_body_entity(cpShapeGetBody(closest_to_point_in_radius_result)); return cp_shape_entity(closest_to_point_in_radius_result);
} }
return NULL; return NULL;
} }
static THREADLOCAL BOX_UNLOCKS_TYPE scanner_has_learned = 0;
static bool scanner_filter(Entity *e)
{
if (!e->is_box)
return false;
if (learned_boxes_has_box(scanner_has_learned, e->box_type))
return false;
return true;
}
static float cur_explosion_damage = 0.0f; static float cur_explosion_damage = 0.0f;
static V2 explosion_origin = {0}; static V2 explosion_origin = {0};
static void explosion_callback_func(cpShape *shape, cpContactPointSet *points, void *data) static void explosion_callback_func(cpShape *shape, cpContactPointSet *points, void *data)
@ -1474,7 +1459,7 @@ uint64_t tick(GameState *gs)
Entity *grid_to_build_on(GameState *gs, V2 world_hand_pos) Entity *grid_to_build_on(GameState *gs, V2 world_hand_pos)
{ {
return closest_to_point_in_radius(gs, world_hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP); return box_grid(closest_box_to_point_in_radius(gs, world_hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP, NULL));
} }
V2 potentially_snap_hand_pos(GameState *gs, V2 world_hand_pos) V2 potentially_snap_hand_pos(GameState *gs, V2 world_hand_pos)
@ -1561,7 +1546,7 @@ EntityID create_initial_world(GameState *gs)
bool indestructible = false; bool indestructible = false;
Entity *grid = new_entity(gs); Entity *grid = new_entity(gs);
grid_create(gs, grid); grid_create(gs, grid);
entity_set_pos(grid, (V2){-16.0f, 0.0f}); entity_set_pos(grid, (V2){-5.0f, 0.0f});
entity_ensure_in_orbit(grid); entity_ensure_in_orbit(grid);
Entity *explosion_box = new_entity(gs); Entity *explosion_box = new_entity(gs);
box_create(gs, explosion_box, grid, (V2){0}); box_create(gs, explosion_box, grid, (V2){0});
@ -1796,7 +1781,7 @@ void process(GameState *gs, float dt)
grid_remove_box(gs, cur_grid, cur_box); grid_remove_box(gs, cur_grid, cur_box);
} }
} }
else else if(box_unlocked(player, player->input.build_type))
{ {
// creating a box // creating a box
p->damage += DAMAGE_TO_PLAYER_PER_BLOCK; p->damage += DAMAGE_TO_PLAYER_PER_BLOCK;
@ -1821,6 +1806,7 @@ void process(GameState *gs, float dt)
grid_correct_for_holes(gs, target_grid); // no holey ship for you! grid_correct_for_holes(gs, target_grid); // no holey ship for you!
new_box->box_type = player->input.build_type; new_box->box_type = player->input.build_type;
new_box->compass_rotation = player->input.build_rotation; new_box->compass_rotation = player->input.build_rotation;
if(new_box->box_type == BoxScanner) new_box->blueprints_learned = player->box_unlocks;
if (new_box->box_type == BoxBattery) if (new_box->box_type == BoxBattery)
new_box->energy_used = BATTERY_CAPACITY; new_box->energy_used = BATTERY_CAPACITY;
} }
@ -1954,7 +1940,7 @@ void process(GameState *gs, float dt)
} }
if (cur_box->box_type == BoxScanner) if (cur_box->box_type == BoxScanner)
{ {
// set the nearest platonic solid! // set the nearest platonic solid! only on server as only the server sees everything
if (gs->server_side_computing) if (gs->server_side_computing)
{ {
float energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, SCANNER_ENERGY_USE * dt); float energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, SCANNER_ENERGY_USE * dt);
@ -1993,6 +1979,30 @@ void process(GameState *gs, float dt)
} }
} }
} }
// unlock the nearest platonic solid!
scanner_has_learned = cur_box->blueprints_learned;
Entity *to_learn = closest_box_to_point_in_radius(gs, entity_pos(cur_box), SCANNER_RADIUS, scanner_filter);
if(to_learn != NULL)
assert(to_learn->is_box);
EntityID new_id = get_id(gs, to_learn);
if(!entityids_same(cur_box->currently_scanning, new_id))
{
cur_box->currently_scanning_progress = 0.0f;
cur_box->currently_scanning = new_id;
}
if(to_learn != NULL)
cur_box->currently_scanning_progress += dt*SCANNER_SCAN_RATE;
else
cur_box->currently_scanning_progress = 0.0f;
if(cur_box->currently_scanning_progress >= 1.0f)
{
cur_box->blueprints_learned |= box_unlock_number(to_learn->box_type);
}
cur_box->scanner_head_rotate_speed = lerp(cur_box->scanner_head_rotate_speed, cur_box->platonic_detection_strength > 0.0f ? 3.0f : 0.0f, dt * 3.0f); cur_box->scanner_head_rotate_speed = lerp(cur_box->scanner_head_rotate_speed, cur_box->platonic_detection_strength > 0.0f ? 3.0f : 0.0f, dt * 3.0f);
cur_box->scanner_head_rotate += cur_box->scanner_head_rotate_speed * dt; cur_box->scanner_head_rotate += cur_box->scanner_head_rotate_speed * dt;
cur_box->scanner_head_rotate = fmodf(cur_box->scanner_head_rotate, 2.0f * PI); cur_box->scanner_head_rotate = fmodf(cur_box->scanner_head_rotate, 2.0f * PI);

204
main.c

@ -7,6 +7,8 @@
#include <enet/enet.h> #include <enet/enet.h>
#include <process.h> // starting server thread #include <process.h> // starting server thread
#define TOOLBAR_SLOTS 9
#pragma warning(disable : 33010) // this warning is so broken, doesn't #pragma warning(disable : 33010) // this warning is so broken, doesn't
// understand assert() // understand assert()
#include "sokol_app.h" #include "sokol_app.h"
@ -106,9 +108,11 @@ static sg_image image_check;
static sg_image image_no; static sg_image image_no;
static sg_image image_solarpanel_charging; static sg_image image_solarpanel_charging;
static sg_image image_scanner_head; static sg_image image_scanner_head;
static sg_image image_itemswitch;
static int cur_editing_boxtype = -1; static enum BoxType toolbar[TOOLBAR_SLOTS] = {BoxInvalid};
static int cur_editing_rotation = 0; static int cur_toolbar_slot = 0;
static int cur_editing_rotation = Right;
// audio // audio
static bool muted = false; static bool muted = false;
@ -218,6 +222,13 @@ struct SquadMeta squad_meta(enum Squad squad)
return (struct SquadMeta){0}; return (struct SquadMeta){0};
} }
static enum BoxType currently_building()
{
assert(cur_toolbar_slot >= 0);
assert(cur_toolbar_slot < TOOLBAR_SLOTS);
return toolbar[cur_toolbar_slot];
}
struct BoxInfo boxinfo(enum BoxType type) struct BoxInfo boxinfo(enum BoxType type)
{ {
for (int i = 0; i < ARRLEN(boxes); i++) for (int i = 0; i < ARRLEN(boxes); i++)
@ -505,6 +516,7 @@ static void init(void)
image_no = load_image("loaded/no.png"); image_no = load_image("loaded/no.png");
image_solarpanel_charging = load_image("loaded/solarpanel_charging.png"); image_solarpanel_charging = load_image("loaded/solarpanel_charging.png");
image_scanner_head = load_image("loaded/scanner_head.png"); image_scanner_head = load_image("loaded/scanner_head.png");
image_itemswitch = load_image("loaded/itemswitch.png");
} }
// socket initialization // socket initialization
@ -613,12 +625,6 @@ bool can_build(int i)
return allow_building; return allow_building;
} }
void attempt_to_build(int i)
{
if (can_build(i))
cur_editing_boxtype = i;
}
static V2 screen_to_world(float width, float height, V2 screen) static V2 screen_to_world(float width, float height, V2 screen)
{ {
V2 world = screen; V2 world = screen;
@ -651,6 +657,105 @@ static void ui(bool draw, float dt, float width, float height)
if (draw) if (draw)
sgp_push_transform(); sgp_push_transform();
// draw pick new box type menu
static bool picking_new_boxtype = false;
static float pick_opacity = 0.0f;
{
AABB pick_modal = (AABB){
.x = width * 0.25f,
.y = height * 0.25f,
.width = width * 0.5f,
.height = height * 0.5f,
};
pick_opacity = lerp(pick_opacity, picking_new_boxtype ? 1.0f : 0.0f, dt * 7.0f);
if (picking_new_boxtype)
{
if (build_pressed)
{
if (has_point(pick_modal, mouse_pos))
{
}
else
{
build_pressed = false;
picking_new_boxtype = false;
}
}
}
static float item_scaling[ARRLEN(boxes)] = {1.0f};
{
float alpha = pick_opacity * 0.8f;
if (draw)
{
sgp_set_color(0.4f, 0.4f, 0.4f, alpha);
sgp_draw_filled_rect(pick_modal.x, pick_modal.y, pick_modal.width, pick_modal.height);
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f * pick_opacity);
}
int boxes_per_row = (int)floorf(pick_modal.width / 128.0f);
float cell_width = pick_modal.width / (float)boxes_per_row;
float cell_height = cell_width;
float padding = 0.2f * cell_width;
int cur_row = 0;
int cur_column = 0;
for (int i = 0; i < ARRLEN(boxes); i++)
{
if (cur_column >= boxes_per_row)
{
cur_column = 0;
cur_row++;
}
float item_width = cell_width - padding * 2.0f;
float item_height = cell_height - padding * 2.0f;
item_width *= item_scaling[i];
item_height *= item_scaling[i];
float cell_y = pick_modal.y + (float)cur_row * cell_height;
float cell_x = pick_modal.x + (float)cur_column * cell_width;
float item_x = cell_x + (cell_width - item_width) / 2.0f;
float item_y = cell_y + (cell_height - item_height) / 2.0f;
bool item_being_hovered = has_point((AABB){
.x = item_x,
.y = item_y,
.width = item_width,
.height = item_height,
},
mouse_pos);
item_scaling[i] = lerp(item_scaling[i], item_being_hovered ? 1.3f : 1.0f, dt*4.0f);
struct BoxInfo info = boxes[i];
if(item_being_hovered && build_pressed)
{
toolbar[cur_toolbar_slot] = info.type;
build_pressed = false;
}
if (draw)
{
if (can_build(info.type))
{
sgp_set_image(0, info.image);
}
else
{
sgp_set_image(0, image_mystery);
}
transform_scope
{
sgp_scale_at(1.0f, -1.0f, item_x + item_width / 2.0f, item_y + item_height / 2.0f);
pipeline_scope(goodpixel_pipeline)
sgp_draw_textured_rect(item_x, item_y, item_width, item_height);
sgp_reset_image(0);
}
}
cur_column++;
}
}
}
// draw squad invite // draw squad invite
static float invite_y = -200.0f; static float invite_y = -200.0f;
static enum Squad draw_as_squad = SquadNone; static enum Squad draw_as_squad = SquadNone;
@ -978,7 +1083,7 @@ static void ui(bool draw, float dt, float width, float height)
(float)sg_query_image_info(image_itemframe).width * 2.0f; (float)sg_query_image_info(image_itemframe).width * 2.0f;
float itemframe_height = float itemframe_height =
(float)sg_query_image_info(image_itemframe).height * 2.0f; (float)sg_query_image_info(image_itemframe).height * 2.0f;
float total_width = itemframe_width * (float)ARRLENF(boxes); float total_width = itemframe_width * (float)TOOLBAR_SLOTS;
float item_width = itemframe_width * 0.75f; float item_width = itemframe_width * 0.75f;
float item_height = itemframe_height * 0.75f; float item_height = itemframe_height * 0.75f;
float item_offset_x = (itemframe_width - item_width) / 2.0f; float item_offset_x = (itemframe_width - item_width) / 2.0f;
@ -986,8 +1091,9 @@ static void ui(bool draw, float dt, float width, float height)
float x = width / 2.0f - total_width / 2.0f; float x = width / 2.0f - total_width / 2.0f;
float y = height - itemframe_height * 1.5f; float y = height - itemframe_height * 1.5f;
for (int i = 0; i < ARRLEN(boxes); i++) for (int i = 0; i < TOOLBAR_SLOTS; i++)
{ {
// mouse over the item frame box
if (has_point( if (has_point(
(AABB){ (AABB){
.x = x, .x = x,
@ -999,14 +1105,38 @@ static void ui(bool draw, float dt, float width, float height)
build_pressed) build_pressed)
{ {
// "handle" mouse pressed // "handle" mouse pressed
attempt_to_build(i); cur_toolbar_slot = i;
build_pressed = false;
}
// mouse over the item switch button
bool switch_hovered = false;
if (has_point(
(AABB){
.x = x,
.y = y - 20.0f,
.width = itemframe_width,
.height = itemframe_height * 0.2f,
},
mouse_pos))
{
switch_hovered = true;
}
if (switch_hovered && build_pressed)
{
picking_new_boxtype = true;
build_pressed = false; build_pressed = false;
} }
if (draw) if (draw)
{ {
sgp_set_color(1.0f, 1.0f, 1.0f, cur_opacity); sgp_set_color(1.0f, 1.0f, 1.0f, cur_opacity);
if (cur_editing_boxtype == i)
bool is_current = cur_toolbar_slot == i;
static float switch_scaling = 1.0f;
switch_scaling = lerp(switch_scaling, switch_hovered ? 1.8f : 1.2f, dt * 3.0f);
if (is_current)
{ {
sgp_set_image(0, image_itemframe_selected); sgp_set_image(0, image_itemframe_selected);
} }
@ -1016,26 +1146,36 @@ static void ui(bool draw, float dt, float width, float height)
} }
pipeline_scope(goodpixel_pipeline) pipeline_scope(goodpixel_pipeline)
sgp_draw_textured_rect(x, y, itemframe_width, itemframe_height); sgp_draw_textured_rect(x, y, itemframe_width, itemframe_height);
struct BoxInfo info = boxinfo((enum BoxType)i); sgp_reset_image(0);
if (can_build(i))
{
sgp_set_image(0, info.image);
}
else
{
sgp_set_image(0, image_mystery);
}
transform_scope transform_scope
{ {
float item_x = x + item_offset_x; float item_x = x + item_offset_x;
float item_y = y + item_offset_y; float item_y = y + item_offset_y;
sgp_scale_at(1.0f, -1.0f, item_x + item_width / 2.0f, sgp_scale_at(1.0f, -1.0f, item_x + item_width / 2.0f,
item_y + item_height / 2.0f); item_y + item_height / 2.0f);
// sgp_scale(1.0f, -1.0f);
pipeline_scope(goodpixel_pipeline) pipeline_scope(goodpixel_pipeline)
{
if (toolbar[i] != BoxInvalid)
{
struct BoxInfo info = boxinfo(toolbar[i]);
sgp_set_image(0, info.image);
sgp_draw_textured_rect(item_x, item_y, item_width, item_height); sgp_draw_textured_rect(item_x, item_y, item_width, item_height);
sgp_reset_image(0);
}
if (is_current)
{
sgp_set_image(0, image_itemswitch);
float switch_item_width = item_width * switch_scaling;
float switch_item_height = item_height * switch_scaling;
item_x -= (switch_item_width - item_width) / 2.0f;
item_y -= (switch_item_height - item_height) / 2.0f;
sgp_draw_textured_rect(item_x, item_y + 20.0f, switch_item_width, switch_item_height);
sgp_reset_image(0);
}
}
} }
sgp_reset_image(0);
} }
x += itemframe_width; x += itemframe_width;
} }
@ -1324,10 +1464,10 @@ static void frame(void)
reject_invite = false; reject_invite = false;
} }
if (build_pressed && cur_editing_boxtype != -1) if (build_pressed && currently_building() != BoxInvalid)
{ {
cur_input_frame.dobuild = build_pressed; cur_input_frame.dobuild = build_pressed;
cur_input_frame.build_type = cur_editing_boxtype; cur_input_frame.build_type = currently_building();
cur_input_frame.build_rotation = cur_editing_rotation; cur_input_frame.build_rotation = cur_editing_rotation;
} }
@ -1405,8 +1545,8 @@ static void frame(void)
global_hand_pos = global_hand_pos =
get_global_hand_pos(world_mouse_pos, &hand_at_arms_length); get_global_hand_pos(world_mouse_pos, &hand_at_arms_length);
Entity *placing_grid = closest_to_point_in_radius( Entity *placing_grid = box_grid(closest_box_to_point_in_radius(
&gs, global_hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP); &gs, global_hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP, NULL));
if (placing_grid == NULL) if (placing_grid == NULL)
{ {
build_preview = (struct BuildPreviewInfo){ build_preview = (struct BuildPreviewInfo){
@ -1530,14 +1670,14 @@ static void frame(void)
} }
// building preview // building preview
if (cur_editing_boxtype != -1) if (currently_building() != BoxInvalid)
{ {
sgp_set_color(0.5f, 0.5f, 0.5f, sgp_set_color(0.5f, 0.5f, 0.5f,
(sinf((float)time * 9.0f) + 1.0f) / 3.0f + 0.2f); (sinf((float)time * 9.0f) + 1.0f) / 3.0f + 0.2f);
transform_scope transform_scope
{ {
sgp_set_image(0, boxinfo(cur_editing_boxtype).image); sgp_set_image(0, boxinfo(currently_building()).image);
sgp_rotate_at(build_preview.grid_rotation + sgp_rotate_at(build_preview.grid_rotation +
rotangle(cur_editing_rotation), rotangle(cur_editing_rotation),
global_hand_pos.x, global_hand_pos.y); global_hand_pos.x, global_hand_pos.y);
@ -1785,10 +1925,10 @@ void event(const sapp_event *e)
fullscreened = false; fullscreened = false;
} }
int key_num = e->key_code - SAPP_KEYCODE_0; int key_num = e->key_code - SAPP_KEYCODE_0;
int target_box = key_num - 1; int target_slot = key_num - 1;
if (target_box < BoxLast && target_box >= 0) if (target_slot <= TOOLBAR_SLOTS && target_slot >= 0)
{ {
attempt_to_build(target_box); cur_toolbar_slot = target_slot;
} }
if (!mouse_frozen) if (!mouse_frozen)

@ -13,6 +13,8 @@
#define PLAYER_JETPACK_SPICE_PER_SECOND 0.1f #define PLAYER_JETPACK_SPICE_PER_SECOND 0.1f
#define SCANNER_ENERGY_USE 0.05f #define SCANNER_ENERGY_USE 0.05f
#define MAX_HAND_REACH 1.0f #define MAX_HAND_REACH 1.0f
#define SCANNER_SCAN_RATE 1.0f
#define SCANNER_RADIUS 1.0f
#define GOLD_COLLECT_RADIUS 0.3f #define GOLD_COLLECT_RADIUS 0.3f
#define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2f #define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2f
#define BOX_MASS 1.0f #define BOX_MASS 1.0f
@ -60,6 +62,9 @@
#define LOCAL_INPUT_QUEUE_MAX 90 // please god let you not have more than 90 frames of game latency #define LOCAL_INPUT_QUEUE_MAX 90 // please god let you not have more than 90 frames of game latency
#define INPUT_QUEUE_MAX 15 #define INPUT_QUEUE_MAX 15
// fucks up serialization if you change this, fix it if you do that!
#define BOX_UNLOCKS_TYPE uint64_t
// cross platform threadlocal variables // cross platform threadlocal variables
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#define THREADLOCAL __declspec(thread) #define THREADLOCAL __declspec(thread)
@ -130,6 +135,7 @@ typedef sgp_point P2;
enum BoxType enum BoxType
{ {
BoxInvalid, // zero initialized box is invalid!
BoxHullpiece, BoxHullpiece,
BoxThruster, BoxThruster,
BoxBattery, BoxBattery,
@ -231,32 +237,42 @@ typedef struct Entity
// boxes // boxes
bool is_box; bool is_box;
bool is_platonic; // can't be destroyed, unaffected by physical forces
bool always_visible; // always serialized to the player
enum BoxType box_type; enum BoxType box_type;
EntityID next_box; bool is_platonic; // can't be destroyed, unaffected by physical forces
bool always_visible; // always serialized to the player. @Robust check if not used
EntityID next_box; // for the grid!
EntityID prev_box; // doubly linked so can remove in middle of chain EntityID prev_box; // doubly linked so can remove in middle of chain
enum CompassRotation compass_rotation; enum CompassRotation compass_rotation;
bool indestructible; bool indestructible;
// used by medbay and cockpit
EntityID player_who_is_inside_of_me;
// only serialized when box_type is thruster
float wanted_thrust; // the thrust command applied to the thruster float wanted_thrust; // the thrust command applied to the thruster
float thrust; // the actual thrust it can provide based on energy sources in the grid float thrust; // the actual thrust it can provide based on energy sources in the grid
// only serialized when box_type is battery
float energy_used; // battery, between 0 battery capacity. You have to look through code to figure out what that is! haha sucker! float energy_used; // battery, between 0 battery capacity. You have to look through code to figure out what that is! haha sucker!
// only serialized when box_type is solar panel
float sun_amount; // solar panel, between 0 and 1 float sun_amount; // solar panel, between 0 and 1
EntityID player_who_is_inside_of_me;
// only updated when it's a scanner // scanner only stuff!
EntityID currently_scanning;
float currently_scanning_progress; // when 1.0, scans it!
BOX_UNLOCKS_TYPE blueprints_learned; // @Robust make this same type as blueprints
float scanner_head_rotate_speed; // not serialized, cosmetic float scanner_head_rotate_speed; // not serialized, cosmetic
float scanner_head_rotate; float scanner_head_rotate;
V2 platonic_nearest_direction; // normalized V2 platonic_nearest_direction; // normalized
float platonic_detection_strength; // from zero to one float platonic_detection_strength; // from zero to one
} Entity; } Entity;
typedef struct Player typedef struct Player
{ {
bool connected; bool connected;
uint64_t box_unlocks; // each bit is that box's unlock BOX_UNLOCKS_TYPE box_unlocks; // each bit is that box's unlock
enum Squad squad; enum Squad squad;
EntityID entity; EntityID entity;
EntityID last_used_medbay; EntityID last_used_medbay;
@ -267,7 +283,7 @@ typedef struct GameState
{ {
cpSpace *space; cpSpace *space;
double time; // @Robust separate tick integer not prone to precision issues double time; // @Robust separate tick integer not prone to precision issues. Could be very large as is saved to disk!
V2 goldpos; V2 goldpos;
@ -351,7 +367,7 @@ void initialize(struct GameState *gs, void *entity_arena, size_t entity_arena_si
void destroy(struct GameState *gs); void destroy(struct GameState *gs);
void process_fixed_timestep(GameState *gs); void process_fixed_timestep(GameState *gs);
void process(struct GameState *gs, float dt); // does in place void process(struct GameState *gs, float dt); // does in place
Entity *closest_to_point_in_radius(struct GameState *gs, V2 point, float radius); Entity *closest_box_to_point_in_radius(struct GameState *gs, V2 point, float radius, bool(*filter_func)(Entity*));
uint64_t tick(struct GameState *gs); uint64_t tick(struct GameState *gs);
// all of these return if successful or not // all of these return if successful or not
@ -376,6 +392,7 @@ void entity_destroy(GameState *gs, Entity *e);
// grid // grid
void grid_create(struct GameState *gs, Entity *e); void grid_create(struct GameState *gs, Entity *e);
void box_create(struct GameState *gs, Entity *new_box, Entity *grid, V2 pos); void box_create(struct GameState *gs, Entity *new_box, Entity *grid, V2 pos);
Entity *box_grid(Entity *box);
V2 grid_com(Entity *grid); V2 grid_com(Entity *grid);
V2 grid_vel(Entity *grid); V2 grid_vel(Entity *grid);
V2 box_vel(Entity *box); V2 box_vel(Entity *box);

Loading…
Cancel
Save