X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=fishladder.c;h=412182cd21619e4821865ac1733290d1b3f5be71;hb=951f90aaee84fdbb7b3d42af7164127fca183277;hp=3bb8abafc35f73c92b6cd40fb0e166c1d65c7fcf;hpb=fd38181c04912e86aeb757603ca08041ce2e6f69;p=fishladder.git diff --git a/fishladder.c b/fishladder.c index 3bb8aba..412182c 100644 --- a/fishladder.c +++ b/fishladder.c @@ -27,7 +27,7 @@ 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 ) + Play button / Speed controller PLAY/PAUSED.. todo: speed, wire connecty After release: @@ -63,7 +63,7 @@ enum e_world_button { k_world_button_none = -1, k_world_button_sim = 0, - k_world_button_ff = 1, + k_world_button_pause = 1, k_world_button_wire_mode = 2 }; @@ -107,27 +107,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 } }, { }, { } }; @@ -169,16 +171,19 @@ struct world *data; #pragma pack(pop) - int frame; int initialzed; - int sim_frame; - float sim_start; int sim_run, max_runs; - - float sim_speed; - float frame_lerp; + + 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 + frame_lerp, // Fractional part of sim_internal_time + pause_offset_target; // struct cell_terminal { @@ -740,6 +745,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; @@ -1049,6 +1057,7 @@ static int is_simulation_running(void) static void simulation_stop(void) { world.buttons[ k_world_button_sim ].pressed = 0; + world.buttons[ k_world_button_pause ].pressed = 0; world.num_fishes = 0; world.sim_frame = 0; @@ -1185,7 +1194,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; @@ -1220,16 +1229,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 = local_x > 0.5f? 1: 0; + + // 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) ) + 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; @@ -1290,9 +1321,19 @@ void vg_update(void) // ======================================================================================================== if( is_simulation_running() ) { - while( world.sim_frame < (int)((vg_time-world.sim_start)*world.sim_speed) ) + 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); + + 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 ++ ) @@ -1344,6 +1385,7 @@ void vg_update(void) } fish->state = k_fish_state_dead; + fish->death_time = -1000.0f; continue; } @@ -1386,7 +1428,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; } @@ -1402,7 +1447,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] || @@ -1410,7 +1458,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 ); @@ -1420,7 +1476,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 ); @@ -1525,7 +1584,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; @@ -1613,9 +1672,20 @@ void vg_update(void) vg_success( "Run passed, starting next\n" ); world.sim_run ++; world.sim_frame = 0; - world.sim_start = vg_time; + world.sim_target = 0; world.num_fishes = 0; + // Reset timing reference points + world.sim_delta_ref = vg_time; + world.sim_internal_ref = 0.0f; + + if( world.buttons[ k_world_button_pause ].pressed ) + world.pause_offset_target = 0.5f; + else + world.pause_offset_target = 0.0f; + + world.sim_internal_time = 0.0f; + for( int i = 0; i < world.w*world.h; i ++ ) world.data[ i ].state &= ~FLAG_FLIP_FLOP; @@ -1658,9 +1728,7 @@ void vg_update(void) // Position update // ===================================================================================================== - float scaled_time = 0.0f; - scaled_time = (vg_time-world.sim_start)*world.sim_speed; - world.frame_lerp = scaled_time - (float)world.sim_frame; + world.frame_lerp = world.sim_internal_time - floorf( world.sim_internal_time ); for( int i = 0; i < world.num_fishes; i ++ ) { @@ -1669,7 +1737,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); @@ -1735,6 +1803,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 ); } } } @@ -1812,8 +1883,39 @@ 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.sim_internal_time = 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 @@ -1822,50 +1924,91 @@ static void wbutton_run( enum e_world_button btn_name ) if( vg_get_button_up( "primary" ) && is_hovering ) { // Click event - btn->pressed ^= 0x1; - if( btn_name == k_world_button_sim ) { - if( btn->pressed ) + if( world.buttons[ k_world_button_pause ].pressed ) { - 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_start = vg_time; - world.sim_run = 0; - world.sim_speed = 2.5f; - - for( int i = 0; i < world.w*world.h; i ++ ) - world.data[ i ].state &= ~FLAG_FLIP_FLOP; - - io_reset(); + if( !btn->pressed ) + { + btn->pressed = 1; + simulation_start(); + + world.pause_offset_target = 0.5f; + } + else + world.pause_offset_target += 1.0f; } else { - simulation_stop(); + btn->pressed ^= 0x1; + + if( btn->pressed ) + simulation_start(); + else + simulation_stop(); } } + else if( btn_name == k_world_button_pause ) + { + btn->pressed ^= 0x1; + + world.sim_internal_ref = world.sim_internal_time; + world.sim_delta_ref = vg_time; + + if( btn->pressed ) + { + float time_frac = world.sim_internal_time-floorf(world.sim_internal_time); + world.pause_offset_target = 0.5f - time_frac; + } + else + world.pause_offset_target = 0.0f; + } + else + { + btn->pressed ^= 0x1; + } + + sfx_set_play( &audio_clicks, &audio_system_ui, btn->pressed?1:0 ); } // Drawing - if( btn->pressed ) btn->light_target = is_hovering? 0.9f: 0.8f; - else btn->light_target = is_hovering? 0.2f: 0.0f; + if( btn->pressed ) + { + if( is_hovering ) + { + btn->light_target = 0.9f; + } + else + { + if( btn_name == k_world_button_sim && world.buttons[ k_world_button_pause ].pressed ) + btn->light_target = fabsf(sinf( vg_time * 2.0f )) * 0.3f + 0.3f; + else + btn->light_target = 0.8f; + } + } + else + { + btn->light_target = is_hovering? 0.2f: 0.0f; + } if( vg_get_button( "primary" ) && is_hovering ) btn->light_target = 1.0f; - + btn->light = vg_lerpf( btn->light, btn->light_target, vg_time_delta*26.0f ); + // Draw + + 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, 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 ); } @@ -1881,6 +2024,11 @@ 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 = 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; // BACKGROUND // ======================================================================================================== @@ -1940,18 +2088,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 ); } @@ -2047,7 +2211,8 @@ void vg_render(void) glUniform1i( SHADER_UNIFORM( shader_buttons, "uTexMain" ), 0 ); wbutton_run( k_world_button_sim ); - wbutton_run( k_world_button_ff ); + wbutton_run( k_world_button_pause ); + //wbutton_run( k_world_button_wire_mode ); // WIRES // ======================================================================================================== @@ -2060,7 +2225,7 @@ void vg_render(void) glUniform4f( SHADER_UNIFORM( shader_wire, "uColour" ), 0.2f, 0.2f, 0.2f, 1.0f ); if( world.id_drag_from ) - { + { 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 ); @@ -2081,6 +2246,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; @@ -2092,8 +2259,10 @@ 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 ); 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 ); @@ -2110,12 +2279,6 @@ void vg_render(void) SHADER_USE( shader_tile_colour ); glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour, "uPv" ), 1, GL_FALSE, (float *)vg_pv ); use_mesh( &world.shapes ); - - 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; glEnable(GL_BLEND); @@ -2134,9 +2297,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() ) + { + 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 ) { - glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1.0f, 1.0f, 1.0f, 0.1f ); + 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 ); } @@ -2311,14 +2500,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: "", 4, 0 ); gui_end_right(); gui_reset_colours();