X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=fishladder.c;h=becb812d88b5bb9b48a800ad8846962b2f7509f6;hb=fefce08c7eecf3eb66553825ba421b5b81dae5eb;hp=71b714b53a731aa025dab5586f55f728ed89aed4;hpb=315d9f4e88f3b0204b7161e25b01543849dbc8b7;p=fishladder.git diff --git a/fishladder.c b/fishladder.c index 71b714b..becb812 100644 --- a/fishladder.c +++ b/fishladder.c @@ -1,38 +1,12 @@ // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved +//#define VG_CAPTURE_MODE #define VG_STEAM #define VG_STEAM_APPID 1218140U #include "vg/vg.h" #include "fishladder_resources.h" -/* - Todo for release: - Tutorial levels: - 1. Transport - 2. Split - 3. Merge (and explode) - 4. Principle 1 (divide colours) - 5. Principle 2 (combine colours) - - Trainee levels: - Simple maths (x3) - Colour ordering (x2) - Routing problems (x2) - - Medium levels: - Reverse order - - New things to program: - UI text element renderer (SDF) DONE(sorta) - Particle system thing for ball collision - Level descriptions / titles HALF - Row Gridlines for I/O DONE - Play button / Speed controller ( play buttern, pause, speed control, step button ) - - - After release: - -*/ +// #define STEAM_LEADERBOARDS // CONSTANTS // =========================================================================================================== @@ -107,27 +81,29 @@ static struct cell_description int is_special; int is_linear; + + v2f trigger_pos; } cell_descriptions[] = { // 0-3 {}, - { .start = { 1, 0 }, .end = { -1, 0 } }, - { .start = { 0, 1 }, .end = { 0, -1 } }, - { .start = { 0, 1 }, .end = { 1, 0 } }, + { .start = { 1, 0 }, .end = { -1, 0 }, .trigger_pos = { 0.5f, 0.25f } }, + { .start = { 0, 1 }, .end = { 0, -1 }, .trigger_pos = { 0.25f, 0.5f } }, + { .start = { 0, 1 }, .end = { 1, 0 }, .trigger_pos = { 0.25f, 0.25f } }, // 4-7 - { .start = { -1, 0 }, .end = { 1, 0 } }, - { .start = { -1, 0 }, .end = { 1, 0 }, .is_linear = 1 }, - { .start = { 0, 1 }, .end = { -1, 0 } }, + { .start = { -1, 0 }, .end = { 1, 0 }, .trigger_pos = { 0.5f, 0.25f } }, + { .start = { -1, 0 }, .end = { 1, 0 }, .trigger_pos = { 0.5f, 0.25f }, .is_linear = 1 }, + { .start = { 0, 1 }, .end = { -1, 0 }, .trigger_pos = { 0.5f, 0.25f } }, { .start = { 0, 1 }, .is_special = 1 }, // 8-11 - { .start = { 0, -1 }, .end = { 0, 1 } }, - { .start = { 1, 0 }, .end = { 0, -1 } }, - { .start = { 0, 1 }, .end = { 0, -1 }, .is_linear = 1 }, + { .start = { 0, -1 }, .end = { 0, 1 }, .trigger_pos = { 0.25f, 0.5f } }, + { .start = { 1, 0 }, .end = { 0, -1 }, .trigger_pos = { 0.25f, 0.75f } }, + { .start = { 0, 1 }, .end = { 0, -1 }, .trigger_pos = { 0.25f, 0.5f }, .is_linear = 1 }, { }, // 12-15 - { .start = { -1, 0 }, .end = { 0, -1 } }, - { .end = { 0, -1 }, .is_special = 1 }, + { .start = { -1, 0 }, .end = { 0, -1 }, .trigger_pos = { 0.75f, 0.75f } }, + { .end = { 0, -1 }, .is_special = 1, .trigger_pos = { 0.5f, 0.75f } }, { }, { } }; @@ -176,6 +152,7 @@ struct world int sim_frame, sim_target; float sim_internal_time, // current tick-time + sim_internal_delta, // time delta sim_internal_ref, // Reference point of internal time sim_delta_ref, // Reference point of vg_time when we started at current sim_speed sim_delta_speed, // Rate to apply time delta @@ -561,7 +538,7 @@ static int map_load( const char *str, const char *name ) struct cell_terminal *terminal = &world.io[ reg_start ]; struct terminal_run *run = &terminal->runs[ terminal->run_count-1 ]; - if( *c >= 'a' && *c <= 'z' ) + if( (*c >= 'a' && *c <= 'z') || *c == ' ' ) { run->conditions[ run->condition_count ++ ] = *c; } @@ -725,6 +702,20 @@ static int map_load( const char *str, const char *name ) px[3] = 0; } } + + // Level selection area + + for( int y = 16+2; y < 16+world.h-2; y ++ ) + { + for( int x = 14; x < 16; x ++ ) + { + u8 *px = &info_buffer[((y*64)+x)*4]; + px[0] = 0x10; + } + } + + info_buffer[(((16+world.h-3)*64)+world.w+16-1)*4] = 0x30; + info_buffer[(((16+world.h-2)*64)+world.w+16-1)*4] = 0x30; // Random walks.. kinda for( int i = 0; i < arrlen(world.io); i ++ ) @@ -742,6 +733,9 @@ static int map_load( const char *str, const char *name ) turtle_dir[1] = pcell(term->pos)->state & FLAG_INPUT? 1: -1; original_y = turtle_dir[1]; + info_buffer[((turtle[1]*64)+turtle[0])*4] = 0; + v2i_add( turtle_dir, turtle, turtle ); + for( int i = 0; i < 100; i ++ ) { info_buffer[((turtle[1]*64)+turtle[0])*4] = 0; @@ -917,8 +911,13 @@ struct dcareer_state }; #pragma pack(pop) +static int career_load_success = 0; + static void career_serialize(void) { + if( !career_load_success ) + return; + struct dcareer_state encoded; encoded.version = 2; encoded.in_map = world.pCmpLevel? world.pCmpLevel->serial_id: -1; @@ -997,7 +996,6 @@ static void career_load(void) { vg_warn( "This save file is too small to have a header. Creating a blank one\n" ); free( cr ); - return; } memcpy( (void*)&encoded, cr, VG_MIN( sizeof( struct dcareer_state ), sz ) ); @@ -1039,6 +1037,8 @@ static void career_load(void) } } } + + career_load_success = 1; } // MAIN GAMEPLAY @@ -1048,6 +1048,12 @@ static int is_simulation_running(void) return world.buttons[ k_world_button_sim ].pressed; } +static void clear_animation_flags(void) +{ + for( int i = 0; i < world.w*world.h; i ++ ) + world.data[ i ].state &= ~(FLAG_FLIP_FLOP|FLAG_FLIP_ROTATING); +} + static void simulation_stop(void) { world.buttons[ k_world_button_sim ].pressed = 0; @@ -1056,14 +1062,40 @@ static void simulation_stop(void) world.num_fishes = 0; world.sim_frame = 0; world.sim_run = 0; + world.frame_lerp = 0.0f; io_reset(); sfx_system_fadeout( &audio_system_balls_rolling, 44100 ); + clear_animation_flags(); + vg_info( "Stopping simulation!\n" ); } +static void simulation_start(void) +{ + vg_success( "Starting simulation!\n" ); + + sfx_set_playrnd( &audio_rolls, &audio_system_balls_rolling, 0, 1 ); + + world.num_fishes = 0; + world.sim_frame = 0; + world.sim_run = 0; + + world.sim_delta_speed = 2.5f; + world.sim_delta_ref = vg_time; + world.sim_internal_ref = 0.0f; + world.sim_internal_time = 0.0f; + world.pause_offset_target = 0.0f; + + world.sim_target = 0; + + clear_animation_flags(); + + io_reset(); +} + static int world_check_pos_ok( v2i co ) { return (co[0] < 2 || co[0] >= world.w-2 || co[1] < 2 || co[1] >= world.h-2)? 0: 1; @@ -1188,7 +1220,7 @@ void vg_update(void) if( !is_simulation_running() && !gui_want_mouse() ) { v2_copy( vg_mouse_ws, world.drag_to_co ); - + if( cell_interactive( (v2i){ world.tile_x, world.tile_y } )) { world.selected = world.tile_y * world.w + world.tile_x; @@ -1223,16 +1255,38 @@ void vg_update(void) (v2i){ world.tile_x +2, world.tile_y +2 }, 1 ); } - if( vg_get_button_down("secondary") && !(cell_ptr->config == k_cell_type_split) ) + if( vg_get_button_down("secondary") && (cell_ptr->state & FLAG_CANAL) && !(cell_ptr->config == k_cell_type_split) ) { world.id_drag_from = world.selected; - world.drag_from_co[0] = world.tile_x + 0.5f; - world.drag_from_co[1] = world.tile_y + 0.5f; + + struct cell_description *desc = &cell_descriptions[ world.data[world.id_drag_from].config ]; + v2_add( desc->trigger_pos, (v2f){ world.tile_x, world.tile_y }, world.drag_from_co ); + } + + float local_x = vg_mouse_ws[0] - (float)world.tile_x; + + if( vg_get_button_up("secondary") && world.id_drag_from == world.selected ) + { + u32 link_id = cell_ptr->links[ 0 ]? 0: 1; + + // break existing connection off + if( cell_ptr->links[ link_id ] ) + { + struct cell *current_connection = &world.data[ cell_ptr->links[ link_id ]]; + + if( !current_connection->links[ link_id ^ 0x1 ] ) + current_connection->state &= ~FLAG_TARGETED; + + current_connection->links[ link_id ] = 0; + cell_ptr->links[ link_id ] = 0; + } + + cell_ptr->state &= ~FLAG_IS_TRIGGER; + world.id_drag_from = 0; } - if( world.id_drag_from && (cell_ptr->config == k_cell_type_split) ) + else if( world.id_drag_from && (cell_ptr->state & FLAG_CANAL) && (cell_ptr->config == k_cell_type_split) ) { - float local_x = vg_mouse_ws[0] - (float)world.tile_x; world.drag_to_co[0] = (float)world.tile_x + (local_x > 0.5f? 0.75f: 0.25f); world.drag_to_co[1] = (float)world.tile_y + 0.25f; @@ -1293,20 +1347,22 @@ void vg_update(void) // ======================================================================================================== if( is_simulation_running() ) { + float old_time = world.sim_internal_time; + if( !world.buttons[ k_world_button_pause ].pressed ) - { world.sim_internal_time = world.sim_internal_ref + (vg_time-world.sim_delta_ref) * world.sim_delta_speed; - } else - { world.sim_internal_time = vg_lerpf( world.sim_internal_time, world.sim_internal_ref + world.pause_offset_target, vg_time_delta*15.0f ); - } + world.sim_internal_delta = world.sim_internal_time-old_time; world.sim_target = (int)floorf(world.sim_internal_time); + int success_this_frame = 0; + int failure_this_frame = 0; + while( world.sim_frame < world.sim_target ) { - sfx_set_playrnd( &audio_random, &audio_system_balls_switching, 0, 9 ); + sfx_set_playrnd( &audio_random, &audio_system_balls_switching, 0, 8 ); // Update splitter deltas for( int i = 0; i < world.h*world.w; i ++ ) @@ -1351,13 +1407,23 @@ void vg_update(void) { struct terminal_run *run = &term->runs[ world.sim_run ]; if( run->recv_count < vg_list_size( run->recieved ) ) + { + if( fish->payload == run->conditions[ run->recv_count ] ) + success_this_frame = 1; + else + failure_this_frame = 1; + run->recieved[ run->recv_count ++ ] = fish->payload; + } + else + failure_this_frame = 1; break; } } fish->state = k_fish_state_dead; + fish->death_time = -1000.0f; continue; } @@ -1400,7 +1466,10 @@ void vg_update(void) if( cell_next->config == k_cell_type_merge ) { if( fish->dir[0] == 0 ) + { fish->state = k_fish_state_dead; + fish->death_time = world.sim_internal_time; + } else fish->flow_reversed = 0; } @@ -1416,7 +1485,10 @@ void vg_update(void) fish->flow_reversed = 0; } else + { fish->state = k_fish_state_dead; + fish->death_time = world.sim_internal_time; + } } else fish->flow_reversed = ( fish->dir[0] != -desc->start[0] || @@ -1424,7 +1496,15 @@ void vg_update(void) } } else - fish->state = world_check_pos_ok( fish->pos )? k_fish_state_bg: k_fish_state_dead; + { + if( world_check_pos_ok( fish->pos ) ) + fish->state = k_fish_state_bg; + else + { + fish->state = k_fish_state_dead; + fish->death_time = world.sim_internal_time; + } + } } //v2i_add( fish->pos, fish->dir, fish->pos ); @@ -1434,7 +1514,10 @@ void vg_update(void) v2i_add( fish->pos, fish->dir, fish->pos ); if( !world_check_pos_ok( fish->pos ) ) + { fish->state = k_fish_state_dead; + fish->death_time = -1000.0f; + } else { struct cell *cell_entry = pcell( fish->pos ); @@ -1482,11 +1565,6 @@ void vg_update(void) if( cell_current->state & FLAG_IS_TRIGGER ) { int trigger_id = cell_current->links[0]?0:1; - int connection_id = cell_current->links[trigger_id]; - int target_px = connection_id % world.w; - int target_py = (connection_id - target_px)/world.w; - - vg_line2( (v2f){ fish->pos[0], fish->pos[1] }, (v2f){ target_px, target_py }, 0xffffffff, 0xffffffff ); struct cell *target_peice = &world.data[ cell_current->links[trigger_id] ]; @@ -1539,7 +1617,7 @@ void vg_update(void) sw_set_achievement( "BANG" ); // Shatter death (+0.5s) - float death_time = collide_this_frame? 0.0f: 0.5f; + float death_time = world.sim_internal_time + ( collide_this_frame? 0.0f: 0.5f ); fi->state = k_fish_state_soon_dead; fj->state = k_fish_state_soon_dead; @@ -1566,11 +1644,15 @@ void vg_update(void) { if( world.sim_frame < term->runs[ world.sim_run ].condition_count ) { + char emit = term->runs[ world.sim_run ].conditions[ world.sim_frame ]; + if( emit == ' ' ) + continue; + struct fish *fish = &world.fishes[ world.num_fishes ]; v2i_copy( term->pos, fish->pos ); fish->state = k_fish_state_alive; - fish->payload = term->runs[ world.sim_run ].conditions[ world.sim_frame ]; + fish->payload = emit; struct cell *cell_ptr = pcell( fish->pos ); @@ -1680,6 +1762,21 @@ void vg_update(void) world.sim_frame ++; } + // Sounds + if( failure_this_frame ) + { + sfx_set_play( &audio_tones, &audio_system_balls_extra, 0 ); + } + else if( success_this_frame ) + { + static int succes_counter = 0; + + sfx_set_play( &audio_tones, &audio_system_balls_extra, 1+(succes_counter++) ); + + if( succes_counter == 7 ) + succes_counter = 0; + } + // Position update // ===================================================================================================== @@ -1692,7 +1789,7 @@ void vg_update(void) if( fish->state == k_fish_state_dead ) continue; - if( fish->state == k_fish_state_soon_dead && (world.frame_lerp > fish->death_time) ) + if( fish->state == k_fish_state_soon_dead && (world.sim_internal_time > fish->death_time) ) continue; // Todo: particle thing? struct cell *cell = pcell(fish->pos); @@ -1758,6 +1855,9 @@ void vg_update(void) fish->physics_co[0] = origin[0] + (float)fish->dir[0]*t; fish->physics_co[1] = origin[1] + (float)fish->dir[1]*t; } + + v2_sub( fish->physics_co, fish->physics_v, fish->physics_v ); + v2_divs( fish->physics_v, world.sim_internal_delta, fish->physics_v ); } } } @@ -1835,34 +1935,19 @@ static void draw_numbers( v3f coord, int number ) } }*/ -static void simulation_start(void) -{ - vg_success( "Starting simulation!\n" ); - - sfx_set_playrnd( &audio_rolls, &audio_system_balls_rolling, 0, 1 ); - - world.num_fishes = 0; - world.sim_frame = 0; - world.sim_run = 0; - - world.sim_delta_speed = 2.5f; - world.sim_delta_ref = vg_time; - world.sim_internal_ref = 0.0f; - world.pause_offset_target = 0.0f; - - world.sim_target = 0; - - for( int i = 0; i < world.w*world.h; i ++ ) - world.data[ i ].state &= ~FLAG_FLIP_FLOP; - - io_reset(); -} - static void wbutton_run( enum e_world_button btn_name ) { + static v3f button_colours[] = { + {0.204f, 0.345f, 0.553f}, + {0.204f, 0.345f, 0.553f}, + {0.741f, 0.513f, 0.078f}, + {1.0f, 0.0f, 0.0f} + }; + struct cell_button *btn = &world.buttons[btn_name]; // Interaction + int tex_offset = 0; int is_hovering = (world.tile_x == world.w-1 && world.tile_y == world.h-btn_name-2)?1:0; if( vg_get_button_up( "primary" ) && is_hovering ) @@ -1876,9 +1961,11 @@ static void wbutton_run( enum e_world_button btn_name ) { btn->pressed = 1; simulation_start(); + + world.pause_offset_target = 0.5f; } - - world.pause_offset_target += 1.0f; + else + world.pause_offset_target += 1.0f; } else { @@ -1905,6 +1992,12 @@ static void wbutton_run( enum e_world_button btn_name ) else world.pause_offset_target = 0.0f; } + else + { + btn->pressed ^= 0x1; + } + + sfx_set_play( &audio_clicks, &audio_system_ui, btn->pressed?1:0 ); } // Drawing @@ -1932,13 +2025,26 @@ static void wbutton_run( enum e_world_button btn_name ) btn->light = vg_lerpf( btn->light, btn->light_target, vg_time_delta*26.0f ); + // Draw + if( btn_name == k_world_button_sim && world.buttons[ k_world_button_sim ].pressed ) + { + if( world.buttons[ k_world_button_pause ].pressed ) + tex_offset = 3; + else + tex_offset = 2; + } + + v4f final_colour; + v3_copy( button_colours[ btn_name ], final_colour ); + final_colour[3] = btn->light; + glUniform4f( SHADER_UNIFORM( shader_buttons, "uOffset" ), world.w-1, world.h-btn_name-2, - (float)btn_name, + (float)(btn_name+tex_offset), 3.0f ); - glUniform4f( SHADER_UNIFORM( shader_buttons, "uColour" ), 0.204f, 0.345f, 0.553f, btn->light ); + glUniform4fv( SHADER_UNIFORM( shader_buttons, "uColour" ), 1, final_colour ); draw_mesh( 0, 2 ); } @@ -1954,6 +2060,14 @@ void vg_render(void) v4f const colour_default = {1.0f, 1.0f, 1.0f, 1.0f}; v4f const colour_selected = {0.90f, 0.92f, 1.0f, 1.0f}; + int const circle_base = 6; + int const filled_start = circle_base+0; + int const filled_count = circle_base+32; + int const empty_start = circle_base+32; + int const empty_count = circle_base+32*2; + + if( !world.initialzed ) + return; // BACKGROUND // ======================================================================================================== @@ -2013,18 +2127,34 @@ void vg_render(void) for( int i = 0; i < world.num_fishes; i ++ ) { struct fish *fish = &world.fishes[i]; + v3f render_pos; + render_pos[2] = 1.0f; - if( fish->state == k_fish_state_dead || fish->state == k_fish_state_bg ) + if( fish->state == k_fish_state_dead || fish->state == k_fish_state_soon_dead ) + { + float death_anim_time = world.sim_internal_time - fish->death_time; + + // Death animation + if( death_anim_time > 0.0f && death_anim_time < 1.0f ) + { + float amt = 1.0f-death_anim_time*death_anim_time; + + v2_muladds( fish->physics_co, fish->physics_v, -1.0f * world.sim_internal_delta * amt, fish->physics_co ); + render_pos[2] = amt; + } + else if( world.sim_internal_time > fish->death_time ) + continue; + } + else if( fish->state == k_fish_state_bg ) continue; - if( fish->state == k_fish_state_soon_dead && (world.frame_lerp > fish->death_time) ) - continue; + v2_copy( fish->physics_co, render_pos ); v4f dot_colour = { 0.0f, 0.0f, 0.0f, 1.0f }; colour_code_v3( fish->payload, dot_colour ); glUniform3fv( SHADER_UNIFORM( shader_ball, "uColour" ), 1, dot_colour ); - glUniform2fv( SHADER_UNIFORM( shader_ball, "uOffset" ), 1, fish->physics_co ); + glUniform3fv( SHADER_UNIFORM( shader_ball, "uOffset" ), 1, render_pos ); glUniform2f( SHADER_UNIFORM( shader_ball, "uTexOffset" ), (float)i * 1.2334, (float)i * -0.3579f ); draw_mesh( 0, 2 ); } @@ -2121,28 +2251,37 @@ void vg_render(void) wbutton_run( k_world_button_sim ); wbutton_run( k_world_button_pause ); + //wbutton_run( k_world_button_wire_mode ); // WIRES // ======================================================================================================== - glDisable(GL_BLEND); + //glDisable(GL_BLEND); SHADER_USE( shader_wire ); glBindVertexArray( world.wire.vao ); glUniformMatrix3fv( SHADER_UNIFORM( shader_wire, "uPv" ), 1, GL_FALSE, (float *)vg_pv ); - glUniform4f( SHADER_UNIFORM( shader_wire, "uColour" ), 0.2f, 0.2f, 0.2f, 1.0f ); + + v4f const wire_left_colour = { 0.5f, 0.5f, 0.5f, 1.0f }; + v4f const wire_right_colour = { 0.2f, 0.2f, 0.2f, 1.0f }; + v4f const wire_drag_colour = { 0.2f, 0.2f, 0.2f, 0.6f }; + + glUniform1f( SHADER_UNIFORM( shader_wire, "uTime" ), world.frame_lerp ); + glUniform1f( SHADER_UNIFORM( shader_wire, "uGlow" ), 0.0f ); if( world.id_drag_from ) { + glUniform4fv( SHADER_UNIFORM( shader_wire, "uColour" ), 1, wire_drag_colour ); glUniform1f( SHADER_UNIFORM( shader_wire, "uCurve" ), 0.4f ); - glUniform3f( SHADER_UNIFORM( shader_wire, "uStart" ), world.drag_from_co[0], world.drag_from_co[1], 0.06f ); - glUniform3f( SHADER_UNIFORM( shader_wire, "uEnd" ), world.drag_to_co[0], world.drag_to_co[1], 0.06f ); + glUniform3f( SHADER_UNIFORM( shader_wire, "uStart" ), world.drag_from_co[0], world.drag_from_co[1], 0.20f ); + glUniform3f( SHADER_UNIFORM( shader_wire, "uEnd" ), world.drag_to_co[0], world.drag_to_co[1], 0.20f ); glDrawElements( GL_TRIANGLES, world.wire.em, GL_UNSIGNED_SHORT, (void*)(0) ); } // Pulling animation float rp_x1 = world.frame_lerp*9.0f; - float rp_x2 = 1.0f-rp_x1*expf(1.0f-rp_x1)* 0.36f; + float rp_xa = rp_x1*expf(1.0f-rp_x1)* 0.36f; + float rp_x2 = 1.0f-rp_xa; for( int y = 2; y < world.h-2; y ++ ) { @@ -2154,6 +2293,8 @@ void vg_render(void) { if( cell->state & FLAG_IS_TRIGGER ) { + struct cell_description *desc = &cell_descriptions[ cell->config ]; + int trigger_id = cell->links[0]?0:1; int x2 = cell->links[trigger_id] % world.w; @@ -2165,32 +2306,96 @@ void vg_render(void) startpoint[0] = (float)x2 + (trigger_id? 0.75f: 0.25f); startpoint[1] = (float)y2 + 0.25f; - endpoint[0] = x+0.5f; - endpoint[1] = y+0.5f; + endpoint[0] = x; + endpoint[1] = y; + v2_add( desc->trigger_pos, endpoint, endpoint ); + + glUniform4fv( SHADER_UNIFORM( shader_wire, "uColour" ), 1, trigger_id? wire_right_colour: wire_left_colour ); glUniform1f( SHADER_UNIFORM( shader_wire, "uCurve" ), cell->state & FLAG_TRIGGERED? rp_x2 * 0.4f: 0.4f ); - glUniform3f( SHADER_UNIFORM( shader_wire, "uStart" ), startpoint[0], startpoint[1], 0.04f ); - glUniform3f( SHADER_UNIFORM( shader_wire, "uEnd" ), endpoint[0], endpoint[1], 0.04f ); + glUniform1f( SHADER_UNIFORM( shader_wire, "uGlow" ), cell->state & FLAG_TRIGGERED? rp_xa: 0.0f ); + glUniform3f( SHADER_UNIFORM( shader_wire, "uStart" ), startpoint[0], startpoint[1], 0.18f ); + glUniform3f( SHADER_UNIFORM( shader_wire, "uEnd" ), endpoint[0], endpoint[1], 0.18f ); glDrawElements( GL_TRIANGLES, world.wire.em, GL_UNSIGNED_SHORT, (void*)(0) ); } } } } - // I/O ARRAYS + // WIRE ENDPOINTS // ======================================================================================================== SHADER_USE( shader_tile_colour ); glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour, "uPv" ), 1, GL_FALSE, (float *)vg_pv ); use_mesh( &world.shapes ); + + for( int y = 2; y < world.h-2; y ++ ) + { + for( int x = 2; x < world.w-2; x ++ ) + { + struct cell *cell = pcell((v2i){x,y}); - int const circle_base = 4; - int const filled_start = circle_base+0; - int const filled_count = circle_base+32; - int const empty_start = circle_base+32; - int const empty_count = circle_base+32*2; + if( cell->state & FLAG_CANAL ) + { + if( cell->state & FLAG_IS_TRIGGER ) + { + struct cell_description *desc = &cell_descriptions[ cell->config ]; + + int trigger_id = cell->links[0]?0:1; + + int x2 = cell->links[trigger_id] % world.w; + int y2 = (cell->links[trigger_id] - x2) / world.w; + + v2f pts[2]; + + pts[0][0] = (float)x2 + (trigger_id? 0.75f: 0.25f); + pts[0][1] = (float)y2 + 0.25f; + + pts[1][0] = x; + pts[1][1] = y; + + v2_add( desc->trigger_pos, pts[1], pts[1] ); + + glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), + 1, trigger_id? wire_right_colour: wire_left_colour ); + + for( int i = 0; i < 2; i ++ ) + { + glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), + pts[i][0], + pts[i][1], + 0.08f + ); + draw_mesh( filled_start, filled_count ); + } + } + } + } + } - glEnable(GL_BLEND); + // SUB SPLITTER DIRECTION + // ======================================================================================================== + + glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.9f, 0.35f, 0.1f, 0.75f ); + + for( int y = 2; y < world.h-2; y ++ ) + { + for( int x = 2; x < world.w-2; x ++ ) + { + struct cell *cell = pcell((v2i){x,y}); + + if( cell->state & FLAG_CANAL && cell->state & FLAG_TARGETED && cell->config == k_cell_type_split ) + { + glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), x, y, 1.0f ); + draw_mesh( cell->state & FLAG_FLIP_FLOP? 5: 4, 1 ); + } + } + } + + // I/O ARRAYS + // ======================================================================================================== + + //glEnable(GL_BLEND); for( int i = 0; i < arrlen( world.io ); i ++ ) { @@ -2207,9 +2412,35 @@ void vg_render(void) (arr_base + (float)term->pos[1] + (float)(term->run_count-1)*0.2f) - run_offset: (float)term->pos[1] + arr_base + run_offset; - if( k & 0x1 ) + v4f bar_colour; + int bar_draw = 0; + + if( is_simulation_running() ) { - glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1.0f, 1.0f, 1.0f, 0.1f ); + if( k == world.sim_run ) + { + float a = fabsf(sinf( vg_time * 2.0f )) * 0.075f + 0.075f; + + v4_copy( (v4f){ 1.0f, 1.0f, 1.0f, a }, bar_colour ); + } + else + v4_copy( (v4f){ 0.0f, 0.0f, 0.0f, 0.13f }, bar_colour ); + + bar_draw = 1; + } + else if( 1 || k & 0x1 ) + { + if( k & 0x1 ) + v4_copy( (v4f){ 1.0f, 1.0f, 1.0f, 0.07f }, bar_colour ); + else + v4_copy( (v4f){ 0.0f, 0.0f, 0.0f, 0.13f }, bar_colour ); + + bar_draw = 1; + } + + if( bar_draw ) + { + glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, bar_colour ); glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)term->pos[0], y_position - 0.1f, 1.0f ); draw_mesh( 2, 2 ); } @@ -2224,14 +2455,18 @@ void vg_render(void) if( is_input ) { - colour_code_v3( term->runs[k].conditions[j], dot_colour ); - glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); - - // Draw filled if tick not passed, draw empty if empty - if( (world.sim_frame > j && world.sim_run >= k) || world.sim_run > k ) - draw_mesh( empty_start, empty_count ); - else - draw_mesh( filled_start, filled_count ); + char cc = term->runs[k].conditions[j]; + if( cc != ' ' ) + { + colour_code_v3( cc, dot_colour ); + glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour ); + + // Draw filled if tick not passed, draw empty if empty + if( (world.sim_frame > j && world.sim_run >= k) || world.sim_run > k ) + draw_mesh( empty_start, empty_count ); + else + draw_mesh( filled_start, filled_count ); + } } else { @@ -2331,6 +2566,7 @@ ui_data; void vg_ui(void) { + /* // UI memory static int pack_selection = 0; static struct pack_info @@ -2372,7 +2608,7 @@ void vg_ui(void) gui_capture_mouse( 9999 ); gui_fill_rect( ui_global_ctx.cursor, 0xff5a4e4d ); - gui_text( "ASSIGNMENTS", 8, 0 ); + gui_text( "ASSIGNMENTS", 32, 0 ); ui_global_ctx.cursor[1] += 30; ui_global_ctx.cursor[3] = 25; @@ -2384,14 +2620,16 @@ void vg_ui(void) for( int i = 0; i < 3; i ++ ) { - if( i == pack_selection ) + int pack_is_unlocked = pack_infos[i].levels[0].unlocked; + + if( i == pack_selection || !pack_is_unlocked ) gui_override_colours( &flcol_list_locked ); - - if( gui_button( 2000 + i ) == k_button_click ) + + if( gui_button( 2000 + i ) == k_button_click && pack_is_unlocked ) pack_selection = i; ui_global_ctx.cursor[1] += 2; - gui_text( pack_infos[i].name, 4, 0 ); + gui_text( pack_is_unlocked? pack_infos[i].name: "???", 24, 0 ); gui_end_right(); gui_reset_colours(); @@ -2463,18 +2701,18 @@ void vg_ui(void) } ui_global_ctx.override_colour = 0xffffffff; - gui_text( lvl_info->title, 6, 0 ); + gui_text( lvl_info->title, 24, 0 ); ui_global_ctx.cursor[1] += 18; - gui_text( lvl_info->completed_score>0? "passed": "incomplete", 4, 0 ); + gui_text( lvl_info->completed_score>0? "passed": "incomplete", 24, 0 ); } else { gui_button( 2 + i ); ui_global_ctx.override_colour = 0xff786f6f; - gui_text( "???", 6, 0 ); + gui_text( "???", 24, 0 ); ui_global_ctx.cursor[1] += 18; - gui_text( "locked", 4, 0 ); + gui_text( "locked", 24, 0 ); } gui_end_down(); @@ -2504,7 +2742,7 @@ void vg_ui(void) gui_fill_rect( ui_global_ctx.cursor, 0xff5a4e4d ); ui_global_ctx.cursor[1] += 4; - gui_text( ui_data.level_selected->title, 6, 0 ); + gui_text( ui_data.level_selected->title, 24, 0 ); ui_global_ctx.cursor[1] += 30; ui_rect_pad( ui_global_ctx.cursor, 8 ); @@ -2516,11 +2754,11 @@ void vg_ui(void) } gui_end_down(); - ui_text_use_paragraph( &ui_global_ctx ); + //ui_text_use_paragraph( &ui_global_ctx ); ui_global_ctx.cursor[1] += 2; - gui_text( ui_data.level_selected->description, 5, 0 ); - ui_text_use_title( &ui_global_ctx ); + gui_text( ui_data.level_selected->description, 16, 0 ); + //ui_text_use_title( &ui_global_ctx ); // Buttons at the bottom ui_global_ctx.cursor[3] = 25; @@ -2533,7 +2771,7 @@ void vg_ui(void) { ui_data.level_selected = NULL; } - gui_text( "BACK", 6, k_text_alignment_center ); + gui_text( "BACK", 24, k_text_alignment_center ); gui_end(); gui_align_right(); @@ -2547,7 +2785,7 @@ void vg_ui(void) ui_global_ctx.override_colour = 0xff888888; - gui_text( "RESTORE SOLUTION", 6, k_text_alignment_center ); + gui_text( "RESTORE SOLUTION", 24, k_text_alignment_center ); gui_end_right(); ui_global_ctx.override_colour = 0xffffffff; } @@ -2567,7 +2805,7 @@ void vg_ui(void) ui_data.leaderboard_show = 0; } } - gui_text( "PLAY", 6, k_text_alignment_center ); + gui_text( "PLAY", 24, k_text_alignment_center ); gui_end(); } @@ -2585,7 +2823,7 @@ void vg_ui(void) gui_new_node(); { gui_fill_rect( ui_global_ctx.cursor, 0xff5a4e4d ); - gui_text( "FRIEND LEADERBOARD", 6, 0 ); + gui_text( "FRIEND LEADERBOARD", 24, 0 ); } gui_end_down(); @@ -2606,7 +2844,7 @@ void vg_ui(void) // 1,2,3 ... static const char *places[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" }; - gui_text( places[i], 7, 0 ); + gui_text( places[i], 24, 0 ); ui_global_ctx.cursor[0] += 32; struct leaderboard_player *player = &ui_data.leaderboard_players[i]; @@ -2622,12 +2860,12 @@ void vg_ui(void) gui_end_right(); // Players name - gui_text( player->player_name, 7, 0 ); + gui_text( player->player_name, 24, 0 ); ui_global_ctx.cursor[2] = 50; gui_align_right(); - gui_text( player->score_text, 7, k_text_alignment_right ); + gui_text( player->score_text, 24, k_text_alignment_right ); } gui_end_down(); @@ -2637,10 +2875,12 @@ void vg_ui(void) gui_end(); } } + */ } void leaderboard_dispatch_score(void) { +#if STEAM_LEADERBOARDS sw_upload_leaderboard_score( ui_data.upload_request.level->steam_leaderboard, k_ELeaderboardUploadScoreMethodKeepBest, @@ -2652,10 +2892,12 @@ void leaderboard_dispatch_score(void) ui_data.upload_request.is_waiting = 0; vg_success( "Dispatched leaderboard score\n" ); +#endif } void leaderboard_found( LeaderboardFindResult_t *pCallback ) { +#ifdef STEAM_LEADERBOARDS if( !pCallback->m_bLeaderboardFound ) { vg_error( "Leaderboard could not be found\n" ); @@ -2685,10 +2927,12 @@ void leaderboard_found( LeaderboardFindResult_t *pCallback ) } } } +#endif } void leaderboard_downloaded( LeaderboardScoresDownloaded_t *pCallback ) { +#ifdef STEAM_LEADERBOARDS // Update UI if this leaderboard matches what we currently have in view if( ui_data.level_selected->steam_leaderboard == pCallback->m_hSteamLeaderboard ) { @@ -2723,10 +2967,12 @@ void leaderboard_downloaded( LeaderboardScoresDownloaded_t *pCallback ) ui_data.leaderboard_show = 0; } else vg_warn( "Downloaded leaderboard does not match requested!\n" ); +#endif } void leaderboard_set_score( struct cmp_level *cmp_level, u32 score ) { +#ifdef STEAM_LEADERBOARDS if( ui_data.upload_request.is_waiting ) vg_warn( "You are uploading leaderboard entries too quickly!\n" ); @@ -2739,6 +2985,7 @@ void leaderboard_set_score( struct cmp_level *cmp_level, u32 score ) leaderboard_dispatch_score(); else sw_find_leaderboard( cmp_level->map_name ); +#endif } // CONSOLE COMMANDS @@ -2856,11 +3103,16 @@ static int console_changelevel( int argc, char const *argv[] ) // START UP / SHUTDOWN // =========================================================================================================== +#define TRANSFORM_TRI_2D( S, OX, OY, X1, Y1, X2, Y2, X3, Y3 ) \ + X1*S+OX, Y1*S+OY, X2*S+OX, Y2*S+OY, X3*S+OX, Y3*S+OY + void vg_start(void) { // Steamworks callbacks + #ifdef STEAM_LEADERBOARDS sw_leaderboard_found = &leaderboard_found; sw_leaderboard_downloaded = &leaderboard_downloaded; + #endif vg_function_push( (struct vg_cmd){ .name = "_map_write", @@ -2884,15 +3136,18 @@ void vg_start(void) // Combined quad, long quad / empty circle / filled circle mesh { - float combined_mesh[6*8 + 32*6*3] = { + float combined_mesh[6*6 + 32*6*3] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 1.0f, 0.2f, - 0.0f, 0.0f, 1.0f, 0.2f, 1.0f, 0.0f + 0.0f, 0.0f, 1.0f, 0.2f, 1.0f, 0.0f, + + TRANSFORM_TRI_2D( 0.15f,0.05f,0.4f, 0.0f, 1.0f, 1.0f, 2.0f, 1.0f, 0.0f ), + TRANSFORM_TRI_2D( 0.15f,0.80f,0.4f, 0.0f, 0.0f, 0.0f, 2.0f, 1.0f, 1.0f ) }; - float *circle_mesh = combined_mesh + 6*4; + float *circle_mesh = combined_mesh + 6*6; int const res = 32; for( int i = 0; i < res; i ++ )