Add tutorial helpers and gyroscope spin feedback

main
parent f61a766d60
commit b2fcd2d606

@ -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

Binary file not shown.

@ -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)
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 417 B

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 B

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

132
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)

@ -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

@ -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 <math.h> // sqrt and cos vector functions
#include <math.h> // sqrt and cos vector functions
#include <stdint.h> // tick is unsigned integer
#include <stdio.h> // 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)

Loading…
Cancel
Save