added scroll bar ui
[fishladder.git] / fishladder.c
index 372f21e2a7e780b1e1a2ae846900cdeb3ffec50b..7cda200bd7727d537cc849e214f27d7c3c3e5ec1 100644 (file)
@@ -1,9 +1,39 @@
 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
 
-//#define VG_STEAM
+#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
+                       Play button / Speed controller
+                       
+                       
+       After release:
+               
+*/
+
 const char *level_pack_1[] = { 
        "level0", 
        "level1", 
@@ -182,7 +212,7 @@ v2f const curve_7_1[] = {{0.5f,0.8438f},{1.0f-0.875f,0.8438f},{1.0-0.625f,0.5f},
 float const curve_7_linear_section = 0.1562f;
 
 v3f colour_sets[] =
-{ { 0.9f, 0.6f, 0.20f },
+{ { 1.0f, 0.9f, 0.3f },
   { 0.2f, 0.9f, 0.14f },
   { 0.4f, 0.8f, 1.00f } };
 
@@ -245,7 +275,8 @@ enum e_fish_state
        k_fish_state_soon_dead = -1,
        k_fish_state_dead = 0,
        k_fish_state_alive,
-       k_fish_state_bg
+       k_fish_state_bg,
+       k_fish_state_soon_alive
 };
 
 struct world
@@ -270,6 +301,7 @@ struct world
        int simulating;
        int sim_run, max_runs;
        
+       float sim_speed;
        float frame_lerp;
        
        struct cell_terminal
@@ -323,13 +355,12 @@ struct world
        
        int num_fishes;
        
-       char map_name[128];
+       char map_name[64];
        struct career_level *ptr_career_level;
        
        u32 score;
        u32 completed;
        u32 time;
-       
 } world = {};
 
 static void map_free(void)
@@ -371,9 +402,9 @@ static int map_load( const char *str, const char *name )
        // Scan for width
        for(;; world.w ++)
        {
-               if( str[world.w] == ';' )
+               if( c[world.w] == ';' )
                        break;
-               else if( !str[world.w] )
+               else if( !c[world.w] )
                {
                        vg_error( "Unexpected EOF when parsing level\n" );
                        return 0;
@@ -706,6 +737,7 @@ static void map_serialize( FILE *stream )
 int main( int argc, char *argv[] )
 {
        vg_init( argc, argv, "Marble Computing | SPACE: Test | LeftClick: Toggle tile | RightClick: Drag wire" );
+       return 0;
 }
 
 static int console_credits( int argc, char const *argv[] )
@@ -1234,7 +1266,7 @@ void vg_update(void)
        world.tile_y = floorf( world.tile_pos[1] );
 
        // Tilemap editing
-       if( !world.simulating )
+       if( !world.simulating && !gui_want_mouse() )
        {
                v2_copy( vg_mouse_ws, drag_to_co );
        
@@ -1356,6 +1388,7 @@ void vg_update(void)
                        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;
@@ -1367,7 +1400,7 @@ void vg_update(void)
        // Fish ticks
        if( world.simulating )
        {       
-               while( world.sim_frame < (int)((vg_time-world.sim_start)*2.0f) )
+               while( world.sim_frame < (int)((vg_time-world.sim_start)*world.sim_speed) )
                {
                        //vg_info( "frame: %u\n", world.sim_frame );
                        sfx_set_playrnd( &audio_random, &audio_system_balls_switching, 0, 9 );
@@ -1394,6 +1427,9 @@ void vg_update(void)
                                if( fish->state == k_fish_state_soon_dead )
                                        fish->state = k_fish_state_dead;
                                
+                               if( fish->state == k_fish_state_soon_alive )
+                                       fish->state = k_fish_state_alive;
+                               
                                if( fish->state < k_fish_state_alive )
                                        continue;
                                
@@ -1459,7 +1495,12 @@ void vg_update(void)
                                                        struct cell_description *desc = &cell_descriptions[ cell_next->config ];
                                                        
                                                        if( cell_next->config == k_cell_type_merge )
-                                                               fish->flow_reversed = 0;
+                                                       {
+                                                               if( fish->dir[0] == 0 )
+                                                                       fish->state = k_fish_state_dead;
+                                                               else
+                                                                       fish->flow_reversed = 0;
+                                                       }
                                                        else
                                                        {
                                                                if( cell_next->config == k_cell_type_split )
@@ -1468,6 +1509,8 @@ void vg_update(void)
                                                                        {
                                                                                sfx_set_playrnd( &audio_splitter, &audio_system_balls_important, 0, 1 );
                                                                                cell_next->state |= FLAG_FLIP_ROTATING;
+                                                                               
+                                                                               fish->flow_reversed = 0;
                                                                        }
                                                                        else
                                                                                fish->state = k_fish_state_dead;
@@ -1481,7 +1524,7 @@ void vg_update(void)
                                                        fish->state = world_check_pos_ok( fish->pos )? k_fish_state_bg: k_fish_state_dead;
                                        }
                                        
-                                       v2i_add( fish->pos, fish->dir, fish->pos );
+                                       //v2i_add( fish->pos, fish->dir, fish->pos );
                                }
                                else if( fish->state == k_fish_state_bg )
                                {
@@ -1498,7 +1541,9 @@ void vg_update(void)
                                                        if( cell_entry->config == k_cell_type_con_r || cell_entry->config == k_cell_type_con_u 
                                                                || cell_entry->config == k_cell_type_con_l || cell_entry->config == k_cell_type_con_d )
                                                        {
-                                                               fish->state = k_fish_state_alive;
+                                                               sw_set_achievement( "CAN_DO_THAT" );
+                                                       
+                                                               fish->state = k_fish_state_soon_alive;
                                                                
                                                                fish->dir[0] = 0;
                                                                fish->dir[1] = 0;
@@ -1528,6 +1573,7 @@ void vg_update(void)
                                
                                if( fish->state == k_fish_state_alive )
                                {
+                                       v2i_add( fish->pos, fish->dir, fish->pos );
                                        struct cell *cell_current = pcell( fish->pos );
 
                                        if( cell_current->state & FLAG_IS_TRIGGER )
@@ -1552,23 +1598,58 @@ void vg_update(void)
                        }
                        
                        // Third pass (collisions)
+                       struct fish *fi, *fj;
+                       
                        for( int i = 0; i < world.num_fishes; i ++ )
                        {
-                               if( world.fishes[i].state == k_fish_state_alive )
+                               fi = &world.fishes[i];
+                               
+                               if( fi->state == k_fish_state_alive )
                                {
+                                       int continue_again = 0;
+                               
                                        for( int j = i+1; j < world.num_fishes; j ++ )
                                        {
-                                               if( (world.fishes[j].state == k_fish_state_alive) && 
-                                                        (world.fishes[i].pos[0] == world.fishes[j].pos[0]) &&
-                                                        (world.fishes[i].pos[1] == world.fishes[j].pos[1]) )
+                                               fj = &world.fishes[j];
+                                               
+                                               if( (fj->state == k_fish_state_alive) )
                                                {
-                                                       // Shatter death (+0.5s)
-                                                       world.fishes[i].state = k_fish_state_soon_dead;
-                                                       world.fishes[j].state = k_fish_state_soon_dead;
-                                                       world.fishes[i].death_time = 0.5f;
-                                                       world.fishes[j].death_time = 0.5f;
+                                                       v2i fi_prev;
+                                                       v2i fj_prev;
+                                                       
+                                                       v2i_sub( fi->pos, fi->dir, fi_prev );
+                                                       v2i_sub( fj->pos, fj->dir, fj_prev );
+                                               
+                                                       int 
+                                                       collide_next_frame = ( 
+                                                                (fi->pos[0] == fj->pos[0]) &&
+                                                                (fi->pos[1] == fj->pos[1]))? 1: 0,
+                                                       collide_this_frame = (
+                                                                (fi_prev[0] == fj->pos[0]) &&
+                                                                (fi_prev[1] == fj->pos[1]) &&
+                                                                (fj_prev[0] == fi->pos[0]) &&
+                                                                (fj_prev[1] == fi->pos[1])
+                                                               )? 1: 0;
+                                                       
+                                                       if( collide_next_frame || collide_this_frame )
+                                                       {
+                                                               sw_set_achievement( "BANG" );
+                                                       
+                                                               // Shatter death (+0.5s)
+                                                               float death_time = collide_this_frame? 0.0f: 0.5f;
+                                                               
+                                                               fi->state = k_fish_state_soon_dead;
+                                                               fj->state = k_fish_state_soon_dead;
+                                                               fi->death_time = death_time;
+                                                               fj->death_time = death_time;
+                                                               
+                                                               continue_again = 1;
+                                                               break;
+                                                       }
                                                }
                                        }
+                                       if( continue_again )
+                                               continue;
                                }
                        }
                        
@@ -1647,6 +1728,10 @@ void vg_update(void)
                                                world.sim_frame = 0;
                                                world.sim_start = vg_time;
                                                world.num_fishes = 0;
+                                               
+                                               for( int i = 0; i < world.w*world.h; i ++ )
+                                                       world.data[ i ].state &= ~FLAG_FLIP_FLOP;
+                                               
                                                continue;
                                        }
                                        else
@@ -1664,6 +1749,9 @@ void vg_update(void)
                                }
                                else
                                {
+                                       if( world.sim_run > 0 )
+                                               sw_set_achievement( "GOOD_ENOUGH" );
+                                       
                                        vg_error( "Level failed :(\n" );
                                }
                                
@@ -1683,7 +1771,7 @@ void vg_update(void)
                }
                
                float scaled_time = 0.0f;
-               scaled_time = (vg_time-world.sim_start)*2.0f;
+               scaled_time = (vg_time-world.sim_start)*world.sim_speed;
                world.frame_lerp = scaled_time - (float)world.sim_frame;
                
                // Update positions
@@ -1877,6 +1965,27 @@ void vg_render(void)
        }
        
        
+       // Level title
+       ui_begin( &ui_global_ctx, 512, 256 );
+       
+       ui_global_ctx.override_colour = 0xff9a8a89;
+       //ui_text( &ui_global_ctx, world.map_title, 6, 0 );
+       ui_global_ctx.override_colour = 0xffffffff;
+       
+       ui_resolve( &ui_global_ctx );
+       
+       m3x3f world_text;
+       m3x3_copy( vg_pv, world_text );
+       m3x3_translate( world_text, (v3f){ 1.55f, 1.9f, 0.0f } );
+       m3x3_rotate( world_text, VG_PIf*0.5f );
+       m3x3_scale( world_text, (v3f){0.01f,-0.01f,0.01f} );
+       
+       ui_draw( &ui_global_ctx, world_text );
+       
+       // Main
+       // =========================================================================================
+       
+       use_mesh( &world.tile );
        SHADER_USE( shader_tile_main );
 
        m2x2f subtransform;
@@ -1904,7 +2013,7 @@ void vg_render(void)
        SHADER_USE( shader_ball );
        glUniformMatrix3fv( SHADER_UNIFORM( shader_ball, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
        
-       vg_tex2d_bind( &tex_ball, 0 );
+       vg_tex2d_bind( &tex_ball_noise, 0 );
        glUniform1i( SHADER_UNIFORM( shader_ball, "uTexMain" ), 0 );
        
        // Draw 'fish'
@@ -1925,7 +2034,8 @@ void vg_render(void)
                        
                        glUniform3fv( SHADER_UNIFORM( shader_ball, "uColour" ), 1, dot_colour );
                        glUniform2fv( SHADER_UNIFORM( shader_ball, "uOffset" ), 1, fish->physics_co );
-                       draw_mesh( 0, 32 );
+                       glUniform2f( SHADER_UNIFORM( shader_ball, "uTexOffset" ), (float)i * 1.2334, (float)i * -0.3579f );
+                       draw_mesh( 0, 2 );
                }
        }
        
@@ -2222,12 +2332,63 @@ void vg_render(void)
                level_ui_space[1] -= 0.01f;
                draw_numbers( level_ui_space, i );
        }
-       
-       //use_mesh( &world.numbers );
-       //draw_numbers( (v3f){ 0.0f, -0.5f, 0.1f }, 128765 );
 }
 
 void vg_ui(void)
 {
-       ui_test();
+       ui_global_ctx.cursor[0] = 0;
+       ui_global_ctx.cursor[1] = 0;
+       ui_global_ctx.cursor[2] = 256;
+
+       gui_fill_y();
+       
+       ui_global_ctx.id_base = 4 << 16;
+       
+       gui_new_node();
+       {
+               gui_fill_rect( ui_global_ctx.cursor, 0xff5577ff );
+               
+               gui_text( "MARBLE COMPUTING", 4, 0 );
+               
+               ui_global_ctx.cursor[1] += 45;
+               ui_global_ctx.cursor[3] = 709;
+               
+               // Level scroll view
+               gui_new_node();
+               {
+                       gui_fill_rect( ui_global_ctx.cursor, 0xffff7729 );
+                       gui_set_clip( ui_global_ctx.cursor );
+                       
+                       ui_global_ctx.cursor[2] = 16;
+                       gui_align_right();
+                       
+                       static struct ui_scrollbar sb = {
+                               .bar_height = 400
+                       };
+                       ui_scrollbar( &ui_global_ctx, &sb, 0 );
+                       
+                       ui_global_ctx.cursor[2] = 240;
+                       ui_global_ctx.cursor[3] = 50;
+                       gui_align_left();
+                       
+                       ui_px content_height = vg_list_size(cmp_levels_basic)*ui_global_ctx.cursor[3];
+                       ui_global_ctx.cursor[1] -= ui_calculate_content_scroll( &sb, content_height );
+                       
+                       for( int i = 0; i < vg_list_size(cmp_levels_basic); i ++ )
+                       {
+                               struct cmp_level *lvl_info = &cmp_levels_basic[i];
+                       
+                               gui_new_node();
+                               {
+                                       gui_fill_rect( ui_global_ctx.cursor, i&0x1?0xff23fce45:0xff8722f8 );
+                                       gui_text( lvl_info->title, 3, 0 );
+                               }
+                               gui_end_down();
+                       }
+                       
+                       gui_release_clip();
+               }
+               gui_end_down();
+       }
+       gui_end();
 }