implement repeating sound effects
[fishladder.git] / fishladder.c
index 6d6486be3cbb1741602233b1a25505594dfb1141..ac1efcfc19045eec2175c81e72359e0385b43d3e 100644 (file)
@@ -2,118 +2,7 @@
 
 //#define VG_STEAM
 #include "vg/vg.h"
-
-SHADER_DEFINE( shader_tile_colour,
-
-       // VERTEX
-       "layout (location=0) in vec2 a_co;"
-       "uniform mat3 uPv;"
-       "uniform vec3 uOffset;"
-       ""
-       "void main()"
-       "{"
-               "gl_Position = vec4( uPv * vec3( a_co * uOffset.z + uOffset.xy, 1.0 ), 1.0 );"
-       "}",
-       
-       // FRAGMENT
-       "out vec4 FragColor;"
-       "uniform vec4 uColour;"
-       ""
-       "void main()"
-       "{"
-               "FragColor = uColour;"
-       "}"
-       ,
-       UNIFORMS({ "uPv", "uOffset", "uColour" })
-)
-
-SHADER_DEFINE( shader_ball,
-       // VERTEX
-       "layout (location=0) in vec2 a_co;"
-       "uniform vec2 uOffset;"
-       "uniform mat3 uPv;"
-       ""
-       "out vec2 aTexCoords;"
-       ""
-       "void main()"
-       "{"
-               // Create texture coords
-               "aTexCoords = a_co;"
-               
-               // Vertex transform
-               "vec3 worldpos = vec3( a_co * 0.5 - 0.25 + uOffset, 1.0 );"
-               "gl_Position = vec4( uPv * worldpos, 1.0 );"
-       "}",
-       
-       // FRAGMENT
-       "out vec4 FragColor;"
-       ""
-       "uniform sampler2D uTexMain;"
-       "uniform vec3 uColour;"
-       ""
-       "in vec2 aTexCoords;"
-       ""
-       "void main()"
-       "{"
-               "vec4 glyph = texture( uTexMain, aTexCoords );"
-               "FragColor = vec4( uColour + glyph.rgb * 0.2, glyph.a );"
-       "}"
-       ,
-       UNIFORMS({ "uTexMain", "uColour", "uOffset", "uPv" })
-)
-
-SHADER_DEFINE( shader_tile_main,
-       // VERTEX
-       "layout (location=0) in vec2 a_co;"
-       "uniform vec4 uOffset;" // Tile x/y, uv x/y
-       "uniform mat3 uPv;"
-       "uniform mat2 uSubTransform;"
-       ""
-       "out vec4 aTexCoords;"
-       ""
-       "vec2 hash22(vec2 p)"
-       "{"
-               "vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));"
-               "p3 += dot(p3, p3.yzx+33.33);"
-               "return fract((p3.xx+p3.yz)*p3.zy);"
-       "}"
-       ""
-       "void main()"
-       "{"
-               // Create texture coords
-               "vec2 random_offset = floor(hash22(uOffset.xy) * 4.0) * 0.25;"
-               "vec2 edge_safe_coords = a_co * 0.98 + 0.01;"
-               "aTexCoords = vec4((edge_safe_coords + uOffset.zw) * 0.25, edge_safe_coords * 0.25 + random_offset );"
-               
-               // Vertex transform
-               "vec2 subtransform = uSubTransform * (a_co-0.5) + 0.5;"
-               "vec3 worldpos = vec3( subtransform + uOffset.xy, 1.0 );"
-               "gl_Position = vec4( uPv * worldpos, 1.0 );"
-       "}",
-       
-       // FRAGMENT
-       "out vec4 FragColor;"
-       ""
-       "uniform sampler2D uTexGlyphs;"
-       "uniform sampler2D uTexWood;"
-       ""
-       "in vec4 aTexCoords;"
-       ""
-       "void main()"
-       "{"
-               "vec3 shadowing_colour = vec3( 0.93, 0.88536, 0.8184 );"
-               "vec4 glyph = texture( uTexGlyphs, aTexCoords.xy );"
-               "vec4 wood = texture( uTexWood, aTexCoords.zw );"
-               "vec4 wood_secondary = texture( uTexWood, aTexCoords.zw + 0.25 );"
-               "vec3 wood_comp = mix( wood_secondary.rgb * shadowing_colour, wood.rgb, clamp( glyph.b * 2.0 - 1.0, 0.0, 1.0 ) );"
-               
-               "vec3 shadows = mix( vec3( 0.85, 0.7344, 0.561 ), vec3(1.0,1.0,1.0), glyph.r );"
-               
-               "FragColor = vec4( wood_comp * shadows, glyph.b );"
-       "}"
-       ,
-       UNIFORMS({ "uPv", "uOffset", "uTexGlyphs", "uTexWood", "uSubTransform" })
-)
+#include "fishladder_resources.h"
 
 const char *level_pack[] = 
 {
@@ -173,33 +62,6 @@ const char *level_pack[] =
        "#############;\n"
 };
 
-GLuint tex_tile_data;
-GLuint tex_tile_detail;
-GLuint tex_wood;
-GLuint tex_ball;
-
-sfx_system_t audio_system_sfx = 
-{
- .vol = 1.f,
- .spd = 1.f,
- .ch = 1,
- .cur = 0,
- .vol_src = &g_vol_sfx,
- .flags = 0x00,
- .fadeout = FADEOUT_LENGTH,
- .name = "sfx"
-};
-
-sfx_set_t audio_tile_mod = 
-{
- .sources = "\
-sound/mod00.ogg\0\
-sound/mod01.ogg\0\
-sound/mod02.ogg\0\
-sound/mod03.ogg\0",
- .flags = 0
-};
-
 m3x3f m_projection;
 m3x3f m_view;
 m3x3f m_mdl;
@@ -208,10 +70,6 @@ m3x3f m_mdl;
 #define FLAG_OUTPUT 0x2
 #define FLAG_CANAL 0x4
 #define FLAG_WALL 0x8
-#define FLAG_DROP_L 0x10
-#define FLAG_SPLIT 0x20
-#define FLAG_MERGER 0x40
-#define FLAG_DROP_R 0x80
 #define FLAG_FLIP_FLOP 0x100
 #define FLAG_FLIP_ROTATING 0x200
 
@@ -328,7 +186,8 @@ struct world
        
        struct mesh tile, circle;
        
-       int selected;
+       int selected, tile_x, tile_y;
+       v2f tile_pos;
        
        struct fish
        {
@@ -356,6 +215,7 @@ static void map_free(void)
        world.io = NULL;
 }
 
+static void map_reclassify( v2i start, v2i end );
 static int map_load( const char *str )
 {
        map_free();
@@ -472,6 +332,7 @@ static int map_load( const char *str )
                c ++;
        }
        
+       map_reclassify( NULL, NULL );
        vg_success( "Map loaded! (%u:%u)\n", world.w, world.h );
        return 1;
 }
@@ -486,13 +347,6 @@ int main( int argc, char *argv[] )
        vg_init( argc, argv, "Fish (Marbles Computer) Ladder Simulator 2022 | N,M: change level | SPACE: Test | LeftClick: Toggle tile" );
 }
 
-void vg_register(void)
-{
-       SHADER_INIT( shader_tile_colour );
-       SHADER_INIT( shader_tile_main );
-       SHADER_INIT( shader_ball );
-}
-
 void vg_start(void)
 {
        // Quad mesh
@@ -542,50 +396,19 @@ void vg_start(void)
                init_mesh( &world.circle, circle_mesh, vg_list_size( circle_mesh ) );
        }
        
-       // Textures
-       {
-               tex_tile_detail = vg_tex2d_rgba( "textures/tile_overlays.png" );
-               vg_tex2d_mipmap();
-               vg_tex2d_linear_mipmap();
-               vg_tex2d_repeat();
-               
-               tex_tile_data = vg_tex2d_rgba( "textures/tileset.png" );
-               vg_tex2d_mipmap();
-               vg_tex2d_linear_mipmap();
-               vg_tex2d_repeat();
-               
-               tex_wood = vg_tex2d_rgba( "textures/wood.png" );
-               vg_tex2d_mipmap();
-               vg_tex2d_linear_mipmap();
-               vg_tex2d_repeat();
-               
-               tex_ball = vg_tex2d_rgba( "textures/ball.png" );
-               vg_tex2d_mipmap();
-               vg_tex2d_linear_mipmap();
-               vg_tex2d_clamp();
-       }
-       
-       // Audio
-       {
-               sfx_set_init( &audio_tile_mod, NULL );
-       }
+       resource_load_main();
        
        map_load( level_pack[ 0 ] );
 }
 
 void vg_free(void)
 {
+       resource_free_main();
+
        free_mesh( &world.tile );
        free_mesh( &world.circle );
        
        map_free();
-       
-       glDeleteTextures( 1, &tex_tile_data );
-       glDeleteTextures( 1, &tex_tile_detail );
-       glDeleteTextures( 1, &tex_wood );
-       glDeleteTextures( 1, &tex_ball );
-       
-       sfx_set_free( &audio_tile_mod );
 }
 
 static int cell_interactive( v2i co )
@@ -673,6 +496,41 @@ static int cell_interactive( v2i co )
        return 1;
 }
 
+static void map_reclassify( v2i start, v2i end )
+{
+       v2i full_start = { 2,2 };
+       v2i full_end = { world.w-2, world.h-2 };
+       
+       if( !start || !end )
+       {
+               start = full_start;
+               end = full_end;
+       }
+
+       for( int y = vg_max( start[1], full_start[1] ); y < vg_min( end[1], full_end[1] ); y ++ )
+       {
+               for( int x = vg_max( start[0], full_start[0] ); x < vg_min( end[0], full_end[0] ); x ++ )
+               {
+                       v2i dirs[] = {{1,0},{0,1},{-1,0},{0,-1}};
+                       
+                       u8 config = 0x00;
+                       
+                       if( pcell((v2i){x,y})->state & FLAG_CANAL )
+                       {
+                               for( int i = 0; i < vg_list_size( dirs ); i ++ )
+                               {
+                                       struct cell *neighbour = pcell((v2i){x+dirs[i][0], y+dirs[i][1]});
+                                       if( neighbour->state & (FLAG_CANAL|FLAG_INPUT|FLAG_OUTPUT) )
+                                               config |= 0x1 << i;
+                               }
+                       } 
+                       else config = 0xF;
+                       
+                       pcell((v2i){x,y})->config = config;
+               }
+       }
+}
+
 void vg_update(void)
 {
        static int curlevel = 0;
@@ -711,24 +569,29 @@ void vg_update(void)
        vg_projection_update();
        
        // Input stuff
+       v2_copy( vg_mouse_ws, world.tile_pos );
        
-       v2f tile_pos;
-       v2_copy( vg_mouse_ws, tile_pos );
-       
-       int tile_x = floorf( tile_pos[0] );
-       int tile_y = floorf( tile_pos[1] );
+       world.tile_x = floorf( world.tile_pos[0] );
+       world.tile_y = floorf( world.tile_pos[1] );
 
        // Tilemap editing
        if( !world.simulating )
        {
-               if( cell_interactive( (v2i){ tile_x, tile_y } ))
+               if( cell_interactive( (v2i){ world.tile_x, world.tile_y } ))
                {
-                       world.selected = tile_y * world.w + tile_x;
+                       world.selected = world.tile_y * world.w + world.tile_x;
                        
                        if( vg_get_button_down("primary") )
                        {
                                world.data[ world.selected ].state ^= FLAG_CANAL;
-                               sfx_set_playrnd( &audio_tile_mod, &audio_system_sfx );
+                               
+                               if( world.data[ world.selected ].state & FLAG_CANAL )
+                                       sfx_set_playrnd( &audio_tile_mod, &audio_system_sfx, 3, 6 );
+                               else
+                                       sfx_set_playrnd( &audio_tile_mod, &audio_system_sfx, 0, 3 );
+                                       
+                               map_reclassify(         (v2i){ world.tile_x -2, world.tile_y -2 }, 
+                                                                               (v2i){ world.tile_x +2, world.tile_y +2 } );
                        }
                }
                else
@@ -753,6 +616,8 @@ void vg_update(void)
                {
                        vg_success( "Starting simulation!\n" );
                        
+                       sfx_set_playrnd( &audio_rolls, &audio_system_balls_rolling, 0, 1 );
+                       
                        world.simulating = 1;
                        world.num_fishes = 0;
                        world.sim_frame = 0;
@@ -767,56 +632,7 @@ void vg_update(void)
                                world.io[i].recv_count = 0;
                }
        }
-       
-       // Simulation stuff
-       // ========================================================
-       
-       for( int y = 2; y < world.h-2; y ++ )
-       {
-               for( int x = 2; x < world.w-2; x ++ )
-               {
-                       v2i dirs[] = {{1,0},{0,1},{-1,0},{0,-1}};
-                       
-                       u8 config = 0x00;
-                       
-                       if( pcell((v2i){x,y})->state & FLAG_CANAL )
-                       {
-                               for( int i = 0; i < vg_list_size( dirs ); i ++ )
-                               {
-                                       struct cell *neighbour = pcell((v2i){x+dirs[i][0], y+dirs[i][1]});
-                                       if( neighbour->state & (FLAG_CANAL|FLAG_INPUT|FLAG_OUTPUT) )
-                                               config |= 0x1 << i;
-                               }
-                       } else config = 0xF;
-                       
-                       pcell((v2i){x,y})->config = config;
-                       pcell((v2i){x,y})->state &= ~(FLAG_DROP_L|FLAG_DROP_R|FLAG_SPLIT|FLAG_MERGER);
-               }
-       }
-       
-       for( int y = 2; y < world.h-2; y ++ )
-               for( int x = 2; x < world.w-2; x ++ )
-               {
-                       // R,D,L,-   1110 (splitter, 1 drop created)
-                       
-                       // R,-,L,U - 1011 (merger, 2 drop created)
-                       
-                       u8 config = pcell((v2i){x,y})->config;
-                       
-                       if( config == k_cell_type_split ) // splitter
-                       {
-                               struct cell *cell = pcell((v2i){x,y});
-                       
-                               cell->state |= (FLAG_SPLIT | FLAG_DROP_L | FLAG_DROP_R);
-                       }
-                       else if( config == k_cell_type_merge )
-                       {
-                               world.data[y*world.w+x-1].state |= FLAG_DROP_R;
-                               world.data[y*world.w+x+1].state |= FLAG_DROP_L;
-                               world.data[y*world.w+x].state |= FLAG_MERGER;
-                       }
-               }
-               
+
        // Fish ticks
        if( world.simulating )
        {
@@ -895,7 +711,7 @@ void vg_update(void)
                                        continue;
                                }
                                
-                               if( cell_current->state & FLAG_SPLIT )
+                               if( cell_current->config == k_cell_type_split )
                                {
                                        // Flip flop L/R
                                        fish->dir[0] = cell_current->state&FLAG_FLIP_FLOP?1:-1;
@@ -903,7 +719,7 @@ void vg_update(void)
                                        
                                        cell_current->state ^= FLAG_FLIP_FLOP;
                                }
-                               else if( cell_current->state & FLAG_MERGER )
+                               else if( cell_current->config == k_cell_type_merge )
                                {
                                        // Can only move up
                                        fish->dir[0] = 0;
@@ -941,7 +757,10 @@ void vg_update(void)
                                        fish->alive = 0;
                                else
                                        if( cell_entry->config == k_cell_type_split )
+                                       {
+                                               sfx_set_playrnd( &audio_splitter, &audio_system_balls_important, 0, 1 );
                                                cell_entry->state |= FLAG_FLIP_ROTATING;
+                                       }
                        }
                        
                        world.sim_frame ++;
@@ -949,11 +768,22 @@ void vg_update(void)
        }
 }
 
-static void render_tiles(void)
+static void render_tiles( v2i start, v2i end, v4f const regular_colour, v4f const selected_colour )
 {
-       for( int y = 0; y < world.h; y ++ )
+       v2i full_start = { 0,0 };
+       v2i full_end = { world.w, world.h };
+       
+       if( !start || !end )
        {
-               for( int x = 0; x < world.w; x ++ )
+               start = full_start;
+               end = full_end;
+       }
+       
+       glUniform4fv( SHADER_UNIFORM( shader_tile_main, "uColour" ), 1, regular_colour );
+
+       for( int y = start[1]; y < end[1]; y ++ )
+       {
+               for( int x = start[0]; x < end[0]; x ++ )
                {
                        struct cell *cell = pcell((v2i){x,y});
                        int selected = world.selected == y*world.w + x;
@@ -975,7 +805,14 @@ static void render_tiles(void)
                        }
                        
                        glUniform4f( SHADER_UNIFORM( shader_tile_main, "uOffset" ), (float)x, (float)y, uv[0], uv[1] );
-                       draw_mesh( 0, 2 );
+                       if( selected )
+                       {
+                               glUniform4fv( SHADER_UNIFORM( shader_tile_main, "uColour" ), 1, selected_colour );
+                               draw_mesh( 0, 2 );
+                               glUniform4fv( SHADER_UNIFORM( shader_tile_main, "uColour" ), 1, regular_colour );
+                       }
+                       else                    
+                               draw_mesh( 0, 2 );
                }
        }
 }
@@ -996,6 +833,9 @@ void vg_render(void)
                frame_lerp = scaled_time - (float)world.sim_frame;
        }
        
+       v4f const colour_default = {1.0f, 1.0f, 1.0f, 1.0f};
+       v4f const colour_selected = {0.90f, 0.92f, 1.0f, 1.0f};
+       
        v2f const curve_3[] = {{0.5f,1.0f},{0.5f,0.625f},{0.625f,0.5f},{1.0f,0.5f}};
        v2f const curve_6[] = {{0.5f,1.0f},{0.5f,0.625f},{0.375f,0.5f},{0.0f,0.5f}};
        v2f const curve_9[] = {{1.0f,0.5f},{0.625f,0.5f},{0.5f,0.375f},{0.5f,0.0f}};
@@ -1022,17 +862,16 @@ void vg_render(void)
        m2x2_identity( subtransform );
        glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main, "uSubTransform" ), 1, GL_FALSE, (float *)subtransform );  
        glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_main, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
+       glUniform1f( SHADER_UNIFORM( shader_tile_main, "uGhost" ), 0.0f );
        
        // Bind textures
-       glActiveTexture( GL_TEXTURE0 );
-       glBindTexture( GL_TEXTURE_2D, tex_tile_data );
+       vg_tex2d_bind( &tex_tile_data, 0 );
        glUniform1i( SHADER_UNIFORM( shader_tile_main, "uTexGlyphs" ), 0 );
        
-       glActiveTexture( GL_TEXTURE1 );
-       glBindTexture( GL_TEXTURE_2D, tex_wood );
+       vg_tex2d_bind( &tex_wood, 1 );
        glUniform1i( SHADER_UNIFORM( shader_tile_main, "uTexWood" ), 1 );
        
-       render_tiles();
+       render_tiles( NULL, NULL, colour_default, colour_default );
        
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -1041,8 +880,7 @@ void vg_render(void)
        SHADER_USE( shader_ball );
        glUniformMatrix3fv( SHADER_UNIFORM( shader_ball, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
        
-       glActiveTexture( GL_TEXTURE0 );
-       glBindTexture( GL_TEXTURE_2D, tex_ball );
+       vg_tex2d_bind( &tex_ball, 0 );
        glUniform1i( SHADER_UNIFORM( shader_ball, "uTexMain" ), 0 );
        
        // Draw 'fish'
@@ -1125,15 +963,13 @@ void vg_render(void)
        SHADER_USE( shader_tile_main );
 
        // Bind textures
-       glActiveTexture( GL_TEXTURE0 );
-       glBindTexture( GL_TEXTURE_2D, tex_tile_data );
+       vg_tex2d_bind( &tex_tile_data, 0 );
        glUniform1i( SHADER_UNIFORM( shader_tile_main, "uTexGlyphs" ), 0 );
        
-       glActiveTexture( GL_TEXTURE1 );
-       glBindTexture( GL_TEXTURE_2D, tex_wood );
+       vg_tex2d_bind( &tex_wood, 1 );
        glUniform1i( SHADER_UNIFORM( shader_tile_main, "uTexWood" ), 1 );
        
-       render_tiles();
+       render_tiles( NULL, NULL, colour_default, colour_selected  );
 
        //      Draw splitters
 
@@ -1143,7 +979,7 @@ void vg_render(void)
                {
                        struct cell *cell = pcell((v2i){x,y});
                        
-                       if( cell->state & FLAG_SPLIT )
+                       if( cell->config == k_cell_type_split )
                        {
                                float rotation = cell->state & FLAG_FLIP_FLOP? vg_rad( -45.0f ): vg_rad( 45.0f );
                                
@@ -1174,6 +1010,26 @@ void vg_render(void)
                }
        }
        
+       // Edit overlay
+       if( world.selected != -1 && !(world.data[ world.selected ].state & FLAG_CANAL) )
+       {
+               v2i new_begin = { world.tile_x - 2, world.tile_y - 2 };
+               v2i new_end = { world.tile_x + 2, world.tile_y + 2 };
+       
+               world.data[ world.selected ].state ^= FLAG_CANAL;
+               map_reclassify( new_begin, new_end );
+               
+               m2x2_identity( subtransform );
+               glUniform1f( SHADER_UNIFORM( shader_tile_main, "uGhost" ), 1.0f );
+               glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main, "uSubTransform" ), 1, GL_FALSE, (float *)subtransform );  
+               glUniform2fv( SHADER_UNIFORM( shader_tile_main, "uMousePos" ), 1, world.tile_pos );
+               
+               render_tiles( new_begin, new_end, colour_default, colour_default );
+               
+               world.data[ world.selected ].state ^= FLAG_CANAL;
+               map_reclassify( new_begin, new_end );
+       }
+       
        //glDisable(GL_BLEND);
        
        glDisable(GL_BLEND);