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
+ Row Gridlines for I/O DONE
+ Play button / Speed controller ( play buttern, pause, speed control, step button )
After release:
v3f colour_sets[] =
{ { 1.0f, 0.9f, 0.3f },
{ 0.2f, 0.9f, 0.14f },
- { 0.4f, 0.8f, 1.00f } };
+ { 0.4f, 0.8f, 1.00f },
+ { 0.882f, 0.204f, 0.922f }
+};
static void colour_code_v3( char cc, v3f target )
{
k_fish_state_soon_alive
};
+enum e_world_button
+{
+ k_world_button_none = -1,
+ k_world_button_sim = 0,
+ k_world_button_ff = 1,
+ k_world_button_wire_mode = 2
+};
+
struct world
{
#pragma pack(push,1)
int sim_frame;
float sim_start;
- int simulating;
int sim_run, max_runs;
float sim_speed;
}
*io;
+ struct cell_button
+ {
+ float light_target, light;
+ int pressed;
+ }
+ buttons[4];
+
int w, h;
- struct mesh tile, circle, numbers;
+ struct mesh shapes, numbers;
struct mesh_wire
{
GLuint vao, vbo, ebo;
u32 time;
} world;
+static struct cell_button *get_wbutton( enum e_world_button btn )
+{
+ return &world.buttons[ btn ];
+}
+
void leaderboard_set_score( struct cmp_level *cmp_level, u32 score );
static void map_free(void)
return random_noise[ (random_noise[p[1] & 1023] + p[0]) & 1023 ] & umod;
}
+static struct cell *pcell( v2i pos )
+{
+ return &world.data[ pos[1]*world.w + pos[0] ];
+}
+
static void map_reclassify( v2i start, v2i end, int update_texbuffer );
static int map_load( const char *str, const char *name )
{
char link_id_buffer[32];
int link_id_n = 0;
-
+
for(;;)
{
if( !*c )
turtle[1] = 16+posy;
turtle_dir[0] = 0;
- turtle_dir[1] = posy <= 4? -1: 1;
+ turtle_dir[1] = pcell((v2i){ posx,posy })->state & FLAG_INPUT? 1: -1;
original_y = turtle_dir[1];
for( int i = 0; i < 100; i ++ )
return 0;
}
-static struct cell *pcell( v2i pos )
-{
- return &world.data[ pos[1]*world.w + pos[0] ];
-}
-
static void map_serialize( FILE *stream )
{
for( int y = 0; y < world.h; y ++ )
static void simulation_stop(void)
{
- world.simulating = 0;
+ get_wbutton( k_world_button_sim )->pressed = 0;
+ get_wbutton( k_world_button_ff )->pressed = 0;
+
world.num_fishes = 0;
world.sim_frame = 0;
world.sim_run = 0;
vg_info( "Stopping simulation!\n" );
}
+static void wbutton_run( enum e_world_button btn_name )
+{
+ struct cell_button *btn = &world.buttons[btn_name];
+
+ // Interaction
+
+ int is_hovering = (world.tile_x == world.w-1 && world.tile_y == world.h-btn_name-2)?1:0;
+ if( vg_get_button_up( "primary" ) && is_hovering )
+ {
+ // Click event
+ btn->pressed ^= 0x1;
+
+ if( btn_name == k_world_button_sim )
+ {
+ if( btn->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();
+ }
+ else
+ {
+ simulation_stop();
+ }
+ }
+ }
+
+ // Drawing
+ if( btn->pressed ) btn->light_target = is_hovering? 0.9f: 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 );
+
+ 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 );
+
+ draw_mesh( 0, 2 );
+}
+
static int console_changelevel( int argc, char const *argv[] )
{
if( argc >= 1 )
.function = console_credits
});
- // Quad mesh
+ // Combined quad, long quad / empty circle / filled circle mesh
{
- float quad_mesh[] =
- {
+ float combined_mesh[6*8 + 32*6*3] = {
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f,
- 0.0f, 0.0f, 0.0f, 1.0f, 4.0f, 1.0f,
- 0.0f, 0.0f, 4.0f, 1.0f, 4.0f, 0.0f
+ 0.0f, 0.0f, 0.0f, 0.2f, 1.0f, 0.2f,
+ 0.0f, 0.0f, 1.0f, 0.2f, 1.0f, 0.0f
};
- init_mesh( &world.tile, quad_mesh, vg_list_size(quad_mesh) );
- }
-
- // Circle mesh
- {
- float circle_mesh[32*6*3];
- int res = vg_list_size( circle_mesh ) / (6*3);
+ float *circle_mesh = combined_mesh + 6*4;
+ int const res = 32;
for( int i = 0; i < res; i ++ )
{
v2_copy( v1, circle_mesh+i*6+2 );
}
- init_mesh( &world.circle, circle_mesh, vg_list_size( circle_mesh ) );
+ init_mesh( &world.shapes, combined_mesh, vg_list_size( combined_mesh ) );
}
// Numbers mesh
glDeleteBuffers( 1, &world.wire.vbo );
glDeleteBuffers( 1, &world.wire.ebo );
- free_mesh( &world.tile );
- free_mesh( &world.circle );
+ free_mesh( &world.shapes );
free_mesh( &world.numbers );
map_free();
world.tile_y = floorf( world.tile_pos[1] );
// Tilemap editing
- if( !world.simulating && !gui_want_mouse() )
+ if( !get_wbutton( k_world_button_sim )->pressed && !gui_want_mouse() )
{
v2_copy( vg_mouse_ws, drag_to_co );
}
}
else
+ {
world.selected = -1;
+ }
if( !(vg_get_button("secondary") && id_drag_from) )
id_drag_from = 0;
id_drag_from = 0;
}
- // Simulation stop/start
- if( vg_get_button_down("go") )
- {
- if( world.simulating )
- {
- simulation_stop();
- }
- else
- {
- 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;
- 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();
- }
- }
-
// Fish ticks
- if( world.simulating )
+ if( get_wbutton( k_world_button_sim )->pressed )
{
while( world.sim_frame < (int)((vg_time-world.sim_start)*world.sim_speed) )
{
vg_error( "Level failed :(\n" );
}
-
- simulation_stop(); // TODO: Async?
+ simulation_stop();
break;
}
// NOTE: this is for final optimisations ONLY!
// ======================================================================
- use_mesh( &world.tile );
+ use_mesh( &world.shapes );
// Draw background
// Main
// =========================================================================================
- use_mesh( &world.tile );
+ use_mesh( &world.shapes );
SHADER_USE( shader_tile_main );
m2x2f subtransform;
glUniform1i( SHADER_UNIFORM( shader_ball, "uTexMain" ), 0 );
// Draw 'fish'
- if( world.simulating )
+ if( get_wbutton( k_world_button_sim )->pressed )
{
for( int i = 0; i < world.num_fishes; i ++ )
{
//glDisable(GL_BLEND);
+ SHADER_USE( shader_buttons );
+ glUniformMatrix3fv( SHADER_UNIFORM( shader_buttons, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
+
+ vg_tex2d_bind( &tex_buttons, 0 );
+ glUniform1i( SHADER_UNIFORM( shader_buttons, "uTexMain" ), 0 );
+
+ wbutton_run( k_world_button_sim );
+ wbutton_run( k_world_button_ff );
+
+
// Draw connecting wires
glDisable(GL_BLEND);
SHADER_USE( shader_tile_colour );
glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
- use_mesh( &world.circle );
+ use_mesh( &world.shapes );
- int const filled_start = 0;
- int const filled_count = 32;
- int const empty_start = 32;
- int const empty_count = 32*2;
+ 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);
// Draw i/o arrays
for( int i = 0; i < arrlen( world.io ); i ++ )
for( int k = 0; k < term->run_count; k ++ )
{
+ float y_offset = is_input? 1.2f: -0.2f;
+ float y_h = (is_input? 0.2f: -0.2f) * (float)k;
+ float y_row = is_input?
+ (y_offset + (float)posy + (float)(term->run_count-1)*0.2f) - y_h:
+ (float)posy + y_offset + y_h;
+
+ if( k & 0x1 )
+ {
+ glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1.0f, 1.0f, 1.0f, 0.1f );
+ glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)posx, y_row - 0.1f, 1.0f );
+ draw_mesh( 2, 2 );
+ }
+
for( int j = 0; j < term->runs[k].condition_count; j ++ )
{
- float y_offset = is_input? 1.2f: -0.2f;
- float y_h = (is_input? 0.2f: -0.2f) * (float)k;
-
if( is_input )
{
- glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)posx + 0.2f + 0.2f * (float)j,
- (y_offset + (float)posy + (float)(term->run_count-1)*0.2f) - y_h, 0.1f );
+ glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)posx + 0.2f + 0.2f * (float)j, y_row, 0.1f );
colour_code_v3( term->runs[k].conditions[j], dot_colour );
glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour );
}
else
{
- glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)posx + 0.2f + 0.2f * (float)j, (float)posy + y_offset + y_h, 0.1f );
+ glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)posx + 0.2f + 0.2f * (float)j, y_row, 0.1f );
if( term->runs[k].recv_count > j )
{
}
}
- if( world.simulating )
- {
- glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.0f, 0.0f, 0.0f, 1.0f );
- glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), -0.5f + cosf( vg_time * 4.0f ) * 0.2f, sinf( vg_time * 4.0f ) * 0.2f + (float)world.h * 0.5f, 0.05f );
- draw_mesh( filled_start, filled_count );
- }
+ glDisable(GL_BLEND);
// Draw score
float const score_bright = 1.25f;
ui_global_ctx.override_colour = 0xffffffff;
gui_text( lvl_info->title, 6, 0 );
ui_global_ctx.cursor[1] += 18;
- gui_text( "incomplete", 4, 0 );
+ gui_text( lvl_info->completed_score>0? "passed": "incomplete", 4, 0 );
}
else
{
vg_tex2d tex_ball_noise = { .path = "textures/bnoise.qoi" };
vg_tex2d tex_monofur = { .path = "textures/ascii.qoi", .flags = VG_TEXTURE_NO_MIP };
vg_tex2d tex_unkown = { .path = "textures/unkown.qoi" };
+vg_tex2d tex_buttons = { .path = "textures/buttons.qoi" };
-vg_tex2d *texture_list[] = { &tex_tile_detail, &tex_tile_data, &tex_wood, &tex_background, &tex_ball_noise, &tex_monofur, &tex_unkown };
+vg_tex2d *texture_list[] = { &tex_tile_detail, &tex_tile_data, &tex_wood, &tex_background, &tex_ball_noise, &tex_monofur, &tex_unkown, &tex_buttons };
// AUDIO
// ===========================================================================================================
UNIFORMS({ "uPv", "uColour", "uTexMain", "uStart", "uEnd", "uCurve" })
)
+SHADER_DEFINE( shader_buttons,
+ // VERTEX
+ "layout (location=0) in vec2 a_co;"
+ "uniform vec4 uOffset;" // Tile x/y, uv x/y
+ "uniform mat3 uPv;"
+ ""
+ "out vec2 aTexCoords;"
+ ""
+ "void main()"
+ "{"
+ // Vertex transform
+ "vec3 worldpos = vec3( a_co + uOffset.xy, 1.0 );"
+ "gl_Position = vec4( uPv * worldpos, 1.0 );"
+
+ // Create texture coords
+ "vec2 edge_safe_coords = a_co * 0.98 + 0.01;"
+ "aTexCoords = (edge_safe_coords + uOffset.zw) * 0.25;"
+ "}",
+
+ // FRAGMENT
+ "out vec4 FragColor;"
+ ""
+ "uniform sampler2D uTexMain;"
+ "uniform vec4 uColour;" // rgb, light amount
+ ""
+ "in vec2 aTexCoords;"
+ ""
+ "void main()"
+ "{"
+ "vec4 glyph = texture( uTexMain, aTexCoords.xy );"
+
+ "FragColor = vec4( uColour.rgb * (mix(glyph.r, glyph.g, uColour.a)+0.02)*2.6 + glyph.b * 0.4, glyph.a );"
+ "}"
+ ,
+ UNIFORMS({ "uPv", "uOffset", "uTexMain", "uColour" })
+)
+
void vg_register(void)
{
SHADER_INIT( shader_ball );
SHADER_INIT( shader_background );
SHADER_INIT( shader_wire );
+ SHADER_INIT( shader_buttons );
}
/*
static struct cmp_level cmp_levels_tutorials[] =
{
+ // r1
{
.serial_id = 0,
.title = "PRINCIPLE 1",
._unlock = 1,
.is_tutorial = 1
},
+ // r1
{
.serial_id = 1,
.title = "PRINCIPLE 2",
._unlock = 2,
.is_tutorial = 1,
},
+ // r1
{
.serial_id = 2,
.title = "PRINCIPLE 3",
._unlock = 12,
.is_tutorial = 1
},
+ // r1
{
.serial_id = 12,
.title = "PRINCIPLE 4",
static struct cmp_level cmp_levels_basic[] =
{
+ // r1
{
.serial_id = 3,
.title = "SUBDIVISION 1",
.map_name = "cmp_b01",
- .description = "Simple maths, branching required.",
+ .description = "Sometimes getting the desired amount takes\n"
+ "dividing up the input and recombining it.",
._linked = 4,
._unlock = 6
},
+ // r1
{
.serial_id = 4,
.title = "SUBDIVISION 2",
.map_name = "cmp_b02",
- .description = "Simple maths, except more.",
+ .description = "",
._linked = 5,
._unlock = 7
},
+ // r1
{
.serial_id = 5,
.title = "RESTRUCTURE",
.map_name = "cmp_b03",
- .description = "Not so simple swap",
+ .description = "It is possible to swap these values using\n"
+ "simple division and addition.",
._unlock = 8
},
._unlock = 15
},
+ // r2
{
.serial_id = 15,
.title = "PRINCIPLE 5",
.map_name = "cmp_b10",
.description =
- "The competent engineers among you may have already\n"
- "spotted and utilized these parts of the system\n"
+ "The eager engineers among you may have already spotted\n"
+ "and utilized these parts of the system\n"
"\n"
"We forgot to include the relevant principle tasks as\n"
"of your training package, you will now be tasked to\n"
"complete them",
- ._unlock = 16
+ ._unlock = 16,
+ .is_tutorial = 1
},
+ // r2
{
.serial_id = 16,
.title = "ROUTING PROBLEM",
"this results in no operation being performed, and no\n"
"state changes take place in the Twisty Turny(TM)\n",
- ._unlock = 18
+ ._unlock = 18,
+ .is_tutorial = 1
},
{
.serial_id = 18,