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;
}
static uint64_t box_unlock_number(enum BoxType box)
static BOX_UNLOCKS_TYPE box_unlock_number(enum BoxType box)
{
assert((uint64_t)box < 64);
return (uint64_t)((uint64_t)1 << ((uint64_t)box));
assert((BOX_UNLOCKS_TYPE)box < 64);
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)
{
assert(box < MAX_BOX_TYPES);
assert(box != BoxInvalid);
player->box_unlocks |= box_unlock_number(box);
}
bool box_unlocked(Player *player, enum BoxType box)
{
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)
@ -611,6 +618,8 @@ float entity_angular_velocity(Entity *grid)
}
Entity *box_grid(Entity *box)
{
if(box == NULL) return NULL;
assert(box->is_box);
return (Entity *)cpBodyGetUserData(cpShapeGetBody(box->shape));
}
// in local space
@ -787,18 +796,6 @@ SerMaybeFailure ser_var(SerState *ser, char *var_pointer, size_t var_size, const
enum GameVersion
{
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
};
@ -839,12 +836,9 @@ SerMaybeFailure ser_inputframe(SerState *ser, InputFrame *i)
SER_VAR(&i->take_over_squad);
SER_ASSERT(i->take_over_squad >= 0 || i->take_over_squad == -1);
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_MAYBE_RETURN(ser_entityid(ser, &i->invite_this_player));
}
SER_VAR(&i->accept_cur_squad_invite);
SER_VAR(&i->reject_cur_squad_invite);
SER_MAYBE_RETURN(ser_entityid(ser, &i->invite_this_player));
SER_VAR(&i->seat_action);
SER_MAYBE_RETURN(ser_V2(ser, &i->hand_pos));
@ -863,20 +857,11 @@ SerMaybeFailure ser_player(SerState *ser, Player *p)
SER_VAR(&p->connected);
if (p->connected)
{
if (ser->version >= VSwitchedToUnlocks)
{
SER_VAR(&p->box_unlocks);
}
else
{
bool throwaway;
SER_VAR_NAME(&throwaway, "&p->unlocked_bombs");
}
if (ser->version >= VAddedSquads)
SER_VAR(&p->squad);
SER_VAR(&p->box_unlocks);
SER_VAR(&p->squad);
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));
}
@ -889,10 +874,6 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
SER_VAR(&e->generation);
SER_VAR(&e->damage);
int test;
if (ser->version < VRemovedTest && ser->version >= VAddedTest)
SER_VAR(&test);
bool has_body = ser->serializing && e->body != NULL;
SER_VAR(&has_body);
@ -922,14 +903,7 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
V2 shape_pos;
if (ser->serializing)
shape_pos = entity_shape_pos(e);
if (ser->version < VChangedVectorSerializing)
{
SER_VAR(&shape_pos);
}
else
{
SER_MAYBE_RETURN(ser_V2(ser, &shape_pos));
}
SER_MAYBE_RETURN(ser_V2(ser, &shape_pos));
float shape_mass;
if (ser->serializing)
@ -958,10 +932,8 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
SER_ASSERT(e->no_save_to_disk);
SER_MAYBE_RETURN(ser_entityid(ser, &e->currently_inside_of_box));
if (ser->version >= VAddedSquads)
SER_VAR(&e->presenting_squad);
if (ser->version >= VAddedSquadInvites)
SER_VAR(&e->squad_invited_to);
SER_VAR(&e->presenting_squad);
SER_VAR(&e->squad_invited_to);
SER_VAR(&e->goldness);
}
@ -984,37 +956,39 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
if (e->is_box)
{
SER_VAR(&e->box_type);
SER_VAR(&e->is_platonic);
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->prev_box));
SER_VAR(&e->compass_rotation);
SER_VAR(&e->indestructible);
SER_VAR(&e->thrust);
SER_VAR(&e->wanted_thrust);
SER_VAR(&e->energy_used);
SER_VAR(&e->sun_amount);
if (ser->version >= VAddedPlatonic)
switch (e->box_type)
{
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->platonic_nearest_direction);
SER_VAR(&e->platonic_detection_strength);
}
if (ser->version >= VRemovedInsideOfMe && ser->save_or_load_from_disk)
{
}
else
{
SER_MAYBE_RETURN(ser_entityid(ser, &e->player_who_is_inside_of_me));
break;
default:
break;
}
}
@ -1094,13 +1068,7 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s)
SER_ASSERT(cur_next_entity <= ser->max_entity_index);
SER_VAR(&s->your_player);
if (ser->version >= VReallyRemovedTimeFromDiskSave && ser->save_or_load_from_disk)
{
}
else
{
SER_VAR(&gs->time);
}
SER_VAR(&gs->time);
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
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 bool (*closest_to_point_in_radius_filter_func)(Entity *);
static void closest_point_callback_func(cpShape *shape, cpContactPointSet *points, void *data)
{
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;
float dist = V2length(cp_to_v2(cpvsub(points->points[0].pointA, points->points[0].pointB)));
// 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_largest_dist = 0.0f;
closest_to_point_in_radius_filter_func = filter_func;
cpBody *tmpbody = cpBodyNew(0.0f, 0.0f);
cpShape *circle = cpCircleShapeNew(tmpbody, radius, v2_to_cp(point));
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)
{
// @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;
}
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 V2 explosion_origin = {0};
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)
{
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)
@ -1561,7 +1546,7 @@ EntityID create_initial_world(GameState *gs)
bool indestructible = false;
Entity *grid = new_entity(gs);
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 *explosion_box = new_entity(gs);
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);
}
}
else
else if(box_unlocked(player, player->input.build_type))
{
// creating a box
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!
new_box->box_type = player->input.build_type;
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)
new_box->energy_used = BATTERY_CAPACITY;
}
@ -1954,7 +1940,7 @@ void process(GameState *gs, float dt)
}
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)
{
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 += cur_box->scanner_head_rotate_speed * dt;
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 <process.h> // starting server thread
#define TOOLBAR_SLOTS 9
#pragma warning(disable : 33010) // this warning is so broken, doesn't
// understand assert()
#include "sokol_app.h"
@ -106,9 +108,11 @@ static sg_image image_check;
static sg_image image_no;
static sg_image image_solarpanel_charging;
static sg_image image_scanner_head;
static sg_image image_itemswitch;
static int cur_editing_boxtype = -1;
static int cur_editing_rotation = 0;
static enum BoxType toolbar[TOOLBAR_SLOTS] = {BoxInvalid};
static int cur_toolbar_slot = 0;
static int cur_editing_rotation = Right;
// audio
static bool muted = false;
@ -218,6 +222,13 @@ struct SquadMeta squad_meta(enum Squad squad)
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)
{
for (int i = 0; i < ARRLEN(boxes); i++)
@ -505,6 +516,7 @@ static void init(void)
image_no = load_image("loaded/no.png");
image_solarpanel_charging = load_image("loaded/solarpanel_charging.png");
image_scanner_head = load_image("loaded/scanner_head.png");
image_itemswitch = load_image("loaded/itemswitch.png");
}
// socket initialization
@ -613,12 +625,6 @@ bool can_build(int i)
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)
{
V2 world = screen;
@ -651,6 +657,105 @@ static void ui(bool draw, float dt, float width, float height)
if (draw)
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
static float invite_y = -200.0f;
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 itemframe_height =
(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_height = itemframe_height * 0.75f;
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 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(
(AABB){
.x = x,
@ -999,14 +1105,38 @@ static void ui(bool draw, float dt, float width, float height)
build_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;
}
if (draw)
{
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);
}
@ -1016,26 +1146,36 @@ static void ui(bool draw, float dt, float width, float height)
}
pipeline_scope(goodpixel_pipeline)
sgp_draw_textured_rect(x, y, itemframe_width, itemframe_height);
struct BoxInfo info = boxinfo((enum BoxType)i);
if (can_build(i))
{
sgp_set_image(0, info.image);
}
else
{
sgp_set_image(0, image_mystery);
}
sgp_reset_image(0);
transform_scope
{
float item_x = x + item_offset_x;
float item_y = y + item_offset_y;
sgp_scale_at(1.0f, -1.0f, item_x + item_width / 2.0f,
item_y + item_height / 2.0f);
// sgp_scale(1.0f, -1.0f);
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_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;
}
@ -1324,10 +1464,10 @@ static void frame(void)
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.build_type = cur_editing_boxtype;
cur_input_frame.build_type = currently_building();
cur_input_frame.build_rotation = cur_editing_rotation;
}
@ -1405,8 +1545,8 @@ static void frame(void)
global_hand_pos =
get_global_hand_pos(world_mouse_pos, &hand_at_arms_length);
Entity *placing_grid = closest_to_point_in_radius(
&gs, global_hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP);
Entity *placing_grid = box_grid(closest_box_to_point_in_radius(
&gs, global_hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP, NULL));
if (placing_grid == NULL)
{
build_preview = (struct BuildPreviewInfo){
@ -1530,14 +1670,14 @@ static void frame(void)
}
// building preview
if (cur_editing_boxtype != -1)
if (currently_building() != BoxInvalid)
{
sgp_set_color(0.5f, 0.5f, 0.5f,
(sinf((float)time * 9.0f) + 1.0f) / 3.0f + 0.2f);
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 +
rotangle(cur_editing_rotation),
global_hand_pos.x, global_hand_pos.y);
@ -1785,10 +1925,10 @@ void event(const sapp_event *e)
fullscreened = false;
}
int key_num = e->key_code - SAPP_KEYCODE_0;
int target_box = key_num - 1;
if (target_box < BoxLast && target_box >= 0)
int target_slot = key_num - 1;
if (target_slot <= TOOLBAR_SLOTS && target_slot >= 0)
{
attempt_to_build(target_box);
cur_toolbar_slot = target_slot;
}
if (!mouse_frozen)

@ -13,6 +13,8 @@
#define PLAYER_JETPACK_SPICE_PER_SECOND 0.1f
#define SCANNER_ENERGY_USE 0.05f
#define MAX_HAND_REACH 1.0f
#define SCANNER_SCAN_RATE 1.0f
#define SCANNER_RADIUS 1.0f
#define GOLD_COLLECT_RADIUS 0.3f
#define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2f
#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 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
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#define THREADLOCAL __declspec(thread)
@ -130,6 +135,7 @@ typedef sgp_point P2;
enum BoxType
{
BoxInvalid, // zero initialized box is invalid!
BoxHullpiece,
BoxThruster,
BoxBattery,
@ -231,32 +237,42 @@ typedef struct Entity
// boxes
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;
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
enum CompassRotation compass_rotation;
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 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!
// only serialized when box_type is solar panel
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;
V2 platonic_nearest_direction; // normalized
float platonic_detection_strength; // from zero to one
} Entity;
typedef struct Player
{
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;
EntityID entity;
EntityID last_used_medbay;
@ -267,7 +283,7 @@ typedef struct GameState
{
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;
@ -351,7 +367,7 @@ void initialize(struct GameState *gs, void *entity_arena, size_t entity_arena_si
void destroy(struct GameState *gs);
void process_fixed_timestep(GameState *gs);
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);
// all of these return if successful or not
@ -376,6 +392,7 @@ void entity_destroy(GameState *gs, Entity *e);
// grid
void grid_create(struct GameState *gs, Entity *e);
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_vel(Entity *grid);
V2 box_vel(Entity *box);

Loading…
Cancel
Save