diff --git a/flight.rdbg b/flight.rdbg index f805329..5da8056 100644 Binary files a/flight.rdbg and b/flight.rdbg differ diff --git a/gamestate.c b/gamestate.c index 7fe2ef8..1cca247 100644 --- a/gamestate.c +++ b/gamestate.c @@ -73,6 +73,28 @@ Entity *get_entity(GameState *gs, EntityID id) return to_return; } +bool cloaking_active(GameState *gs, Entity *e) +{ + // cloaking doesn't work for first 1/2 second of game because when initializing + // everything needs to be uncloaked + return gs->time >= 0.5 && (gs->time - e->time_was_last_cloaked) <= TIMESTEP * 2.0f; +} + +bool is_cloaked(GameState *gs, Entity *e, Entity *this_players_perspective) +{ + assert(this_players_perspective != NULL); + assert(this_players_perspective->is_player); + bool cloaked = cloaking_active(gs, e); + if (e->is_player) + { + return cloaked && e->presenting_squad != this_players_perspective->presenting_squad; + } + else + { + return cloaked && this_players_perspective->presenting_squad != e->last_cloaked_by_squad; + } +} + static BOX_UNLOCKS_TYPE box_unlock_number(enum BoxType box) { assert((BOX_UNLOCKS_TYPE)box < 64); @@ -129,6 +151,20 @@ static GameState *cp_space_gs(cpSpace *space) return (GameState *)cpSpaceGetUserData(space); } +static GameState *entitys_gamestate(Entity *e) +{ + assert(e->body != NULL || e->shape != NULL); + if (e->shape != NULL) + { + return cp_space_gs(cpShapeGetSpace(e->shape)); + } + if (e->body != NULL) + { + return cp_space_gs(cpBodyGetSpace(e->body)); + } + return NULL; +} + int grid_num_boxes(GameState *gs, Entity *e) { assert(e->is_grid); @@ -316,6 +352,10 @@ void create_rectangle_shape(GameState *gs, Entity *e, Entity *parent, V2 pos, V2 void create_player(Player *player) { // default box unlocks, required for survival and growth +#ifdef UNLOCK_ALL + for (enum BoxType t = BoxInvalid + 1; t < BoxLast; t++) + unlock_box(player, t); +#else unlock_box(player, BoxHullpiece); unlock_box(player, BoxThruster); unlock_box(player, BoxBattery); @@ -323,6 +363,7 @@ void create_player(Player *player) unlock_box(player, BoxMedbay); unlock_box(player, BoxSolarPanel); unlock_box(player, BoxScanner); +#endif } void create_player_entity(GameState *gs, Entity *e) @@ -609,10 +650,17 @@ V2 grid_snapped_box_pos(Entity *grid, V2 world) return cp_to_v2(cpBodyLocalToWorld(grid->body, v2_to_cp(local))); } -float entity_rotation(Entity *grid) + +// for boxes does not include box's compass rotation +float entity_rotation(Entity *e) { - return (float)cpBodyGetAngle(grid->body); + assert(e->body != NULL || e->shape != NULL); + if (e->body != NULL) + return (float)cpBodyGetAngle(e->body); + else + return (float)cpBodyGetAngle(cpShapeGetBody(e->shape)); } + float entity_angular_velocity(Entity *grid) { return (float)cpBodyGetAngularVelocity(grid->body); @@ -798,6 +846,7 @@ SerMaybeFailure ser_var(SerState *ser, char *var_pointer, size_t var_size, const enum GameVersion { VInitial, + VMoreBoxes, VMax, // this minus one will be the version used }; @@ -855,6 +904,14 @@ SerMaybeFailure ser_inputframe(SerState *ser, InputFrame *i) return ser_ok; } +SerMaybeFailure ser_no_player(SerState *ser) +{ + bool connected = false; + SER_VAR_NAME(&connected, "&p->connected"); + + return ser_ok; +} + SerMaybeFailure ser_player(SerState *ser, Player *p) { SER_VAR(&p->connected); @@ -929,6 +986,9 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) } } + if (ser->version >= VMoreBoxes && !ser->save_or_load_from_disk) + SER_VAR(&e->time_was_last_cloaked); + SER_VAR(&e->is_player); if (e->is_player) { @@ -960,6 +1020,10 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) { SER_VAR(&e->box_type); SER_VAR(&e->is_platonic); + + if (ser->version >= VMoreBoxes) + SER_VAR(&e->owning_squad); + SER_VAR(&e->always_visible); SER_MAYBE_RETURN(ser_entityid(ser, &e->next_box)); SER_MAYBE_RETURN(ser_entityid(ser, &e->prev_box)); @@ -991,6 +1055,9 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) SER_VAR(&e->platonic_nearest_direction); SER_VAR(&e->platonic_detection_strength); break; + case BoxCloaking: + SER_VAR(&e->cloaking_power); + break; default: break; } @@ -1076,12 +1143,19 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s) SER_MAYBE_RETURN(ser_V2(ser, &gs->goldpos)); - if (!ser->save_or_load_from_disk) + if (!ser->save_or_load_from_disk) // don't save player info to disk, this is filled on connection/disconnection { // @Robust save player data with their ID or something somehow. Like local backup of their account for (size_t i = 0; i < MAX_PLAYERS; i++) { - SER_MAYBE_RETURN(ser_player(ser, &gs->players[i])); + if (get_entity(gs, gs->players[i].entity) != NULL && is_cloaked(gs, get_entity(gs, gs->players[i].entity), ser->for_player)) + { + SER_MAYBE_RETURN(ser_no_player(ser)); + } + else + { + SER_MAYBE_RETURN(ser_player(ser, &gs->players[i])); + } } } if (ser->serializing) @@ -1090,11 +1164,12 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s) for (size_t i = 0; i < gs->cur_next_entity; i++) { Entity *e = &gs->entities[i]; +#define DONT_SEND_BECAUSE_CLOAKED(entity) (!ser->save_or_load_from_disk && ser->for_player != NULL && is_cloaked(gs, entity, ser->for_player)) #define SER_ENTITY() \ SER_VAR(&entities_done); \ SER_VAR(&i); \ SER_MAYBE_RETURN(ser_entity(ser, gs, e)) - if (e->exists && !(ser->save_or_load_from_disk && e->no_save_to_disk)) + if (e->exists && !(ser->save_or_load_from_disk && e->no_save_to_disk) && !DONT_SEND_BECAUSE_CLOAKED(e)) { if (!e->is_box && !e->is_grid) { @@ -1105,10 +1180,14 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s) bool serialized_grid_yet = false; // serialize boxes always after bodies, so that by the time the boxes // are loaded in the parent body is loaded in and can be referenced. - BOXES_ITER(gs, cur, e) + BOXES_ITER(gs, cur_box, e) { - 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) + bool this_box_in_range = ser->save_or_load_from_disk; + this_box_in_range |= ser->for_player == NULL; + this_box_in_range |= (ser->for_player != NULL && V2distsqr(entity_pos(ser->for_player), entity_pos(cur_box)) < VISION_RADIUS * VISION_RADIUS); // only in vision radius + if (DONT_SEND_BECAUSE_CLOAKED(cur_box)) + this_box_in_range = false; + if (cur_box->always_visible) this_box_in_range = true; if (this_box_in_range) { @@ -1119,12 +1198,12 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s) } // serialize this box - EntityID cur_id = get_id(gs, cur); + EntityID cur_id = get_id(gs, cur_box); SER_ASSERT(cur_id.index < gs->max_entities); SER_VAR(&entities_done); size_t the_index = (size_t)cur_id.index; // super critical. Type of &i is size_t. @Robust add debug info in serialization for what size the expected type is, maybe string nameof the type SER_VAR_NAME(&the_index, "&i"); - SER_MAYBE_RETURN(ser_entity(ser, gs, cur)); + SER_MAYBE_RETURN(ser_entity(ser, gs, cur_box)); } } } @@ -1358,6 +1437,16 @@ bool client_to_server_deserialize(GameState *gs, struct ClientToServer *msg, uns } } +static void cloaking_shield_callback_func(cpShape *shape, cpContactPointSet *points, void *data) +{ + Entity *from_cloaking_box = (Entity *)data; + GameState *gs = entitys_gamestate(from_cloaking_box); + Entity *to_cloak = cp_shape_entity(shape); + + to_cloak->time_was_last_cloaked = gs->time; + to_cloak->last_cloaked_by_squad = from_cloaking_box->owning_squad; +} + // 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; @@ -1545,7 +1634,7 @@ V2 box_vel(Entity *box) return cp_to_v2(cpBodyGetVelocityAtWorldPoint(grid->body, v2_to_cp(entity_pos(box)))); } -void create_station(GameState *gs, V2 pos, enum BoxType platonic_type) +void create_bomb_station(GameState *gs, V2 pos, enum BoxType platonic_type) { #define BOX_AT_TYPE(grid, pos, type) \ @@ -1562,10 +1651,10 @@ void create_station(GameState *gs, V2 pos, enum BoxType platonic_type) grid_create(gs, grid); entity_set_pos(grid, pos); entity_ensure_in_orbit(grid); - Entity *explosion_box = new_entity(gs); - box_create(gs, explosion_box, grid, (V2){0}); - explosion_box->box_type = platonic_type; - explosion_box->is_platonic = true; + Entity *platonic_box = new_entity(gs); + box_create(gs, platonic_box, grid, (V2){0}); + platonic_box->box_type = platonic_type; + platonic_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); @@ -1590,15 +1679,54 @@ void create_station(GameState *gs, V2 pos, enum BoxType platonic_type) BOX_AT_TYPE(grid, ((V2){-BOX_SIZE * 6.0, -BOX_SIZE * 5.0}), BoxExplosive); } +void create_hard_shell_station(GameState *gs, V2 pos, enum BoxType platonic_type) +{ + +#define BOX_AT_TYPE(grid, pos, type) \ + { \ + Entity *box = new_entity(gs); \ + box_create(gs, box, grid, pos); \ + box->box_type = type; \ + box->indestructible = indestructible; \ + } +#define BOX_AT(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece) + + bool indestructible = false; + Entity *grid = new_entity(gs); + grid_create(gs, grid); + entity_set_pos(grid, pos); + entity_ensure_in_orbit(grid); + Entity *platonic_box = new_entity(gs); + box_create(gs, platonic_box, grid, (V2){0}); + platonic_box->box_type = platonic_type; + platonic_box->is_platonic = true; + BOX_AT_TYPE(grid, ((V2){BOX_SIZE * 2, 0}), BoxHullpiece); + BOX_AT_TYPE(grid, ((V2){BOX_SIZE * 3, 0}), BoxHullpiece); + BOX_AT_TYPE(grid, ((V2){BOX_SIZE * 4, 0}), BoxHullpiece); + + indestructible = true; + for (float y = -BOX_SIZE * 5.0; y <= BOX_SIZE * 5.0; y += BOX_SIZE) + { + BOX_AT_TYPE(grid, ((V2){BOX_SIZE * 5.0, y}), BoxHullpiece); + } + for (float x = -BOX_SIZE * 5.0; x <= BOX_SIZE * 5.0; x += BOX_SIZE) + { + BOX_AT_TYPE(grid, ((V2){x, BOX_SIZE * 5.0}), BoxHullpiece); + BOX_AT_TYPE(grid, ((V2){x, -BOX_SIZE * 5.0}), BoxHullpiece); + } + indestructible = false; +} void create_initial_world(GameState *gs) { -#if 0 +#ifdef DEBUG_WORLD Log("Creating debug world\n"); - create_station(gs, (V2){-5.0f,0.0f}, BoxExplosive); - create_station(gs, (V2){0.0f, 5.0f}, BoxGyroscope); + create_bomb_station(gs, (V2){-5.0f, 0.0f}, BoxExplosive); + create_bomb_station(gs, (V2){0.0f, 5.0f}, BoxGyroscope); + create_hard_shell_station(gs, (V2){-5.0f, 5.0f}, BoxCloaking); #else - create_station(gs, (V2){-50.0f, 0.0f}, BoxExplosive); - create_station(gs, (V2){0.0f, 100.0f}, BoxGyroscope); + create_bomb_station(gs, (V2){-50.0f, 0.0f}, BoxExplosive); + create_hard_shell_station(gs, (V2){0.0f, 100.0f}, BoxGyroscope); + create_bomb_station(gs, (V2){0.0f, -100.0f}, BoxCloaking); #endif } @@ -1788,6 +1916,10 @@ void process(GameState *gs, float dt) cpBodySetPosition(p->body, v2_to_cp(entity_pos(seat_inside_of))); cpBodySetVelocity(p->body, v2_to_cp(box_vel(seat_inside_of))); + // share cloaking with box + p->time_was_last_cloaked = seat_inside_of->time_was_last_cloaked; + p->last_cloaked_by_squad = seat_inside_of->last_cloaked_by_squad; + // set thruster thrust from movement if (seat_inside_of->box_type == BoxCockpit) { @@ -1859,6 +1991,7 @@ void process(GameState *gs, float dt) } Entity *new_box = new_entity(gs); box_create(gs, new_box, target_grid, created_box_position); + new_box->owning_squad = player->squad; 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; @@ -1888,7 +2021,6 @@ void process(GameState *gs, float dt) // sun processing { - cpVect pos_rel_sun = v2_to_cp(V2sub(entity_pos(e), SUN_POS)); cpFloat sqdist = cpvlengthsq(pos_rel_sun); if (e->body != NULL && sqdist > (INSTANT_DEATH_DISTANCE_FROM_SUN * INSTANT_DEATH_DISTANCE_FROM_SUN)) @@ -2041,6 +2173,27 @@ void process(GameState *gs, float dt) } } } + if (cur_box->box_type == BoxCloaking) + { + float energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, CLOAKING_ENERGY_USE * dt); + if (energy_unconsumed >= CLOAKING_ENERGY_USE * dt) + { + cur_box->cloaking_power = lerp(cur_box->cloaking_power, 0.0, dt * 3.0f); + } + else + { + cur_box->cloaking_power = lerp(cur_box->cloaking_power, 1.0, dt * 3.0f); + cpBody *tmp = cpBodyNew(0.0, 0.0); + cpBodySetPosition(tmp, v2_to_cp(entity_pos(cur_box))); + cpBodySetAngle(tmp, entity_rotation(cur_box)); + // subtract a little from the panel size so that boxes just at the boundary of the panel + // aren't (sometimes cloaked)/(sometimes not) from floating point imprecision + cpShape *box_shape = cpBoxShapeNew(tmp, CLOAKING_PANEL_SIZE - 0.03f, CLOAKING_PANEL_SIZE - 0.03f, 0.0); + cpSpaceShapeQuery(gs->space, box_shape, cloaking_shield_callback_func, (void *)cur_box); + cpShapeFree(box_shape); + cpBodyFree(tmp); + } + } if (cur_box->box_type == BoxScanner) { // set the nearest platonic solid! only on server as only the server sees everything diff --git a/loaded/cloaking_device.png b/loaded/cloaking_device.png new file mode 100644 index 0000000..c966dca Binary files /dev/null and b/loaded/cloaking_device.png differ diff --git a/loaded/cloaking_panel.png b/loaded/cloaking_panel.png new file mode 100644 index 0000000..55313bd Binary files /dev/null and b/loaded/cloaking_panel.png differ diff --git a/main.c b/main.c index d322702..31bdc07 100644 --- a/main.c +++ b/main.c @@ -51,6 +51,7 @@ typedef struct KeyPressed static KeyPressed keypressed[MAX_KEYDOWN] = {0}; static V2 mouse_pos = {0}; static bool fullscreened = false; +static bool picking_new_boxtype = false; static bool build_pressed = false; static bool interact_pressed = false; @@ -109,6 +110,7 @@ static sg_image image_no; static sg_image image_solarpanel_charging; static sg_image image_scanner_head; static sg_image image_itemswitch; +static sg_image image_cloaking_panel; static enum BoxType toolbar[TOOLBAR_SLOTS] = { BoxHullpiece, @@ -185,6 +187,10 @@ static struct BoxInfo .image_path = "loaded/gyroscope.png", }, + { + .type = BoxCloaking, + .image_path = "loaded/cloaking_device.png", + }, }; #define ENTITIES_ITER(cur) \ for (Entity *cur = gs.entities; cur < gs.entities + gs.cur_next_entity; \ @@ -527,6 +533,7 @@ static void init(void) 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"); + image_cloaking_panel = load_image("loaded/cloaking_panel.png"); } // socket initialization @@ -634,6 +641,15 @@ bool can_build(int i) allow_building = box_unlocked(myplayer(), box_type); return allow_building; } +static void setup_hueshift(enum Squad squad) +{ + struct SquadMeta meta = squad_meta(squad); + hueshift_uniforms_t uniform = { + .is_colorless = meta.is_colorless, + .target_hue = meta.hue, + }; + sgp_set_uniform(&uniform, sizeof(hueshift_uniforms_t)); +} static V2 screen_to_world(float width, float height, V2 screen) { @@ -668,7 +684,6 @@ static void ui(bool draw, float dt, float width, float height) sgp_push_transform(); // draw pick new box type menu - static bool picking_new_boxtype = false; static float pick_opacity = 0.0f; { if (keypressed[SAPP_KEYCODE_ESCAPE].pressed) @@ -705,6 +720,7 @@ static void ui(bool draw, float dt, float width, float 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); + boxes_per_row = boxes_per_row < 4 ? 4 : boxes_per_row; float cell_width = pick_modal.width / (float)boxes_per_row; float cell_height = cell_width; float padding = 0.2f * cell_width; @@ -742,6 +758,7 @@ static void ui(bool draw, float dt, float width, float height) if (item_being_hovered && build_pressed && picking_new_boxtype) { toolbar[cur_toolbar_slot] = info.type; + picking_new_boxtype = false; build_pressed = false; } if (draw) @@ -816,12 +833,7 @@ static void ui(bool draw, float dt, float width, float height) { pipeline_scope(hueshift_pipeline) { - struct SquadMeta meta = squad_meta(draw_as_squad); - hueshift_uniforms_t uniform = { - .is_colorless = meta.is_colorless, - .target_hue = meta.hue, - }; - sgp_set_uniform(&uniform, sizeof(hueshift_uniforms_t)); + setup_hueshift(draw_as_squad); sgp_scale_at(1.0f, -1.0f, x, invite_y); // images upside down by default :( sgp_set_image(0, image_squad_invite); @@ -892,11 +904,7 @@ static void ui(bool draw, float dt, float width, float height) } pipeline_scope(hueshift_pipeline) { - struct SquadMeta meta = squad_meta(myplayer()->squad); - hueshift_uniforms_t uniform = {0}; - uniform.is_colorless = meta.is_colorless; - uniform.target_hue = meta.hue; - sgp_set_uniform(&uniform, sizeof(hueshift_uniforms_t)); + setup_hueshift(myplayer()->squad); sgp_scale_at(1.0f, -1.0f, pos.x, pos.y); // images upside down by default :( @@ -1020,11 +1028,7 @@ static void ui(bool draw, float dt, float width, float height) pipeline_scope(hueshift_pipeline) { - struct SquadMeta meta = squad_meta(this_squad); - hueshift_uniforms_t uniform = {0}; - uniform.is_colorless = meta.is_colorless; - uniform.target_hue = meta.hue; - sgp_set_uniform(&uniform, sizeof(hueshift_uniforms_t)); + setup_hueshift(this_squad); sgp_rotate_at(flag_rot[i], flag_pos[i].x, flag_pos[i].y); sgp_scale_at(1.0f, -1.0f, flag_pos[i].x, @@ -1786,8 +1790,22 @@ static void frame(void) { set_color(GOLD); } - pipeline_scope(goodpixel_pipeline) + + // all of these box types show team colors so are drawn with the hue shifting shader + // used with the player + if (b->box_type == BoxCloaking) + { + pipeline_scope(hueshift_pipeline) + { + setup_hueshift(b->owning_squad); draw_texture_centered(entity_pos(b), BOX_SIZE); + } + } + else + { + pipeline_scope(goodpixel_pipeline) + draw_texture_centered(entity_pos(b), BOX_SIZE); + } sgp_reset_image(0); if (b->box_type == BoxScanner) @@ -1795,9 +1813,11 @@ static void frame(void) sgp_set_image(0, image_scanner_head); transform_scope { - 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); + { + sgp_rotate_at(b->scanner_head_rotate, entity_pos(b).x, entity_pos(b).y); + draw_texture_centered(entity_pos(b), BOX_SIZE); + } } sgp_reset_image(0); set_color(WHITE); @@ -1811,12 +1831,21 @@ static void frame(void) } sgp_set_color(0.5f, 0.1f, 0.1f, b->damage); draw_color_rect_centered(entity_pos(b), BOX_SIZE); + + if (b->box_type == BoxCloaking) + { + sgp_set_color(1.0f, 1.0f, 1.0f, b->cloaking_power); + sgp_set_image(0, image_cloaking_panel); + pipeline_scope(goodpixel_pipeline) + draw_texture_centered(entity_pos(b), CLOAKING_PANEL_SIZE); + sgp_reset_image(0); + } } // outside of the transform scope if (b->box_type == BoxScanner) { - if (b->platonic_detection_strength > 0.0f) + if (b->platonic_detection_strength > 0.0) { set_color(colhexcode(0xf2d75c)); V2 to = V2add(entity_pos(b), V2scale(b->platonic_nearest_direction, b->platonic_detection_strength)); @@ -1839,11 +1868,7 @@ static void frame(void) pipeline_scope(hueshift_pipeline) { - struct SquadMeta meta = squad_meta(e->presenting_squad); - hueshift_uniforms_t uniform = {0}; - uniform.is_colorless = meta.is_colorless; - uniform.target_hue = meta.hue; - sgp_set_uniform(&uniform, sizeof(hueshift_uniforms_t)); + setup_hueshift(e->presenting_squad); sgp_set_image(0, image_player); draw_texture_rectangle_centered( entity_pos(e), V2scale(PLAYER_SIZE, player_scaling)); @@ -1967,6 +1992,14 @@ void event(const sapp_event *e) int target_slot = key_num - 1; if (target_slot <= TOOLBAR_SLOTS && target_slot >= 0) { + if (target_slot == cur_toolbar_slot) + { + picking_new_boxtype = !picking_new_boxtype; + } + else + { + picking_new_boxtype = false; + } cur_toolbar_slot = target_slot; } diff --git a/server.c b/server.c index f0d77eb..86d763e 100644 --- a/server.c +++ b/server.c @@ -95,15 +95,9 @@ void server(void *info_raw) OpusEncoder *player_encoders[MAX_PLAYERS] = {0}; OpusDecoder *player_decoders[MAX_PLAYERS] = {0}; - // for (int i = 0; i < MAX_PLAYERS; i++) - //{ - // int error = 0; - // player_encoders[i] = opus_encoder_create(VOIP_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &error); - // if (error != OPUS_OK) Log("Failed to create encoder\n"); - // player_decoders[i] = opus_decoder_create(VOIP_SAMPLE_RATE, 1, &error); - // if (error != OPUS_OK) Log("Failed to create decoder\n"); - // } - + #ifdef DEBUG_WORLD + world_save_name = NULL; + #endif if (world_save_name != NULL) { size_t read_game_data_buffer_size = entities_size; @@ -263,11 +257,6 @@ void server(void *info_raw) player_decoders[player_slot] = opus_decoder_create(VOIP_SAMPLE_RATE, 1, &error); if (error != OPUS_OK) Log("Failed to create decoder: %d\n", error); - -#ifdef UNLOCK_ALL - gs.players[player_slot].unlocked_bombs = true; -#endif - gs.players[player_slot].squad = SquadPurple; } } break; diff --git a/tooling.ahk b/tooling.ahk index 7925ae5..f5d8c5a 100644 --- a/tooling.ahk +++ b/tooling.ahk @@ -9,6 +9,7 @@ SetWorkingDir, %A_ScriptDir% ^b:: WinKill, Flight Hosting +WinKill, Flight Not Hosting WinActivate, flightbuild If WinActive("flightbuild") { @@ -18,14 +19,12 @@ If WinActive("flightbuild") return ^+b:: -WinKill, Flight -Sleep, 20 -WinKill, Flight -Sleep, 20 -WinKill, Flight +WinKill, Flight Hosting +WinKill, Flight Not Hosting WinActivate, flightbuild If WinActive("flightbuild") { - Send, cd C:\Users\Cameron\Documents\flight{Enter} build_debug.bat && START /B flight_debug.exe && flight_debug.exe --host{Enter} + Send, {Enter} + Send, remedybg continue-execution && sleep 0.1 && remedybg.exe stop-debugging && msbuild && remedybg.exe start-debugging && sleep 0.2 && x64\Debug\Flight.exe {Enter} } return \ No newline at end of file diff --git a/types.h b/types.h index c849d1a..67f2473 100644 --- a/types.h +++ b/types.h @@ -26,6 +26,8 @@ #define THRUSTER_ENERGY_USED_PER_SECOND 0.005f #define GYROSCOPE_ENERGY_USED_PER_SECOND 0.005f #define GYROSCOPE_TORQUE 0.5f +#define CLOAKING_ENERGY_USE 0.1f +#define CLOAKING_PANEL_SIZE BOX_SIZE*3.0f #define VISION_RADIUS 12.0f #define MAX_SERVER_TO_CLIENT 1024 * 512 // maximum size of serialized gamestate buffer #define MAX_CLIENT_TO_SERVER 1024 * 10 // maximum size of serialized inputs and mic data @@ -151,6 +153,7 @@ enum BoxType BoxExplosive, BoxScanner, BoxGyroscope, + BoxCloaking, BoxLast, }; @@ -218,7 +221,13 @@ typedef struct Entity float damage; // used by box and player cpBody *body; // used by grid, player, and box cpShape *shape; // must be a box so shape_size can be set appropriately, and serialized - + + + // players and boxes can be cloaked + // If this is within 2 timesteps of the current game time, the entity is invisible. + double time_was_last_cloaked; + enum Squad last_cloaked_by_squad; + // for serializing the shape // @Robust remove shape_parent_entity from this struct, use the shape's body to figure out // what the shape's parent entity is @@ -227,7 +236,7 @@ typedef struct Entity // player bool is_player; - enum Squad presenting_squad; + enum Squad presenting_squad; // also controls what the player can see, because of cloaking! EntityID currently_inside_of_box; enum Squad squad_invited_to; // if squad none, then no squad invite float goldness; // how much the player is a winner @@ -245,6 +254,7 @@ typedef struct Entity // boxes bool is_box; + enum Squad owning_squad; // which squad owns this box enum BoxType box_type; bool is_platonic; // can't be destroyed, unaffected by physical forces bool always_visible; // always serialized to the player. @Robust check if not used @@ -267,6 +277,9 @@ typedef struct Entity // only serialized when box_type is solar panel float sun_amount; // solar panel, between 0 and 1 + // cloaking only + float cloaking_power; // 0.0 if unable to be used because no power, 1.0 if fully cloaking! + // scanner only stuff! EntityID currently_scanning; float currently_scanning_progress; // when 1.0, scans it! @@ -292,6 +305,8 @@ typedef struct GameState { cpSpace *space; + // @Robust for the integer tick, also store a float for how much time has been processed. + // Like a whole timestep then a float for subtimestep double time; // @Robust separate tick integer not prone to precision issues. Could be very large as is saved to disk! V2 goldpos;