diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..207edf2 --- /dev/null +++ b/README.txt @@ -0,0 +1,4 @@ +@Cosmetic - places where cosmetics could be added +@Robust - things to do to increase robustness of program +@BeforeShip - important things to handle before seriously shipping +no checkin - without the space between no and checkin, should never appear in commits, use a pre commit hook for this diff --git a/buildsettings.h b/buildsettings.h index 835875f..a4a635b 100644 --- a/buildsettings.h +++ b/buildsettings.h @@ -18,15 +18,15 @@ // Intensive profiling means profiling a lot of little tiny stuff. Not always enabled because tanks performance #define INTENSIVE_PROFILING // #define DEBUG_RENDERING -// #define DEBUG_WORLD -#define UNLOCK_ALL +#define DEBUG_WORLD +// #define UNLOCK_ALL #define TIME_BETWEEN_WORLD_SAVE 1000000.0f // #define TIME_BETWEEN_WORLD_SAVE 1.0f // #define INFINITE_RESOURCES #define DEBUG_TOOLS #define CHIPMUNK_INTEGRITY_CHECK // #define FAT_THRUSTERS -// #define NO_GRAVITY +#define NO_GRAVITY // #define NO_SUNS #else diff --git a/flight.rdbg b/flight.rdbg index 30d4cfe..26ace69 100644 Binary files a/flight.rdbg and b/flight.rdbg differ diff --git a/gamestate.c b/gamestate.c index e9b2492..9645894 100644 --- a/gamestate.c +++ b/gamestate.c @@ -352,7 +352,15 @@ static int sort_bodies_callback(const void *a, const void *b) { double a_dist = cpvdist(cpBodyGetPosition((cpBody *)a), from_point); double b_dist = cpvdist(cpBodyGetPosition((cpBody *)b), from_point); - return (int)(a_dist - b_dist); + if (a_dist - b_dist < 0.0) + { + return -1; + } + else + { + return 1; + } + // return (int)(a_dist - b_dist); } LauncherTarget missile_launcher_target(GameState *gs, Entity *launcher) @@ -757,6 +765,14 @@ bool merge_box_is_merged(GameState *gs, Entity *merge_box) return false; } } + +bool could_learn_from_scanner(Player *for_player, Entity *box) +{ + flight_assert(box->is_box); + flight_assert(box->box_type == BoxScanner); + return (for_player->box_unlocks | box->blueprints_learned) != for_player->box_unlocks; +} + bool box_interactible(GameState *gs, Player *for_player, Entity *box) { flight_assert(box->is_box); @@ -771,10 +787,6 @@ bool box_interactible(GameState *gs, Player *for_player, Entity *box) { return merge_box_is_merged(gs, box); } - else if (box->box_type == BoxScanner) - { - return (for_player->box_unlocks | box->blueprints_learned) != for_player->box_unlocks; - } else { return false; @@ -1286,6 +1298,7 @@ SerMaybeFailure ser_var(SerState *ser, char *var_pointer, size_t var_size, const enum GameVersion { VInitial, + VNoGold, VMax, // this minus one will be the version used }; @@ -1495,7 +1508,12 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) SER_MAYBE_RETURN(ser_entityid(ser, &e->currently_inside_of_box)); SER_VAR(&e->squad_invited_to); - SER_MAYBE_RETURN(ser_f(ser, &e->goldness)); + + if (ser->version < VNoGold) + { + double goldness; + SER_VAR_NAME(&goldness, "&e->goldness"); + } } SER_VAR(&e->is_explosion); @@ -2397,19 +2415,35 @@ void create_initial_world(GameState *gs) create_bomb_station(gs, cpvadd(SUN_POS(3), cpv(0.0, 300.0)), BoxMerge); #undef SUN_POS #else + +#if 0 // present the stations Log("Creating debug world\n"); // pos, mass, radius create_bomb_station(gs, (cpVect){-5.0, 0.0}, BoxExplosive); create_bomb_station(gs, (cpVect){0.0, 5.0}, BoxGyroscope); create_hard_shell_station(gs, (cpVect){-5.0, 5.0}, BoxCloaking); +#endif +#if 1 // scanner box bool indestructible = false; + enum CompassRotation rot = Right; + { + Entity *grid = new_entity(gs); + grid_create(gs, grid); + entity_set_pos(grid, cpv(1.5, 0.0)); + BOX_AT_TYPE(grid, cpv(0.0, 0.0), BoxExplosive); + BOX_AT_TYPE(grid, cpv(-BOX_SIZE, 0.0), BoxScanner); + BOX_AT_TYPE(grid, cpv(-BOX_SIZE, BOX_SIZE), BoxSolarPanel); + BOX_AT_TYPE(grid, cpv(-BOX_SIZE, BOX_SIZE * 2.0), BoxSolarPanel); + entity_ensure_in_orbit(gs, grid); + } +#endif +#if 0 // merge box + bool indestructible = false; double theta = deg2rad(65.0); - cpVect from = (cpVect){BOX_SIZE * 4.0, -1}; - enum CompassRotation rot = Right; { Entity *grid = new_entity(gs); @@ -2437,7 +2471,9 @@ void create_initial_world(GameState *gs) cpBodySetVelocity(grid->body, (cpvspin((cpVect){-0.4, 0.0}, theta))); entity_ensure_in_orbit(gs, grid); } -#endif +#endif // grid tests + +#endif // debug world } void exit_seat(GameState *gs, Entity *seat_in, Entity *p) @@ -2644,6 +2680,20 @@ void process(struct GameState *gs, double dt) } #endif + // general player logic + { + circle_query(gs->space, entity_pos(p), SCANNER_RADIUS); + QUEUE_ITER(&query_result, QueryResult, res) + { + Entity *maybe_scanner = cp_shape_entity(res->shape); + if (maybe_scanner->box_type == BoxScanner && could_learn_from_scanner(player, maybe_scanner)) + { + player->box_unlocks |= maybe_scanner->blueprints_learned; + // @Cosmetic here + } + } + } + // process movement { // no cheating by making movement bigger than length 1 diff --git a/lightning.glsl b/lightning.glsl new file mode 100644 index 0000000..a7ec873 --- /dev/null +++ b/lightning.glsl @@ -0,0 +1,144 @@ +@module lightning + +@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 uniforms { + float iTime; +}; +in vec2 texUV; +out vec4 fragColor; +vec4 permute(vec4 t) { + return t * (t * 34.0 + 133.0); +} + +// Gradient set is a normalized expanded rhombic dodecahedron +vec3 grad(float hash) { + + // Random vertex of a cube, +/- 1 each + vec3 cube = mod(floor(hash / vec3(1.0, 2.0, 4.0)), 2.0) * 2.0 - 1.0; + + // Random edge of the three edges connected to that vertex + // Also a cuboctahedral vertex + // And corresponds to the face of its dual, the rhombic dodecahedron + vec3 cuboct = cube; + int selected_edge =int(hash / 16.0) ; + if(selected_edge == 0) + cuboct.x = 0.0; + if(selected_edge == 1) + cuboct.y = 0.0; + if(selected_edge == 2) + cuboct.z = 0.0; + + // In a funky way, pick one of the four points on the rhombic face + float type = mod(floor(hash / 8.0), 2.0); + vec3 rhomb = (1.0 - type) * cube + type * (cuboct + cross(cube, cuboct)); + + // Expand it so that the new edges are the same length + // as the existing ones + vec3 grad = cuboct * 1.22474487139 + rhomb; + + // To make all gradients the same length, we only need to shorten the + // second type of vector. We also put in the whole noise scale constant. + // The compiler should reduce it into the existing floats. I think. + grad *= (1.0 - 0.042942436724648037 * type) * 3.5946317686139184; + + return grad; +} + +// BCC lattice split up into 2 cube lattices +vec4 bccNoiseDerivativesPart(vec3 X) { + vec3 b = floor(X); + vec4 i4 = vec4(X - b, 2.5); + + // Pick between each pair of oppposite corners in the cube. + vec3 v1 = b + floor(dot(i4, vec4(.25))); + vec3 v2 = b + vec3(1, 0, 0) + vec3(-1, 1, 1) * floor(dot(i4, vec4(-.25, .25, .25, .35))); + vec3 v3 = b + vec3(0, 1, 0) + vec3(1, -1, 1) * floor(dot(i4, vec4(.25, -.25, .25, .35))); + vec3 v4 = b + vec3(0, 0, 1) + vec3(1, 1, -1) * floor(dot(i4, vec4(.25, .25, -.25, .35))); + + // Gradient hashes for the four vertices in this half-lattice. + vec4 hashes = permute(mod(vec4(v1.x, v2.x, v3.x, v4.x), 289.0)); + hashes = permute(mod(hashes + vec4(v1.y, v2.y, v3.y, v4.y), 289.0)); + hashes = mod(permute(mod(hashes + vec4(v1.z, v2.z, v3.z, v4.z), 289.0)), 48.0); + + // Gradient extrapolations & kernel function + vec3 d1 = X - v1; vec3 d2 = X - v2; vec3 d3 = X - v3; vec3 d4 = X - v4; + vec4 a = max(0.75 - vec4(dot(d1, d1), dot(d2, d2), dot(d3, d3), dot(d4, d4)), 0.0); + vec4 aa = a * a; vec4 aaaa = aa * aa; + vec3 g1 = grad(hashes.x); vec3 g2 = grad(hashes.y); + vec3 g3 = grad(hashes.z); vec3 g4 = grad(hashes.w); + vec4 extrapolations = vec4(dot(d1, g1), dot(d2, g2), dot(d3, g3), dot(d4, g4)); + + // Derivatives of the noise + vec3 derivative = -8.0 * mat4x3(d1, d2, d3, d4) * (aa * a * extrapolations) + + mat4x3(g1, g2, g3, g4) * aaaa; + + // Return it all as a vec4 + return vec4(derivative, dot(aaaa, extrapolations)); +} + + +// Gives X and Y a triangular alignment, and lets Z move up the main diagonal. +// Might be good for terrain, or a time varying X/Y plane. Z repeats. +vec4 bccNoiseDerivatives_XYBeforeZ(vec3 X) { + + // Not a skew transform. + mat3 orthonormalMap = mat3( + 0.788675134594813, -0.211324865405187, -0.577350269189626, + -0.211324865405187, 0.788675134594813, -0.577350269189626, + 0.577350269189626, 0.577350269189626, 0.577350269189626); + + X = orthonormalMap * X; + vec4 result = bccNoiseDerivativesPart(X) + bccNoiseDerivativesPart(X + 144.5); + + return vec4(result.xyz * orthonormalMap, result.w); +} + + +void main() +{ + vec2 uv = texUV; + vec2 p = uv; + uv = uv * 2. -1.; + + + float tickle = 0.001*1000*iTime; + vec3 offset = vec3(cos(tickle), sin(tickle), 0.0); + vec3 p3 = vec3(p, 0.0) + offset; + + vec3 noise_input = vec3(p3*25.0+12.0); + //float intensity = noise(noise_input); + float intensity = 0.0; + intensity += bccNoiseDerivatives_XYBeforeZ(noise_input).w*0.4; + intensity += bccNoiseDerivatives_XYBeforeZ(noise_input*0.55).w*0.7; + intensity += bccNoiseDerivatives_XYBeforeZ(noise_input*0.44).w*0.8; + + + float t = clamp((uv.x * -uv.x * 0.16) + 0.15, 0., 1.); + //fragColor.rgb = vec3(t); + + float dist = length(uv); + + float y = abs(dist - 0.5 - intensity * 0.1); + + float g = pow(y, 0.2); + + vec3 col = vec3(1.50, 1.48, 1.78); + col = col * -g + col; + col = col * col; + col = col * col; + + fragColor.rgb = col; + fragColor.a = (col.r + col.g + col.b)/3.0; +} +@end + +@program program vs fs diff --git a/main.c b/main.c index ae23189..89c74a0 100644 --- a/main.c +++ b/main.c @@ -38,8 +38,10 @@ // shaders #include "goodpixel.gen.h" #include "hueshift.gen.h" +#include "lightning.gen.h" static sg_pipeline hueshift_pipeline; static sg_pipeline goodpixel_pipeline; +static sg_pipeline lightning_pipeline; static struct GameState gs = {0}; static int my_player_index = -1; @@ -59,6 +61,7 @@ static cpVect mouse_pos = {0}; static bool fullscreened = false; static bool picking_new_boxtype = false; static double exec_time = 0.0; // cosmetic bouncing, network stats +static float iTime = 0.0; // fmodded to 1000, shader trick http://the-witness.net/news/2022/02/a-shader-trick/ // for network statistics, printed to logs with F3 static uint64_t total_bytes_sent = 0; static uint64_t total_bytes_received = 0; @@ -618,6 +621,21 @@ static void init(void) quit_with_popup("Couldn't make a shader! Uhhh ooooohhhhhh!!!", "Shader error BONED"); } } + + { + sgp_pipeline_desc pip_desc = { + .shader = *lightning_program_shader_desc(sg_query_backend()), + .blend_mode = SGP_BLENDMODE_BLEND, + }; + + lightning_pipeline = sgp_make_pipeline(&pip_desc); + sg_resource_state errstate = sg_query_pipeline_state(lightning_pipeline); + if (errstate != SG_RESOURCESTATE_VALID) + { + Log("Failed to make lightning pipeline\n"); + quit_with_popup("Couldn't make a shader! Uhhh ooooohhhhhh!!!", "Shader error BONED"); + } + } } // images loading @@ -734,6 +752,7 @@ static void draw_texture_centered(cpVect center, double size) { draw_texture_rectangle_centered(center, (cpVect){size, size}); } + static void draw_flipped_texture_rectangle_centered(cpVect center, cpVect width_height) { transform_scope() @@ -1469,6 +1488,7 @@ static void frame(void) double width = (float)sapp_width(), height = (float)sapp_height(); double dt = sapp_frame_duration(); exec_time += dt; + iTime = (float)fmod(exec_time, 1000.0); // pressed input management { @@ -2116,6 +2136,20 @@ static void frame(void) if (b->box_type == BoxScanner) { + if (myplayer() != NULL && could_learn_from_scanner(myplayer(), b)) + { + set_color(WHITE); + pipeline_scope(lightning_pipeline) + { + sgp_set_image(0, (sg_image){0}); + lightning_uniforms_t uniform = { + .iTime = iTime, + }; + sgp_set_uniform(&uniform, sizeof(uniform)); + draw_color_rect_centered(entity_pos(b), BOX_SIZE*2.0); + sgp_reset_image(0); + } + } sgp_set_image(0, image_scanner_head); transform_scope() { @@ -2301,21 +2335,25 @@ static void frame(void) { if (e != myentity() && has_point(centered_at(entity_pos(e), cpvmult(PLAYER_SIZE, player_scaling)), world_mouse_pos)) hovering_this_player = get_id(&gs, e); - if (get_entity(&gs, e->currently_inside_of_box) == NULL) - transform_scope() - { - rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y); - set_color_values(1.0, 1.0, 1.0, 1.0); - pipeline_scope(hueshift_pipeline) + // the body + { + if (get_entity(&gs, e->currently_inside_of_box) == NULL) + transform_scope() { - setup_hueshift(e->owning_squad); - sgp_set_image(0, image_player); - draw_texture_rectangle_centered( - entity_pos(e), cpvmult(PLAYER_SIZE, player_scaling)); - sgp_reset_image(0); + rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y); + set_color_values(1.0, 1.0, 1.0, 1.0); + + pipeline_scope(hueshift_pipeline) + { + setup_hueshift(e->owning_squad); + sgp_set_image(0, image_player); + draw_texture_rectangle_centered( + entity_pos(e), cpvmult(PLAYER_SIZE, player_scaling)); + sgp_reset_image(0); + } } - } + } } // draw explosion diff --git a/shadergen.bat b/shadergen.bat index 2d4afed..478b6cb 100644 --- a/shadergen.bat +++ b/shadergen.bat @@ -5,4 +5,5 @@ IF %ERRORLEVEL% NEQ 0 ECHO ERROR download sokol-shdc from https://github.com/flo @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 lightning.glsl --output lightning.gen.h --slang glsl330:hlsl5:metal_macos sokol-shdc.exe --format sokol --input goodpixel.glsl --output goodpixel.gen.h --slang glsl330:hlsl5:metal_macos \ No newline at end of file diff --git a/types.h b/types.h index 57823ed..6a4210b 100644 --- a/types.h +++ b/types.h @@ -278,8 +278,7 @@ typedef struct Entity enum Squad owning_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 - double goldness; // how much the player is a winner - + // explosion bool is_explosion; cpVect explosion_pos; @@ -496,6 +495,7 @@ EntityID get_id(struct GameState *gs, Entity *e); cpVect entity_pos(Entity *e); bool box_interactible(GameState *gs, Player *for_player, Entity *box); void entity_set_rotation(Entity *e, double rot); +bool could_learn_from_scanner(Player *for_player, Entity *box); void entity_set_pos(Entity *e, cpVect pos); double entity_rotation(Entity *e); void entity_ensure_in_orbit(GameState *gs, Entity *e);