Add squad invites

main
Cameron Murphy Reikes 2 years ago
parent 5f56bdfbb6
commit 660ec75faf

@ -682,7 +682,7 @@ SerMaybeFailure ser_data(SerState* ser, char* data, size_t data_len, const char*
{ {
char var_name[512] = { 0 }; char var_name[512] = { 0 };
size_t var_name_len = 0; size_t var_name_len = 0;
if(ser->write_varnames) if (ser->write_varnames)
{ {
snprintf(var_name, 512, "%d%s", line, name); // can't have separator before the name, when comparing names skips past the digit snprintf(var_name, 512, "%d%s", line, name); // can't have separator before the name, when comparing names skips past the digit
var_name_len = strlen(var_name); var_name_len = strlen(var_name);
@ -771,6 +771,7 @@ enum GameVersion
VChangedVectorSerializing, VChangedVectorSerializing,
VAddedLastUsedMedbay, VAddedLastUsedMedbay,
VAddedSquads, VAddedSquads,
VAddedSquadInvites,
VMax, // this minus one will be the version used VMax, // this minus one will be the version used
}; };
@ -810,6 +811,14 @@ SerMaybeFailure ser_inputframe(SerState* ser, InputFrame* i)
SER_VAR(&i->id); SER_VAR(&i->id);
SER_MAYBE_RETURN(ser_V2(ser, &i->movement)); SER_MAYBE_RETURN(ser_V2(ser, &i->movement));
SER_VAR(&i->take_over_squad); 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->seat_action); SER_VAR(&i->seat_action);
SER_MAYBE_RETURN(ser_entityid(ser, &i->seat_to_inhabit)); SER_MAYBE_RETURN(ser_entityid(ser, &i->seat_to_inhabit));
@ -831,7 +840,7 @@ SerMaybeFailure ser_player(SerState* ser, Player* p)
if (p->connected) if (p->connected)
{ {
SER_VAR(&p->unlocked_bombs); SER_VAR(&p->unlocked_bombs);
if(ser->version >= VAddedSquads) if (ser->version >= VAddedSquads)
SER_VAR(&p->squad); SER_VAR(&p->squad);
SER_MAYBE_RETURN(ser_entityid(ser, &p->entity)); SER_MAYBE_RETURN(ser_entityid(ser, &p->entity));
if (ser->version >= VAddedLastUsedMedbay) if (ser->version >= VAddedLastUsedMedbay)
@ -915,9 +924,12 @@ SerMaybeFailure ser_entity(SerState* ser, GameState* gs, Entity* e)
if (e->is_player) if (e->is_player)
{ {
SER_ASSERT(e->no_save_to_disk); SER_ASSERT(e->no_save_to_disk);
SER_MAYBE_RETURN(ser_entityid(ser, &e->currently_inside_of_box)); SER_MAYBE_RETURN(ser_entityid(ser, &e->currently_inside_of_box));
if(ser->version >= VAddedSquads) if (ser->version >= VAddedSquads)
SER_VAR(&e->presenting_squad); SER_VAR(&e->presenting_squad);
if (ser->version >= VAddedSquadInvites)
SER_VAR(&e->squad_invited_to);
SER_VAR(&e->goldness); SER_VAR(&e->goldness);
} }
@ -1495,6 +1507,15 @@ void process(GameState* gs, float dt)
} }
player->input.take_over_squad = -1; player->input.take_over_squad = -1;
} }
// squad invites
Entity* possibly_to_invite = get_entity(gs, player->input.invite_this_player);
if (player->input.invite_this_player.generation > 0)
player->input.invite_this_player = (EntityID){ 0 }; // just in case
if (player->squad != SquadNone && possibly_to_invite != NULL && possibly_to_invite->is_player)
{
possibly_to_invite->squad_invited_to = player->squad;
}
Entity* p = get_entity(gs, player->entity); Entity* p = get_entity(gs, player->entity);
if (p == NULL) if (p == NULL)
{ {
@ -1511,6 +1532,21 @@ void process(GameState* gs, float dt)
assert(p->is_player); assert(p->is_player);
p->presenting_squad = player->squad; p->presenting_squad = player->squad;
if (p->squad_invited_to != SquadNone)
{
if (player->input.accept_cur_squad_invite)
{
player->squad = p->squad_invited_to;
p->squad_invited_to = SquadNone;
player->input.accept_cur_squad_invite = false;
}
if (player->input.reject_cur_squad_invite)
{
p->squad_invited_to = SquadNone;
player->input.reject_cur_squad_invite = false;
}
}
#ifdef INFINITE_RESOURCES #ifdef INFINITE_RESOURCES
p->damage = 0.0f; p->damage = 0.0f;
#endif #endif
@ -1657,7 +1693,7 @@ void process(GameState* gs, float dt)
} }
p->damage = clamp01(p->damage); p->damage = clamp01(p->damage);
} }
if (get_entity(gs, gs->cur_spacestation) == NULL) if (get_entity(gs, gs->cur_spacestation) == NULL)
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

367
main.c

@ -33,39 +33,46 @@
#include "hueshift.gen.h" #include "hueshift.gen.h"
static sg_pipeline pip; static sg_pipeline pip;
static struct GameState gs = {0}; static struct GameState gs = { 0 };
static int myplayer = -1; static int my_player_index = -1;
static bool right_mouse_down = false; static bool right_mouse_down = false;
#define MAX_KEYDOWN SAPP_KEYCODE_MENU #define MAX_KEYDOWN SAPP_KEYCODE_MENU
static bool keydown[MAX_KEYDOWN] = {0}; static bool keydown[MAX_KEYDOWN] = { 0 };
typedef struct KeyPressed typedef struct KeyPressed
{ {
bool pressed; bool pressed;
uint64_t frame; uint64_t frame;
} KeyPressed; } KeyPressed;
static KeyPressed keypressed[MAX_KEYDOWN] = {0}; static KeyPressed keypressed[MAX_KEYDOWN] = { 0 };
static V2 mouse_pos = {0}; static V2 mouse_pos = { 0 };
static bool fullscreened = false; static bool fullscreened = false;
static bool build_pressed = false; static bool build_pressed = false;
static bool interact_pressed = false; static bool interact_pressed = false;
#define MAX_MOUSEBUTTON SAPP_MOUSEBUTTON_MIDDLE #define MAX_MOUSEBUTTON (SAPP_MOUSEBUTTON_MIDDLE+1)
static bool mousedown[MAX_MOUSEBUTTON] = { 0 };
typedef struct MousePressed typedef struct MousePressed
{ {
bool pressed; bool pressed;
uint64_t frame; uint64_t frame;
} MousePressed; } MousePressed;
static MousePressed mousepressed[MAX_MOUSEBUTTON] = { 0 }; static MousePressed mousepressed[MAX_MOUSEBUTTON] = { 0 };
static EntityID maybe_inviting_this_player = { 0 };
bool confirm_invite_this_player = false;
bool accept_invite = false;
bool reject_invite = false;
static V2 camera_pos = { 0 }; // it being a global variable keeps camera at same position after player death
static float player_scaling = 1.0f;
static bool mouse_frozen = false; // @BeforeShip make this debug only thing static bool mouse_frozen = false; // @BeforeShip make this debug only thing
static float funval = 0.0f; // easy to play with value controlled by left mouse button when held static float funval = 0.0f; // easy to play with value controlled by left mouse button when held
// down @BeforeShip remove on release builds // down @BeforeShip remove on release builds
static struct ClientToServer client_to_server = {0}; // buffer of inputs static struct ClientToServer client_to_server = { 0 }; // buffer of inputs
static ENetHost *client; static ENetHost* client;
static ENetPeer *peer; static ENetPeer* peer;
static float zoom_target = 300.0f; static float zoom_target = 300.0f;
static float zoom = 300.0f; static float zoom = 300.0f;
static enum Squad take_over_squad = (enum Squad) - 1; // -1 means not taking over any squad static enum Squad take_over_squad = (enum Squad)-1; // -1 means not taking over any squad
// images // images
static sg_image image_itemframe; static sg_image image_itemframe;
@ -83,6 +90,9 @@ static sg_image image_low_health;
static sg_image image_mic_muted; static sg_image image_mic_muted;
static sg_image image_flag_available; static sg_image image_flag_available;
static sg_image image_flag_taken; static sg_image image_flag_taken;
static sg_image image_squad_invite;
static sg_image image_check;
static sg_image image_no;
static int cur_editing_boxtype = -1; static int cur_editing_boxtype = -1;
static int cur_editing_rotation = 0; static int cur_editing_rotation = 0;
@ -91,21 +101,21 @@ static int cur_editing_rotation = 0;
static bool muted = false; static bool muted = false;
static ma_device microphone_device; static ma_device microphone_device;
static ma_device speaker_device; static ma_device speaker_device;
OpusEncoder *enc; OpusEncoder* enc;
OpusDecoder *dec; OpusDecoder* dec;
OpusBuffer packets_to_send = {0}; OpusBuffer packets_to_send = { 0 };
OpusBuffer packets_to_play = {0}; OpusBuffer packets_to_play = { 0 };
ma_mutex send_packets_mutex = {0}; ma_mutex send_packets_mutex = { 0 };
ma_mutex play_packets_mutex = {0}; ma_mutex play_packets_mutex = { 0 };
// server thread // server thread
void *server_thread_handle = 0; void* server_thread_handle = 0;
ServerThreadInfo server_info = {0}; ServerThreadInfo server_info = { 0 };
static struct BoxInfo static struct BoxInfo
{ {
enum BoxType type; enum BoxType type;
const char *image_path; const char* image_path;
sg_image image; sg_image image;
bool needs_tobe_unlocked; bool needs_tobe_unlocked;
} boxes[] = { } boxes[] = {
@ -140,6 +150,7 @@ static struct BoxInfo
.needs_tobe_unlocked = true, .needs_tobe_unlocked = true,
}, },
}; };
#define ENTITIES_ITER(cur) for(Entity* cur = gs.entities; cur < gs.entities + gs.cur_next_entity; cur++) if(cur->exists)
#define ARRLEN(arr) (sizeof(arr) / sizeof(*arr)) #define ARRLEN(arr) (sizeof(arr) / sizeof(*arr))
static struct SquadMeta static struct SquadMeta
@ -178,11 +189,11 @@ struct SquadMeta squad_meta(enum Squad squad)
return squad_metas[i]; return squad_metas[i];
} }
Log("Could not find squad %d!\n", squad); Log("Could not find squad %d!\n", squad);
return (struct SquadMeta){0}; return (struct SquadMeta) { 0 };
} }
struct BoxInfo struct BoxInfo
boxinfo(enum BoxType type) boxinfo(enum BoxType type)
{ {
for (int i = 0; i < ARRLEN(boxes); i++) for (int i = 0; i < ARRLEN(boxes); i++)
{ {
@ -190,11 +201,11 @@ boxinfo(enum BoxType type)
return boxes[i]; return boxes[i];
} }
Log("No box info found for type %d\n", type); Log("No box info found for type %d\n", type);
return (struct BoxInfo){0}; return (struct BoxInfo) { 0 };
} }
static sg_image static sg_image
load_image(const char *path) load_image(const char* path)
{ {
sg_image to_return = sg_alloc_image(); sg_image to_return = sg_alloc_image();
@ -203,10 +214,10 @@ load_image(const char *path)
int comp = 0; int comp = 0;
const int desired_channels = 4; const int desired_channels = 4;
stbi_set_flip_vertically_on_load(true); stbi_set_flip_vertically_on_load(true);
stbi_uc *image_data = stbi_load(path, &x, &y, &comp, desired_channels); stbi_uc* image_data = stbi_load(path, &x, &y, &comp, desired_channels);
if (!image_data) if (!image_data)
{ {
fprintf(stderr, "Failed to load image: %s\n", stbi_failure_reason()); fprintf(stderr, "Failed to load %s image: %s\n", path,stbi_failure_reason());
exit(-1); exit(-1);
} }
sg_init_image(to_return, sg_init_image(to_return,
@ -225,17 +236,17 @@ load_image(const char *path)
return to_return; return to_return;
} }
void microphone_data_callback(ma_device *pDevice, void *pOutput, const void *pInput, ma_uint32 frameCount) void microphone_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{ {
assert(frameCount == VOIP_EXPECTED_FRAME_COUNT); assert(frameCount == VOIP_EXPECTED_FRAME_COUNT);
if (peer != NULL) if (peer != NULL)
{ {
ma_mutex_lock(&send_packets_mutex); ma_mutex_lock(&send_packets_mutex);
OpusPacket *packet = push_packet(&packets_to_send); OpusPacket* packet = push_packet(&packets_to_send);
if (packet != NULL) if (packet != NULL)
{ {
opus_int16 muted_audio[VOIP_EXPECTED_FRAME_COUNT] = {0}; opus_int16 muted_audio[VOIP_EXPECTED_FRAME_COUNT] = { 0 };
const opus_int16 *audio_buffer = (const opus_int16 *)pInput; const opus_int16* audio_buffer = (const opus_int16*)pInput;
if (muted) if (muted)
audio_buffer = muted_audio; audio_buffer = muted_audio;
opus_int32 written = opus_encode(enc, audio_buffer, VOIP_EXPECTED_FRAME_COUNT, packet->data, VOIP_PACKET_MAX_SIZE); opus_int32 written = opus_encode(enc, audio_buffer, VOIP_EXPECTED_FRAME_COUNT, packet->data, VOIP_PACKET_MAX_SIZE);
@ -246,18 +257,18 @@ void microphone_data_callback(ma_device *pDevice, void *pOutput, const void *pIn
(void)pOutput; (void)pOutput;
} }
void speaker_data_callback(ma_device *pDevice, void *pOutput, const void *pInput, ma_uint32 frameCount) void speaker_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{ {
assert(frameCount == VOIP_EXPECTED_FRAME_COUNT); assert(frameCount == VOIP_EXPECTED_FRAME_COUNT);
ma_mutex_lock(&play_packets_mutex); ma_mutex_lock(&play_packets_mutex);
OpusPacket *cur_packet = pop_packet(&packets_to_play); OpusPacket* cur_packet = pop_packet(&packets_to_play);
if (cur_packet != NULL && cur_packet->length > 0) // length of 0 means skipped packet if (cur_packet != NULL && cur_packet->length > 0) // length of 0 means skipped packet
{ {
opus_decode(dec, cur_packet->data, cur_packet->length, (opus_int16 *)pOutput, frameCount, 0); opus_decode(dec, cur_packet->data, cur_packet->length, (opus_int16*)pOutput, frameCount, 0);
} }
else else
{ {
opus_decode(dec, NULL, 0, (opus_int16 *)pOutput, frameCount, 0); // I think opus makes it sound good if packets are skipped with null opus_decode(dec, NULL, 0, (opus_int16*)pOutput, frameCount, 0); // I think opus makes it sound good if packets are skipped with null
} }
ma_mutex_unlock(&play_packets_mutex); ma_mutex_unlock(&play_packets_mutex);
(void)pInput; (void)pInput;
@ -337,10 +348,10 @@ init(void)
// failure instead of exit(-1), replace the macros in sokol with this as well, // failure instead of exit(-1), replace the macros in sokol with this as well,
// like assert // like assert
Entity *entity_data = malloc(sizeof *entity_data * MAX_ENTITIES); Entity* entity_data = malloc(sizeof * entity_data * MAX_ENTITIES);
initialize(&gs, entity_data, sizeof *entity_data * MAX_ENTITIES); initialize(&gs, entity_data, sizeof * entity_data * MAX_ENTITIES);
sg_desc sgdesc = {.context = sapp_sgcontext()}; sg_desc sgdesc = { .context = sapp_sgcontext() };
sg_setup(&sgdesc); sg_setup(&sgdesc);
if (!sg_isvalid()) if (!sg_isvalid())
{ {
@ -348,7 +359,7 @@ init(void)
exit(-1); exit(-1);
} }
sgp_desc sgpdesc = {0}; sgp_desc sgpdesc = { 0 };
sgp_setup(&sgpdesc); sgp_setup(&sgpdesc);
if (!sgp_is_valid()) if (!sgp_is_valid())
{ {
@ -394,6 +405,9 @@ init(void)
image_mic_muted = load_image("loaded/mic_muted.png"); image_mic_muted = load_image("loaded/mic_muted.png");
image_flag_available = load_image("loaded/flag_available.png"); image_flag_available = load_image("loaded/flag_available.png");
image_flag_taken = load_image("loaded/flag_ripped.png"); image_flag_taken = load_image("loaded/flag_ripped.png");
image_squad_invite = load_image("loaded/squad_invite.png");
image_check = load_image("loaded/check.png");
image_no = load_image("loaded/no.png");
} }
// socket initialization // socket initialization
@ -467,7 +481,7 @@ draw_texture_rectangle_centered(V2 center, V2 width_height)
static void static void
draw_texture_centered(V2 center, float size) draw_texture_centered(V2 center, float size)
{ {
draw_texture_rectangle_centered(center, (V2){size, size}); draw_texture_rectangle_centered(center, (V2) { size, size });
} }
static void static void
@ -479,22 +493,30 @@ draw_circle(V2 point, float radius)
{ {
float progress = (float)i / (float)POINTS; float progress = (float)i / (float)POINTS;
float next_progress = (float)(i + 1) / (float)POINTS; float next_progress = (float)(i + 1) / (float)POINTS;
lines[i].a = (V2){.x = cosf(progress * 2.0f * PI) * radius, lines[i].a = (V2){ .x = cosf(progress * 2.0f * PI) * radius,
.y = sinf(progress * 2.0f * PI) * radius}; .y = sinf(progress * 2.0f * PI) * radius };
lines[i].b = (V2){.x = cosf(next_progress * 2.0f * PI) * radius, lines[i].b = (V2){ .x = cosf(next_progress * 2.0f * PI) * radius,
.y = sinf(next_progress * 2.0f * PI) * radius}; .y = sinf(next_progress * 2.0f * PI) * radius };
lines[i].a = V2add(lines[i].a, point); lines[i].a = V2add(lines[i].a, point);
lines[i].b = V2add(lines[i].b, point); lines[i].b = V2add(lines[i].b, point);
} }
sgp_draw_lines(lines, POINTS); sgp_draw_lines(lines, POINTS);
} }
static Entity *
static Player* myplayer()
{
if (my_player_index == -1)
return NULL;
return &gs.players[my_player_index];
}
static Entity*
myentity() myentity()
{ {
if (myplayer == -1) if (myplayer() == NULL)
return NULL; return NULL;
Entity *to_return = get_entity(&gs, gs.players[myplayer].entity); Entity* to_return = get_entity(&gs, myplayer()->entity);
if (to_return != NULL) if (to_return != NULL)
assert(to_return->is_player); assert(to_return->is_player);
return to_return; return to_return;
@ -505,7 +527,9 @@ bool can_build(int i)
bool allow_building = true; bool allow_building = true;
if (boxinfo((enum BoxType)i).needs_tobe_unlocked) if (boxinfo((enum BoxType)i).needs_tobe_unlocked)
{ {
allow_building = gs.players[myplayer].unlocked_bombs; allow_building = false;
if (myplayer() != NULL)
allow_building = myplayer()->unlocked_bombs;
} }
return allow_building; return allow_building;
} }
@ -516,6 +540,26 @@ void attempt_to_build(int i)
cur_editing_boxtype = i; cur_editing_boxtype = i;
} }
static V2 screen_to_world(float width, float height, V2 screen)
{
V2 world = screen;
world = V2sub(world, (V2) { .x = width / 2.0f, .y = height / 2.0f });
world.x /= zoom;
world.y /= -zoom;
world = V2add(world, camera_pos);
return world;
}
static V2 world_to_screen(float width, float height, V2 world)
{
V2 screen = world;
screen = V2sub(screen, camera_pos);
screen.x *= zoom;
screen.y *= -zoom;
screen = V2add(screen, (V2) { .x = width / 2.0f, .y = height / 2.0f });
return screen;
}
static void static void
ui(bool draw, float dt, float width, float height) ui(bool draw, float dt, float width, float height)
{ {
@ -529,20 +573,127 @@ ui(bool draw, float dt, float width, float height)
if (draw) if (draw)
sgp_push_transform(); sgp_push_transform();
// draw squad invite
static float invite_y = -200.0f;
static enum Squad draw_as_squad = SquadNone;
static float yes_size = 50.0f;
static float no_size = 50.0f;
{
bool invited = myentity() != NULL && myentity()->squad_invited_to != SquadNone;
float size = 200.0f;
float yes_no_size = 50.0f;
float x_center = 0.75f * width;
float x = x_center - size / 2.0f;
//AABB box = (AABB){ .x = x, .y = invite_y, .width = size, .height = size };
float yes_x = x - size/4.0f;
float no_x = x + size/4.0f;
float buttons_y = invite_y + size / 2.0f;
bool yes_hovered = invited && V2dist(mouse_pos, (V2) { yes_x, buttons_y }) < yes_size/2.0f;
bool no_hovered = invited && V2dist(mouse_pos, (V2) { no_x, buttons_y }) < no_size/2.0f;
yes_size = lerp(yes_size, yes_hovered ? 75.0f : 50.0f, dt * 9.0f);
no_size = lerp(no_size, no_hovered ? 75.0f : 50.0f, dt * 9.0f);
if (invited && build_pressed && yes_hovered) accept_invite = true;
if (invited && build_pressed && no_hovered) reject_invite = true;
if (draw)
{
invite_y = lerp(invite_y, invited ? 50.0f : -200.0f, dt * 5.0f);
if (invited) draw_as_squad = myentity()->squad_invited_to;
transform_scope{
sgp_set_pipeline(pip);
struct SquadMeta meta = squad_meta(draw_as_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));
sgp_scale_at(1.0f, -1.0f, x, invite_y); // images upside down by default :(
sgp_set_image(0, image_squad_invite);
draw_texture_centered((V2) { x, invite_y }, size);
sgp_reset_image(0);
sgp_reset_pipeline();
}
// yes
transform_scope{
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
sgp_scale_at(1.0f, -1.0f, yes_x, buttons_y);
sgp_set_image(0, image_check);
draw_texture_centered((V2) { yes_x, buttons_y }, yes_size);
sgp_reset_image(0);
}
// no
transform_scope{
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
sgp_scale_at(1.0f, -1.0f, no_x, buttons_y);
sgp_set_image(0, image_no);
draw_texture_centered((V2) { no_x, buttons_y }, no_size);
sgp_reset_image(0);
}
}
}
// draw maybe inviting
{
Entity* inviting = get_entity(&gs, maybe_inviting_this_player);
if (inviting != NULL && myplayer() != NULL)
{
V2 top_of_head = world_to_screen(width, height, V2add(entity_pos(inviting), (V2) { .y = player_scaling * PLAYER_SIZE.y / 2.0f }));
V2 pos = V2add(top_of_head, (V2) { .y = -30.0f });
V2 to_mouse = V2sub(mouse_pos, world_to_screen(width, height, entity_pos(inviting)));
bool selecting_to_invite = V2dot(V2normalize(to_mouse), (V2) { 0.0f, -1.0f }) > 0.5f && V2length(to_mouse) > 15.0f;
if (!mousedown[SAPP_MOUSEBUTTON_RIGHT])
{
if (selecting_to_invite)
confirm_invite_this_player = true;
}
if (draw)
transform_scope {
const float size = 64.0f;
if (selecting_to_invite)
{
sgp_set_color(0.5f, 0.5f, 0.5f, 0.4f);
sgp_draw_filled_rect(pos.x - size / 2.0f, pos.y - size / 2.0f, size, size);
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
}
sgp_set_pipeline(pip);
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));
sgp_scale_at(1.0f, -1.0f, pos.x, pos.y); // images upside down by default :(
sgp_set_image(0, image_squad_invite);
draw_texture_centered(pos, size);
sgp_reset_image(0);
sgp_reset_pipeline();
}
}
}
// draw flags // draw flags
static V2 flag_pos[SquadLast] = {0}; static V2 flag_pos[SquadLast] = { 0 };
static float flag_rot[SquadLast] = {0}; static float flag_rot[SquadLast] = { 0 };
static float flag_scaling_increase[SquadLast] = {0}; static float flag_scaling_increase[SquadLast] = { 0 };
static bool choosing_flags = false; static bool choosing_flags = false;
const float flag_padding = 70.0f; const float flag_padding = 70.0f;
const float center_panel_height = 200.0f; const float center_panel_height = 200.0f;
static float center_panel_width = 0.0f; static float center_panel_width = 0.0f;
const float target_center_panel_width = ((SquadLast) + 2) * flag_padding; const float target_center_panel_width = ((SquadLast)+2) * flag_padding;
#define FLAG_ITER(i) for (int i = 0; i < SquadLast; i++) #define FLAG_ITER(i) for (int i = 0; i < SquadLast; i++)
{ {
FLAG_ITER(i) FLAG_ITER(i)
{ {
V2 target_pos = {0}; V2 target_pos = { 0 };
float target_rot = 0.0f; float target_rot = 0.0f;
float flag_progress = (float)i / (float)(SquadLast - 1.0f); float flag_progress = (float)i / (float)(SquadLast - 1.0f);
if (choosing_flags) if (choosing_flags)
@ -706,7 +857,7 @@ ui(bool draw, float dt, float width, float height)
for (int i = 0; i < ARRLEN(boxes); i++) for (int i = 0; i < ARRLEN(boxes); i++)
{ {
if (has_point( if (has_point(
(AABB){ (AABB) {
.x = x, .x = x,
.y = y, .y = y,
.width = itemframe_width, .width = itemframe_width,
@ -767,7 +918,7 @@ static void draw_dots(V2 camera_pos, float gap)
{ {
for (int y = -num; y < num; y++) for (int y = -num; y < num; y++)
{ {
V2 star = (V2){(float)x * gap, (float)y * gap}; V2 star = (V2){ (float)x * gap, (float)y * gap };
if (V2lengthsqr(V2sub(star, camera_pos)) > VISION_RADIUS * VISION_RADIUS) if (V2lengthsqr(V2sub(star, camera_pos)) > VISION_RADIUS * VISION_RADIUS)
continue; continue;
@ -778,6 +929,7 @@ static void draw_dots(V2 camera_pos, float gap)
} }
} }
static void static void
frame(void) frame(void)
{ {
@ -825,7 +977,7 @@ frame(void)
case ENET_EVENT_TYPE_RECEIVE: case ENET_EVENT_TYPE_RECEIVE:
{ {
char *decompressed = malloc(sizeof *decompressed * MAX_SERVER_TO_CLIENT); // @Robust no malloc char* decompressed = malloc(sizeof * decompressed * MAX_SERVER_TO_CLIENT); // @Robust no malloc
size_t decompressed_max_len = MAX_SERVER_TO_CLIENT; size_t decompressed_max_len = MAX_SERVER_TO_CLIENT;
assert(LZO1X_MEM_DECOMPRESS == 0); assert(LZO1X_MEM_DECOMPRESS == 0);
@ -838,7 +990,7 @@ frame(void)
if (return_value == LZO_E_OK) if (return_value == LZO_E_OK)
{ {
server_to_client_deserialize(&msg, decompressed, decompressed_max_len, false); server_to_client_deserialize(&msg, decompressed, decompressed_max_len, false);
myplayer = msg.your_player; my_player_index = msg.your_player;
} }
else else
{ {
@ -872,17 +1024,15 @@ frame(void)
// gameplay // gameplay
ui(false, dt, width, height); // handle events ui(false, dt, width, height); // handle events
V2 build_target_pos = {0}; V2 build_target_pos = { 0 };
float build_target_rotation = 0.0f; float build_target_rotation = 0.0f;
static V2 camera_pos = { V2 world_mouse_pos = screen_to_world(width, height, mouse_pos); // processed later in scope
0}; // keeps camera at same position after player death
V2 world_mouse_pos = mouse_pos; // processed later in scope
struct BuildPreviewInfo struct BuildPreviewInfo
{ {
V2 grid_pos; V2 grid_pos;
float grid_rotation; float grid_rotation;
} build_preview = {0}; } build_preview = { 0 };
V2 hand_pos = {0}; // in local space of grid when hovering over a grid V2 hand_pos = { 0 }; // in local space of grid when hovering over a grid
bool hand_at_arms_length = false; bool hand_at_arms_length = false;
{ {
// interpolate zoom // interpolate zoom
@ -894,15 +1044,11 @@ frame(void)
{ {
camera_pos = entity_pos(myentity()); camera_pos = entity_pos(myentity());
} }
world_mouse_pos = V2sub(world_mouse_pos, (V2){.x = width / 2.0f, .y = height / 2.0f});
world_mouse_pos.x /= zoom;
world_mouse_pos.y /= -zoom;
world_mouse_pos = V2add(world_mouse_pos, (V2){.x = camera_pos.x, .y = camera_pos.y});
} }
// calculate build preview stuff // calculate build preview stuff
EntityID grid_to_build_on = (EntityID){0}; EntityID grid_to_build_on = (EntityID){ 0 };
V2 possibly_local_hand_pos = (V2){0}; V2 possibly_local_hand_pos = (V2){ 0 };
if (myentity() != NULL) if (myentity() != NULL)
{ {
hand_pos = V2sub(world_mouse_pos, entity_pos(myentity())); hand_pos = V2sub(world_mouse_pos, entity_pos(myentity()));
@ -920,7 +1066,7 @@ frame(void)
hand_pos = V2add(hand_pos, entity_pos(myentity())); hand_pos = V2add(hand_pos, entity_pos(myentity()));
possibly_local_hand_pos = V2sub(hand_pos, entity_pos(myentity())); possibly_local_hand_pos = V2sub(hand_pos, entity_pos(myentity()));
Entity *placing_grid = closest_to_point_in_radius(&gs, hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP); Entity* placing_grid = closest_to_point_in_radius(&gs, hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP);
if (placing_grid == NULL) if (placing_grid == NULL)
{ {
build_preview = (struct BuildPreviewInfo){ build_preview = (struct BuildPreviewInfo){
@ -940,11 +1086,22 @@ frame(void)
} }
} }
// process player interaction (squad invites)
if (interact_pressed && myplayer() != NULL && myplayer()->squad != SquadNone)
ENTITIES_ITER(cur)
{
if (cur != myentity() && cur->is_player && has_point(centered_at(entity_pos(cur), V2scale(PLAYER_SIZE, player_scaling)), world_mouse_pos))
{
maybe_inviting_this_player = get_id(&gs, cur);
interact_pressed = false;
}
}
// Create and send input packet // Create and send input packet
{ {
static size_t last_frame_id = 0; static size_t last_frame_id = 0;
InputFrame cur_input_frame = {0}; InputFrame cur_input_frame = { 0 };
cur_input_frame.id = last_frame_id; cur_input_frame.id = last_frame_id;
V2 input = (V2){ V2 input = (V2){
.x = (float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A], .x = (float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A],
@ -956,7 +1113,29 @@ frame(void)
cur_input_frame.seat_action = interact_pressed; cur_input_frame.seat_action = interact_pressed;
cur_input_frame.grid_hand_pos_local_to = grid_to_build_on; cur_input_frame.grid_hand_pos_local_to = grid_to_build_on;
cur_input_frame.hand_pos = possibly_local_hand_pos; cur_input_frame.hand_pos = possibly_local_hand_pos;
if (take_over_squad >= 0)
{
cur_input_frame.take_over_squad = take_over_squad; cur_input_frame.take_over_squad = take_over_squad;
take_over_squad = -1;
}
else {
cur_input_frame.take_over_squad = -1; // @Robust make this zero initialized
}
if (confirm_invite_this_player)
{
cur_input_frame.invite_this_player = maybe_inviting_this_player;
maybe_inviting_this_player = (EntityID){ 0 };
}
if (accept_invite) {
cur_input_frame.accept_cur_squad_invite = true;
accept_invite = false;
}
if (reject_invite)
{
cur_input_frame.reject_cur_squad_invite = true;
reject_invite = false;
}
confirm_invite_this_player = false;
if (build_pressed && cur_editing_boxtype != -1) if (build_pressed && cur_editing_boxtype != -1)
{ {
@ -980,6 +1159,9 @@ frame(void)
input_differs = input_differs || cur_input_frame.build_rotation != latest.build_rotation; input_differs = input_differs || cur_input_frame.build_rotation != latest.build_rotation;
input_differs = input_differs || !entityids_same(cur_input_frame.grid_hand_pos_local_to, latest.grid_hand_pos_local_to); input_differs = input_differs || !entityids_same(cur_input_frame.grid_hand_pos_local_to, latest.grid_hand_pos_local_to);
input_differs = input_differs || cur_input_frame.accept_cur_squad_invite != latest.accept_cur_squad_invite;
input_differs = input_differs || cur_input_frame.reject_cur_squad_invite != latest.reject_cur_squad_invite;
input_differs = input_differs || !entityids_same(cur_input_frame.invite_this_player, latest.invite_this_player);
input_differs = input_differs || cur_input_frame.take_over_squad != latest.take_over_squad; input_differs = input_differs || cur_input_frame.take_over_squad != latest.take_over_squad;
if (input_differs) if (input_differs)
@ -1006,11 +1188,11 @@ frame(void)
{ {
ma_mutex_lock(&send_packets_mutex); ma_mutex_lock(&send_packets_mutex);
client_to_server.mic_data = &packets_to_send; client_to_server.mic_data = &packets_to_send;
char serialized[MAX_CLIENT_TO_SERVER] = {0}; char serialized[MAX_CLIENT_TO_SERVER] = { 0 };
size_t out_len = 0; size_t out_len = 0;
if (client_to_server_serialize(&gs, &client_to_server, serialized, &out_len, MAX_CLIENT_TO_SERVER)) if (client_to_server_serialize(&gs, &client_to_server, serialized, &out_len, MAX_CLIENT_TO_SERVER))
{ {
ENetPacket *packet = enet_packet_create((void *)serialized, ENetPacket* packet = enet_packet_create((void*)serialized,
out_len, out_len,
ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT); ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
enet_peer_send(peer, 0, packet); // @Robust error check this enet_peer_send(peer, 0, packet); // @Robust error check this
@ -1146,17 +1328,13 @@ frame(void)
} }
} }
static float player_scaling = 1.0f;
player_scaling = lerp(player_scaling, zoom < 6.5f ? 100.0f : 1.0f, dt * 7.0f); player_scaling = lerp(player_scaling, zoom < 6.5f ? 100.0f : 1.0f, dt * 7.0f);
for (size_t i = 0; i < gs.cur_next_entity; i++) ENTITIES_ITER(e)
{ {
Entity *e = &gs.entities[i];
if (!e->exists)
continue;
// draw grid // draw grid
if (e->is_grid) if (e->is_grid)
{ {
Entity *g = e; Entity* g = e;
BOXES_ITER(&gs, b, g) BOXES_ITER(&gs, b, g)
{ {
if (b->is_explosion_unlock) if (b->is_explosion_unlock)
@ -1165,8 +1343,8 @@ frame(void)
draw_circle(entity_pos(b), GOLD_UNLOCK_RADIUS); draw_circle(entity_pos(b), GOLD_UNLOCK_RADIUS);
} }
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f); sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
// debug draw force vectors for thrusters // debug draw force vectors for thrusters
#if 0 #if 0
{ {
if (b->type == BoxThruster) if (b->type == BoxThruster)
{ {
@ -1174,7 +1352,7 @@ frame(void)
dbg_line(entity_pos(b), V2add(entity_pos(b), V2scale(thruster_force(b), -1.0f))); dbg_line(entity_pos(b), V2add(entity_pos(b), V2scale(thruster_force(b), -1.0f)));
} }
} }
#endif #endif
if (b->box_type == BoxBattery) if (b->box_type == BoxBattery)
{ {
float cur_alpha = sgp_get_color().a; float cur_alpha = sgp_get_color().a;
@ -1239,12 +1417,12 @@ frame(void)
} }
// draw the velocity // draw the velocity
#if 0 #if 0
sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f); sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f);
V2 vel = grid_vel(g); V2 vel = grid_vel(g);
V2 to = V2add(grid_com(g), vel); V2 to = V2add(grid_com(g), vel);
sgp_draw_line(grid_com(g).x, grid_com(g).y, to.x, to.y); sgp_draw_line(grid_com(g).x, grid_com(g).y, to.x, to.y);
#endif #endif
} }
// draw player // draw player
@ -1252,6 +1430,7 @@ frame(void)
{ {
transform_scope transform_scope
{ {
sgp_rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y); sgp_rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y);
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f); sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
@ -1286,12 +1465,12 @@ frame(void)
sgp_translate(SUN_POS.x, SUN_POS.y); sgp_translate(SUN_POS.x, SUN_POS.y);
set_color(WHITE); set_color(WHITE);
sgp_set_image(0, image_sun); sgp_set_image(0, image_sun);
draw_texture_centered((V2){0}, SUN_RADIUS * 2.0f); draw_texture_centered((V2) { 0 }, SUN_RADIUS * 2.0f);
sgp_reset_image(0); sgp_reset_image(0);
// sun DEATH RADIUS // sun DEATH RADIUS
set_color(RED); set_color(RED);
draw_circle((V2){0}, INSTANT_DEATH_DISTANCE_FROM_SUN); draw_circle((V2) { 0 }, INSTANT_DEATH_DISTANCE_FROM_SUN);
} }
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f); sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
@ -1303,7 +1482,7 @@ frame(void)
{ {
sgp_set_color(1.0f, 1.0f, 1.0f, myentity()->damage); sgp_set_color(1.0f, 1.0f, 1.0f, myentity()->damage);
sgp_set_image(0, image_low_health); sgp_set_image(0, image_low_health);
draw_texture_rectangle_centered((V2){width / 2.0f, height / 2.0f}, (V2){width, height}); draw_texture_rectangle_centered((V2) { width / 2.0f, height / 2.0f }, (V2) { width, height });
sgp_reset_image(0); sgp_reset_image(0);
} }
@ -1311,13 +1490,13 @@ frame(void)
ui(true, dt, width, height); ui(true, dt, width, height);
} }
sg_pass_action pass_action = {0}; sg_pass_action pass_action = { 0 };
sg_begin_default_pass(&pass_action, (int)width, (int)height); sg_begin_default_pass(&pass_action, (int)width, (int)height);
sgp_flush(); sgp_flush();
sgp_end(); sgp_end();
sg_end_pass(); sg_end_pass();
sg_commit(); sg_commit();
} }
void cleanup(void) void cleanup(void)
{ {
@ -1346,7 +1525,7 @@ void cleanup(void)
ma_mutex_uninit(&server_info.info_mutex); ma_mutex_uninit(&server_info.info_mutex);
} }
void event(const sapp_event *e) void event(const sapp_event* e)
{ {
switch (e->type) switch (e->type)
{ {
@ -1399,6 +1578,8 @@ void event(const sapp_event *e)
keydown[e->key_code] = false; keydown[e->key_code] = false;
keypressed[e->key_code].pressed = false; keypressed[e->key_code].pressed = false;
keypressed[e->key_code].frame = 0; keypressed[e->key_code].frame = 0;
} }
break; break;
@ -1407,6 +1588,7 @@ void event(const sapp_event *e)
zoom_target = clamp(zoom_target, 0.5f, 900.0f); zoom_target = clamp(zoom_target, 0.5f, 900.0f);
break; break;
case SAPP_EVENTTYPE_MOUSE_DOWN: case SAPP_EVENTTYPE_MOUSE_DOWN:
mousedown[e->mouse_button] = true;
if (mousepressed[e->mouse_button].frame == 0) if (mousepressed[e->mouse_button].frame == 0)
{ {
mousepressed[e->mouse_button].pressed = true; mousepressed[e->mouse_button].pressed = true;
@ -1414,13 +1596,14 @@ void event(const sapp_event *e)
} }
break; break;
case SAPP_EVENTTYPE_MOUSE_UP: case SAPP_EVENTTYPE_MOUSE_UP:
mousedown[e->mouse_button] = false;
mousepressed[e->mouse_button].pressed = false; mousepressed[e->mouse_button].pressed = false;
mousepressed[e->mouse_button].frame = 0; mousepressed[e->mouse_button].frame = 0;
break; break;
case SAPP_EVENTTYPE_MOUSE_MOVE: case SAPP_EVENTTYPE_MOUSE_MOVE:
if (!mouse_frozen) if (!mouse_frozen)
{ {
mouse_pos = (V2){.x = e->mouse_x, .y = e->mouse_y}; mouse_pos = (V2){ .x = e->mouse_x, .y = e->mouse_y };
} }
if (right_mouse_down) if (right_mouse_down)
{ {
@ -1432,7 +1615,7 @@ void event(const sapp_event *e)
} }
sapp_desc sapp_desc
sokol_main(int argc, char *argv[]) sokol_main(int argc, char* argv[])
{ {
bool hosting = false; bool hosting = false;
stm_setup(); stm_setup();
@ -1440,11 +1623,11 @@ sokol_main(int argc, char *argv[])
server_info.world_save = "debug_world.bin"; server_info.world_save = "debug_world.bin";
if (argc > 1) if (argc > 1)
{ {
server_thread_handle = (void *)_beginthread(server, 0, (void *)&server_info); server_thread_handle = (void*)_beginthread(server, 0, (void*)&server_info);
hosting = true; hosting = true;
} }
(void)argv; (void)argv;
return (sapp_desc){ return (sapp_desc) {
.init_cb = init, .init_cb = init,
.frame_cb = frame, .frame_cb = frame,
.cleanup_cb = cleanup, .cleanup_cb = cleanup,

@ -299,6 +299,14 @@ void server(void* info_raw)
// prevents setting the event input to false before it's been processed. // prevents setting the event input to false before it's been processed.
if (cur_input.take_over_squad >= 0) if (cur_input.take_over_squad >= 0)
gs.players[player_slot].input.take_over_squad = cur_input.take_over_squad; gs.players[player_slot].input.take_over_squad = cur_input.take_over_squad;
if (cur_input.accept_cur_squad_invite)
gs.players[player_slot].input.accept_cur_squad_invite = cur_input.accept_cur_squad_invite;
if (cur_input.reject_cur_squad_invite)
gs.players[player_slot].input.reject_cur_squad_invite = cur_input.reject_cur_squad_invite;
if (cur_input.invite_this_player.generation > 0)
{
gs.players[player_slot].input.invite_this_player = cur_input.invite_this_player;
}
if (cur_input.seat_action) if (cur_input.seat_action)
{ {
gs.players[player_slot].input.seat_action = cur_input.seat_action; gs.players[player_slot].input.seat_action = cur_input.seat_action;

@ -143,8 +143,8 @@ static 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, AND comparison // when updated, must update serialization, comparison in main.c, and the server
// function in main.c // on input received processing function
typedef struct InputFrame typedef struct InputFrame
{ {
uint64_t tick; uint64_t tick;
@ -152,6 +152,9 @@ typedef struct InputFrame
V2 movement; V2 movement;
int take_over_squad; // -1 means not taking over any squad int take_over_squad; // -1 means not taking over any squad
bool accept_cur_squad_invite;
bool reject_cur_squad_invite;
EntityID invite_this_player; // null means inviting nobody! @Robust make it so just sends interact pos input, and server processes who to invite. This depends on client side prediction + proper input processing at the right tick.
bool seat_action; bool seat_action;
EntityID seat_to_inhabit; EntityID seat_to_inhabit;
@ -186,6 +189,7 @@ typedef struct Entity
bool is_player; bool is_player;
enum Squad presenting_squad; enum Squad presenting_squad;
EntityID currently_inside_of_box; 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 float goldness; // how much the player is a winner
// explosion // explosion
@ -457,6 +461,17 @@ typedef struct AABB
float x, y, width, height; float x, y, width, height;
} AABB; } AABB;
static AABB centered_at(V2 point, V2 size)
{
return (AABB)
{
.x = point.x - size.x / 2.0f,
.y = point.y - size.y / 2.0f,
.width = size.x,
.height = size.y,
};
}
static bool has_point(AABB aabb, V2 point) static bool has_point(AABB aabb, V2 point)
{ {
return point.x > aabb.x && point.x < aabb.x + aabb.width && point.y > aabb.y && point.y < aabb.y + aabb.height; return point.x > aabb.x && point.x < aabb.x + aabb.width && point.y > aabb.y && point.y < aabb.y + aabb.height;

Loading…
Cancel
Save