Compare commits

...

22 Commits

Author SHA1 Message Date
Cameron Murphy Reikes 657c791aad Vcvars before release all 2 years ago
Cameron Murphy Reikes 2d84fbbf9c Add cloaking device 2 years ago
Cameron Murphy Reikes 669798caef Small fixes and remedy autohotkey tooling 2 years ago
Cameron Murphy Reikes 90b11aef4e Fixes/escape close build panel 2 years ago
Cameron Murphy Reikes 0902089108 Rotator block and no gravity radius from sun 2 years ago
Cameron Murphy Reikes bc22342d75 Players can learn from scanners with right click 2 years ago
Cameron Murphy Reikes d1e705dc5c Platonic fixes, platonic can't be destroyed 2 years ago
Cameron Murphy Reikes e8ed1b25b4 Toolbar defaults and fix toolbar when panel close 2 years ago
Cameron Murphy Reikes 98d7dc146a Added blueprint palette and item toolbar 2 years ago
Cameron Murphy Reikes 0ce9ecad9d Fixes and helix fixign 2 years ago
Cameron Murphy Reikes 9519189cd8 Box bitfield unlocks and helix fix warnings 2 years ago
Cameron Murphy Reikes f48a609ac8 Add cosmetic scanner block 2 years ago
Cameron Murphy Reikes 1bd53ab62a Small changes and helix config files 2 years ago
Cameron Murphy Reikes 105abd07b6 Move space station back out, fix flag artifact 2 years ago
Cameron Murphy Reikes e6199fc021 Bug fixes? No more save guy in medbay to disk 2 years ago
root f54fb58537 Linux fixes 2 years ago
Cameron Murphy Reikes 421bb99952 Better pixel art rendering 2 years ago
Cameron Murphy Reikes d8b8f2c925 Pixel edge rendering fix, game design fixing! 2 years ago
Cameron Murphy Reikes b5223ab09b Separate image for solar panel while charging 2 years ago
Cameron Murphy Reikes f0fdd865d7 Some new sprites 2 years ago
Cameron Murphy Reikes 099157e3bc Set player velocity to medbay velocity on exit 2 years ago
Cameron Murphy Reikes 46eb3456ba Remove logs and moar audio send 2 years ago

4
.gitignore vendored

@ -1,4 +1,6 @@
enc_temp_folder/ # visual studio
compile_commands.json
.cache/
enc_temp_folder/
*.spall # profiling
*.bin # world files
debug_world.bin

@ -0,0 +1 @@
thirdparty/

@ -235,6 +235,7 @@
<ClCompile Include="thirdparty\minilzo\minilzo.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="goodpixel.gen.h" />
<ClInclude Include="hueshift.gen.h" />
<ClInclude Include="ipsettings.h" />
<ClInclude Include="queue.h" />

@ -197,6 +197,9 @@
<ClInclude Include="queue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="goodpixel.gen.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="thirdparty\enet\enet_dll.cbp" />

Binary file not shown.

File diff suppressed because it is too large Load Diff

@ -0,0 +1,34 @@
@module goodpixel
@vs vs
in vec4 coord;
out vec2 texUV;
void main() {
gl_Position = vec4(coord.xy, 0.0, 1.0);
texUV = coord.zw;
}
@end
@fs fs
uniform sampler2D iChannel0;
uniform fs_params {
vec4 iColor;
};
in vec2 texUV;
out vec4 fragColor;
vec4 texture2DAA(sampler2D tex, vec2 uv) {
vec2 texsize = vec2(textureSize(tex,0));
vec2 uv_texspace = uv*texsize;
vec2 seam = floor(uv_texspace+.5);
uv_texspace = (uv_texspace-seam)/fwidth(uv_texspace)+seam;
uv_texspace = clamp(uv_texspace, seam-.5, seam+.5);
return texture(tex, uv_texspace/texsize);
}
void main() {
fragColor = texture2DAA(iChannel0, texUV) * iColor;
}
@end
@program program vs fs

@ -29,6 +29,15 @@ vec3 rgb2hsv(vec3 c)
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec4 texture2DAA(sampler2D tex, vec2 uv) {
vec2 texsize = vec2(textureSize(tex,0));
vec2 uv_texspace = uv*texsize;
vec2 seam = floor(uv_texspace+.5);
uv_texspace = (uv_texspace-seam)/fwidth(uv_texspace)+seam;
uv_texspace = clamp(uv_texspace, seam-.5, seam+.5);
return texture(tex, uv_texspace/texsize);
}
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
@ -37,7 +46,7 @@ vec3 hsv2rgb(vec3 c)
}
void main() {
vec4 outColor = texture(iChannel0, texUV);
vec4 outColor = texture2DAA(iChannel0, texUV);
vec3 hsv = rgb2hsv(outColor.rgb);
if(is_colorless > 0)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 B

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 B

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 691 B

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 B

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 363 B

490
main.c

@ -7,6 +7,8 @@
#include <enet/enet.h>
#include <process.h> // starting server thread
#define TOOLBAR_SLOTS 9
#pragma warning(disable : 33010) // this warning is so broken, doesn't
// understand assert()
#include "sokol_app.h"
@ -31,8 +33,10 @@
#include "miniaudio.h"
// shaders
#include "goodpixel.gen.h"
#include "hueshift.gen.h"
static sg_pipeline pip;
static sg_pipeline hueshift_pipeline;
static sg_pipeline goodpixel_pipeline;
static struct GameState gs = {0};
static int my_player_index = -1;
@ -47,6 +51,7 @@ typedef struct KeyPressed
static KeyPressed keypressed[MAX_KEYDOWN] = {0};
static V2 mouse_pos = {0};
static bool fullscreened = false;
static bool picking_new_boxtype = false;
static bool build_pressed = false;
static bool interact_pressed = false;
@ -102,9 +107,22 @@ 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;
static sg_image image_solarpanel_charging;
static sg_image image_scanner_head;
static sg_image image_itemswitch;
static sg_image image_cloaking_panel;
static enum BoxType toolbar[TOOLBAR_SLOTS] = {
BoxHullpiece,
BoxThruster,
BoxBattery,
BoxCockpit,
BoxMedbay,
BoxSolarPanel,
BoxScanner,
};
static int cur_toolbar_slot = 0;
static int cur_editing_rotation = Right;
// audio
static bool muted = false;
@ -130,7 +148,6 @@ static struct BoxInfo
enum BoxType type;
const char *image_path;
sg_image image;
bool needs_tobe_unlocked;
} boxes[] = {
// if added to here will show up in toolbar, is placeable
{
@ -160,7 +177,19 @@ static struct BoxInfo
{
.type = BoxExplosive,
.image_path = "loaded/explosive.png",
.needs_tobe_unlocked = true,
},
{
.type = BoxScanner,
.image_path = "loaded/scanner_base.png",
},
{
.type = BoxGyroscope,
.image_path = "loaded/gyroscope.png",
},
{
.type = BoxCloaking,
.image_path = "loaded/cloaking_device.png",
},
};
#define ENTITIES_ITER(cur) \
@ -168,7 +197,8 @@ static struct BoxInfo
cur++) \
if (cur->exists)
#define ARRLEN(arr) (sizeof(arr) / sizeof(*arr))
// suppress compiler warning about ^^ above used in floating point context
#define ARRLENF(arr) ((float)sizeof(arr) / sizeof(*arr))
static struct SquadMeta
{
enum Squad squad;
@ -208,6 +238,13 @@ struct SquadMeta squad_meta(enum Squad squad)
return (struct SquadMeta){0};
}
static enum BoxType currently_building()
{
assert(cur_toolbar_slot >= 0);
assert(cur_toolbar_slot < TOOLBAR_SLOTS);
return toolbar[cur_toolbar_slot];
}
struct BoxInfo boxinfo(enum BoxType type)
{
for (int i = 0; i < ARRLEN(boxes); i++)
@ -239,8 +276,9 @@ static sg_image load_image(const char *path)
&(sg_image_desc){.width = x,
.height = y,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.min_filter = SG_FILTER_NEAREST,
.mag_filter = SG_FILTER_NEAREST,
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_CLAMP_TO_EDGE,
.data.subimage[0][0] = {
.ptr = image_data,
.size = (size_t)(x * y * desired_channels),
@ -439,14 +477,30 @@ static void init(void)
// shaders
{
// initialize shader
{
{
sgp_pipeline_desc pip_desc = {
.shader = *hueshift_program_shader_desc(sg_query_backend()),
.blend_mode = SGP_BLENDMODE_BLEND,
};
pip = sgp_make_pipeline(&pip_desc);
if (sg_query_pipeline_state(pip) != SG_RESOURCESTATE_VALID)
hueshift_pipeline = sgp_make_pipeline(&pip_desc);
if (sg_query_pipeline_state(hueshift_pipeline) != SG_RESOURCESTATE_VALID)
{
fprintf(stderr, "failed to make custom pipeline\n");
fprintf(stderr, "failed to make hueshift pipeline\n");
exit(-1);
}
}
{
sgp_pipeline_desc pip_desc = {
.shader = *goodpixel_program_shader_desc(sg_query_backend()),
.blend_mode = SGP_BLENDMODE_BLEND,
};
goodpixel_pipeline = sgp_make_pipeline(&pip_desc);
if (sg_query_pipeline_state(goodpixel_pipeline) != SG_RESOURCESTATE_VALID)
{
fprintf(stderr, "failed to make goodpixel pipeline\n");
exit(-1);
}
}
@ -476,6 +530,10 @@ static void init(void)
image_squad_invite = load_image("loaded/squad_invite.png");
image_check = load_image("loaded/check.png");
image_no = load_image("loaded/no.png");
image_solarpanel_charging = load_image("loaded/solarpanel_charging.png");
image_scanner_head = load_image("loaded/scanner_head.png");
image_itemswitch = load_image("loaded/itemswitch.png");
image_cloaking_panel = load_image("loaded/cloaking_panel.png");
}
// socket initialization
@ -526,23 +584,30 @@ static void init(void)
}
}
}
}
}
#define transform_scope DeferLoop(sgp_push_transform(), sgp_pop_transform())
static void set_pipeline_and_pull_color(sg_pipeline pip)
{
sgp_set_pipeline(pip);
sgp_set_uniform(&sgp_query_state()->color, sizeof(sgp_query_state()->color));
}
#define pipeline_scope(pipeline) DeferLoop(set_pipeline_and_pull_color(pipeline), sgp_reset_pipeline())
static void draw_color_rect_centered(V2 center, float size)
{
float halfbox = size / 2.0f;
sgp_draw_filled_rect(center.x - halfbox, center.y - halfbox, size, size);
}
static void draw_texture_rectangle_centered(V2 center, V2 width_height)
{
V2 halfsize = V2scale(width_height, 0.5f);
sgp_draw_textured_rect(center.x - halfsize.x, center.y - halfsize.y,
width_height.x, width_height.y);
sgp_draw_textured_rect(center.x - halfsize.x, center.y - halfsize.y, width_height.x, width_height.y);
}
static void draw_texture_centered(V2 center, float size)
{
draw_texture_rectangle_centered(center, (V2){size, size});
@ -569,19 +634,21 @@ static void draw_circle(V2 point, float radius)
bool can_build(int i)
{
bool allow_building = true;
if (boxinfo((enum BoxType)i).needs_tobe_unlocked)
{
enum BoxType box_type = (enum BoxType)i;
allow_building = false;
if (myplayer() != NULL)
allow_building = myplayer()->unlocked_bombs;
}
allow_building = box_unlocked(myplayer(), box_type);
return allow_building;
}
void attempt_to_build(int i)
static void setup_hueshift(enum Squad squad)
{
if (can_build(i))
cur_editing_boxtype = i;
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)
@ -616,6 +683,108 @@ static void ui(bool draw, float dt, float width, float height)
if (draw)
sgp_push_transform();
// draw pick new box type menu
static float pick_opacity = 0.0f;
{
if (keypressed[SAPP_KEYCODE_ESCAPE].pressed)
picking_new_boxtype = false;
AABB pick_modal = (AABB){
.x = width * 0.25f,
.y = height * 0.25f,
.width = width * 0.5f,
.height = height * 0.5f,
};
pick_opacity = lerp(pick_opacity, picking_new_boxtype ? 1.0f : 0.0f, dt * 7.0f);
if (picking_new_boxtype)
{
if (build_pressed)
{
if (has_point(pick_modal, mouse_pos))
{
}
else
{
build_pressed = false;
picking_new_boxtype = false;
}
}
}
static float item_scaling[ARRLEN(boxes)] = {1.0f};
{
float alpha = pick_opacity * 0.8f;
if (draw)
{
sgp_set_color(0.4f, 0.4f, 0.4f, alpha);
sgp_draw_filled_rect(pick_modal.x, pick_modal.y, pick_modal.width, pick_modal.height);
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f * pick_opacity);
}
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_height = cell_width;
float padding = 0.2f * cell_width;
int cur_row = 0;
int cur_column = 0;
for (int i = 0; i < ARRLEN(boxes); i++)
{
if (cur_column >= boxes_per_row)
{
cur_column = 0;
cur_row++;
}
float item_width = cell_width - padding * 2.0f;
float item_height = cell_height - padding * 2.0f;
item_width *= item_scaling[i];
item_height *= item_scaling[i];
float cell_y = pick_modal.y + (float)cur_row * cell_height;
float cell_x = pick_modal.x + (float)cur_column * cell_width;
float item_x = cell_x + (cell_width - item_width) / 2.0f;
float item_y = cell_y + (cell_height - item_height) / 2.0f;
bool item_being_hovered = has_point((AABB){
.x = item_x,
.y = item_y,
.width = item_width,
.height = item_height,
},
mouse_pos);
item_scaling[i] = lerp(item_scaling[i], item_being_hovered ? 1.3f : 1.0f, dt * 4.0f);
struct BoxInfo info = boxes[i];
if (item_being_hovered && build_pressed && picking_new_boxtype)
{
toolbar[cur_toolbar_slot] = info.type;
picking_new_boxtype = false;
build_pressed = false;
}
if (draw)
{
if (can_build(info.type))
{
sgp_set_image(0, info.image);
}
else
{
sgp_set_image(0, image_mystery);
}
transform_scope
{
sgp_scale_at(1.0f, -1.0f, item_x + item_width / 2.0f, item_y + item_height / 2.0f);
pipeline_scope(goodpixel_pipeline)
sgp_draw_textured_rect(item_x, item_y, item_width, item_height);
sgp_reset_image(0);
}
}
cur_column++;
}
}
}
// draw squad invite
static float invite_y = -200.0f;
static enum Squad draw_as_squad = SquadNone;
@ -643,9 +812,15 @@ static void ui(bool draw, float dt, float width, float height)
no_size = lerp(no_size, no_hovered ? 75.0f : 50.0f, dt * 9.0f);
if (invited && build_pressed && yes_hovered)
{
accept_invite = true;
build_pressed = false;
}
if (invited && build_pressed && no_hovered)
{
reject_invite = true;
build_pressed = false;
}
if (draw)
{
@ -656,19 +831,15 @@ static void ui(bool draw, float dt, float width, float height)
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));
pipeline_scope(hueshift_pipeline)
{
setup_hueshift(draw_as_squad);
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
@ -677,7 +848,10 @@ static void ui(bool draw, float dt, float width, float height)
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);
pipeline_scope(goodpixel_pipeline)
{
draw_texture_centered((V2){yes_x, buttons_y}, yes_size);
}
sgp_reset_image(0);
}
@ -687,7 +861,10 @@ static void ui(bool draw, float dt, float width, float height)
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);
pipeline_scope(goodpixel_pipeline)
{
draw_texture_centered((V2){no_x, buttons_y}, no_size);
}
sgp_reset_image(0);
}
}
@ -725,19 +902,16 @@ static void ui(bool draw, float dt, float width, float height)
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));
pipeline_scope(hueshift_pipeline)
{
setup_hueshift(myplayer()->squad);
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();
}
}
}
}
@ -852,12 +1026,9 @@ static void ui(bool draw, float dt, float width, float height)
sgp_set_image(0, image_flag_taken);
}
sgp_set_pipeline(pip);
struct SquadMeta meta = squad_meta(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));
pipeline_scope(hueshift_pipeline)
{
setup_hueshift(this_squad);
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,
@ -865,7 +1036,7 @@ static void ui(bool draw, float dt, float width, float height)
draw_texture_centered(flag_pos[i], size);
sgp_reset_image(0);
sgp_reset_pipeline();
}
}
}
}
@ -934,7 +1105,7 @@ static void ui(bool draw, float dt, float width, float height)
(float)sg_query_image_info(image_itemframe).width * 2.0f;
float itemframe_height =
(float)sg_query_image_info(image_itemframe).height * 2.0f;
float total_width = itemframe_width * ARRLEN(boxes);
float total_width = itemframe_width * (float)TOOLBAR_SLOTS;
float item_width = itemframe_width * 0.75f;
float item_height = itemframe_height * 0.75f;
float item_offset_x = (itemframe_width - item_width) / 2.0f;
@ -942,8 +1113,9 @@ static void ui(bool draw, float dt, float width, float height)
float x = width / 2.0f - total_width / 2.0f;
float y = height - itemframe_height * 1.5f;
for (int i = 0; i < ARRLEN(boxes); i++)
for (int i = 0; i < TOOLBAR_SLOTS; i++)
{
// mouse over the item frame box
if (has_point(
(AABB){
.x = x,
@ -955,14 +1127,38 @@ static void ui(bool draw, float dt, float width, float height)
build_pressed)
{
// "handle" mouse pressed
attempt_to_build(i);
cur_toolbar_slot = i;
build_pressed = false;
}
// mouse over the item switch button
bool switch_hovered = false;
if (has_point(
(AABB){
.x = x,
.y = y - 20.0f,
.width = itemframe_width,
.height = itemframe_height * 0.2f,
},
mouse_pos))
{
switch_hovered = true;
}
if (switch_hovered && build_pressed)
{
picking_new_boxtype = true;
build_pressed = false;
}
if (draw)
{
sgp_set_color(1.0f, 1.0f, 1.0f, cur_opacity);
if (cur_editing_boxtype == i)
bool is_current = cur_toolbar_slot == i;
static float switch_scaling = 1.0f;
switch_scaling = lerp(switch_scaling, switch_hovered ? 1.8f : 1.2f, dt * 3.0f);
if (is_current)
{
sgp_set_image(0, image_itemframe_selected);
}
@ -970,27 +1166,42 @@ static void ui(bool draw, float dt, float width, float height)
{
sgp_set_image(0, image_itemframe);
}
pipeline_scope(goodpixel_pipeline)
sgp_draw_textured_rect(x, y, itemframe_width, itemframe_height);
struct BoxInfo info = boxinfo((enum BoxType)i);
if (can_build(i))
{
sgp_set_image(0, info.image);
}
else
{
sgp_set_image(0, image_mystery);
}
sgp_reset_image(0);
transform_scope
{
float item_x = x + item_offset_x;
float item_y = y + item_offset_y;
sgp_scale_at(1.0f, -1.0f, item_x + item_width / 2.0f,
item_y + item_height / 2.0f);
// sgp_scale(1.0f, -1.0f);
pipeline_scope(goodpixel_pipeline)
{
if (toolbar[i] != BoxInvalid)
{
struct BoxInfo info = boxinfo(toolbar[i]);
if (can_build(info.type))
sgp_set_image(0, info.image);
else
sgp_set_image(0, image_mystery);
sgp_draw_textured_rect(item_x, item_y, item_width, item_height);
sgp_reset_image(0);
}
if (is_current)
{
sgp_set_image(0, image_itemswitch);
float switch_item_width = item_width * switch_scaling;
float switch_item_height = item_height * switch_scaling;
item_x -= (switch_item_width - item_width) / 2.0f;
item_y -= (switch_item_height - item_height) / 2.0f;
sgp_draw_textured_rect(item_x, item_y + 20.0f, switch_item_width, switch_item_height);
sgp_reset_image(0);
}
}
}
}
x += itemframe_width;
}
}
@ -1076,6 +1287,11 @@ static void frame(void)
{
switch (event.type)
{
case ENET_EVENT_TYPE_NONE:
{
Log("Wtf none event type?\n");
break;
}
case ENET_EVENT_TYPE_CONNECT:
{
Log("New client from host %x\n", event.peer->address.host);
@ -1084,7 +1300,7 @@ static void frame(void)
case ENET_EVENT_TYPE_RECEIVE:
{
char *decompressed = malloc(
unsigned char *decompressed = malloc(
sizeof *decompressed * MAX_SERVER_TO_CLIENT); // @Robust no malloc
size_t decompressed_max_len = MAX_SERVER_TO_CLIENT;
assert(LZO1X_MEM_DECOMPRESS == 0);
@ -1246,6 +1462,7 @@ static void frame(void)
if (V2length(input) > 0.0)
input = V2normalize(input);
cur_input_frame.movement = input;
cur_input_frame.rotation = (float)keydown[SAPP_KEYCODE_E] - (float)keydown[SAPP_KEYCODE_Q];
if (interact_pressed)
cur_input_frame.seat_action = interact_pressed;
@ -1273,20 +1490,16 @@ static void frame(void)
reject_invite = false;
}
if (build_pressed && cur_editing_boxtype != -1)
if (build_pressed && currently_building() != BoxInvalid)
{
cur_input_frame.dobuild = build_pressed;
cur_input_frame.build_type = cur_editing_boxtype;
cur_input_frame.build_type = currently_building();
cur_input_frame.build_rotation = cur_editing_rotation;
}
// "commit" the input. each input must be on a successive tick.
if (tick(&gs) > last_input_committed_tick)
{
if (cur_input_frame.take_over_squad != -1)
{
Log("Sending take over squad at tick %zu\n", tick(&gs));
}
cur_input_frame.tick = tick(&gs);
last_input_committed_tick = tick(&gs);
@ -1326,12 +1539,12 @@ static void frame(void)
.mic_data = &packets_to_send,
.input_data = &input_queue,
};
char serialized[MAX_CLIENT_TO_SERVER] = {0};
unsigned char serialized[MAX_CLIENT_TO_SERVER] = {0};
size_t out_len = 0;
if (client_to_server_serialize(&gs, &to_send, serialized, &out_len,
MAX_CLIENT_TO_SERVER))
{
char compressed[MAX_CLIENT_TO_SERVER] = {0};
unsigned char compressed[MAX_CLIENT_TO_SERVER] = {0};
char lzo_working_mem[LZO1X_1_MEM_COMPRESS] = {0};
size_t compressed_len = 0;
@ -1358,8 +1571,8 @@ static void frame(void)
global_hand_pos =
get_global_hand_pos(world_mouse_pos, &hand_at_arms_length);
Entity *placing_grid = closest_to_point_in_radius(
&gs, global_hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP);
Entity *placing_grid = box_grid(closest_box_to_point_in_radius(
&gs, global_hand_pos, BUILD_BOX_SNAP_DIST_TO_SHIP, NULL));
if (placing_grid == NULL)
{
build_preview = (struct BuildPreviewInfo){
@ -1410,8 +1623,8 @@ static void frame(void)
(float)sg_query_image_info(image_stars).width;
const float stars_width = 35.0f;
float stars_height = stars_width * stars_height_over_width;
sgp_draw_textured_rect(-stars_width / 2.0f, -stars_height / 2.0f,
stars_width, stars_height);
pipeline_scope(goodpixel_pipeline)
sgp_draw_textured_rect(-stars_width / 2.0f, -stars_height / 2.0f, stars_width, stars_height);
// sgp_draw_textured_rect(0, 0, stars_width, stars_height);
sgp_reset_image(0);
}
@ -1427,8 +1640,8 @@ static void frame(void)
(float)sg_query_image_info(image_stars).width;
const float stars_width = 35.0f;
float stars_height = stars_width * stars_height_over_width;
sgp_draw_textured_rect(-stars_width / 2.0f, -stars_height / 2.0f,
stars_width, stars_height);
pipeline_scope(goodpixel_pipeline)
sgp_draw_textured_rect(-stars_width / 2.0f, -stars_height / 2.0f, stars_width, stars_height);
// sgp_draw_textured_rect(0, 0, stars_width, stars_height);
sgp_reset_image(0);
}
@ -1483,17 +1696,18 @@ static void frame(void)
}
// building preview
if (cur_editing_boxtype != -1)
if (currently_building() != BoxInvalid && can_build(currently_building()))
{
sgp_set_color(0.5f, 0.5f, 0.5f,
(sinf((float)time * 9.0f) + 1.0f) / 3.0f + 0.2f);
transform_scope
{
sgp_set_image(0, boxinfo(cur_editing_boxtype).image);
sgp_set_image(0, boxinfo(currently_building()).image);
sgp_rotate_at(build_preview.grid_rotation +
rotangle(cur_editing_rotation),
global_hand_pos.x, global_hand_pos.y);
pipeline_scope(goodpixel_pipeline)
draw_texture_centered(global_hand_pos, BOX_SIZE);
// drawbox(hand_pos, build_preview.grid_rotation, 0.0f,
// cur_editing_boxtype, cur_editing_rotation);
@ -1511,11 +1725,6 @@ static void frame(void)
Entity *g = e;
BOXES_ITER(&gs, b, g)
{
if (b->is_explosion_unlock)
{
set_color(colhexcode(0xfcba03));
draw_circle(entity_pos(b), GOLD_UNLOCK_RADIUS);
}
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
if (b->box_type == BoxBattery)
{
@ -1539,7 +1748,10 @@ static void frame(void)
sgp_set_image(0, image_thrusterburn);
float scaling = 0.95f + lerp(0.0f, 0.3f, b->thrust);
sgp_scale_at(scaling, 1.0f, entity_pos(b).x, entity_pos(b).y);
pipeline_scope(goodpixel_pipeline)
{
draw_texture_centered(entity_pos(b), BOX_SIZE);
}
sgp_reset_image(0);
}
}
@ -1554,24 +1766,93 @@ static void frame(void)
if (get_entity(&gs, b->player_who_is_inside_of_me) != NULL)
img = image_medbay_used;
}
if (b->box_type == BoxSolarPanel)
{
sgp_set_image(0, image_solarpanel_charging);
sgp_set_color(1.0f, 1.0f, 1.0f, b->sun_amount);
pipeline_scope(goodpixel_pipeline)
draw_texture_centered(entity_pos(b), BOX_SIZE);
sgp_reset_image(0);
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f - b->sun_amount);
/* Color to_set = colhexcode(0xeb9834);
to_set.a = b->sun_amount * 0.5f;
set_color(to_set);
draw_color_rect_centered(entity_pos(b), BOX_SIZE);
*/
}
sgp_set_image(0, img);
if (b->indestructible)
{
sgp_set_color(0.2f, 0.2f, 0.2f, 1.0f);
}
else if (b->is_platonic)
{
set_color(GOLD);
}
// 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);
}
}
else
{
pipeline_scope(goodpixel_pipeline)
draw_texture_centered(entity_pos(b), BOX_SIZE);
}
sgp_reset_image(0);
if (b->box_type == BoxSolarPanel)
if (b->box_type == BoxScanner)
{
Color to_set = colhexcode(0xeb9834);
to_set.a = b->sun_amount * 0.5f;
set_color(to_set);
draw_color_rect_centered(entity_pos(b), BOX_SIZE);
sgp_set_image(0, image_scanner_head);
transform_scope
{
pipeline_scope(goodpixel_pipeline)
{
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);
set_color(WHITE);
}
if (b->box_type == BoxScanner)
{
set_color(BLUE);
draw_circle(entity_pos(b), SCANNER_RADIUS);
set_color(WHITE);
}
sgp_set_color(0.5f, 0.1f, 0.1f, b->damage);
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
if (b->box_type == BoxScanner)
{
if (b->platonic_detection_strength > 0.0)
{
set_color(colhexcode(0xf2d75c));
V2 to = V2add(entity_pos(b), V2scale(b->platonic_nearest_direction, b->platonic_detection_strength));
dbg_rect(to);
dbg_rect(entity_pos(b));
sgp_draw_line(entity_pos(b).x, entity_pos(b).y, to.x, to.y);
}
}
}
}
@ -1585,17 +1866,14 @@ static void frame(void)
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_pipeline(pip);
struct SquadMeta meta = squad_meta(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));
pipeline_scope(hueshift_pipeline)
{
setup_hueshift(e->presenting_squad);
sgp_set_image(0, image_player);
draw_texture_rectangle_centered(
entity_pos(e), V2scale(PLAYER_SIZE, player_scaling));
sgp_reset_image(0);
sgp_reset_pipeline();
}
}
}
if (e->is_explosion)
@ -1621,9 +1899,14 @@ static void frame(void)
draw_texture_centered((V2){0}, SUN_RADIUS * 2.0f);
sgp_reset_image(0);
// can draw at 0,0 because everything relative to sun now!
// sun DEATH RADIUS
set_color(RED);
draw_circle((V2){0}, INSTANT_DEATH_DISTANCE_FROM_SUN);
set_color(BLUE);
draw_circle((V2){0}, SUN_NO_MORE_ELECTRICITY_OR_GRAVITY);
}
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
@ -1654,7 +1937,7 @@ static void frame(void)
void cleanup(void)
{
sg_destroy_pipeline(pip);
sg_destroy_pipeline(hueshift_pipeline);
ma_mutex_lock(&server_info.info_mutex);
server_info.should_quit = true;
@ -1706,10 +1989,18 @@ void event(const sapp_event *e)
fullscreened = false;
}
int key_num = e->key_code - SAPP_KEYCODE_0;
int target_box = key_num - 1;
if (target_box < BoxLast && target_box >= 0)
int target_slot = key_num - 1;
if (target_slot <= TOOLBAR_SLOTS && target_slot >= 0)
{
if (target_slot == cur_toolbar_slot)
{
attempt_to_build(target_box);
picking_new_boxtype = !picking_new_boxtype;
}
else
{
picking_new_boxtype = false;
}
cur_toolbar_slot = target_slot;
}
if (!mouse_frozen)
@ -1759,6 +2050,9 @@ void event(const sapp_event *e)
Log("Funval %f\n", funval);
}
break;
default:
{
}
}
}

@ -0,0 +1,10 @@
main.c
gamestate.c
server.c
-Ithirdparty
-Ithirdparty/minilzo
-Ithirdparty/enet/include
-Ithirdparty/Chipmunk2D/include/chipmunk
-Ithirdparty/Chipmunk2D/include
-Ithirdparty/opus/include
-Ithirdparty/opus/src

@ -3,8 +3,8 @@
#include <stdbool.h>
#ifndef QUEUE_ASSERT
void __assert(bool cond, const char *file, int line, const char *cond_string);
#define QUEUE_ASSERT(condition) __assert(condition, __FILE__, __LINE__, #condition)
void __flight_assert(bool cond, const char *file, int line, const char *cond_string);
#define QUEUE_ASSERT(condition) __flight_assert(condition, __FILE__, __LINE__, #condition)
#endif
typedef struct QueueElementHeader
@ -53,7 +53,7 @@ void queue_clear(Queue *q)
q->next = NULL;
}
#define QUEUE_ELEM_ITER(cur) for (QueueElementHeader *cur = (QueueElementHeader *)q->data; (char *)cur < q->data + q->data_length; (char *)cur += (sizeof(QueueElementHeader) + q->element_size))
#define QUEUE_ELEM_ITER(cur) for (QueueElementHeader *cur = (QueueElementHeader *)q->data; (char *)cur < q->data + q->data_length; cur = (QueueElementHeader*)((char*)cur + (sizeof(QueueElementHeader) + q->element_size)))
// you push an element, get the return value, cast it to your type, and fill it with data. It's that easy!
// if it's null the queue is out of space

@ -1,3 +1,5 @@
vcvars
git push
call build_release.bat
call update_server.bat
tar.exe -a -c -f releases\flight-nonumber.zip flight_release.exe loaded

@ -76,8 +76,11 @@ void server(void *info_raw)
size_t entities_size = (sizeof(Entity) * MAX_ENTITIES);
Entity *entity_data = malloc(entities_size);
initialize(&gs, entity_data, entities_size);
gs.server_side_computing = true;
Log("Allocated %zu bytes for entities\n", entities_size);
create_initial_world(&gs);
// inputs
Queue player_input_queues[MAX_PLAYERS] = {0};
size_t input_queue_data_size = QUEUE_SIZE_FOR_ELEMENTS(sizeof(InputFrame), INPUT_QUEUE_MAX);
@ -92,19 +95,13 @@ void server(void *info_raw)
OpusEncoder *player_encoders[MAX_PLAYERS] = {0};
OpusDecoder *player_decoders[MAX_PLAYERS] = {0};
// for (int i = 0; i < MAX_PLAYERS; i++)
//{
// int error = 0;
// 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");
// }
#ifdef DEBUG_WORLD
world_save_name = NULL;
#endif
if (world_save_name != NULL)
{
size_t read_game_data_buffer_size = entities_size;
char *read_game_data = malloc(read_game_data_buffer_size);
unsigned char *read_game_data = malloc(read_game_data_buffer_size);
FILE *file = NULL;
fopen_s(&file, (const char *)world_save_name, "rb");
@ -201,7 +198,7 @@ void server(void *info_raw)
uint64_t last_sent_gamestate_time = stm_now();
float audio_time_to_send = 0.0f;
float total_time = 0.0f;
char *world_save_buffer = malloc(entities_size);
unsigned char *world_save_buffer = malloc(entities_size);
while (true)
{
ma_mutex_lock(&info->info_mutex);
@ -251,6 +248,7 @@ void server(void *info_raw)
event.peer->data = (void *)player_slot;
gs.players[player_slot] = (struct Player){0};
gs.players[player_slot].connected = true;
create_player(&gs.players[player_slot]);
int error;
player_encoders[player_slot] = opus_encoder_create(VOIP_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &error);
@ -259,11 +257,6 @@ void server(void *info_raw)
player_decoders[player_slot] = opus_decoder_create(VOIP_SAMPLE_RATE, 1, &error);
if (error != OPUS_OK)
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;
@ -292,7 +285,7 @@ void server(void *info_raw)
queue_clear(&player_input_queues[player_slot]);
struct ClientToServer received = {.mic_data = buffer_to_fill, .input_data = &player_input_queues[player_slot]};
char decompressed[MAX_CLIENT_TO_SERVER] = {0};
unsigned char decompressed[MAX_CLIENT_TO_SERVER] = {0};
size_t decompressed_max_len = MAX_CLIENT_TO_SERVER;
assert(LZO1X_MEM_DECOMPRESS == 0);
@ -423,7 +416,7 @@ void server(void *info_raw)
last_sent_audio_time = stm_now();
int num_audio_packets = (int)floor(1.0f / (VOIP_TIME_PER_PACKET / audio_time_to_send));
#define MAX_AUDIO_PACKETS_TO_SEND 6
#define MAX_AUDIO_PACKETS_TO_SEND 12
if (num_audio_packets > MAX_AUDIO_PACKETS_TO_SEND)
{
Log("Wants %d, this is too many packets. Greater than the maximum %d\n", num_audio_packets, MAX_AUDIO_PACKETS_TO_SEND);
@ -457,8 +450,8 @@ void server(void *info_raw)
if (this_player_entity == NULL)
continue;
// @Speed don't recreate the packet for every peer, gets expensive copying gamestate over and over again
char *bytes_buffer = malloc(sizeof *bytes_buffer * MAX_SERVER_TO_CLIENT);
char *compressed_buffer = malloc(sizeof *compressed_buffer * MAX_SERVER_TO_CLIENT);
unsigned char *bytes_buffer = malloc(sizeof *bytes_buffer * MAX_SERVER_TO_CLIENT);
unsigned char *compressed_buffer = malloc(sizeof *compressed_buffer * MAX_SERVER_TO_CLIENT);
// mix audio to be sent
VOIP_QUEUE_DECL(buffer_to_play, buffer_to_play_data);

@ -1,6 +1,8 @@
@echo off
WHERE sokol-shdc.exe
IF %ERRORLEVEL% NEQ 0 ECHO ERROR download sokol-shdc from https://github.com/floooh/sokol-tools-bin/blob/master/bin/win32/sokol-shdc.exe and put it in this folder
@REM example of how to compile shaders: sokol-shdc.exe --input triangle.glsl --output triangle.gen.h --slang glsl330:hlsl5:metal_macos
sokol-shdc.exe --format sokol --input hueshift.glsl --output hueshift.gen.h --slang glsl330:hlsl5:metal_macos
sokol-shdc.exe --format sokol --input goodpixel.glsl --output goodpixel.gen.h --slang glsl330:hlsl5:metal_macos

@ -8,24 +8,23 @@ SetWorkingDir, %A_ScriptDir%
^Esc::return
^b::
WinKill, "Flight Not Hosting"
Sleep, 20
WinKill, Flight Hosting
WinKill, Flight Not Hosting
WinActivate, flightbuild
If WinActive("flightbuild")
{
Send, cd C:\Users\Cameron\Documents\flight{Enter} build_debug.bat && flight_debug.exe{Enter}
Send, {Enter}
Send, remedybg continue-execution && sleep 0.1 && remedybg.exe stop-debugging && msbuild && remedybg.exe start-debugging {Enter}
}
return
^+b::
WinKill, Flight
Sleep, 20
WinKill, Flight
Sleep, 20
WinKill, Flight
WinKill, Flight Hosting
WinKill, Flight Not Hosting
WinActivate, 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

@ -2,36 +2,48 @@
#include "ipsettings.h"
#define MAX_BOX_TYPES 64
#define MAX_PLAYERS 16
#define MAX_ENTITIES 1024 * 25
#define BOX_SIZE 0.25f
#define PLAYER_SIZE ((V2){.x = BOX_SIZE, .y = BOX_SIZE})
#define PLAYER_MASS 0.5f
#define PLAYER_JETPACK_FORCE 1.5f
#define PLAYER_JETPACK_TORQUE 0.05f
// #define PLAYER_JETPACK_FORCE 20.0f
#define PLAYER_JETPACK_SPICE_PER_SECOND 0.1f
// 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.2f
#define SCANNER_ENERGY_USE 0.05f
#define MAX_HAND_REACH 1.0f
#define SCANNER_SCAN_RATE 0.5f
#define SCANNER_RADIUS 1.0f
#define GOLD_COLLECT_RADIUS 0.3f
#define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2f
#define BOX_MASS 1.0f
#define COLLISION_DAMAGE_SCALING 0.15f
#define THRUSTER_FORCE 12.0f
#define THRUSTER_ENERGY_USED_PER_SECOND 0.005f
#define GYROSCOPE_ENERGY_USED_PER_SECOND 0.005f
#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 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 SUN_RADIUS 10.0f
#define SUN_NO_MORE_ELECTRICITY_OR_GRAVITY 200.0f
#define INSTANT_DEATH_DISTANCE_FROM_SUN 2000.0f
#define SUN_POS ((V2){50.0f, 0.0f})
#ifdef NO_GRAVITY
#define SUN_GRAVITY_STRENGTH 0.1f
#define SUN_GRAVITY_STRENGTH 0.0f
#else
#define SUN_GRAVITY_STRENGTH (9.0e2f)
#define SUN_GRAVITY_STRENGTH (11.0e2f)
#endif
#define SOLAR_ENERGY_PER_SECOND 0.02f
#define SOLAR_ENERGY_PER_SECOND 0.09f
#define DAMAGE_TO_PLAYER_PER_BLOCK 0.1f
#define BATTERY_CAPACITY DAMAGE_TO_PLAYER_PER_BLOCK * 0.7f
#define PLAYER_ENERGY_RECHARGE_PER_SECOND 0.1f
#define BATTERY_CAPACITY 1.5f
#define PLAYER_ENERGY_RECHARGE_PER_SECOND 0.2f
#define EXPLOSION_TIME 0.5f
#define EXPLOSION_PUSH_STRENGTH 5.0f
#define EXPLOSION_DAMAGE_PER_SEC 10.0f
@ -44,10 +56,12 @@
#define VOIP_PACKET_BUFFER_SIZE 15 // audio. Must be bigger than 2
#define VOIP_EXPECTED_FRAME_COUNT 480
#define VOIP_SAMPLE_RATE 48000
#define VOIP_TIME_PER_PACKET (1.0f / ((float)(VOIP_SAMPLE_RATE / VOIP_EXPECTED_FRAME_COUNT))) // in seconds
// in seconds
#define VOIP_TIME_PER_PACKET (1.0f / ((float)((float)VOIP_SAMPLE_RATE / VOIP_EXPECTED_FRAME_COUNT)))
#define VOIP_PACKET_MAX_SIZE 4000
#define VOIP_DISTANCE_WHEN_CANT_HEAR (VISION_RADIUS * 0.8f)
// multiplayer
#define MAX_REPREDICTION_TIME (TIMESTEP * 50.0f)
#define TIME_BETWEEN_SEND_GAMESTATE (1.0f / 20.0f)
#define TIME_BETWEEN_INPUT_PACKETS (1.0f / 20.0f)
@ -56,6 +70,9 @@
#define LOCAL_INPUT_QUEUE_MAX 90 // please god let you not have more than 90 frames of game latency
#define INPUT_QUEUE_MAX 15
// fucks up serialization if you change this, fix it if you do that!
#define BOX_UNLOCKS_TYPE uint64_t
// cross platform threadlocal variables
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#define THREADLOCAL __declspec(thread)
@ -75,7 +92,7 @@
// defined in gamestate.c. Janky
#ifndef assert
#define assert(condition) __assert(condition, __FILE__, __LINE__, #condition)
#define assert(condition) __flight_assert(condition, __FILE__, __LINE__, #condition)
#endif
// including headers from headers bad
@ -126,6 +143,7 @@ typedef sgp_point P2;
enum BoxType
{
BoxInvalid, // zero initialized box is invalid!
BoxHullpiece,
BoxThruster,
BoxBattery,
@ -133,6 +151,9 @@ enum BoxType
BoxMedbay,
BoxSolarPanel,
BoxExplosive,
BoxScanner,
BoxGyroscope,
BoxCloaking,
BoxLast,
};
@ -173,6 +194,7 @@ typedef struct InputFrame
{
uint64_t tick;
V2 movement;
float rotation;
int take_over_squad; // -1 means not taking over any squad
bool accept_cur_squad_invite;
@ -200,6 +222,12 @@ typedef struct Entity
cpBody *body; // used by grid, player, and box
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
// @Robust remove shape_parent_entity from this struct, use the shape's body to figure out
// what the shape's parent entity is
@ -208,7 +236,7 @@ typedef struct Entity
// 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;
enum Squad squad_invited_to; // if squad none, then no squad invite
float goldness; // how much the player is a winner
@ -226,24 +254,47 @@ typedef struct Entity
// boxes
bool is_box;
bool always_visible; // always serialized to the player
enum Squad owning_squad; // which squad owns this box
enum BoxType box_type;
bool is_explosion_unlock;
EntityID next_box;
bool is_platonic; // can't be destroyed, unaffected by physical forces
bool always_visible; // always serialized to the player. @Robust check if not used
EntityID next_box; // for the grid!
EntityID prev_box; // doubly linked so can remove in middle of chain
enum CompassRotation compass_rotation;
bool indestructible;
// used by medbay and cockpit
EntityID player_who_is_inside_of_me;
// only serialized when box_type is thruster or gyroscope, used for both. Thrust
// can mean rotation thrust!
float wanted_thrust; // the thrust command applied to the thruster
float thrust; // the actual thrust it can provide based on energy sources in the grid
float energy_used; // battery
// only serialized when box_type is battery
float energy_used; // battery, between 0 battery capacity. You have to look through code to figure out what that is! haha sucker!
// only serialized when box_type is solar panel
float sun_amount; // solar panel, between 0 and 1
EntityID player_who_is_inside_of_me;
// cloaking only
float cloaking_power; // 0.0 if unable to be used because no power, 1.0 if fully cloaking!
// scanner only stuff!
EntityID currently_scanning;
float currently_scanning_progress; // when 1.0, scans it!
BOX_UNLOCKS_TYPE blueprints_learned; // @Robust make this same type as blueprints
float scanner_head_rotate_speed; // not serialized, cosmetic
float scanner_head_rotate;
V2 platonic_nearest_direction; // normalized
float platonic_detection_strength; // from zero to one
} Entity;
typedef struct Player
{
bool connected;
bool unlocked_bombs;
BOX_UNLOCKS_TYPE box_unlocks; // each bit is that box's unlock
enum Squad squad;
EntityID entity;
EntityID last_used_medbay;
@ -254,13 +305,17 @@ typedef struct GameState
{
cpSpace *space;
double time; // @Robust separate tick integer not prone to precision issues
// @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!
V2 goldpos;
Player players[MAX_PLAYERS];
EntityID cur_spacestation;
V2 platonic_positions[MAX_BOX_TYPES]; // don't want to search over every entity to get the nearest platonic box!
bool server_side_computing; // some things only the server should know and calculate, like platonic locations
// Entity arena
// ent:ity pointers can't move around because of how the physics engine handles user data.
@ -306,7 +361,7 @@ static float rotangle(enum CompassRotation rot)
typedef struct OpusPacket
{
opus_int32 length;
char data[VOIP_PACKET_MAX_SIZE];
unsigned char data[VOIP_PACKET_MAX_SIZE];
} OpusPacket;
typedef struct ServerToClient
@ -327,21 +382,23 @@ typedef struct ClientToServer
// server
void server(void *info); // data parameter required from thread api...
void create_player(Player *player);
bool box_unlocked(Player *player, enum BoxType box);
// gamestate
EntityID create_spacestation(GameState *gs);
void create_initial_world(GameState *gs);
void initialize(struct GameState *gs, void *entity_arena, size_t entity_arena_size);
void destroy(struct GameState *gs);
void process_fixed_timestep(GameState *gs);
void process(struct GameState *gs, float dt); // does in place
Entity *closest_to_point_in_radius(struct GameState *gs, V2 point, float radius);
Entity *closest_box_to_point_in_radius(struct GameState *gs, V2 point, float radius, bool(*filter_func)(Entity*));
uint64_t tick(struct GameState *gs);
// all of these return if successful or not
bool server_to_client_serialize(struct ServerToClient *msg, char *bytes, size_t *out_len, size_t max_len, Entity *for_this_player, bool to_disk);
bool server_to_client_deserialize(struct ServerToClient *msg, char *bytes, size_t max_len, bool from_disk);
bool client_to_server_deserialize(GameState *gs, struct ClientToServer *msg, char *bytes, size_t max_len);
bool client_to_server_serialize(GameState *gs, struct ClientToServer *msg, char *bytes, size_t *out_len, size_t max_len);
bool server_to_client_serialize(struct ServerToClient *msg, unsigned char*bytes, size_t *out_len, size_t max_len, Entity *for_this_player, bool to_disk);
bool server_to_client_deserialize(struct ServerToClient *msg, unsigned char*bytes, size_t max_len, bool from_disk);
bool client_to_server_deserialize(GameState *gs, struct ClientToServer *msg, unsigned char*bytes, size_t max_len);
bool client_to_server_serialize(GameState *gs, struct ClientToServer *msg, unsigned char*bytes, size_t *out_len, size_t max_len);
// entities
Entity *get_entity(struct GameState *gs, EntityID id);
@ -359,6 +416,7 @@ void entity_destroy(GameState *gs, Entity *e);
// grid
void grid_create(struct GameState *gs, Entity *e);
void box_create(struct GameState *gs, Entity *new_box, Entity *grid, V2 pos);
Entity *box_grid(Entity *box);
V2 grid_com(Entity *grid);
V2 grid_vel(Entity *grid);
V2 box_vel(Entity *box);
@ -626,4 +684,6 @@ static void set_color(Color c)
(Color) { .r = 1.0f, .g = 1.0f, .b = 1.0f, .a = 1.0f }
#define RED \
(Color) { .r = 1.0f, .g = 0.0f, .b = 0.0f, .a = 1.0f }
#define BLUE \
(Color) { .r = 0.0f, .g = 0.0f, .b = 1.0f, .a = 1.0f }
#define GOLD colhex(255, 215, 0)

Binary file not shown.
Loading…
Cancel
Save