diff --git a/buildsettings.h b/buildsettings.h index 3d3b2a2..feb161f 100644 --- a/buildsettings.h +++ b/buildsettings.h @@ -19,10 +19,10 @@ // #define DEBUG_WORLD // #define UNLOCK_ALL #define TIME_BETWEEN_WORLD_SAVE 1.0f -#define INFINITE_RESOURCES -// #define DEBUG_TOOLS +// #define INFINITE_RESOURCES +#define DEBUG_TOOLS // #define FAT_THRUSTERS -#define NO_GRAVITY +// #define NO_GRAVITY // #define NO_SUNS #else diff --git a/flight.rdbg b/flight.rdbg index e51b7d1..1ed3c22 100644 Binary files a/flight.rdbg and b/flight.rdbg differ diff --git a/gamestate.c b/gamestate.c index 2591908..50ea811 100644 --- a/gamestate.c +++ b/gamestate.c @@ -644,12 +644,8 @@ static void grid_correct_for_holes(GameState *gs, struct Entity *grid) #define MAX_SEPARATE_GRIDS 8 EntityID separate_grids[MAX_SEPARATE_GRIDS] = {0}; int cur_separate_grid_index = 0; - int cur_separate_grid_size = 0; int processed_boxes = 0; - int biggest_separate_grid_index = 0; - uint32_t biggest_separate_grid_length = 0; - // process all boxes into separate, but correctly connected, grids while (processed_boxes < num_boxes) { @@ -679,7 +675,6 @@ static void grid_correct_for_holes(GameState *gs, struct Entity *grid) { N->next_box = separate_grids[cur_separate_grid_index]; separate_grids[cur_separate_grid_index] = get_id(gs, N); - cur_separate_grid_size++; processed_boxes++; if (get_id(gs, N).index > biggest_box_index) @@ -741,14 +736,8 @@ static void grid_correct_for_holes(GameState *gs, struct Entity *grid) } } - if (biggest_box_index > biggest_separate_grid_length) - { - biggest_separate_grid_length = biggest_box_index; - biggest_separate_grid_index = cur_separate_grid_index; - } cur_separate_grid_index++; flight_assert(cur_separate_grid_index < MAX_SEPARATE_GRIDS); - cur_separate_grid_size = 0; } // create new grids for all lists of boxes except for the biggest one. @@ -1106,14 +1095,13 @@ SerMaybeFailure ser_data(SerState *ser, char *data, size_t data_len, const char // now compare! SER_ASSERT(strcmp(read_name, name) == 0); - // deserialize and check the size too! SER_ASSERT(data_len < 65535); // uh oh stinky! uint16_t expected_size = (uint16_t)data_len; uint16_t got_size = 0; for (int b = 0; b < sizeof(got_size); b++) { - ((char*)&got_size)[b] = ser->bytes[ser->cursor]; + ((char *)&got_size)[b] = ser->bytes[ser->cursor]; ser->cursor += 1; SER_ASSERT(ser->cursor <= ser->max_size); } @@ -1382,6 +1370,8 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) case BoxGyroscope: SER_MAYBE_RETURN(ser_f(ser, &e->thrust)); SER_MAYBE_RETURN(ser_f(ser, &e->wanted_thrust)); + SER_MAYBE_RETURN(ser_f(ser, &e->gyrospin_angle)); + SER_MAYBE_RETURN(ser_f(ser, &e->gyrospin_velocity)); break; case BoxBattery: SER_MAYBE_RETURN(ser_f(ser, &e->energy_used)); @@ -2408,10 +2398,12 @@ void process(struct GameState *gs, double dt) if (potential_seat->box_type == BoxScanner) // learn everything from the scanner { + flight_assert(box_interactible(potential_seat->box_type)); player->box_unlocks |= potential_seat->blueprints_learned; } if (potential_seat->box_type == BoxMerge) // disconnect! { + flight_assert(box_interactible(potential_seat->box_type)); potential_seat->wants_disconnect = true; grid_correct_for_holes(gs, box_grid(potential_seat)); flight_assert(potential_seat->exists); @@ -2420,6 +2412,7 @@ void process(struct GameState *gs, double dt) } if (potential_seat->box_type == BoxCockpit || potential_seat->box_type == BoxMedbay) { + flight_assert(box_interactible(potential_seat->box_type)); // don't let players get inside of cockpits that somebody else is already inside of if (get_entity(gs, potential_seat->player_who_is_inside_of_me) == NULL) { @@ -2833,6 +2826,10 @@ void process(struct GameState *gs, double dt) { double energy_to_consume = cur_box->wanted_thrust * THRUSTER_ENERGY_USED_PER_SECOND * dt; + if (cur_box->wanted_thrust == 0.0) + { + cur_box->thrust = 0.0; + } if (energy_to_consume > 0.0) { cur_box->thrust = 0.0; @@ -2844,6 +2841,12 @@ void process(struct GameState *gs, double dt) } if (cur_box->box_type == BoxGyroscope) { + cur_box->gyrospin_velocity = lerp(cur_box->gyrospin_velocity, cur_box->thrust * 20.0, dt * 5.0); + cur_box->gyrospin_angle += cur_box->gyrospin_velocity * dt; + if (cur_box->wanted_thrust == 0.0) + { + cur_box->thrust = 0.0; + } double energy_to_consume = fabs(cur_box->wanted_thrust * GYROSCOPE_ENERGY_USED_PER_SECOND * dt); if (energy_to_consume > 0.0) { diff --git a/loaded/cockpit.png b/loaded/cockpit.png index c1e2549..c464a85 100644 Binary files a/loaded/cockpit.png and b/loaded/cockpit.png differ diff --git a/loaded/cockpit_used.png b/loaded/cockpit_used.png index 026726e..c12b268 100644 Binary files a/loaded/cockpit_used.png and b/loaded/cockpit_used.png differ diff --git a/loaded/gyroscope.png b/loaded/gyroscope.png index 46fd47b..b97b039 100644 Binary files a/loaded/gyroscope.png and b/loaded/gyroscope.png differ diff --git a/loaded/gyroscope_spinner.png b/loaded/gyroscope_spinner.png new file mode 100644 index 0000000..4e09e8e Binary files /dev/null and b/loaded/gyroscope_spinner.png differ diff --git a/loaded/right_click.png b/loaded/right_click.png new file mode 100644 index 0000000..4e0784f Binary files /dev/null and b/loaded/right_click.png differ diff --git a/loaded/rothelp.png b/loaded/rothelp.png new file mode 100644 index 0000000..f0d1658 Binary files /dev/null and b/loaded/rothelp.png differ diff --git a/main.c b/main.c index 8ef7dbb..543df65 100644 --- a/main.c +++ b/main.c @@ -46,6 +46,9 @@ static int my_player_index = -1; static bool right_mouse_down = false; #define MAX_KEYDOWN SAPP_KEYCODE_MENU static bool keydown[MAX_KEYDOWN] = {0}; +static bool piloting_rotation_capable_ship = false; +static double rotation_learned = 0.0; +static double rotation_in_cockpit_learned = 0.0; typedef struct KeyPressed { bool pressed; @@ -55,6 +58,10 @@ static KeyPressed keypressed[MAX_KEYDOWN] = {0}; static cpVect mouse_pos = {0}; static bool fullscreened = false; static bool picking_new_boxtype = false; +static double exec_time = 0.0; // cosmetic bouncing, network stats +// for network statistics, printed to logs with F3 +static uint64_t total_bytes_sent = 0; +static uint64_t total_bytes_received = 0; static bool build_pressed = false; static double dilating_time_factor = 1.0; @@ -112,10 +119,14 @@ static sg_image image_itemswitch; static sg_image image_cloaking_panel; static sg_image image_missile; static sg_image image_missile_burning; +static sg_image image_rightclick; +static sg_image image_rothelp; +static sg_image image_gyrospin; static enum BoxType toolbar[TOOLBAR_SLOTS] = { BoxHullpiece, BoxThruster, + BoxGyroscope, BoxBattery, BoxCockpit, BoxMedbay, @@ -634,6 +645,9 @@ static void init(void) image_cloaking_panel = load_image("loaded/cloaking_panel.png"); image_missile_burning = load_image("loaded/missile_burning.png"); image_missile = load_image("loaded/missile.png"); + image_rightclick = load_image("loaded/right_click.png"); + image_rothelp = load_image("loaded/rothelp.png"); + image_gyrospin = load_image("loaded/gyroscope_spinner.png"); } // socket initialization @@ -791,6 +805,25 @@ static void ui(bool draw, double dt, double width, double height) if (draw) sgp_push_transform(); + // rotation helper + if (draw) + { + double alpha = 1.0 - clamp01(rotation_learned); + if (piloting_rotation_capable_ship) + alpha = 1.0 - clamp01(rotation_in_cockpit_learned); + set_color_values(1.0, 1.0, 1.0, alpha); + + sgp_set_image(0, image_rothelp); + cpVect draw_at = cpv(width / 2.0, height * 0.25); + transform_scope + { + scale_at(1.0, -1.0, draw_at.x, draw_at.y); + pipeline_scope(goodpixel_pipeline) + draw_texture_centered(draw_at, 200.0); + sgp_reset_image(0); + } + } + // draw pick new box type menu static double pick_opacity = 0.0; { @@ -1412,8 +1445,8 @@ static void frame(void) PROFILE_SCOPE("frame") { double width = (float)sapp_width(), height = (float)sapp_height(); - double exec_time = sapp_frame_count() * sapp_frame_duration(); double dt = sapp_frame_duration(); + exec_time += dt; // pressed input management { @@ -1463,6 +1496,7 @@ static void frame(void) case ENET_EVENT_TYPE_RECEIVE: { + total_bytes_received += event.packet->dataLength; unsigned char *decompressed = malloc( sizeof *decompressed * MAX_SERVER_TO_CLIENT); // @Robust no malloc size_t decompressed_max_len = MAX_SERVER_TO_CLIENT; @@ -1603,6 +1637,26 @@ static void frame(void) local_hand_pos = cpvsub(global_hand_pos, entity_pos(myentity())); } + // for tutorial text + piloting_rotation_capable_ship = false; + + if (myentity() != NULL) + { + Entity *inside_of = get_entity(&gs, myentity()->currently_inside_of_box); + if (inside_of != NULL && inside_of->box_type == BoxCockpit) + { + BOXES_ITER(&gs, cur, box_grid(inside_of)) + { + flight_assert(cur->is_box); + if (cur->box_type == BoxGyroscope) + { + piloting_rotation_capable_ship = true; + break; + } + } + } + } + // process player interaction (squad invites) if (interact_pressed && myplayer() != NULL && myplayer()->squad != SquadNone) ENTITIES_ITER(cur) @@ -1632,7 +1686,15 @@ static void frame(void) if (cpvlength(input) > 0.0) input = cpvnormalize(input); cur_input_frame.movement = input; - cur_input_frame.rotation = (float)keydown[SAPP_KEYCODE_E] - (float)keydown[SAPP_KEYCODE_Q]; + cur_input_frame.rotation = -((float)keydown[SAPP_KEYCODE_E] - (float)keydown[SAPP_KEYCODE_Q]); + + if (fabs(cur_input_frame.rotation) > 0.01f) + { + if (piloting_rotation_capable_ship) + rotation_in_cockpit_learned += dt * 0.35; + else + rotation_learned += dt * 0.35; + } if (interact_pressed) cur_input_frame.seat_action = interact_pressed; @@ -1745,6 +1807,10 @@ static void frame(void) Log("Failed to send packet error %d\n", err); enet_packet_destroy(packet); } + else + { + total_bytes_sent += packet->dataLength; + } last_sent_input_time = stm_now(); } else @@ -1761,8 +1827,9 @@ static void frame(void) global_hand_pos = get_global_hand_pos(world_mouse_pos, &hand_at_arms_length); - Entity *placing_grid = box_grid(closest_box_to_point_in_radius( - &gs, global_hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP, NULL)); + Entity *nearest_box = closest_box_to_point_in_radius(&gs, global_hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP, NULL); + Entity *placing_grid = box_grid(nearest_box); + if (placing_grid == NULL) { build_preview = (struct BuildPreviewInfo){ @@ -1887,15 +1954,14 @@ static void frame(void) // building preview if (currently_building() != BoxInvalid && can_build(currently_building())) { - set_color_values(0.5, 0.5, 0.5, - (sin((float)exec_time * 9.0) + 1.0) / 3.0 + 0.2); transform_scope { - sgp_set_image(0, boxinfo(currently_building()).image); rotate_at(build_preview.grid_rotation + rotangle(cur_editing_rotation), global_hand_pos.x, global_hand_pos.y); + sgp_set_image(0, boxinfo(currently_building()).image); + set_color_values(0.5, 0.5, 0.5, (sin((float)exec_time * 9.0) + 1.0) / 3.0 + 0.2); pipeline_scope(goodpixel_pipeline) draw_texture_centered(global_hand_pos, BOX_SIZE); // drawbox(hand_pos, build_preview.grid_rotation, 0.0, @@ -1972,6 +2038,33 @@ static void frame(void) */ } + if (box_interactible(b->box_type)) + { + if (box_has_point((BoxCentered){ + .pos = entity_pos(b), + .rotation = entity_rotation(b), + .size = (cpVect){BOX_SIZE, BOX_SIZE}, + }, + world_mouse_pos)) + { + // set_color_values(1.0, 1.0, 1.0, 0.2); + // draw_color_rect_centered(entity_pos(b), BOX_SIZE); + set_color(WHITE); + draw_circle(entity_pos(b), BOX_SIZE / 1.75 + sin(exec_time * 5.0) * BOX_SIZE * 0.1); + transform_scope + { + pipeline_scope(goodpixel_pipeline) + { + sgp_set_image(0, image_rightclick); + cpVect draw_at = cpvadd(entity_pos(b), cpv(BOX_SIZE, 0)); + rotate_at(-entity_rotation(b) - rotangle(b->compass_rotation), draw_at.x, draw_at.y); + draw_texture_centered(draw_at, BOX_SIZE + sin(exec_time * 5.0) * BOX_SIZE * 0.1); + sgp_reset_image(0); + } + } + } + } + sgp_set_image(0, img); if (b->indestructible) { @@ -2007,11 +2100,27 @@ static void frame(void) pipeline_scope(goodpixel_pipeline) { rotate_at(b->scanner_head_rotate, entity_pos(b).x, entity_pos(b).y); + set_color(WHITE); + draw_texture_centered(entity_pos(b), BOX_SIZE); + } + } + sgp_reset_image(0); + } + + if (b->box_type == BoxGyroscope) + { + sgp_set_image(0, image_gyrospin); + + transform_scope + { + pipeline_scope(goodpixel_pipeline) + { + set_color(WHITE); + rotate_at(b->gyrospin_angle, entity_pos(b).x, entity_pos(b).y); draw_texture_centered(entity_pos(b), BOX_SIZE); } } sgp_reset_image(0); - set_color(WHITE); } // scanner range, visualizes what scanner can scan @@ -2221,6 +2330,13 @@ void event(const sapp_event *e) mouse_frozen = !mouse_frozen; } #endif + if (e->key_code == SAPP_KEYCODE_F3) + { + // print statistics + double received_per_sec = (double)total_bytes_received / exec_time; + double sent_per_sec = (double)total_bytes_sent / exec_time; + Log("Byte/s received %d byte/s sent %d\n", (int)received_per_sec, (int)sent_per_sec); + } if (e->key_code == SAPP_KEYCODE_TAB) { if (zoom_target < DEFAULT_ZOOM) diff --git a/tooling.ahk b/tooling.ahk index 8763350..5daa540 100644 --- a/tooling.ahk +++ b/tooling.ahk @@ -14,7 +14,7 @@ WinActivate, flightbuild If WinActive("flightbuild") { Send, {Enter} - Send, remedybg continue-execution && timeout 1 && remedybg.exe stop-debugging && msbuild && remedybg.exe start-debugging {Enter} + Send, remedybg continue-execution && timeout 1 && remedybg.exe stop-debugging && shadergen.bat && msbuild && remedybg.exe start-debugging {Enter} } Send, {Blind} ; So it doesn't hold down ctrl after running! WTF return diff --git a/types.h b/types.h index 9f19f1c..f0f950b 100644 --- a/types.h +++ b/types.h @@ -3,7 +3,7 @@ #include "buildsettings.h" #define MAX_BOX_TYPES 64 -#define ZOOM_MIN 0.10 // smaller means you can zoom out more +#define ZOOM_MIN 0.10 // smaller means you can zoom out more #define ZOOM_MAX 1500.0 // bigger means you can zoom in more #define MAX_PLAYERS 16 #define MAX_SUNS 8 @@ -29,7 +29,6 @@ // centered on the sprite #define MISSILE_SPRITE_SIZE ((cpVect){.x = BOX_SIZE, .y = BOX_SIZE}) #define MISSILE_COLLIDER_SIZE ((cpVect){.x = BOX_SIZE * 0.5f, .y = BOX_SIZE * 0.5f}) -// distance at which things become geostationary and no more solar power! #define PLAYER_JETPACK_ROTATION_ENERGY_PER_SECOND 0.2f #define PLAYER_JETPACK_SPICE_PER_SECOND 0.08f #define SCANNER_ENERGY_USE 0.05f @@ -60,7 +59,7 @@ #define EXPLOSION_DAMAGE_PER_SEC 10.0f #define EXPLOSION_DAMAGE_THRESHOLD 0.2f // how much damage until it explodes #define GOLD_UNLOCK_RADIUS 1.0f -#ifndef TIME_BETWEEN_WORLD_SAVE +#ifndef TIME_BETWEEN_WORLD_SAVE #define TIME_BETWEEN_WORLD_SAVE 30.0f #endif @@ -104,7 +103,7 @@ #include "cpVect.h" // offers vector functions and types for the structs #include "miniaudio.h" // @Robust BAD. using miniaudio mutex construct for server thread synchronization. AWFUL! -#include // sqrt and cos vector functions +#include // sqrt and cos vector functions #include // tick is unsigned integer #include // logging on errors for functions @@ -170,6 +169,20 @@ enum BoxType BoxLast, }; +static inline bool box_interactible(enum BoxType type) +{ + enum BoxType types[] = { + BoxCockpit, + BoxMedbay, + BoxMerge, + BoxScanner, + }; + for (int i = 0; i < ARRLEN(types); i++) + if (types[i] == type) + return true; + return false; +} + enum CompassRotation { Right, @@ -198,7 +211,7 @@ typedef struct EntityID static inline bool entityids_same(EntityID a, EntityID b) { - return (a.generation == b.generation) && (a.index == b.index); + return (a.generation == b.generation) && (a.index == b.index); } // when updated, must update serialization, comparison in main.c, and the server @@ -299,6 +312,10 @@ typedef struct Entity // can mean rotation thrust! double wanted_thrust; // the thrust command applied to the thruster double thrust; // the actual thrust it can provide based on energy sources in the grid + + // only gyroscope, velocity not serialized. Cosmetic + double gyrospin_angle; + double gyrospin_velocity; // only serialized when box_type is battery double energy_used; // battery, between 0 battery capacity. You have to look through code to figure out what that is! haha sucker! @@ -311,9 +328,9 @@ typedef struct Entity // scanner only stuff! EntityID currently_scanning; - double currently_scanning_progress; // when 1.0, scans it! + double currently_scanning_progress; // when 1.0, scans it! BOX_UNLOCKS_TYPE blueprints_learned; - double scanner_head_rotate_speed; // not serialized, cosmetic + double scanner_head_rotate_speed; // not serialized, cosmetic double scanner_head_rotate; cpVect platonic_nearest_direction; // normalized double platonic_detection_strength; // from zero to one @@ -537,6 +554,19 @@ static inline double cpvangle(cpVect vec) return atan2(vec.y, vec.x); } +typedef struct BoxCentered +{ + cpVect pos; + double rotation; + cpVect size; +} BoxCentered; + +static inline bool box_has_point(BoxCentered box, cpVect point) +{ + cpVect local_point = cpvspin(cpvsub(point, box.pos), -box.rotation); + return has_point((AABB){.x = -box.size.x/2.0, .y = -box.size.y/2.0, .width = box.size.x, .height = box.size.y}, local_point); +} + static double sign(double f) { if (f >= 0.0f)