X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=fishladder.c;h=f82cebeea04de390e963e0df4d29b14b172d786a;hb=30490c4c08d5c0f811017a901aa9e25a95be7c40;hp=493b70401eabe3fec2ad0d5dd8efa1decf3b78d9;hpb=a69a9e27e7de338a3116ed345ff2d9d19f084329;p=fishladder.git diff --git a/fishladder.c b/fishladder.c index 493b704..f82cebe 100644 --- a/fishladder.c +++ b/fishladder.c @@ -1,7 +1,7 @@ // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved +#define MARBLE_COMP_VERSION 4 //#define VG_CAPTURE_MODE -//#define VG_STEAM #define VG_STEAM_APPID 1218140U #include "vg/vg.h" @@ -68,7 +68,8 @@ enum e_world_button enum e_game_state { k_game_state_main, - k_game_state_settings + k_game_state_settings, + k_game_state_update }; #define FLAG_CANAL 0x1 @@ -121,7 +122,7 @@ static struct cell_description cell_descriptions[] = { // 0-3 - {}, + { .trigger_pos = { 0.5f, 0.25f }, .trigger_sprite = k_sprite_brk_d }, { .start = { 1, 0 }, .end = { -1, 0 }, .trigger_pos = { 0.5f, 0.25f }, .trigger_sprite = k_sprite_brk_d }, { .start = { 0, 1 }, .end = { 0, -1 }, .trigger_pos = { 0.25f, 0.5f }, .trigger_sprite = k_sprite_brk_l }, { .start = { 0, 1 }, .end = { 1, 0 }, .trigger_pos = { 0.25f, 0.5f }, .trigger_sprite = k_sprite_brk_l }, @@ -280,7 +281,8 @@ world = { .mode = k_world_button_mode_toggle }, { .mode = k_world_button_mode_toggle }, { .mode = k_world_button_mode_toggle } } - } + }, + .selected = -1 }; // Forward declerations @@ -310,7 +312,7 @@ static void io_reset(void); static struct cell *pcell( v2i pos ); static void lcell( int id, v2i pos ); static void map_reclassify( v2i start, v2i end, int update_texbuffer ); -static void gen_level_text( struct cmp_level *pLevel ); +static void gen_level_text(void); static int map_load( const char *str, const char *name ); static void map_serialize( FILE *stream ); @@ -401,16 +403,16 @@ static struct world_theme } world_themes[] = { - { - "Wood", - { 0.89f, 0.8f, 0.7f }, - &tex_tiles_wood - }, { "Minimal", { 0.8f, 0.8f, 0.8f }, &tex_tiles_min }, + { + "Wood", + { 0.89f, 0.8f, 0.7f }, + &tex_tiles_wood + }, { "Lab", { 0.7f, 0.7f, 0.7f }, @@ -643,31 +645,67 @@ static void map_reclassify( v2i start, v2i end, int update_texbuffer ) } } -static void gen_level_text( struct cmp_level *pLevel ) +static void gen_level_text(void) { // Old style UI. ui_px const unit_scale_px = 4*UI_GLYPH_SPACING_X; // 4 char per unit ui_begin( &world.st.world_text, world.w*unit_scale_px, world.h*unit_scale_px ); - - for( int i = 0; i < vg_list_size( pLevel->strings ); i ++ ) + + if( world.pCmpLevel ) { - struct world_string *wstr = &pLevel->strings[i]; - - if( wstr->str ) + for( int i = 0; i < vg_list_size( world.pCmpLevel->strings ); i ++ ) { - ui_px pos[2]; + struct world_string *wstr = &world.pCmpLevel->strings[i]; - pos[0] = -UI_GLYPH_SPACING_X/2; + if( wstr->str ) + { + ui_px pos[2]; - if( wstr->placement == k_placement_bottom ) - pos[1] = 2*-unit_scale_px; - else - pos[1] = (world.h-1)*-unit_scale_px -6; + pos[0] = -UI_GLYPH_SPACING_X/2; - ui_text( &world.st.world_text, pos, wstr->str, 1, k_text_align_left ); + if( wstr->placement == k_placement_bottom ) + pos[1] = 2*-unit_scale_px; + else + pos[1] = (world.h-1)*-unit_scale_px -6; + + ui_text( &world.st.world_text, pos, wstr->str, 1, k_text_align_left ); + } + } + } + + // re-create level scores + for( int i = 0; i < vg_list_size( career_packs ); i ++ ) + { + struct career_level_pack *set = &career_packs[i]; + + ui_text( &world.st.world_text, + (ui_px [2]){ + set->origin[0]*unit_scale_px, + -(set->origin[1]+set->dims[1]+1)*unit_scale_px + 18 + }, + set->title, 1, k_text_align_left ); + + for( int j = 0; j < set->count; j ++ ) + { + struct cmp_level *lvl = &set->pack[j]; + + if( lvl->completed_score && !lvl->is_tutorial ) + { + char num[10]; + snprintf( num, 9, "%d", lvl->completed_score ); + + ui_text( &world.st.world_text, + (ui_px [2]){ + lvl->btn.position[0]*unit_scale_px + unit_scale_px/2, + -lvl->btn.position[1]*unit_scale_px - unit_scale_px/2 + }, + num, 1, k_text_align_center ); + } } } + //ui_text( &world.st.world_text, (ui_px [2]){ 0, 0 }, "Preview", 1, k_text_align_left ); + ui_resolve( &world.st.world_text ); } @@ -1169,6 +1207,7 @@ struct dcareer_state struct dlevel_state { i32 score; + i32 unlocked; i32 reserved[2]; } @@ -1184,7 +1223,7 @@ static void career_serialize(void) return; struct dcareer_state encoded; - encoded.version = 2; + encoded.version = MARBLE_COMP_VERSION; encoded.in_map = world.pCmpLevel? world.pCmpLevel->serial_id: -1; memset( encoded.reserved, 0, sizeof( encoded.reserved ) ); @@ -1201,7 +1240,7 @@ static void career_serialize(void) dest->score = lvl->completed_score; dest->unlocked = lvl->unlocked; dest->reserved[0] = 0; - dest->reserved[1] = 0; + dest->reserved[1] = 0; } } @@ -1220,24 +1259,15 @@ static void career_unlock_level( struct cmp_level *lvl ) static void career_pass_level( struct cmp_level *lvl, int score, int upload ) { if( score > 0 ) - { - if( score < lvl->completed_score || lvl->completed_score == 0 ) - { - #ifdef VG_STEAM - if( !lvl->is_tutorial && upload ) - leaderboard_set_score( lvl, score ); - #endif - - lvl->completed_score = score; - } + { + lvl->completed_score = score; + gen_level_text(); - if( lvl->unlock ) career_unlock_level( lvl->unlock ); + if( lvl->unlock ) + career_unlock_level( lvl->unlock ); - #ifdef VG_STEAM if( lvl->achievement ) - { sw_set_achievement( lvl->achievement ); - } // Check ALL maps to trigger master engineer for( int i = 0; i < vg_list_size( career_packs ); i ++ ) @@ -1252,7 +1282,6 @@ static void career_pass_level( struct cmp_level *lvl, int score, int upload ) } sw_set_achievement( "MASTER_ENGINEER" ); - #endif } } @@ -1317,7 +1346,14 @@ static void career_load(void) struct dlevel_state *src = &encoded.levels[lvl->serial_id]; if( src->unlocked ) career_unlock_level( lvl ); - if( src->score ) lvl->completed_score = src->score; + if( src->score ) + { + lvl->completed_score = src->score; + + // Apply unlocking to next levels in case there was an update + if( lvl->unlock ) + career_unlock_level( lvl->unlock ); + } if( lvl->serial_id == encoded.in_map ) lvl_to_load = lvl; @@ -1327,10 +1363,13 @@ static void career_load(void) if( console_changelevel( 1, &lvl_to_load->map_name ) ) { world.pCmpLevel = lvl_to_load; - gen_level_text( world.pCmpLevel ); + gen_level_text(); } career_load_success = 1; + + if( encoded.version < MARBLE_COMP_VERSION || 1 ) + world.st.state = k_game_state_update; } // MAIN GAMEPLAY @@ -1390,6 +1429,7 @@ static void simulation_start(void) if( world.pCmpLevel ) { world.pCmpLevel->completed_score = 0; + gen_level_text(); } } @@ -1504,7 +1544,7 @@ static void vg_update(void) if( console_changelevel( 1, &world.st.lvl_to_load->map_name ) ) { world.pCmpLevel = world.st.lvl_to_load; - gen_level_text( world.pCmpLevel ); + gen_level_text(); } world.st.lvl_to_load = NULL; @@ -1552,6 +1592,9 @@ static void vg_update(void) m3x3_translate( m_view, origin_current ); m3x3_mul( m_projection, m_view, vg_pv ); vg_projection_update(); + + if( world.st.state == k_game_state_update ) + return; // Mouse input // ======================================================================================================== @@ -1922,9 +1965,7 @@ static 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 ) { - #ifdef VG_STEAM sw_set_achievement( "CAN_DO_THAT" ); - #endif fish->state = k_fish_state_soon_alive; @@ -1971,21 +2012,26 @@ static void vg_update(void) // Spawn new marble if( (target_peice->state & FLAG_EMITTER) && !(target_peice->state & FLAG_TRIGGERED)) { - struct fish *fish = &world.fishes[ world.num_fishes ]; - lcell( cell_current->links[trigger_id], fish->pos ); - - fish->state = k_fish_state_soon_alive; - fish->colour = target_peice->emit[ trigger_id ]; - - if( target_peice->config != k_cell_type_stub ) - { - struct cell_description *desc = &cell_descriptions[ target_peice->config ]; - v2i_copy( desc->start, fish->dir ); - fish->flow_reversed = 1; - - world.num_fishes ++; - alive_count ++; - } + if( world.num_fishes < vg_list_size( world.fishes ) ) + { + struct fish *fish = &world.fishes[ world.num_fishes ]; + lcell( cell_current->links[trigger_id], fish->pos ); + + fish->state = k_fish_state_soon_alive; + fish->colour = target_peice->emit[ trigger_id ]; + + if( target_peice->config != k_cell_type_stub ) + { + struct cell_description *desc = &cell_descriptions[ target_peice->config ]; + v2i_copy( desc->start, fish->dir ); + fish->flow_reversed = 1; + + world.num_fishes ++; + alive_count ++; + } + } + else + vg_warn( "Max marbles exceeded\n" ); } else { @@ -2036,9 +2082,7 @@ static void vg_update(void) if( collide_next_frame || collide_this_frame ) { - #ifdef VG_STEAM sw_set_achievement( "BANG" ); - #endif // Shatter death (+0.5s) float death_time = world.sim_internal_time + ( collide_this_frame? 0.0f: 0.5f ); @@ -2081,14 +2125,19 @@ static void vg_update(void) struct cell *cell_ptr = pcell( fish->pos ); if( cell_ptr->config != k_cell_type_stub ) - { - struct cell_description *desc = &cell_descriptions[ cell_ptr->config ]; - - v2i_copy( desc->start, fish->dir ); - fish->flow_reversed = 1; - - world.num_fishes ++; - alive_count ++; + { + if( world.num_fishes < vg_list_size(world.fishes)) + { + struct cell_description *desc = &cell_descriptions[ cell_ptr->config ]; + + v2i_copy( desc->start, fish->dir ); + fish->flow_reversed = 1; + + world.num_fishes ++; + alive_count ++; + } + else + vg_warn( "Max marbles exceeded\n" ); } } } @@ -2176,10 +2225,8 @@ static void vg_update(void) } else { - #ifdef VG_STEAM if( world.sim_run > 0 ) sw_set_achievement( "GOOD_ENOUGH" ); - #endif vg_error( "Level failed :(\n" ); } @@ -3149,11 +3196,16 @@ void vg_render(void) v2_add( center, (v2f){ -0.25f, -0.25f }, p0 ); v2_add( center, (v2f){ 0.25f, -0.25f }, p1 ); - - if( cell->state & FLAG_FLIP_FLOP ) - render_sprite( k_sprite_flare_y, p1 ); - else - render_sprite( k_sprite_flare_b, p0 ); + + if( cell->state & FLAG_TARGETED ) + { + if( cell->state & FLAG_FLIP_FLOP ) + render_sprite( k_sprite_flare_y, p1 ); + else + render_sprite( k_sprite_flare_b, p0 ); + } + else + render_sprite( k_sprite_flare_w, cell->state &FLAG_FLIP_FLOP? p1: p0 ); } } @@ -3183,7 +3235,61 @@ void vg_ui(void) gui_text( (ui_px [2]){ vg_window_x / 2, 28 }, world.pCmpLevel->description, 1, k_text_align_center ); } - if( world.st.state == k_game_state_settings ) + if( world.st.state == k_game_state_update ) + { + gui_group_id( 34 ); + + ui_global_ctx.cursor[2] = 458; + ui_global_ctx.cursor[3] = 316; + ui_global_ctx.cursor[0] = vg_window_x / 2 - 229; + ui_global_ctx.cursor[1] = vg_window_y / 2 - 158; + + gui_new_node(); + { + gui_capture_mouse( 200 ); + gui_fill_rect( ui_global_ctx.cursor, 0xE8303030 ); + + ui_px title_pos[2]; + title_pos[0] = ui_global_ctx.cursor[0] + 229; + title_pos[1] = ui_global_ctx.cursor[1] + 16; + + gui_text( title_pos, "Update 1.5", 2, k_text_align_center ); + + gui_text( (ui_px [2]){ ui_global_ctx.cursor[0] + 16, title_pos[1] + 45 }, + "Welcome to the first update to marble computing!" + "\n" + "New features have been added:\n" + "\n" + " - Settings menu\n" + " - Map skins\n" + " - More levels and a new block type\n" + " - Scores for each level\n" + " - Zooming and panning (mousewheel)\n" + "\n" + "There is much more in the works, such as a\n" + "soundtrack, and the rest of the levels for the\n" + "3 bit computer!\n" + "\n" + "Thank you everyone for enjoying my game :)\n", + 1, k_text_align_left + ); + + ui_global_ctx.cursor[2] = 100; + ui_global_ctx.cursor[3] = 30; + ui_global_ctx.cursor[0] += 229 - 50; + ui_global_ctx.cursor[1] += 316 - 30 - 16; + + if( gui_button( 1 ) ) + { + world.st.state = k_game_state_main; + } + gui_text( (ui_px [2]){ ui_global_ctx.cursor[0] + 50, + ui_global_ctx.cursor[1] + 4 }, "OK", 1, k_text_align_center ); + gui_end(); + } + gui_end(); + } + else if( world.st.state == k_game_state_settings ) { gui_group_id( 35 ); @@ -3241,7 +3347,7 @@ void vg_ui(void) if( colour_set_id > 0 ) colour_set_id --; } - gui_text( ui_global_ctx.cursor, "<", 1, 0 ); + gui_text( ui_global_ctx.cursor, "<", 2, 0 ); gui_end_right(); ui_global_ctx.cursor[2] = 150; @@ -3262,7 +3368,7 @@ void vg_ui(void) if( colour_set_id < vg_list_size( colour_sets )-1 ) colour_set_id ++; } - gui_text( ui_global_ctx.cursor, ">", 1, 0 ); + gui_text( ui_global_ctx.cursor, ">", 2, 0 ); gui_end_down(); } gui_end_down(); @@ -3282,7 +3388,7 @@ void vg_ui(void) if( world_theme_id > 0 ) world_theme_id --; } - gui_text( ui_global_ctx.cursor, "<", 1, 0 ); + gui_text( ui_global_ctx.cursor, "<", 2, 0 ); gui_end_right(); ui_global_ctx.cursor[2] = 150; @@ -3302,7 +3408,7 @@ void vg_ui(void) if( world_theme_id < vg_list_size( world_themes )-1 ) world_theme_id ++; } - gui_text( ui_global_ctx.cursor, ">", 1, 0 ); + gui_text( ui_global_ctx.cursor, ">", 2, 0 ); gui_end_down(); } gui_end_down(); @@ -3426,7 +3532,6 @@ static int console_credits( int argc, char const *argv[] ) vg_info( " miniaudio MIT0 miniaud.io\n" ); vg_info( " QOI MIT phoboslab.org\n" ); vg_info( " STB library MIT nothings.org\n" ); - vg_info( " Ubuntu Regular ubuntu.com\n" ); return 0; } @@ -3708,10 +3813,6 @@ void vg_start(void) void vg_free(void) { -#ifdef VG_STEAM - sw_free_opengl(); -#endif - console_save_map( 0, NULL ); career_serialize();