Add squad invites

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

@ -771,6 +771,7 @@ enum GameVersion
VChangedVectorSerializing,
VAddedLastUsedMedbay,
VAddedSquads,
VAddedSquadInvites,
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_MAYBE_RETURN(ser_V2(ser, &i->movement));
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_MAYBE_RETURN(ser_entityid(ser, &i->seat_to_inhabit));
@ -915,9 +924,12 @@ SerMaybeFailure ser_entity(SerState* ser, GameState* gs, Entity* e)
if (e->is_player)
{
SER_ASSERT(e->no_save_to_disk);
SER_MAYBE_RETURN(ser_entityid(ser, &e->currently_inside_of_box));
if (ser->version >= VAddedSquads)
SER_VAR(&e->presenting_squad);
if (ser->version >= VAddedSquadInvites)
SER_VAR(&e->squad_invited_to);
SER_VAR(&e->goldness);
}
@ -1495,6 +1507,15 @@ void process(GameState* gs, float dt)
}
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);
if (p == NULL)
{
@ -1511,6 +1532,21 @@ void process(GameState* gs, float dt)
assert(p->is_player);
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
p->damage = 0.0f;
#endif

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

221
main.c

@ -34,7 +34,7 @@
static sg_pipeline pip;
static struct GameState gs = { 0 };
static int myplayer = -1;
static int my_player_index = -1;
static bool right_mouse_down = false;
#define MAX_KEYDOWN SAPP_KEYCODE_MENU
static bool keydown[MAX_KEYDOWN] = { 0 };
@ -49,13 +49,20 @@ static bool fullscreened = false;
static bool build_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
{
bool pressed;
uint64_t frame;
} MousePressed;
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 float funval = 0.0f; // easy to play with value controlled by left mouse button when held
@ -83,6 +90,9 @@ static sg_image image_low_health;
static sg_image image_mic_muted;
static sg_image image_flag_available;
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_rotation = 0;
@ -140,6 +150,7 @@ static struct BoxInfo
.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))
static struct SquadMeta
@ -206,7 +217,7 @@ load_image(const char *path)
stbi_uc* image_data = stbi_load(path, &x, &y, &comp, desired_channels);
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);
}
sg_init_image(to_return,
@ -394,6 +405,9 @@ init(void)
image_mic_muted = load_image("loaded/mic_muted.png");
image_flag_available = load_image("loaded/flag_available.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
@ -489,12 +503,20 @@ draw_circle(V2 point, float radius)
sgp_draw_lines(lines, POINTS);
}
static Player* myplayer()
{
if (my_player_index == -1)
return NULL;
return &gs.players[my_player_index];
}
static Entity*
myentity()
{
if (myplayer == -1)
if (myplayer() == 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)
assert(to_return->is_player);
return to_return;
@ -505,7 +527,9 @@ bool can_build(int i)
bool allow_building = true;
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;
}
@ -516,6 +540,26 @@ void attempt_to_build(int 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
ui(bool draw, float dt, float width, float height)
{
@ -529,6 +573,113 @@ ui(bool draw, float dt, float width, float height)
if (draw)
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
static V2 flag_pos[SquadLast] = { 0 };
static float flag_rot[SquadLast] = { 0 };
@ -778,6 +929,7 @@ static void draw_dots(V2 camera_pos, float gap)
}
}
static void
frame(void)
{
@ -838,7 +990,7 @@ frame(void)
if (return_value == LZO_E_OK)
{
server_to_client_deserialize(&msg, decompressed, decompressed_max_len, false);
myplayer = msg.your_player;
my_player_index = msg.your_player;
}
else
{
@ -874,9 +1026,7 @@ frame(void)
ui(false, dt, width, height); // handle events
V2 build_target_pos = { 0 };
float build_target_rotation = 0.0f;
static V2 camera_pos = {
0}; // keeps camera at same position after player death
V2 world_mouse_pos = mouse_pos; // processed later in scope
V2 world_mouse_pos = screen_to_world(width, height, mouse_pos); // processed later in scope
struct BuildPreviewInfo
{
V2 grid_pos;
@ -894,10 +1044,6 @@ frame(void)
{
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
@ -940,6 +1086,17 @@ 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
{
@ -956,7 +1113,29 @@ frame(void)
cur_input_frame.seat_action = interact_pressed;
cur_input_frame.grid_hand_pos_local_to = grid_to_build_on;
cur_input_frame.hand_pos = possibly_local_hand_pos;
if (take_over_squad >= 0)
{
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)
{
@ -980,6 +1159,9 @@ frame(void)
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 || 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;
if (input_differs)
@ -1146,13 +1328,9 @@ frame(void)
}
}
static float player_scaling = 1.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
if (e->is_grid)
{
@ -1252,6 +1430,7 @@ frame(void)
{
transform_scope
{
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);
@ -1399,6 +1578,8 @@ void event(const sapp_event *e)
keydown[e->key_code] = false;
keypressed[e->key_code].pressed = false;
keypressed[e->key_code].frame = 0;
}
break;
@ -1407,6 +1588,7 @@ void event(const sapp_event *e)
zoom_target = clamp(zoom_target, 0.5f, 900.0f);
break;
case SAPP_EVENTTYPE_MOUSE_DOWN:
mousedown[e->mouse_button] = true;
if (mousepressed[e->mouse_button].frame == 0)
{
mousepressed[e->mouse_button].pressed = true;
@ -1414,6 +1596,7 @@ void event(const sapp_event *e)
}
break;
case SAPP_EVENTTYPE_MOUSE_UP:
mousedown[e->mouse_button] = false;
mousepressed[e->mouse_button].pressed = false;
mousepressed[e->mouse_button].frame = 0;
break;

@ -299,6 +299,14 @@ void server(void* info_raw)
// prevents setting the event input to false before it's been processed.
if (cur_input.take_over_squad >= 0)
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)
{
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);
}
// when updated, must update serialization, AND comparison
// function in main.c
// when updated, must update serialization, comparison in main.c, and the server
// on input received processing function
typedef struct InputFrame
{
uint64_t tick;
@ -152,6 +152,9 @@ typedef struct InputFrame
V2 movement;
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;
EntityID seat_to_inhabit;
@ -186,6 +189,7 @@ typedef struct Entity
bool is_player;
enum Squad presenting_squad;
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
// explosion
@ -457,6 +461,17 @@ typedef struct AABB
float x, y, width, height;
} 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)
{
return point.x > aabb.x && point.x < aabb.x + aabb.width && point.y > aabb.y && point.y < aabb.y + aabb.height;

Loading…
Cancel
Save