Fixed runs drawing getting stuck
[fishladder.git] / fishladder.c
index f310bd53ff798ad3f00e018b7a60158163ba610b..26a94eb404d0abf76f19a0a5e9a33ce64342165c 100644 (file)
@@ -98,7 +98,7 @@ m3x3f m_mdl;
 #define FLAG_WALL 0x40
 
 #define FLAG_FLIP_FLOP 0x100
-#define FLAG_TRIGGER_FLIP 0x200
+#define FLAG_TRIGGERED 0x200
 #define FLAG_FLIP_ROTATING 0x400
 #define FLAG_TARGETED 0x800
 
@@ -126,7 +126,11 @@ enum cell_type
        k_cell_type_ramp_right = 3,
        k_cell_type_ramp_left = 6,
        k_cell_type_split = 7,
-       k_cell_type_merge = 13
+       k_cell_type_merge = 13,
+       k_cell_type_con_r = 1,
+       k_cell_type_con_u = 2,
+       k_cell_type_con_l = 4,
+       k_cell_type_con_d = 8
 };
 
 v3f colour_sets[] =
@@ -188,6 +192,14 @@ static void use_mesh( struct mesh *m )
        glBindVertexArray( m->vao );
 }
 
+enum e_fish_state
+{
+       k_fish_state_soon_dead = -1,
+       k_fish_state_dead = 0,
+       k_fish_state_alive,
+       k_fish_state_bg
+};
+
 struct world
 {
 #pragma pack(push,1)
@@ -252,7 +264,7 @@ struct world
        {
                v2i pos;
                v2i dir;
-               int alive;
+               enum e_fish_state state;
                char payload;
                float death_time;
                v2f physics_v;
@@ -463,6 +475,7 @@ static int map_load( const char *str, const char *name )
                        // Tile initialization
                        // row[ cx ] .. etc
                        struct cell *cell = &row[ cx ];
+                       cell->config = 0xF;
                        
                        if( *c == '+' || *c == '-' )
                        {
@@ -643,7 +656,18 @@ static void map_serialize( FILE *stream )
 
 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" );
+       vg_init( argc, argv, "Marble Computing | SPACE: Test | LeftClick: Toggle tile | RightClick: Drag wire" );
+}
+
+static int console_credits( int argc, char const *argv[] )
+{
+       vg_info( "Aknowledgements:\n" );
+       vg_info( "  GLFW         zlib/libpng  glfw.org\n" );
+       vg_info( "  miniaudio    MIT0         miniaud.io\n" );
+       vg_info( "  QOI          MIT          phoboslab.org\n" );
+       vg_info( "  STB library  MIT          nothings.org\n" );
+       vg_info( "  Weiholmir font            justfredrik.itch.io\n" );
+       return 0;
 }
 
 static int console_save_map( int argc, char const *argv[] )
@@ -740,6 +764,7 @@ static void simulation_stop(void)
        world.simulating = 0;
        world.num_fishes = 0;
        world.sim_frame = 0;
+       world.sim_run = 0;
        
        io_reset();
        
@@ -784,6 +809,11 @@ void vg_start(void)
                .name = "map",
                .function = console_changelevel
        });
+       
+       vg_function_push( (struct vg_cmd){
+               .name = "credits",
+               .function = console_credits
+       });
 
        // Quad mesh
        {
@@ -942,10 +972,15 @@ void vg_free(void)
        map_free();
 }
 
+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;
+}
+
 static int cell_interactive( v2i co )
 {
        // Bounds check
-       if( co[0] < 2 || co[0] >= world.w-2 || co[1] < 2 || co[1] >= world.h-2 )
+       if( !world_check_pos_ok( co ) )
                return 0;
 
        // Flags check
@@ -1052,7 +1087,7 @@ static void map_reclassify( v2i start, v2i end, int update_texbuffer )
                for( int x = px0; x < px1; x ++ )
                {
                        struct cell *cell = pcell((v2i){x,y});
-               
+
                        v2i dirs[] = {{1,0},{0,1},{-1,0},{0,-1}};
                        
                        u8 height = 0;
@@ -1084,6 +1119,30 @@ static void map_reclassify( v2i start, v2i end, int update_texbuffer )
                        info_px[1] = cell->state & FLAG_WALL? 0: 255;
                        info_px[2] = 0;
                        info_px[3] = 0;
+                       
+                       if( 
+                               (
+                                       ((cell->state & FLAG_IS_TRIGGER) && (cell->config == 0xF || cell->config == k_cell_type_split)) || 
+                                       ((cell->state & FLAG_TARGETED) && (cell->config != k_cell_type_split)) 
+                               ) && update_texbuffer
+                       ){
+                               cell->state &= ~(FLAG_TARGETED|FLAG_IS_TRIGGER);
+                               for( u32 i = 0; i < 2; i ++ )
+                               {
+                                       if( cell->links[i] )
+                                       {
+                                               struct cell *other_ptr = &world.data[ cell->links[i] ];
+                                               other_ptr->links[ i ] = 0;
+                                               other_ptr->state &= ~FLAG_IS_TRIGGER;
+                                               
+                                               if( other_ptr->links[ i ^ 0x1 ] == 0 )
+                                                       other_ptr->state &= ~FLAG_TARGETED;
+                                       }
+                               }
+                               
+                               cell->links[0] = 0;
+                               cell->links[1] = 0;
+                       }
                }
        }
        
@@ -1110,6 +1169,10 @@ 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;
 
+u16 id_drag_from = 0;
+v2f drag_from_co;
+v2f drag_to_co;
+
 void vg_update(void)
 {
        // Fit within screen
@@ -1137,10 +1200,6 @@ void vg_update(void)
        world.tile_x = floorf( world.tile_pos[0] );
        world.tile_y = floorf( world.tile_pos[1] );
 
-       static u16 id_drag_from = 0;
-       static v2f drag_from_co;
-       static v2f drag_to_co;
-       
        // Tilemap editing
        if( !world.simulating )
        {
@@ -1166,35 +1225,18 @@ void vg_update(void)
                                
                                if( cell_ptr->state & FLAG_CANAL )
                                {
+                                       cell_ptr->links[0] = 0;
+                                       cell_ptr->links[1] = 0;
+                                       
                                        sfx_set_playrnd( &audio_tile_mod, &audio_system_sfx, 3, 6 );
                                        world.score ++;
                                }
                                else
-                               {
-                                       if( cell_ptr->state & (FLAG_IS_TRIGGER|FLAG_TARGETED) )
-                                       {
-                                               cell_ptr->state &= ~(FLAG_IS_TRIGGER|FLAG_TARGETED);
-                                               for( u32 i = 0; i < 2; i ++ )
-                                               {
-                                                       if( cell_ptr->links[i] )
-                                                       {
-                                                               struct cell *other_ptr = &world.data[ cell_ptr->links[i] ];
-                                                               other_ptr->links[ i ] = 0;
-                                                               other_ptr->state &= ~FLAG_IS_TRIGGER;
-                                                               
-                                                               if( other_ptr->links[ i ^ 0x1 ] == 0 )
-                                                                       other_ptr->state &= ~(FLAG_TARGETED);
-                                                       }
-                                               }
-                                       }
-                                       
+                               {                                       
                                        sfx_set_playrnd( &audio_tile_mod, &audio_system_sfx, 0, 3 );
                                        world.score --;
                                }
 
-                               cell_ptr->links[0] = 0;
-                               cell_ptr->links[1] = 0;
-                                       
                                map_reclassify(         (v2i){ world.tile_x -2, world.tile_y -2 }, 
                                                                                (v2i){ world.tile_x +2, world.tile_y +2 }, 1 );
                        }
@@ -1254,14 +1296,8 @@ void vg_update(void)
                else
                        world.selected = -1;
                        
-               if( vg_get_button("secondary") && id_drag_from )
-               {
-                       vg_line2( drag_from_co, drag_to_co, 0xff00ff00, 0xffffff00 );
-               }
-               else
-               {
+               if( !(vg_get_button("secondary") && id_drag_from) )
                        id_drag_from = 0;
-               }
        }
        else
        {
@@ -1311,6 +1347,8 @@ void vg_update(void)
                                {
                                        cell->state &= ~FLAG_FLIP_ROTATING;
                                }
+                               if( cell->state & FLAG_IS_TRIGGER )
+                                       cell->state &= ~FLAG_TRIGGERED;
                        }
                        
                        int alive_count = 0;
@@ -1320,106 +1358,149 @@ void vg_update(void)
                        {
                                struct fish *fish = &world.fishes[i];
                                
-                               if( fish->alive == -1 )
-                                       fish->alive = 0;
+                               if( fish->state == k_fish_state_soon_dead )
+                                       fish->state = k_fish_state_dead;
                                
-                               if( fish->alive != 1 )
+                               if( fish->state < k_fish_state_alive )
                                        continue;
                                
                                struct cell *cell_current = pcell( fish->pos );
 
-                               // Apply to output
-                               if( cell_current->state & FLAG_OUTPUT )
+                               if( fish->state == k_fish_state_alive )
                                {
-                                       for( int j = 0; j < arrlen( world.io ); j ++ )
+                                       // Apply to output
+                                       if( cell_current->state & FLAG_OUTPUT )
+                                       {
+                                               for( int j = 0; j < arrlen( world.io ); j ++ )
+                                               {
+                                                       struct cell_terminal *term = &world.io[j];
+                                                       
+                                                       if( term->id == fish->pos[1]*world.w + fish->pos[0] )
+                                                       {
+                                                               struct terminal_run *run = &term->runs[ world.sim_run ];
+                                                               if( run->recv_count < vg_list_size( run->recieved ) )
+                                                                       run->recieved[ run->recv_count ++ ] = fish->payload;
+                                                               
+                                                               break;
+                                                       }
+                                               }
+                                               
+                                               fish->state = k_fish_state_dead;
+                                               continue;
+                                       }
+                                       
+                                       if( cell_current->config == k_cell_type_split )
                                        {
-                                               struct cell_terminal *term = &world.io[j];
+                                               // Flip flop L/R
+                                               fish->dir[0] = cell_current->state&FLAG_FLIP_FLOP?1:-1;
+                                               fish->dir[1] = 0;
                                                
-                                               if( term->id == fish->pos[1]*world.w + fish->pos[0] )
+                                               if( !(cell_current->state & FLAG_TARGETED) )
+                                                       cell_current->state ^= FLAG_FLIP_FLOP;
+                                       }
+                                       else if( cell_current->config == k_cell_type_merge )
+                                       {
+                                               // Can only move up
+                                               fish->dir[0] = 0;
+                                               fish->dir[1] = -1;
+                                       }
+                                       else
+                                       {
+                                               struct cell *cell_next = pcell( (v2i){ fish->pos[0]+fish->dir[0], fish->pos[1]+fish->dir[1] } );
+                                               if( !(cell_next->state & (FLAG_CANAL|FLAG_OUTPUT)) )
                                                {
-                                                       struct terminal_run *run = &term->runs[ world.sim_run ];
-                                                       if( run->recv_count < vg_list_size( run->recieved ) )
-                                                               run->recieved[ run->recv_count ++ ] = fish->payload;
+                                                       // Try other directions for valid, so down, left, right..
+                                                       v2i dirs[] = {{1,0},{-1,0},{0,-1}};
+                                                       //vg_info( "Trying some other directions...\n" );
                                                        
-                                                       break;
+                                                       for( int j = 0; j < vg_list_size(dirs); j ++ )
+                                                       {
+                                                               if( (dirs[j][0] == -fish->dir[0]) && (dirs[j][1] == -fish->dir[1]) )
+                                                                       continue;
+                                                       
+                                                               if( pcell( (v2i){ fish->pos[0]+dirs[j][0], fish->pos[1]+dirs[j][1] } )->state & (FLAG_CANAL|FLAG_OUTPUT) )
+                                                               {
+                                                                       fish->dir[0] = dirs[j][0];
+                                                                       fish->dir[1] = dirs[j][1];
+                                                               }
+                                                       }
                                                }
                                        }
                                        
-                                       fish->alive = 0;
-                                       continue;
-                               }
-                               
-                               if( cell_current->config == k_cell_type_split )
-                               {
-                                       // Flip flop L/R
-                                       fish->dir[0] = cell_current->state&FLAG_FLIP_FLOP?1:-1;
-                                       fish->dir[1] = 0;
+                                       fish->pos[0] += fish->dir[0];
+                                       fish->pos[1] += fish->dir[1];
                                        
-                                       if( !(cell_current->state & FLAG_TARGETED) )
-                                               cell_current->state ^= FLAG_FLIP_FLOP;
-                               }
-                               else if( cell_current->config == k_cell_type_merge )
-                               {
-                                       // Can only move up
-                                       fish->dir[0] = 0;
-                                       fish->dir[1] = -1;
-                               }
-                               else
-                               {
-                                       struct cell *cell_next = pcell( (v2i){ fish->pos[0]+fish->dir[0], fish->pos[1]+fish->dir[1] } );
-                                       if( !(cell_next->state & (FLAG_CANAL|FLAG_OUTPUT)) )
+                                       struct cell *cell_entry = pcell( fish->pos );
+                                       
+                                       if( !(cell_entry->state & (FLAG_INPUT|FLAG_CANAL|FLAG_OUTPUT) ))
                                        {
-                                               // Try other directions for valid, so down, left, right..
-                                               v2i dirs[] = {{1,0},{-1,0},{0,-1}};
-                                               //vg_info( "Trying some other directions...\n" );
-                                               
-                                               for( int j = 0; j < vg_list_size(dirs); j ++ )
+                                               if( world_check_pos_ok( fish->pos ) )
+                                                       fish->state = k_fish_state_bg;
+                                               else
+                                                       fish->state = k_fish_state_dead;
+                                       }
+                                       else
+                                       {
+                                               if( fish->dir[0] )
                                                {
-                                                       if( (dirs[j][0] == -fish->dir[0]) && (dirs[j][1] == -fish->dir[1]) )
-                                                               continue;
-                                               
-                                                       if( pcell( (v2i){ fish->pos[0]+dirs[j][0], fish->pos[1]+dirs[j][1] } )->state & (FLAG_CANAL|FLAG_OUTPUT) )
+                                                       if( cell_entry->config == k_cell_type_split ||
+                                                                cell_entry->config == k_cell_type_ramp_right ||
+                                                                cell_entry->config == k_cell_type_ramp_left )
                                                        {
-                                                               fish->dir[0] = dirs[j][0];
-                                                               fish->dir[1] = dirs[j][1];
+                                                               // Special death (FALL)
+                                                               /*
+                                                               v2_sub( fish->physics_co, fish->physics_v, fish->physics_v );
+                                                               v2_divs( fish->physics_v, vg_time_delta, fish->physics_v );
+                                                               */
+                                                               
+                                                               fish->state = k_fish_state_dead;
+                                                               vg_error( "REMOVE THIS CONDITION\n" );
+                                                               continue;
                                                        }
                                                }
+                                       
+                                               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;
+                                               }
                                        }
                                }
-                               
-                               fish->pos[0] += fish->dir[0];
-                               fish->pos[1] += fish->dir[1];
-                               
-                               struct cell *cell_entry = pcell( fish->pos );
-                               
-                               if( !(cell_entry->state & (FLAG_INPUT|FLAG_CANAL|FLAG_OUTPUT) ))
-                                       fish->alive = 0;
-                               else
+                               else if( fish->state == k_fish_state_bg )
                                {
-                                       if( fish->dir[0] )
+                                       fish->pos[0] += fish->dir[0];
+                                       fish->pos[1] += fish->dir[1];
+                                       
+                                       if( !world_check_pos_ok( fish->pos ) )
+                                               fish->state = k_fish_state_dead;
+                                       else
                                        {
-                                               if( cell_entry->config == k_cell_type_split ||
-                                                        cell_entry->config == k_cell_type_ramp_right ||
-                                                        cell_entry->config == k_cell_type_ramp_left )
+                                               struct cell *cell_entry = pcell( fish->pos );
+                                               
+                                               if( cell_entry->state & FLAG_CANAL )
                                                {
-                                                       // Special death (FALL)                                                 
-                                                       v2_sub( fish->physics_co, fish->physics_v, fish->physics_v );
-                                                       v2_divs( fish->physics_v, vg_time_delta, fish->physics_v );
-
-                                                       fish->alive = -2;
-                                                       vg_warn( "Special death (fall)\n" );
-                                                       continue;
+                                                       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;
+                                                               
+                                                               fish->dir[0] = 0;
+                                                               fish->dir[1] = 0;
+                                                               
+                                                               switch( cell_entry->config )
+                                                               {
+                                                                       case k_cell_type_con_r: fish->dir[0] =  1; break;
+                                                                       case k_cell_type_con_l: fish->dir[0] = -1; break;
+                                                                       case k_cell_type_con_u: fish->dir[1] =  1; break;
+                                                                       case k_cell_type_con_d: fish->dir[1] = -1; break;
+                                                               }
+                                                       }
                                                }
                                        }
-                               
-                                       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;
-                                       }
                                }
+                               else { vg_error( "fish behaviour unimplemented for behaviour type (%d)\n" ); }
                                
-                               if( fish->alive )
+                               if( fish->state >= k_fish_state_alive )
                                        alive_count ++;
                        }
                        
@@ -1428,7 +1509,7 @@ void vg_update(void)
                        {
                                struct fish *fish = &world.fishes[i];
                                
-                               if( fish->alive == 1 )
+                               if( fish->state == k_fish_state_alive )
                                {
                                        struct cell *cell_current = pcell( fish->pos );
 
@@ -1443,6 +1524,8 @@ void vg_update(void)
                                                
                                                struct cell *target_peice = &world.data[ cell_current->links[trigger_id] ];
                                                
+                                               cell_current->state |= FLAG_TRIGGERED;
+                                               
                                                if( trigger_id )
                                                        target_peice->state |= FLAG_FLIP_FLOP;
                                                else
@@ -1454,16 +1537,17 @@ void vg_update(void)
                        // Third pass (collisions)
                        for( int i = 0; i < world.num_fishes; i ++ )
                        {
-                               if( world.fishes[i].alive == 1 )
+                               if( world.fishes[i].state == k_fish_state_alive )
                                {
                                        for( int j = i+1; j < world.num_fishes; j ++ )
                                        {
-                                               if( (world.fishes[j].alive == 1) && (world.fishes[i].pos[0] == world.fishes[j].pos[0]) &&
+                                               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]) )
                                                {
                                                        // Shatter death (+0.5s)
-                                                       world.fishes[i].alive = -1;
-                                                       world.fishes[j].alive = -1;
+                                                       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;
                                                }
@@ -1486,7 +1570,7 @@ void vg_update(void)
                                                struct fish *fish = &world.fishes[world.num_fishes++];
                                                fish->pos[0] = posx;
                                                fish->pos[1] = posy;
-                                               fish->alive = 1;
+                                               fish->state = k_fish_state_alive;
                                                fish->payload = term->runs[ world.sim_run ].conditions[ world.sim_frame ];
                                                
                                                int can_spawn = 0;
@@ -1594,78 +1678,79 @@ void vg_update(void)
                {
                        struct fish *fish = &world.fishes[i];
                        
-                       if( fish->alive == 0 )
+                       if( fish->state == k_fish_state_dead )
                                continue;
                        
-                       if( fish->alive == -1 && (world.frame_lerp > fish->death_time) )
+                       if( fish->state == k_fish_state_soon_dead && (world.frame_lerp > fish->death_time) )
                                continue; // Todo: particle thing?
+                               
+                       struct cell *cell = pcell(fish->pos);
+                       v2f const *curve;
+                       
+                       float t = world.frame_lerp;
+                       float ti = 1.0f-t;
+                       
+                       v2_copy( fish->physics_co, fish->physics_v );
                        
-                       if( fish->alive == -2 )
+                       switch( cell->config )
                        {
-                               v2_muladds( fish->physics_v, (v2f){ 0.0, -9.8f }, vg_time_delta, fish->physics_v );
-                               v2_muladds( fish->physics_co, fish->physics_v, vg_time_delta, fish->physics_co );
+                               case 13:
+                                       if( fish->dir[0] == 1 )
+                                               curve = curve_12;
+                                       else
+                                               curve = curve_9;
+                               break;
+                               case k_cell_type_con_r: curve = curve_1; 
+                                       if( fish->dir[0] == 1 ) t = ti;
+                               break;
+                               case k_cell_type_con_l: curve = curve_4; 
+                                       if( fish->dir[0] == -1 ) t = ti;
+                               break;
+                               case k_cell_type_con_u: curve = curve_2; 
+                                       if( fish->dir[1] == 1 ) t = ti;
+                               break;
+                               case k_cell_type_con_d: curve = curve_8; 
+                                       if( fish->dir[1] == 1 ) t = ti;
+                               break;
+                               case 3: curve = curve_3; break;
+                               case 6: curve = curve_6; break;
+                               case 9: curve = curve_9; break;
+                               case 12: curve = curve_12; break;
+                               case 7:
+                                       if( t > curve_7_linear_section )
+                                       {
+                                               t -= curve_7_linear_section;
+                                               t *= (1.0f/(1.0f-curve_7_linear_section));
+                                               
+                                               curve = cell->state & FLAG_FLIP_FLOP? curve_7: curve_7_1;
+                                       }
+                                       else curve = NULL;
+                               break;
+                               default: curve = NULL; break;
                        }
-                       else
-                       {               
-                               struct cell *cell = pcell(fish->pos);
-                               v2f const *curve;
-                               
-                               float t = world.frame_lerp;
-                               
-                               v2_copy( fish->physics_co, fish->physics_v );
-                               
-                               switch( cell->config )
-                               {
-                                       case 13:
-                                               if( fish->dir[0] == 1 )
-                                                       curve = curve_12;
-                                               else
-                                                       curve = curve_9;
-                                       break;
-                                       case 1: curve = curve_1; break;
-                                       case 4: curve = curve_4; break;
-                                       case 2: curve = curve_2; break;
-                                       case 8: curve = curve_8; break;
-                                       case 3: curve = curve_3; break;
-                                       case 6: curve = curve_6; break;
-                                       case 9: curve = curve_9; break;
-                                       case 12: curve = curve_12; break;
-                                       case 7:
-                                               if( t > curve_7_linear_section )
-                                               {
-                                                       t -= curve_7_linear_section;
-                                                       t *= (1.0f/(1.0f-curve_7_linear_section));
-                                                       
-                                                       curve = cell->state & FLAG_FLIP_FLOP? curve_7: curve_7_1;
-                                               }
-                                               else curve = NULL;
-                                       break;
-                                       default: curve = NULL; break;
-                               }
 
-                               if( curve )
-                               {
-                                       float t2 = t * t;
-                                       float t3 = t * t * t;
+                       if( curve )
+                       {
+                               float t2 = t * t;
+                               float t3 = t * t * t;
 
-                                       float cA = 3.0f*t2 - 3.0f*t3;
-                                       float cB = 3.0f*t3 - 6.0f*t2 + 3.0f*t;
-                                       float cC = 3.0f*t2 - t3 - 3.0f*t + 1.0f;
+                               float cA = 3.0f*t2 - 3.0f*t3;
+                               float cB = 3.0f*t3 - 6.0f*t2 + 3.0f*t;
+                               float cC = 3.0f*t2 - t3 - 3.0f*t + 1.0f;
 
-                                       fish->physics_co[0] = t3*curve[3][0] + cA*curve[2][0] + cB*curve[1][0] + cC*curve[0][0];
-                                       fish->physics_co[1] = t3*curve[3][1] + cA*curve[2][1] + cB*curve[1][1] + cC*curve[0][1];
-                                       fish->physics_co[0] += (float)fish->pos[0];
-                                       fish->physics_co[1] += (float)fish->pos[1];
-                               }
-                               else
-                               {
-                                       v2f origin;
-                                       origin[0] = (float)fish->pos[0] + (float)fish->dir[0]*-0.5f + 0.5f;
-                                       origin[1] = (float)fish->pos[1] + (float)fish->dir[1]*-0.5f + 0.5f;
-                                       
-                                       fish->physics_co[0] = origin[0] + (float)fish->dir[0]*t;
-                                       fish->physics_co[1] = origin[1] + (float)fish->dir[1]*t;
-                               }
+                               fish->physics_co[0] = t3*curve[3][0] + cA*curve[2][0] + cB*curve[1][0] + cC*curve[0][0];
+                               fish->physics_co[1] = t3*curve[3][1] + cA*curve[2][1] + cB*curve[1][1] + cC*curve[0][1];
+                               fish->physics_co[0] += (float)fish->pos[0];
+                               fish->physics_co[1] += (float)fish->pos[1];
+                       }
+                       else
+                       {
+                               v2f origin;
+                               origin[0] = (float)fish->pos[0] + (float)fish->dir[0]*-0.5f + 0.5f;
+                               origin[1] = (float)fish->pos[1] + (float)fish->dir[1]*-0.5f + 0.5f;
+                               
+                               fish->physics_co[0] = origin[0] + (float)fish->dir[0]*t;
+                               fish->physics_co[1] = origin[1] + (float)fish->dir[1]*t;
                        }
                }
        }
@@ -1821,10 +1906,10 @@ void vg_render(void)
                {
                        struct fish *fish = &world.fishes[i];
                        
-                       if( fish->alive == 0 )
+                       if( fish->state == k_fish_state_dead || fish->state == k_fish_state_bg )
                                continue;
                        
-                       if( fish->alive == -1 && (world.frame_lerp > fish->death_time) )
+                       if( fish->state == k_fish_state_soon_dead && (world.frame_lerp > fish->death_time) )
                                continue;
                        
                        v4f dot_colour = { 0.0f, 0.0f, 0.0f, 1.0f };
@@ -1890,7 +1975,7 @@ void vg_render(void)
        }
        
        // Edit overlay
-       if( world.selected != -1 && !(world.data[ world.selected ].state & FLAG_CANAL) )
+       if( world.selected != -1 && !(world.data[ world.selected ].state & FLAG_CANAL) && !id_drag_from )
        {
                v2i new_begin = { world.tile_x - 2, world.tile_y - 2 };
                v2i new_end = { world.tile_x + 2, world.tile_y + 2 };
@@ -1918,9 +2003,19 @@ void vg_render(void)
        glBindVertexArray( world.wire.vao );
 
        glUniformMatrix3fv( SHADER_UNIFORM( shader_wire, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
-       glUniform1f( SHADER_UNIFORM( shader_wire, "uCurve" ), 0.4f );
        glUniform4f( SHADER_UNIFORM( shader_wire, "uColour" ), 0.2f, 0.2f, 0.2f, 1.0f );
-               
+       
+       if( id_drag_from )
+       {
+               glUniform1f( SHADER_UNIFORM( shader_wire, "uCurve" ), 0.4f );
+               glUniform3f( SHADER_UNIFORM( shader_wire, "uStart" ), drag_from_co[0], drag_from_co[1], 0.06f );
+               glUniform3f( SHADER_UNIFORM( shader_wire, "uEnd" ), drag_to_co[0], drag_to_co[1], 0.06f );
+               glDrawElements( GL_TRIANGLES, world.wire.em, GL_UNSIGNED_SHORT, (void*)(0) );
+       }
+       
+       float rp_x1 = world.frame_lerp*9.0f;
+       float rp_x2 = 1.0f-rp_x1*expf(1.0f-rp_x1)* 0.36f;
+       
        for( int y = 2; y < world.h-2; y ++ )
        {
                for( int x = 2; x < world.w-2; x ++ )
@@ -1945,6 +2040,7 @@ void vg_render(void)
                                        endpoint[0] = x+0.5f;
                                        endpoint[1] = y+0.5f;
                                        
+                                       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 );
                                        glDrawElements( GL_TRIANGLES, world.wire.em, GL_UNSIGNED_SHORT, (void*)(0) );
@@ -1987,7 +2083,7 @@ void vg_render(void)
                                        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 )
+                                       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 );
@@ -2098,6 +2194,8 @@ void vg_render(void)
        }
        
        // Level scores
+       glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.4f*1.25f, 0.39f*1.25f, 0.45f*1.25f, 1.0f );
+       
        use_mesh( &world.numbers );
        for( int i = 0; i < level_count; i ++ )
        {
@@ -2110,9 +2208,11 @@ void vg_render(void)
                };
                
                if( clevel->completed )
-               {
                        draw_numbers( level_ui_space, clevel->score );
-               }
+                       
+               level_ui_space[0] = -0.975f;
+               level_ui_space[1] -= 0.01f;
+               draw_numbers( level_ui_space, i );
        }
        
        //use_mesh( &world.numbers );
@@ -2121,5 +2221,5 @@ void vg_render(void)
 
 void vg_ui(void)
 {
-       //ui_test();
+       ui_test();
 }