Add cloaking device

main
Cameron Murphy Reikes 2 years ago
parent 669798caef
commit 2d84fbbf9c

Binary file not shown.

@ -73,6 +73,28 @@ Entity *get_entity(GameState *gs, EntityID id)
return to_return; 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) static BOX_UNLOCKS_TYPE box_unlock_number(enum BoxType box)
{ {
assert((BOX_UNLOCKS_TYPE)box < 64); assert((BOX_UNLOCKS_TYPE)box < 64);
@ -129,6 +151,20 @@ static GameState *cp_space_gs(cpSpace *space)
return (GameState *)cpSpaceGetUserData(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) int grid_num_boxes(GameState *gs, Entity *e)
{ {
assert(e->is_grid); 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) void create_player(Player *player)
{ {
// default box unlocks, required for survival and growth // 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, BoxHullpiece);
unlock_box(player, BoxThruster); unlock_box(player, BoxThruster);
unlock_box(player, BoxBattery); unlock_box(player, BoxBattery);
@ -323,6 +363,7 @@ void create_player(Player *player)
unlock_box(player, BoxMedbay); unlock_box(player, BoxMedbay);
unlock_box(player, BoxSolarPanel); unlock_box(player, BoxSolarPanel);
unlock_box(player, BoxScanner); unlock_box(player, BoxScanner);
#endif
} }
void create_player_entity(GameState *gs, Entity *e) 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))); 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) float entity_angular_velocity(Entity *grid)
{ {
return (float)cpBodyGetAngularVelocity(grid->body); return (float)cpBodyGetAngularVelocity(grid->body);
@ -798,6 +846,7 @@ SerMaybeFailure ser_var(SerState *ser, char *var_pointer, size_t var_size, const
enum GameVersion enum GameVersion
{ {
VInitial, VInitial,
VMoreBoxes,
VMax, // this minus one will be the version used VMax, // this minus one will be the version used
}; };
@ -855,6 +904,14 @@ SerMaybeFailure ser_inputframe(SerState *ser, InputFrame *i)
return ser_ok; 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) SerMaybeFailure ser_player(SerState *ser, Player *p)
{ {
SER_VAR(&p->connected); 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); SER_VAR(&e->is_player);
if (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->box_type);
SER_VAR(&e->is_platonic); SER_VAR(&e->is_platonic);
if (ser->version >= VMoreBoxes)
SER_VAR(&e->owning_squad);
SER_VAR(&e->always_visible); SER_VAR(&e->always_visible);
SER_MAYBE_RETURN(ser_entityid(ser, &e->next_box)); SER_MAYBE_RETURN(ser_entityid(ser, &e->next_box));
SER_MAYBE_RETURN(ser_entityid(ser, &e->prev_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_nearest_direction);
SER_VAR(&e->platonic_detection_strength); SER_VAR(&e->platonic_detection_strength);
break; break;
case BoxCloaking:
SER_VAR(&e->cloaking_power);
break;
default: default:
break; break;
} }
@ -1076,12 +1143,19 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s)
SER_MAYBE_RETURN(ser_V2(ser, &gs->goldpos)); 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 // @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++) 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) 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++) for (size_t i = 0; i < gs->cur_next_entity; i++)
{ {
Entity *e = &gs->entities[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() \ #define SER_ENTITY() \
SER_VAR(&entities_done); \ SER_VAR(&entities_done); \
SER_VAR(&i); \ SER_VAR(&i); \
SER_MAYBE_RETURN(ser_entity(ser, gs, e)) 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) 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; bool serialized_grid_yet = false;
// serialize boxes always after bodies, so that by the time the boxes // 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. // 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)); bool this_box_in_range = ser->save_or_load_from_disk;
if (cur->always_visible) 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; this_box_in_range = true;
if (this_box_in_range) if (this_box_in_range)
{ {
@ -1119,12 +1198,12 @@ SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s)
} }
// serialize this box // 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_ASSERT(cur_id.index < gs->max_entities);
SER_VAR(&entities_done); 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 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_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 // has to be global var because can only get this information
static THREADLOCAL cpShape *closest_to_point_in_radius_result = NULL; static THREADLOCAL cpShape *closest_to_point_in_radius_result = NULL;
static THREADLOCAL float closest_to_point_in_radius_result_largest_dist = 0.0f; 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)))); 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) \ #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); grid_create(gs, grid);
entity_set_pos(grid, pos); entity_set_pos(grid, pos);
entity_ensure_in_orbit(grid); entity_ensure_in_orbit(grid);
Entity *explosion_box = new_entity(gs); Entity *platonic_box = new_entity(gs);
box_create(gs, explosion_box, grid, (V2){0}); box_create(gs, platonic_box, grid, (V2){0});
explosion_box->box_type = platonic_type; platonic_box->box_type = platonic_type;
explosion_box->is_platonic = true; platonic_box->is_platonic = true;
BOX_AT_TYPE(grid, ((V2){BOX_SIZE, 0}), BoxExplosive); 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 * 2, 0}), BoxHullpiece);
BOX_AT_TYPE(grid, ((V2){BOX_SIZE * 3, 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); 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) void create_initial_world(GameState *gs)
{ {
#if 0 #ifdef DEBUG_WORLD
Log("Creating debug world\n"); Log("Creating debug world\n");
create_station(gs, (V2){-5.0f,0.0f}, BoxExplosive); create_bomb_station(gs, (V2){-5.0f, 0.0f}, BoxExplosive);
create_station(gs, (V2){0.0f, 5.0f}, BoxGyroscope); create_bomb_station(gs, (V2){0.0f, 5.0f}, BoxGyroscope);
create_hard_shell_station(gs, (V2){-5.0f, 5.0f}, BoxCloaking);
#else #else
create_station(gs, (V2){-50.0f, 0.0f}, BoxExplosive); create_bomb_station(gs, (V2){-50.0f, 0.0f}, BoxExplosive);
create_station(gs, (V2){0.0f, 100.0f}, BoxGyroscope); create_hard_shell_station(gs, (V2){0.0f, 100.0f}, BoxGyroscope);
create_bomb_station(gs, (V2){0.0f, -100.0f}, BoxCloaking);
#endif #endif
} }
@ -1788,6 +1916,10 @@ void process(GameState *gs, float dt)
cpBodySetPosition(p->body, v2_to_cp(entity_pos(seat_inside_of))); cpBodySetPosition(p->body, v2_to_cp(entity_pos(seat_inside_of)));
cpBodySetVelocity(p->body, v2_to_cp(box_vel(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 // set thruster thrust from movement
if (seat_inside_of->box_type == BoxCockpit) if (seat_inside_of->box_type == BoxCockpit)
{ {
@ -1859,6 +1991,7 @@ void process(GameState *gs, float dt)
} }
Entity *new_box = new_entity(gs); Entity *new_box = new_entity(gs);
box_create(gs, new_box, target_grid, created_box_position); 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! grid_correct_for_holes(gs, target_grid); // no holey ship for you!
new_box->box_type = player->input.build_type; new_box->box_type = player->input.build_type;
new_box->compass_rotation = player->input.build_rotation; new_box->compass_rotation = player->input.build_rotation;
@ -1888,7 +2021,6 @@ void process(GameState *gs, float dt)
// sun processing // sun processing
{ {
cpVect pos_rel_sun = v2_to_cp(V2sub(entity_pos(e), SUN_POS)); cpVect pos_rel_sun = v2_to_cp(V2sub(entity_pos(e), SUN_POS));
cpFloat sqdist = cpvlengthsq(pos_rel_sun); cpFloat sqdist = cpvlengthsq(pos_rel_sun);
if (e->body != NULL && sqdist > (INSTANT_DEATH_DISTANCE_FROM_SUN * INSTANT_DEATH_DISTANCE_FROM_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) if (cur_box->box_type == BoxScanner)
{ {
// set the nearest platonic solid! only on server as only the server sees everything // set the nearest platonic solid! only on server as only the server sees everything

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -51,6 +51,7 @@ typedef struct 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 picking_new_boxtype = false;
static bool build_pressed = false; static bool build_pressed = false;
static bool interact_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_solarpanel_charging;
static sg_image image_scanner_head; static sg_image image_scanner_head;
static sg_image image_itemswitch; static sg_image image_itemswitch;
static sg_image image_cloaking_panel;
static enum BoxType toolbar[TOOLBAR_SLOTS] = { static enum BoxType toolbar[TOOLBAR_SLOTS] = {
BoxHullpiece, BoxHullpiece,
@ -185,6 +187,10 @@ static struct BoxInfo
.image_path = "loaded/gyroscope.png", .image_path = "loaded/gyroscope.png",
}, },
{
.type = BoxCloaking,
.image_path = "loaded/cloaking_device.png",
},
}; };
#define ENTITIES_ITER(cur) \ #define ENTITIES_ITER(cur) \
for (Entity *cur = gs.entities; cur < gs.entities + gs.cur_next_entity; \ 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_solarpanel_charging = load_image("loaded/solarpanel_charging.png");
image_scanner_head = load_image("loaded/scanner_head.png"); image_scanner_head = load_image("loaded/scanner_head.png");
image_itemswitch = load_image("loaded/itemswitch.png"); image_itemswitch = load_image("loaded/itemswitch.png");
image_cloaking_panel = load_image("loaded/cloaking_panel.png");
} }
// socket initialization // socket initialization
@ -634,6 +641,15 @@ bool can_build(int i)
allow_building = box_unlocked(myplayer(), box_type); allow_building = box_unlocked(myplayer(), box_type);
return allow_building; 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) 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(); sgp_push_transform();
// draw pick new box type menu // draw pick new box type menu
static bool picking_new_boxtype = false;
static float pick_opacity = 0.0f; static float pick_opacity = 0.0f;
{ {
if (keypressed[SAPP_KEYCODE_ESCAPE].pressed) 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); sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f * pick_opacity);
} }
int boxes_per_row = (int)floorf(pick_modal.width / 128.0f); 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_width = pick_modal.width / (float)boxes_per_row;
float cell_height = cell_width; float cell_height = cell_width;
float padding = 0.2f * 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) if (item_being_hovered && build_pressed && picking_new_boxtype)
{ {
toolbar[cur_toolbar_slot] = info.type; toolbar[cur_toolbar_slot] = info.type;
picking_new_boxtype = false;
build_pressed = false; build_pressed = false;
} }
if (draw) if (draw)
@ -816,12 +833,7 @@ static void ui(bool draw, float dt, float width, float height)
{ {
pipeline_scope(hueshift_pipeline) pipeline_scope(hueshift_pipeline)
{ {
struct SquadMeta meta = squad_meta(draw_as_squad); setup_hueshift(draw_as_squad);
hueshift_uniforms_t uniform = {
.is_colorless = meta.is_colorless,
.target_hue = meta.hue,
};
sgp_set_uniform(&uniform, sizeof(hueshift_uniforms_t));
sgp_scale_at(1.0f, -1.0f, x, sgp_scale_at(1.0f, -1.0f, x,
invite_y); // images upside down by default :( invite_y); // images upside down by default :(
sgp_set_image(0, image_squad_invite); 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) pipeline_scope(hueshift_pipeline)
{ {
struct SquadMeta meta = squad_meta(myplayer()->squad); setup_hueshift(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, sgp_scale_at(1.0f, -1.0f, pos.x,
pos.y); // images upside down by default :( 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) pipeline_scope(hueshift_pipeline)
{ {
struct SquadMeta meta = squad_meta(this_squad); setup_hueshift(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));
sgp_rotate_at(flag_rot[i], flag_pos[i].x, flag_pos[i].y); 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, sgp_scale_at(1.0f, -1.0f, flag_pos[i].x,
@ -1786,8 +1790,22 @@ static void frame(void)
{ {
set_color(GOLD); 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); 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); sgp_reset_image(0);
if (b->box_type == BoxScanner) if (b->box_type == BoxScanner)
@ -1795,9 +1813,11 @@ static void frame(void)
sgp_set_image(0, image_scanner_head); sgp_set_image(0, image_scanner_head);
transform_scope transform_scope
{ {
sgp_rotate_at(b->scanner_head_rotate, entity_pos(b).x, entity_pos(b).y);
pipeline_scope(goodpixel_pipeline) 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); sgp_reset_image(0);
set_color(WHITE); set_color(WHITE);
@ -1811,12 +1831,21 @@ static void frame(void)
} }
sgp_set_color(0.5f, 0.1f, 0.1f, b->damage); sgp_set_color(0.5f, 0.1f, 0.1f, b->damage);
draw_color_rect_centered(entity_pos(b), BOX_SIZE); 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 // outside of the transform scope
if (b->box_type == BoxScanner) if (b->box_type == BoxScanner)
{ {
if (b->platonic_detection_strength > 0.0f) if (b->platonic_detection_strength > 0.0)
{ {
set_color(colhexcode(0xf2d75c)); set_color(colhexcode(0xf2d75c));
V2 to = V2add(entity_pos(b), V2scale(b->platonic_nearest_direction, b->platonic_detection_strength)); 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) pipeline_scope(hueshift_pipeline)
{ {
struct SquadMeta meta = squad_meta(e->presenting_squad); setup_hueshift(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));
sgp_set_image(0, image_player); sgp_set_image(0, image_player);
draw_texture_rectangle_centered( draw_texture_rectangle_centered(
entity_pos(e), V2scale(PLAYER_SIZE, player_scaling)); entity_pos(e), V2scale(PLAYER_SIZE, player_scaling));
@ -1967,6 +1992,14 @@ void event(const sapp_event *e)
int target_slot = key_num - 1; int target_slot = key_num - 1;
if (target_slot <= TOOLBAR_SLOTS && target_slot >= 0) 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; cur_toolbar_slot = target_slot;
} }

@ -95,15 +95,9 @@ void server(void *info_raw)
OpusEncoder *player_encoders[MAX_PLAYERS] = {0}; OpusEncoder *player_encoders[MAX_PLAYERS] = {0};
OpusDecoder *player_decoders[MAX_PLAYERS] = {0}; OpusDecoder *player_decoders[MAX_PLAYERS] = {0};
// for (int i = 0; i < MAX_PLAYERS; i++) #ifdef DEBUG_WORLD
//{ world_save_name = NULL;
// int error = 0; #endif
// 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");
// }
if (world_save_name != NULL) if (world_save_name != NULL)
{ {
size_t read_game_data_buffer_size = entities_size; 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); player_decoders[player_slot] = opus_decoder_create(VOIP_SAMPLE_RATE, 1, &error);
if (error != OPUS_OK) if (error != OPUS_OK)
Log("Failed to create decoder: %d\n", error); 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; break;

@ -9,6 +9,7 @@ SetWorkingDir, %A_ScriptDir%
^b:: ^b::
WinKill, Flight Hosting WinKill, Flight Hosting
WinKill, Flight Not Hosting
WinActivate, flightbuild WinActivate, flightbuild
If WinActive("flightbuild") If WinActive("flightbuild")
{ {
@ -18,14 +19,12 @@ If WinActive("flightbuild")
return return
^+b:: ^+b::
WinKill, Flight WinKill, Flight Hosting
Sleep, 20 WinKill, Flight Not Hosting
WinKill, Flight
Sleep, 20
WinKill, Flight
WinActivate, flightbuild WinActivate, flightbuild
If WinActive("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 return

@ -26,6 +26,8 @@
#define THRUSTER_ENERGY_USED_PER_SECOND 0.005f #define THRUSTER_ENERGY_USED_PER_SECOND 0.005f
#define GYROSCOPE_ENERGY_USED_PER_SECOND 0.005f #define GYROSCOPE_ENERGY_USED_PER_SECOND 0.005f
#define GYROSCOPE_TORQUE 0.5f #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 VISION_RADIUS 12.0f
#define MAX_SERVER_TO_CLIENT 1024 * 512 // maximum size of serialized gamestate buffer #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 #define MAX_CLIENT_TO_SERVER 1024 * 10 // maximum size of serialized inputs and mic data
@ -151,6 +153,7 @@ enum BoxType
BoxExplosive, BoxExplosive,
BoxScanner, BoxScanner,
BoxGyroscope, BoxGyroscope,
BoxCloaking,
BoxLast, BoxLast,
}; };
@ -219,6 +222,12 @@ typedef struct Entity
cpBody *body; // used by grid, player, and box cpBody *body; // used by grid, player, and box
cpShape *shape; // must be a box so shape_size can be set appropriately, and serialized 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 // for serializing the shape
// @Robust remove shape_parent_entity from this struct, use the shape's body to figure out // @Robust remove shape_parent_entity from this struct, use the shape's body to figure out
// what the shape's parent entity is // what the shape's parent entity is
@ -227,7 +236,7 @@ typedef struct Entity
// player // player
bool is_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; EntityID currently_inside_of_box;
enum Squad squad_invited_to; // if squad none, then no squad invite 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
@ -245,6 +254,7 @@ typedef struct Entity
// boxes // boxes
bool is_box; bool is_box;
enum Squad owning_squad; // which squad owns this box
enum BoxType box_type; enum BoxType box_type;
bool is_platonic; // can't be destroyed, unaffected by physical forces bool is_platonic; // can't be destroyed, unaffected by physical forces
bool always_visible; // always serialized to the player. @Robust check if not used 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 // only serialized when box_type is solar panel
float sun_amount; // solar panel, between 0 and 1 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! // scanner only stuff!
EntityID currently_scanning; EntityID currently_scanning;
float currently_scanning_progress; // when 1.0, scans it! float currently_scanning_progress; // when 1.0, scans it!
@ -292,6 +305,8 @@ typedef struct GameState
{ {
cpSpace *space; 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! double time; // @Robust separate tick integer not prone to precision issues. Could be very large as is saved to disk!
V2 goldpos; V2 goldpos;

Loading…
Cancel
Save