diff --git a/.gitignore b/.gitignore index cc57c07..e6a0f40 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +compile_commands.json +.cache/ enc_temp_folder/ *.spall # profiling *.bin # world files diff --git a/flight.rdbg b/flight.rdbg index cb9853c..b863b00 100644 Binary files a/flight.rdbg and b/flight.rdbg differ diff --git a/gamestate.c b/gamestate.c index 61ad1ce..64f0888 100644 --- a/gamestate.c +++ b/gamestate.c @@ -81,11 +81,13 @@ static uint64_t box_unlock_number(enum BoxType box) void unlock_box(Player *player, enum BoxType box) { + assert(box < MAX_BOX_TYPES); 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; } @@ -533,8 +535,11 @@ static void on_damage(cpArbiter *arb, cpSpace *space, cpDataPointer userData) // cpSpaceAddPostStepCallback(space, (cpPostStepFunc)postStepRemove, b, NULL); // cpSpaceAddPostStepCallback(space, (cpPostStepFunc)postStepRemove, a, NULL); } + +// must be called with zero initialized game state, because copies the server side computing! void initialize(GameState *gs, void *entity_arena, size_t entity_arena_size) { + bool is_server_side = gs->server_side_computing; *gs = (GameState){0}; memset(entity_arena, 0, entity_arena_size); // SUPER critical. Random vals in the entity data causes big problem gs->entities = (Entity *)entity_arena; @@ -543,6 +548,7 @@ void initialize(GameState *gs, void *entity_arena, size_t entity_arena_size) cpSpaceSetUserData(gs->space, (cpDataPointer)gs); // needed in the handler cpCollisionHandler *handler = cpSpaceAddCollisionHandler(gs->space, 0, 0); // @Robust limit collision type to just blocks that can be damaged handler->postSolveFunc = on_damage; + gs->server_side_computing = is_server_side; } void destroy(GameState *gs) { @@ -757,7 +763,7 @@ SerMaybeFailure ser_data(SerState *ser, char *data, size_t data_len, const char ser->cursor += 1; SER_ASSERT(ser->cursor <= ser->max_size); } - + // now compare! SER_ASSERT(strcmp(read_name, name) == 0); } @@ -792,6 +798,7 @@ enum GameVersion VReallyRemovedTimeFromDiskSave, // apparently last one didn't work VRemovedInsideOfMe, VSwitchedToUnlocks, + VAddedPlatonic, VMax, // this minus one will be the version used }; @@ -979,6 +986,9 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) SER_VAR(&e->box_type); SER_VAR(&e->always_visible); + if (ser->version >= VAddedPlatonic) + SER_VAR(&e->is_platonic); + if (ser->version <= VSwitchedToUnlocks) { bool throwaway; @@ -992,6 +1002,12 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) SER_VAR(&e->wanted_thrust); SER_VAR(&e->energy_used); SER_VAR(&e->sun_amount); + if (ser->version >= VAddedPlatonic) + { + 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) { @@ -1077,9 +1093,6 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s) SER_VAR(&cur_next_entity); SER_ASSERT(cur_next_entity <= ser->max_entity_index); - if (!ser->save_or_load_from_disk) - SER_MAYBE_RETURN(ser_entityid(ser, &gs->cur_spacestation)); - SER_VAR(&s->your_player); if (ser->version >= VReallyRemovedTimeFromDiskSave && ser->save_or_load_from_disk) { @@ -1122,7 +1135,7 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s) // are loaded in the parent body is loaded in and can be referenced. BOXES_ITER(gs, cur, e) { - bool this_box_in_range = (ser->for_player == NULL || (ser->for_player != NULL && V2distsqr(entity_pos(ser->for_player), entity_pos(cur)) < VISION_RADIUS * VISION_RADIUS)); + bool this_box_in_range = (ser->save_or_load_from_disk || ser->for_player == NULL || (ser->for_player != NULL && V2distsqr(entity_pos(ser->for_player), entity_pos(cur)) < VISION_RADIUS * VISION_RADIUS)); if (cur->always_visible) this_box_in_range = true; if (this_box_in_range) @@ -1534,7 +1547,7 @@ V2 box_vel(Entity *box) return cp_to_v2(cpBodyGetVelocityAtWorldPoint(grid->body, v2_to_cp(entity_pos(box)))); } -EntityID create_spacestation(GameState *gs) +EntityID create_initial_world(GameState *gs) { #define BOX_AT_TYPE(grid, pos, type) \ { \ @@ -1542,21 +1555,18 @@ EntityID create_spacestation(GameState *gs) box_create(gs, box, grid, pos); \ box->box_type = type; \ box->indestructible = indestructible; \ - box->always_visible = true; \ - box->no_save_to_disk = true; \ } #define BOX_AT(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece) bool indestructible = false; Entity *grid = new_entity(gs); grid_create(gs, grid); - grid->no_save_to_disk = true; - entity_set_pos(grid, (V2){-80.0f, 0.0f}); + entity_set_pos(grid, (V2){-16.0f, 0.0f}); entity_ensure_in_orbit(grid); Entity *explosion_box = new_entity(gs); box_create(gs, explosion_box, grid, (V2){0}); - explosion_box->no_save_to_disk = true; - explosion_box->always_visible = true; + explosion_box->box_type = BoxExplosive; + explosion_box->is_platonic = true; BOX_AT_TYPE(grid, ((V2){BOX_SIZE, 0}), BoxExplosive); BOX_AT_TYPE(grid, ((V2){BOX_SIZE * 2, 0}), BoxHullpiece); BOX_AT_TYPE(grid, ((V2){BOX_SIZE * 3, 0}), BoxHullpiece); @@ -1826,11 +1836,6 @@ void process(GameState *gs, float dt) p->damage = clamp01(p->damage); } - if (get_entity(gs, gs->cur_spacestation) == NULL) - { - gs->cur_spacestation = create_spacestation(gs); - } - // process entities for (size_t i = 0; i < gs->cur_next_entity; i++) { @@ -1869,13 +1874,19 @@ void process(GameState *gs, float dt) if (e->is_box) { + if (e->is_platonic) + { + e->damage = 0.0f; + gs->platonic_positions[(int)e->box_type] = entity_pos(e); + } if (e->box_type == BoxExplosive && e->damage >= EXPLOSION_DAMAGE_THRESHOLD) { Entity *explosion = new_entity(gs); explosion->is_explosion = true; explosion->explosion_pos = entity_pos(e); explosion->explosion_vel = grid_vel(box_grid(e)); - grid_remove_box(gs, get_entity(gs, e->shape_parent_entity), e); + if (!e->is_platonic) + grid_remove_box(gs, get_entity(gs, e->shape_parent_entity), e); } if (e->damage >= 1.0f) { @@ -1884,9 +1895,10 @@ void process(GameState *gs, float dt) } if (e->is_grid) { + Entity *grid = e; // calculate how much energy solar panels provide float energy_to_add = 0.0f; - BOXES_ITER(gs, cur, e) + BOXES_ITER(gs, cur, grid) { if (cur->box_type == BoxSolarPanel) { @@ -1896,7 +1908,7 @@ void process(GameState *gs, float dt) } // apply all of the energy to all connected batteries - BOXES_ITER(gs, cur, e) + BOXES_ITER(gs, cur, grid) { if (energy_to_add <= 0.0f) break; @@ -1913,33 +1925,78 @@ void process(GameState *gs, float dt) float non_battery_energy_left_over = energy_to_add; // use the energy, stored in the batteries, in various boxes - BOXES_ITER(gs, cur, e) + BOXES_ITER(gs, cur_box, grid) { - if (cur->box_type == BoxThruster) + if (cur_box->box_type == BoxThruster) { - float energy_to_consume = cur->wanted_thrust * THRUSTER_ENERGY_USED_PER_SECOND * dt; + float energy_to_consume = cur_box->wanted_thrust * THRUSTER_ENERGY_USED_PER_SECOND * dt; if (energy_to_consume > 0.0f) { - cur->thrust = 0.0f; - float energy_unconsumed = batteries_use_energy(gs, e, &non_battery_energy_left_over, energy_to_consume); - cur->thrust = (1.0f - energy_unconsumed / energy_to_consume) * cur->wanted_thrust; - if (cur->thrust >= 0.0f) - cpBodyApplyForceAtWorldPoint(e->body, v2_to_cp(thruster_force(cur)), v2_to_cp(entity_pos(cur))); + cur_box->thrust = 0.0f; + float energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, energy_to_consume); + cur_box->thrust = (1.0f - energy_unconsumed / energy_to_consume) * cur_box->wanted_thrust; + if (cur_box->thrust >= 0.0f) + cpBodyApplyForceAtWorldPoint(grid->body, v2_to_cp(thruster_force(cur_box)), v2_to_cp(entity_pos(cur_box))); } } - if (cur->box_type == BoxMedbay) + if (cur_box->box_type == BoxMedbay) { - Entity *potential_meatbag_to_heal = get_entity(gs, cur->player_who_is_inside_of_me); + Entity *potential_meatbag_to_heal = get_entity(gs, cur_box->player_who_is_inside_of_me); if (potential_meatbag_to_heal != NULL) { float wanted_energy_use = fminf(potential_meatbag_to_heal->damage, PLAYER_ENERGY_RECHARGE_PER_SECOND * dt); if (wanted_energy_use > 0.0f) { - float energy_unconsumed = batteries_use_energy(gs, e, &non_battery_energy_left_over, wanted_energy_use); + float energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, wanted_energy_use); potential_meatbag_to_heal->damage -= (1.0f - energy_unconsumed / wanted_energy_use) * wanted_energy_use; } } } + if (cur_box->box_type == BoxScanner) + { + // set the nearest platonic solid! + if (gs->server_side_computing) + { + float energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, SCANNER_ENERGY_USE * dt); + if (energy_unconsumed >= SCANNER_ENERGY_USE * dt) + { + cur_box->platonic_detection_strength = 0.0f; + cur_box->platonic_nearest_direction = (V2){0}; + } + else + { + V2 from_pos = entity_pos(cur_box); + V2 nearest = {0}; + float nearest_dist = INFINITY; + for (int i = 0; i < MAX_BOX_TYPES; i++) + { + V2 cur_pos = gs->platonic_positions[i]; + if (V2length(cur_pos) > 0.0f) // zero is uninitialized, the platonic solid doesn't exist (probably) @Robust do better + { + float length_to_cur = V2dist(from_pos, cur_pos); + if (length_to_cur < nearest_dist) + { + nearest_dist = length_to_cur; + nearest = cur_pos; + } + } + } + if (nearest_dist < INFINITY) + { + cur_box->platonic_nearest_direction = V2normalize(V2sub(nearest, from_pos)); + cur_box->platonic_detection_strength = fmaxf(0.1f, 1.0f - fminf(1.0f, nearest_dist / 100.0f)); + } + else + { + cur_box->platonic_nearest_direction = (V2){0}; + cur_box->platonic_detection_strength = 0.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 = fmodf(cur_box->scanner_head_rotate, 2.0f * PI); + } } } } diff --git a/loaded/itemswitch.png b/loaded/itemswitch.png new file mode 100644 index 0000000..546cd6f Binary files /dev/null and b/loaded/itemswitch.png differ diff --git a/main.c b/main.c index 620605a..c7171a8 100644 --- a/main.c +++ b/main.c @@ -978,7 +978,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)ARRLENF(boxes); float item_width = itemframe_width * 0.75f; float item_height = itemframe_height * 0.75f; float item_offset_x = (itemframe_width - item_width) / 2.0f; @@ -1629,14 +1629,28 @@ static void frame(void) if (b->box_type == BoxScanner) { sgp_set_image(0, image_scanner_head); - sgp_rotate_at((float)gs.time * 3.0f, entity_pos(b).x, entity_pos(b).y); + sgp_rotate_at(b->scanner_head_rotate, entity_pos(b).x, entity_pos(b).y); pipeline_scope(goodpixel_pipeline) draw_texture_centered(entity_pos(b), BOX_SIZE); + set_color(WHITE); } sgp_set_color(0.5f, 0.1f, 0.1f, b->damage); draw_color_rect_centered(entity_pos(b), BOX_SIZE); } + + // outside of the transform scope + if (b->box_type == BoxScanner) + { + if (b->platonic_detection_strength > 0.0f) + { + set_color(colhexcode(0xf2d75c)); + V2 to = V2add(entity_pos(b), V2scale(b->platonic_nearest_direction, b->platonic_detection_strength)); + dbg_rect(to); + dbg_rect(entity_pos(b)); + sgp_draw_line(entity_pos(b).x, entity_pos(b).y, to.x, to.y); + } + } } } diff --git a/compile_flags.txt b/old_compile_flags.txt similarity index 76% rename from compile_flags.txt rename to old_compile_flags.txt index a7162b7..232d75a 100644 --- a/compile_flags.txt +++ b/old_compile_flags.txt @@ -1,3 +1,6 @@ +main.c +gamestate.c +server.c -Ithirdparty -Ithirdparty/minilzo -Ithirdparty/enet/include diff --git a/server.c b/server.c index 221223a..f0d77eb 100644 --- a/server.c +++ b/server.c @@ -76,8 +76,11 @@ void server(void *info_raw) size_t entities_size = (sizeof(Entity) * MAX_ENTITIES); Entity *entity_data = malloc(entities_size); initialize(&gs, entity_data, entities_size); + gs.server_side_computing = true; Log("Allocated %zu bytes for entities\n", entities_size); + create_initial_world(&gs); + // inputs Queue player_input_queues[MAX_PLAYERS] = {0}; size_t input_queue_data_size = QUEUE_SIZE_FOR_ELEMENTS(sizeof(InputFrame), INPUT_QUEUE_MAX); diff --git a/tooling.ahk b/tooling.ahk index 0332727..617678c 100644 --- a/tooling.ahk +++ b/tooling.ahk @@ -8,12 +8,17 @@ SetWorkingDir, %A_ScriptDir% ^Esc::return ^b:: -WinKill, "Flight Not Hosting" +WinKill, Flight Hosting Sleep, 20 +WinActivate flight.rdbg +Sleep 20 +Send, {Shift down}{F5}{Shift up} +Send, {F5} WinActivate, flightbuild If WinActive("flightbuild") { - Send, cd C:\Users\Cameron\Documents\flight{Enter} build_debug.bat && flight_debug.exe{Enter} + Send, {Enter} + Send, msbuild{Enter} } return diff --git a/types.h b/types.h index 86b5491..254026a 100644 --- a/types.h +++ b/types.h @@ -2,6 +2,7 @@ #include "ipsettings.h" +#define MAX_BOX_TYPES 64 #define MAX_PLAYERS 16 #define MAX_ENTITIES 1024 * 25 #define BOX_SIZE 0.25f @@ -10,6 +11,7 @@ #define PLAYER_JETPACK_FORCE 1.5f // #define PLAYER_JETPACK_FORCE 20.0f #define PLAYER_JETPACK_SPICE_PER_SECOND 0.1f +#define SCANNER_ENERGY_USE 0.05f #define MAX_HAND_REACH 1.0f #define GOLD_COLLECT_RADIUS 0.3f #define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2f @@ -229,7 +231,10 @@ 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; EntityID prev_box; // doubly linked so can remove in middle of chain @@ -240,6 +245,12 @@ typedef struct Entity float energy_used; // battery, between 0 battery capacity. You have to look through code to figure out what that is! haha sucker! float sun_amount; // solar panel, between 0 and 1 EntityID player_who_is_inside_of_me; + + // only updated when it's a scanner + 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 @@ -261,8 +272,10 @@ typedef struct GameState V2 goldpos; Player players[MAX_PLAYERS]; - - EntityID cur_spacestation; + + V2 platonic_positions[MAX_BOX_TYPES]; // don't want to search over every entity to get the nearest platonic box! + + bool server_side_computing; // some things only the server should know and calculate, like platonic locations // Entity arena // ent:ity pointers can't move around because of how the physics engine handles user data. @@ -333,7 +346,7 @@ void create_player(Player *player); bool box_unlocked(Player *player, enum BoxType box); // gamestate -EntityID create_spacestation(GameState *gs); +EntityID create_initial_world(GameState *gs); void initialize(struct GameState *gs, void *entity_arena, size_t entity_arena_size); void destroy(struct GameState *gs); void process_fixed_timestep(GameState *gs); diff --git a/world.bin b/world.bin deleted file mode 100644 index 5e8cfaa..0000000 Binary files a/world.bin and /dev/null differ