m3x3f m_view;
m3x3f m_mdl;
-static int colour_set_id = 0;
-static int world_theme_id = 0;
-static int enable_bloom = 1;
-static int enable_vignette = 1;
-static float music_volume = 1.0f;
-
-static void music_volume_update(void)
-{
-#if 0
- sfx_vol_fset( &audio_volume_music, music_volume );
-#endif
-}
-
static v3f colour_sets[][4] =
{
{ { 1.0f, 0.9f, 0.3f },
}
};
-static void colour_code_v3( i8 cc, v3f target )
-{
- if( (cc >= 0) && (cc < vg_list_size( colour_sets[0] )) )
- {
- v3_copy( colour_sets[colour_set_id][ cc ], target );
+static void colour_code_v3( i8 cc, v3f target ){
+ if( (cc >= 0) && (cc < vg_list_size( colour_sets[0] )) ){
+ v3_copy( colour_sets[marblecomp.colour_set][ cc ], target );
return;
}
if( lvl->unlock )
career_unlock_level( lvl->unlock );
- #ifdef VG_STEAM
if( lvl->achievement )
sw_set_achievement( lvl->achievement );
}
sw_set_achievement( "MASTER_ENGINEER" );
- #endif
}
}
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;
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 );
}
else
{
- #ifdef VG_STEAM
if( world.sim_run > 0 )
sw_set_achievement( "GOOD_ENOUGH" );
- #endif
vg_error( "Level failed :(\n" );
}
}
static void _mc_vg1_render(void){
- if( enable_bloom || enable_vignette )
+ if( marblecomp.bloom || marblecomp.vignette )
glBindFramebuffer( GL_FRAMEBUFFER, world.st.framebuffer );
else
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
int const empty_start = circle_base+32;
int const empty_count = circle_base+32*2;
-#if 0
- struct world_theme *theme = &world_themes[ world_theme_id ];
-#else
- struct world_theme *theme = &world_themes[ 0 ];
-#endif
+ struct world_theme *theme = &world_themes[ marblecomp.world_theme ];
if( !world.initialzed )
return;
}
}
- if( world_button_exec( &world.st.buttons[k_world_button_settings], (v2f){ 1.0f, 2.0f }, btn_orange, &stat ))
+ if( world_button_exec( &world.st.buttons[k_world_button_settings],
+ (v2f){ 1.0f, 2.0f }, btn_orange, &stat ))
{
- world.st.state = stat == k_world_button_on_enable?
- k_game_state_settings: k_game_state_main;
+ world.st.buttons[k_world_button_settings].state = 0;
+ vg_settings_open();
}
level_selection_buttons();
draw_numbers( (v3f){ 2.0f, (float)world.h-1.875f, 0.3333f }, world.score );
*/
- if( !enable_bloom )
- {
- if( enable_vignette )
+ if( !marblecomp.bloom ){
+ if( marblecomp.vignette )
goto image_composite;
return;
shader_post_comp_uTexBloom(1);
shader_post_comp_uComp( (v2f){
- enable_bloom? 1.0f: 0.0f,
- enable_vignette? 0.0f: 1.0f
+ marblecomp.bloom? 1.0f: 0.0f,
+ marblecomp.vignette? 0.0f: 1.0f
});
draw_mesh( 0, 2 );
}
void _mc_vg1_ui(void) {
- // Drawing world name
-#if 0
- if( world.pCmpLevel )
- {
- gui_text( (ui_px [2]){ vg.window_x / 2, 4 }, world.pCmpLevel->title, 2, k_text_align_center );
- gui_text( (ui_px [2]){ vg.window_x / 2, 28 }, world.pCmpLevel->description, 1, k_text_align_center );
- }
-#endif
+ ui_px const unit_scale_px = 4*vg_ui.font->spacing;
+
#if 0
- if( world.st.state == k_game_state_update )
- {
- gui_group_id( 34 );
+ if( world.pCmpLevel ){
+ for( int i = 0; i < vg_list_size( world.pCmpLevel->strings ); i ++ ){
+ struct world_string *wstr = &world.pCmpLevel->strings[i];
- 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;
+ if( wstr->str ){
+ ui_px pos[2];
+ pos[0] = -vg_ui.font->spacing/2;
- 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;
+ 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 );
}
- 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
#endif
-#if 0
- if( world.st.state == k_game_state_settings )
- {
- gui_group_id( 35 );
+ u32 sum = 0;
- ui_global_ctx.cursor[2] = 225;
- gui_fill_y();
- gui_align_right();
-
- gui_new_node();
- {
- gui_capture_mouse( 200 );
-
- gui_fill_rect( ui_global_ctx.cursor, 0xC0202020 );
- ui_rect_pad( ui_global_ctx.cursor, 8 );
-
- ui_global_ctx.cursor[3] = 25;
-
- gui_new_node();
- {
- gui_text( ui_global_ctx.cursor, "SETTINGS", 2, 0 );
-
- ui_global_ctx.cursor[2] = 25;
- gui_align_right();
-
- if( gui_button(4) == k_button_click )
- {
- world.st.buttons[ k_world_button_settings ].state = 0;
- world.st.state = k_game_state_main;
- vg_info( "exit\n" );
- }
- ui_global_ctx.cursor[0] += 4;
- ui_global_ctx.cursor[1] -= 4;
- gui_text( ui_global_ctx.cursor, "x", 2, 0 );
- gui_end();
- }
- gui_end();
-
- // Colour scheme selection
- ui_global_ctx.cursor[1] += 30;
-
- gui_text( ui_global_ctx.cursor, "Colour Scheme", 1, 0 );
- ui_global_ctx.cursor[1] += 25;
-
- gui_new_node();
- {
- ui_global_ctx.cursor[2] = 50;
-
- for( int i = 0; i < 4; i ++ )
- {
- gui_new_node();
- {
- // Convert to RGB
- u32 rgb = 0xff000000;
-
- for( int j = 0; j < 3; j ++ )
- rgb |= (u32)(colour_sets[ colour_set_id ][i][j]*255.0f) << j * 8;
-
- gui_fill_rect( ui_global_ctx.cursor, rgb );
- }
- gui_end_right();
- }
- }
- gui_end_down();
-
- gui_new_node();
- {
- ui_global_ctx.cursor[2] = 25;
- if( gui_button( 0 ) == k_button_click )
- {
- if( colour_set_id > 0 )
- colour_set_id --;
- }
- gui_text( ui_global_ctx.cursor, "<", 2, 0 );
- gui_end_right();
-
- ui_global_ctx.cursor[2] = 150;
- gui_new_node();
- {
- gui_fill_rect( ui_global_ctx.cursor, 0x33ffffff );
- gui_text(
- (ui_px [2]){ ui_global_ctx.cursor[0] + 75, ui_global_ctx.cursor[1] + 6 },
- (const char *[]){ "Normal", "Extra1", "Extra2" }[ colour_set_id ],
- 1, k_text_align_center
- );
- }
- gui_end_right();
-
- ui_global_ctx.cursor[2] = 25;
- if( gui_button( 1 ) == k_button_click )
- {
- if( colour_set_id < vg_list_size( colour_sets )-1 )
- colour_set_id ++;
- }
- gui_text( ui_global_ctx.cursor, ">", 2, 0 );
- gui_end_down();
- }
- gui_end_down();
-
- // Theme select
- ui_global_ctx.cursor[1] += 16;
-
-#if 0
- gui_text( ui_global_ctx.cursor, "Tile Theme", 1, 0 );
- ui_global_ctx.cursor[1] += 20;
-
- gui_new_node();
- {
- ui_global_ctx.cursor[2] = 25;
- if( gui_button( 2 ) == k_button_click )
- {
- if( world_theme_id > 0 )
- world_theme_id --;
- }
- gui_text( ui_global_ctx.cursor, "<", 2, 0 );
- gui_end_right();
-
- ui_global_ctx.cursor[2] = 150;
- gui_new_node();
- {
- gui_fill_rect( ui_global_ctx.cursor, 0x33ffffff );
- gui_text(
- (ui_px [2]){ ui_global_ctx.cursor[0] + 75, ui_global_ctx.cursor[1] + 6 },
- world_themes[ world_theme_id ].name, 1, k_text_align_center
- );
- }
- gui_end_right();
-
- ui_global_ctx.cursor[2] = 25;
- if( gui_button( 3 ) == k_button_click )
- {
- if( world_theme_id < vg_list_size( world_themes )-1 )
- world_theme_id ++;
- }
- gui_text( ui_global_ctx.cursor, ">", 2, 0 );
- gui_end_down();
- }
- gui_end_down();
-#endif
-
- gui_text( ui_global_ctx.cursor, "Graphics", 1, 0 );
- ui_global_ctx.cursor[1] += 20;
-
- gui_new_node();
- {
- ui_global_ctx.cursor[2] = 200;
- if( gui_button( 5 ) == k_button_click )
- {
- enable_bloom ^= 0x1;
- }
- ui_global_ctx.cursor[0] += 4;
- ui_global_ctx.cursor[1] += 4;
- gui_text( ui_global_ctx.cursor, enable_bloom?
- "Bloom: ENABLED":
- "Bloom: DISABLED", 1, 0 );
- gui_end_down();
- }
- gui_end_down();
-
- ui_global_ctx.cursor[1] += 10;
- gui_new_node();
- {
- ui_global_ctx.cursor[2] = 200;
- if( gui_button( 6 ) == k_button_click )
- {
- enable_vignette ^= 0x1;
- }
- ui_global_ctx.cursor[0] += 4;
- ui_global_ctx.cursor[1] += 4;
- gui_text( ui_global_ctx.cursor, enable_vignette?
- "Vignette: ENABLED":
- "Vignette: DISABLED", 1, 0 );
- gui_end_down();
- }
- gui_end_down();
-
- ui_global_ctx.cursor[1] += 16;
- gui_text( ui_global_ctx.cursor, "Music Volume", 1, 0 );
- ui_global_ctx.cursor[1] += 20;
+ // re-create level scores
+ for( int i = 0; i < vg_list_size( career_packs ); i ++ ){
+ struct career_level_pack *set = &career_packs[i];
- gui_new_node();
- {
- ui_px slider_start = ui_global_ctx.cursor[0];
-
- float const bar_width = 45.0f,
- bar_total = 200.0f,
- bar_movement = bar_total-bar_width,
- bar_start = bar_width * 0.5f;
-
- ui_global_ctx.cursor[2] = bar_total;
- ui_fill_rect( &ui_global_ctx,
- ui_global_ctx.cursor,
- 0xff111111 );
-
- ui_global_ctx.cursor[2] = bar_width;
- ui_global_ctx.cursor[0] = slider_start + music_volume * bar_movement;
-
- int status = gui_button( 7 );
-
- static ui_px drag_start = 0.0f;
+ for( int j = 0; j < set->count; j ++ ){
+ struct cmp_level *lvl = &set->pack[j];
- if( status == k_button_start_click )
- drag_start = ui_global_ctx.mouse[0];
- else if( ui_global_ctx.capture_lock &&
- (ui_global_ctx.capture_mouse_id == ui_group_id(&ui_global_ctx,7)))
- {
- ui_px drag_offset = ui_global_ctx.mouse[0] - drag_start;
- float offset_local = (drag_start + drag_offset - slider_start - bar_start) / bar_movement;
+ if( lvl->completed_score && !lvl->is_tutorial ){
+ char num[10];
+ snprintf( num, 9, "%d", lvl->completed_score );
+ sum += lvl->completed_score;
+
+ v3f pos = {
+ (f32)lvl->btn.position[0]+0.5f,
+ (f32)lvl->btn.position[1]+0.5f,
+ 1.0f };
+
+ m3x3_mulv( vg.pv, pos, pos );
+ pos[0] += 1.0f;
+ pos[0] *= (f32)vg.window_x * 0.5f;
+ pos[1] += 1.0f;
+ pos[1] = 2.0f - pos[1];
+ pos[1] *= (f32)vg.window_y * 0.5f;
+
+ ui_text( (ui_rect){ pos[0]-100, pos[1], 200, 24 }, num, 1,
+ k_ui_align_center, 0xffcccccc );
+ }
+ }
+ }
- music_volume = vg_minf( vg_maxf( offset_local, 0.0f ), 1.0f );
- music_volume_update();
- }
-
- ui_global_ctx.cursor[0] += 4;
- ui_global_ctx.cursor[1] += 4;
-
- char volbuf[12];
- snprintf( volbuf, 12, "%.2f", music_volume );
- gui_text( ui_global_ctx.cursor, volbuf, 1, 0 );
- gui_end_down();
- }
- gui_end_down();
- }
- gui_end();
- }
-#endif
+ v3f pos = { -2, 8, 1 };
+
+ m3x3_mulv( vg.pv, pos, pos );
+ pos[0] += 1.0f;
+ pos[0] *= (f32)vg.window_x * 0.5f;
+ pos[1] += 1.0f;
+ pos[1] = 2.0f - pos[1];
+ pos[1] *= (f32)vg.window_y * 0.5f;
+
+ char tot[32];
+ snprintf( tot, 31, "%u", sum );
+
+ vg_ui.font = &vg_ui_font_big;
+ ui_text( (ui_rect){ pos[0]-100, pos[1], 200, 24 }, tot, 1,
+ k_ui_align_center, 0xffcccccc );
+ vg_ui.font = &vg_ui_font_small;
+
+ if( world.pCmpLevel ){
+ vg_ui.font = &vg_ui_font_big;
+ ui_text( (ui_rect){ 0, 0, vg.window_x, 28 }, world.pCmpLevel->title,
+ 1, k_ui_align_middle_center, 0 );
+ vg_ui.font = &vg_ui_font_small;
+ ui_text( (ui_rect){ 0, 28,vg.window_x, 14 }, world.pCmpLevel->description,
+ 1, k_ui_align_middle_center, 0 );
+ }
}
// CONSOLE COMMANDS
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( " SDL2 ZLIB libsdl.org\n" );
vg_info( " QOI MIT phoboslab.org\n" );
vg_info( " STB library MIT nothings.org\n" );
return 0;
sw_leaderboard_downloaded = &leaderboard_downloaded;
#endif
-#if 0
- vg_function_push( (struct vg_cmd){
- .name = "_map_write",
- .function = console_save_map
- });
-
- vg_function_push( (struct vg_cmd){
- .name = "_map_load",
- .function = console_load_map
- });
-
- vg_function_push( (struct vg_cmd){
- .name = "map",
- .function = console_changelevel
- });
-
- vg_function_push( (struct vg_cmd){
- .name = "credits",
- .function = console_credits
- });
-
- vg_convar_push( (struct vg_convar){
- .name = "colours",
- .data = &colour_set_id,
- .data_type = k_convar_dtype_i32,
- .opt_i32 = { .min = 0, .max = 2, .clamp = 1 },
- .persistent = 1
- });
-
- vg_convar_push( (struct vg_convar){
- .name = "theme",
- .data = &world_theme_id,
- .data_type = k_convar_dtype_i32,
- .opt_i32 = { .min = 0, .max = vg_list_size( world_themes )-1, .clamp = 1 },
- .persistent = 1,
- .update = NULL
- });
-
- vg_convar_push( (struct vg_convar){
- .name = "enable_bloom",
- .data = &enable_bloom,
- .data_type = k_convar_dtype_i32,
- .opt_i32 = { .min = 0, .max = 1, .clamp = 1 },
- .persistent = 1,
- .update = NULL
- });
-
- vg_convar_push( (struct vg_convar){
- .name = "enable_vignette",
- .data = &enable_vignette,
- .data_type = k_convar_dtype_i32,
- .opt_i32 = { .min = 0, .max = 1, .clamp = 1 },
- .persistent = 1,
- .update = NULL
- });
-
- vg_convar_push( (struct vg_convar){
- .name = "music_volume",
- .data = &music_volume,
- .data_type = k_convar_dtype_f32,
- .opt_f32 = { .min = 0.0f, .max = 1.0f, .clamp = 1 },
- .persistent = 1,
- .update = music_volume_update
- });
-#endif
+ vg_console_reg_cmd( "_map_write", console_save_map, NULL );
+ vg_console_reg_cmd( "_map_load", console_load_map, NULL );
+ vg_console_reg_cmd( "map", console_changelevel, NULL );
+ vg_console_reg_cmd( "credits", console_credits, NULL );
+ vg_console_reg_var( "colours", &marblecomp.colour_set, k_var_dtype_i32,
+ VG_VAR_PERSISTENT );
+ vg_console_reg_var( "theme", &marblecomp.world_theme, k_var_dtype_i32,
+ VG_VAR_PERSISTENT );
+ vg_console_reg_var( "enable_bloom", &marblecomp.bloom, k_var_dtype_i32,
+ VG_VAR_PERSISTENT );
+ vg_console_reg_var( "enable_vignette", &marblecomp.vignette, k_var_dtype_i32,
+ VG_VAR_PERSISTENT );
+ vg_console_reg_var( "music", &marblecomp.music, k_var_dtype_i32,
+ VG_VAR_PERSISTENT );
// Combined quad, long quad / empty circle / filled circle mesh
{
/* FIXME: run this at vg exit */
void _mc_vg1_free(void)
{
-#ifdef VG_STEAM
- sw_free_opengl();
-#endif
-
console_save_map( 0, NULL );
career_serialize();
}
--- /dev/null
+struct ui_enum_opt mc_settings_colourscheme_enum[] = {
+ { 0, "Normal" },
+ { 1, "Extra1" },
+ { 2, "Extra2" }
+};
+
+struct ui_enum_opt mc_settings_binary[] = {
+ { 1, "Enabled" },
+ { 0, "Disabled" },
+};
+
+struct ui_enum_opt mc_settings_theme_enum[] = {
+ { 0, "Minimal" },
+ { 1, "Wood" },
+ { 2, "Lab" },
+};
+
+struct {
+ struct vg_setting_enum colourscheme, bloom, vignette, theme, music;
+}
+static mc_settings = {
+ .colourscheme = { .label = "Colour Scheme",
+ .actual_value = &marblecomp.colour_set,
+ .options = mc_settings_colourscheme_enum,
+ .option_count = 3 },
+ .bloom = { .label = "Bloom",
+ .actual_value = &marblecomp.bloom,
+ .options = mc_settings_binary, .option_count = 2 },
+ .vignette = { .label = "Vignette",
+ .actual_value = &marblecomp.vignette,
+ .options = mc_settings_binary, .option_count = 2 },
+ .theme = { .label = "Tile Theme",
+ .actual_value = &marblecomp.world_theme,
+ .options = mc_settings_theme_enum, .option_count = 3 },
+ .music = { .label = "Music",
+ .actual_value = &marblecomp.music,
+ .options = mc_settings_binary, .option_count = 2 }
+};
+
+static void vg_game_settings_init(void){
+ ui_settings_enum_init( &mc_settings.colourscheme );
+ ui_settings_enum_init( &mc_settings.bloom );
+ ui_settings_enum_init( &mc_settings.vignette );
+ ui_settings_enum_init( &mc_settings.theme );
+ ui_settings_enum_init( &mc_settings.music );
+}
+
+static void vg_game_settings_gui( ui_rect panel ){
+ ui_rect rq;
+ ui_standard_widget( panel, rq, 1 );
+
+ ui_rect opt, display;
+ ui_split_ratio( rq, k_ui_axis_v, 0.5f, 16, opt, display );
+ vg_settings_enum( &mc_settings.colourscheme, opt );
+
+ ui_rect blocks[4];
+ ui_split_ratio( display, k_ui_axis_v, 0.5f, 2, blocks[0], blocks[2] );
+ ui_split_ratio( blocks[0], k_ui_axis_v, 0.5f, 2, blocks[0], blocks[1] );
+ ui_split_ratio( blocks[2], k_ui_axis_v, 0.5f, 2, blocks[2], blocks[3] );
+
+ v3f *set = colour_sets[ mc_settings.colourscheme.new_value ];
+ for( u32 i=0; i<4; i ++ ){
+ u32 rgb = 0xff000000;
+ for( u32 j=0; j < 3; j ++ )
+ rgb |= (u32)(set[i][j]*255.0f) << j * 8;
+
+ ui_fill( blocks[i], rgb );
+ }
+
+ ui_standard_widget( panel, rq, 1 );
+ vg_settings_enum( &mc_settings.theme, rq );
+
+ ui_standard_widget( panel, rq, 1 );
+ vg_settings_enum( &mc_settings.bloom, rq );
+
+ ui_standard_widget( panel, rq, 1 );
+ vg_settings_enum( &mc_settings.vignette, rq );
+
+ ui_standard_widget( panel, rq, 1 );
+ vg_settings_enum( &mc_settings.music, rq );
+
+ if( vg_settings_apply_button( panel, 1 ) ){
+ marblecomp.colour_set = mc_settings.colourscheme.new_value;
+ marblecomp.world_theme = mc_settings.theme.new_value;
+ marblecomp.bloom = mc_settings.bloom.new_value;
+ marblecomp.vignette = mc_settings.vignette.new_value;
+
+ audio_lock();
+ marblecomp.music = mc_settings.music.new_value;
+
+ if( marblecomp.music ){
+ if( !marblecomp.music_channel ){
+ marblecomp.music_channel = audio_get_first_idle_channel();
+ audio_channel_init( marblecomp.music_channel, &audio_music[0],
+ AUDIO_FLAG_LOOP );
+ audio_channel_fadein( marblecomp.music_channel, 0.25f );
+ }
+ }
+ else{
+ if( marblecomp.music_channel ){
+ marblecomp.music_channel = audio_channel_fadeout(
+ marblecomp.music_channel, 0.25f );
+ }
+ }
+
+ audio_unlock();
+ }
+
+#if 0
+ ui_global_ctx.cursor[1] += 16;
+ gui_text( ui_global_ctx.cursor, "Music Volume", 1, 0 );
+ ui_global_ctx.cursor[1] += 20;
+
+ gui_new_node();
+ {
+ ui_px slider_start = ui_global_ctx.cursor[0];
+
+ float const bar_width = 45.0f,
+ bar_total = 200.0f,
+ bar_movement = bar_total-bar_width,
+ bar_start = bar_width * 0.5f;
+
+ ui_global_ctx.cursor[2] = bar_total;
+ ui_fill_rect( &ui_global_ctx,
+ ui_global_ctx.cursor,
+ 0xff111111 );
+
+ ui_global_ctx.cursor[2] = bar_width;
+ ui_global_ctx.cursor[0] = slider_start + music_volume * bar_movement;
+
+ int status = gui_button( 7 );
+
+ static ui_px drag_start = 0.0f;
+
+ if( status == k_button_start_click )
+ drag_start = ui_global_ctx.mouse[0];
+ else if( ui_global_ctx.capture_lock &&
+ (ui_global_ctx.capture_mouse_id == ui_group_id(&ui_global_ctx,7)))
+ {
+ ui_px drag_offset = ui_global_ctx.mouse[0] - drag_start;
+ float offset_local = (drag_start + drag_offset - slider_start - bar_start) / bar_movement;
+
+ music_volume = vg_minf( vg_maxf( offset_local, 0.0f ), 1.0f );
+ music_volume_update();
+ }
+
+ ui_global_ctx.cursor[0] += 4;
+ ui_global_ctx.cursor[1] += 4;
+
+ char volbuf[12];
+ snprintf( volbuf, 12, "%.2f", music_volume );
+ gui_text( ui_global_ctx.cursor, volbuf, 1, 0 );
+ gui_end_down();
+ }
+ gui_end_down();
+ }
+ gui_end();
+ }
+#endif
+}
--- /dev/null
+#ifndef STEAM_H
+#define STEAM_H
+
+#define VG_GAME
+#include "vg/vg_steam.h"
+#include "vg/vg_steam_utils.h"
+#include "vg/vg_steam_networking.h"
+#include "vg/vg_steam_auth.h"
+#include "vg/vg_steam_http.h"
+#include "vg/vg_steam_friends.h"
+#include "vg/vg_steam_user_stats.h"
+#include "submodules/anyascii/impl/c/anyascii.c"
+
+/*
+ * We only want to use steamworks if building for the networked version,
+ * theres not much point otherwise. We mainly want steamworks for setting
+ * achievements etc.. so that includes our own server too.
+ *
+ * This file also wraps the functions and interfaces that we want to use to
+ * make them a bit easier to read, since they are the flat API they have very
+ * long names. in non-networked builds they will return default errors or do
+ * nothing.
+ */
+
+static char steam_username_at_startup[128] = "Unassigned";
+
+static void recv_steam_warning( int severity, const char *msg )
+{
+ if( severity == 0 )
+ vg_low( "%s\n", msg );
+ else
+ vg_info( "%s\n", msg );
+}
+
+static int steam_ready = 0,
+ steam_stats_ready = 0;
+
+static void *hSteamNetworkingSockets,
+ *hSteamUser;
+
+static ISteamUserStats *hSteamUserStats;
+static HSteamPipe hSteamClientPipe;
+
+static const char *steam_achievement_names[] =
+{
+ "MASTER_ENGINEER", "CAN_DO_THAT", "BANG", "GOOD_ENOUGH",
+ "TUTORIALS", "MIGHTY_CONSUMER", "GRADUATE"
+};
+
+static void steam_store_achievements(void)
+{
+ if( steam_ready && steam_stats_ready ){
+ SteamAPI_ISteamUserStats_StoreStats( hSteamUserStats );
+ }
+}
+
+static void steam_set_achievement( const char *name ){
+ if( steam_ready && steam_stats_ready ){
+ if( SteamAPI_ISteamUserStats_SetAchievement( hSteamUserStats, name ) ){
+ vg_success( "Achievement set! '%s'\n", name );
+
+ }
+ else{
+ vg_warn( "Failed to set achievement: %s\n", name );
+ }
+ }
+ else{
+ vg_warn( "Failed to set achievement (steam not ready): %s\n", name );
+ }
+}
+
+static void sw_set_achievement( const char *name ){
+ steam_set_achievement( name );
+ steam_store_achievements();
+}
+
+static void steam_clear_achievement( const char *name )
+{
+ if( steam_ready && steam_stats_ready ){
+ if( SteamAPI_ISteamUserStats_ClearAchievement( hSteamUserStats, name ) ){
+ vg_info( "Achievement cleared: '%s'\n", name );
+ }
+ else{
+ vg_warn( "Failed to clear achievement: %s\n", name );
+ }
+ }
+ else{
+ vg_warn( "Failed to clear achievement (steam not ready): %s\n", name );
+ }
+}
+
+
+static void steam_print_all_achievements(void){
+ vg_info( "Achievements: \n" );
+
+ if( steam_ready && steam_stats_ready ){
+ for( int i=0; i<vg_list_size(steam_achievement_names); i++ ){
+ steamapi_bool set = 0;
+ const char *name = steam_achievement_names[i];
+
+ if( SteamAPI_ISteamUserStats_GetAchievement(
+ hSteamUserStats, name, &set ) )
+ {
+ vg_info( " %s %s\n", (set? "[YES]": "[ ]"), name );
+ }
+ else{
+ vg_warn( " Error while fetching achievement status '%s'\n", name );
+ }
+ }
+ }
+ else{
+ vg_warn( " Steam is not initialized, no results\n" );
+ }
+}
+
+static int steam_achievement_ccmd( int argc, char const *argv[] )
+{
+ if( !(steam_ready && steam_stats_ready) ){
+ vg_error( "steam_ready: %d, steam_stats_ready: %d\n",
+ steam_ready, steam_stats_ready );
+ return 1;
+ }
+
+ if( argc == 1 ){
+ if( !strcmp( argv[0], "list" ) ){
+ steam_print_all_achievements();
+ return 0;
+ }
+ else if( !strcmp( argv[0], "clearall" )){
+ for( int i=0; i<vg_list_size(steam_achievement_names); i++ )
+ steam_clear_achievement( steam_achievement_names[i] );
+
+ steam_store_achievements();
+ }
+ }
+
+ if( argc == 2 ){
+ if( !strcmp( argv[0], "set" ) ){
+ steam_set_achievement( argv[1] );
+ steam_store_achievements();
+ return 0;
+ }
+ else if( strcmp( argv[0], "clear" ) ){
+ steam_clear_achievement( argv[1] );
+ steam_store_achievements();
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void steam_on_recieve_current_stats( CallbackMsg_t *msg )
+{
+ UserStatsReceived_t *rec = (UserStatsReceived_t *)msg->m_pubParam;
+
+ if( rec->m_eResult == k_EResultOK ){
+ vg_info( "Recieved stats for: %lu (user: %lu)\n", rec->m_nGameID,
+ rec->m_steamIDUser );
+ steam_stats_ready = 1;
+ }
+ else{
+ vg_error( "Error recieveing stats for user (%u)\n", rec->m_eResult );
+ }
+}
+
+static u32 utf8_byte0_byte_count( u8 char0 )
+{
+ for( u32 k=2; k<4; k++ ){
+ if( !(char0 & (0x80 >> k)) )
+ return k;
+ }
+
+ return 0;
+}
+
+static u32 str_utf8_collapse( const char *str, char *buf, u32 length ){
+ u8 *ustr = (u8 *)str;
+ u32 utf32_code = 0x00000000;
+ u32 i=0, j=0, utf32_byte_ct=0;
+
+ for(;j < length-1;){
+ if( ustr[i] == 0x00 )
+ break;
+
+ if( ustr[i] & 0x80 ){
+ if( utf32_byte_ct ){
+ utf32_byte_ct --;
+ utf32_code |= (ustr[i] & 0x3F) << (utf32_byte_ct*6);
+
+ if( !utf32_byte_ct ){
+ const char *match;
+ size_t chars = anyascii( utf32_code, &match );
+
+ for( u32 k=0; k<VG_MIN(chars, length-1-j); k++ ){
+ buf[ j++ ] = (u8)match[k];
+ }
+ }
+ }
+ else{
+ utf32_byte_ct = utf8_byte0_byte_count( ustr[i] )-1;
+ utf32_code = ustr[i] & (0x3F >> utf32_byte_ct);
+ utf32_code <<= utf32_byte_ct*6;
+ }
+ }
+ else{
+ utf32_byte_ct = 0x00;
+ buf[j ++] = str[i];
+ }
+
+ i++;
+ }
+
+ buf[j] = 0x00;
+ return j;
+}
+
+static int steam_init(void){
+ const char *username = "offline player";
+
+ vg_info( "Initializing steamworks\n" );
+
+ if( !SteamAPI_Init() ){
+ printf("\n");
+ vg_error( "Steamworks failed to initialize\n" );
+ return 1;
+ }
+
+ steam_ready = 1;
+
+ SteamAPI_ManualDispatch_Init();
+
+ /* Connect interfaces */
+ hSteamClientPipe = SteamAPI_GetHSteamPipe();
+ hSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI();
+ hSteamUser = SteamAPI_SteamUser();
+
+ ISteamUtils *utils = SteamAPI_SteamUtils();
+ SteamAPI_ISteamUtils_SetWarningMessageHook( utils, recv_steam_warning );
+
+ printf("\n");
+ vg_success( "\nSteamworks API running\n" );
+
+ ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
+ username = SteamAPI_ISteamFriends_GetPersonaName( hSteamFriends );
+
+ /*
+ * Request stats
+ * --------------------------------------------------------
+ */
+ hSteamUserStats = SteamAPI_SteamUserStats();
+ steam_register_callback( k_iUserStatsReceived,
+ steam_on_recieve_current_stats );
+
+ if( !SteamAPI_ISteamUserStats_RequestCurrentStats( hSteamUserStats ) )
+ vg_warn( "No Steam Logon: Cannot request stats\n" );
+
+ vg_console_reg_cmd( "ach", steam_achievement_ccmd, NULL );
+
+ /* TODO: On username update callback */
+ str_utf8_collapse( username, steam_username_at_startup,
+ vg_list_size(steam_username_at_startup) );
+
+ return 1;
+}
+
+static void steam_update(void)
+{
+ if( steam_ready ){
+ steamworks_event_loop( hSteamClientPipe );
+ }
+}
+
+static void steam_end(void)
+{
+ if( steam_ready ){
+ vg_info( "Shutting down\n..." );
+ SteamAPI_Shutdown();
+ }
+}
+
+#endif /* STEAM_H */