// 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",
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
int simulating;
int sim_run, max_runs;
+ float sim_speed;
float frame_lerp;
struct cell_terminal
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)
// 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;
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[] )
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 );
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;
// 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 );
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;
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 )
{
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;
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 )
}
// 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;
}
}
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
}
else
{
+ if( world.sim_run > 0 )
+ sw_set_achievement( "GOOD_ENOUGH" );
+
vg_error( "Level failed :(\n" );
}
}
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
}
+ // 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;
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();
}