From: hgn <hgodden00@gmail.com>
Date: Tue, 7 Dec 2021 15:21:07 +0000 (+0000)
Subject: added stuff
X-Git-Url: https://harrygodden.com/git/?a=commitdiff_plain;h=aad3ce335e5a67018359442ee1c3a849d675ea18;p=fishladder.git

added stuff
---

diff --git a/fishladder.c b/fishladder.c
index ecd4681..19e3fb4 100644
--- a/fishladder.c
+++ b/fishladder.c
@@ -48,91 +48,6 @@ const char *level_pack_1[] = {
 	"thirds"
 };
 
-#pragma pack(push,1)
-struct dcareer_state
-{
-	u32 version;
-	i32 total_unlocked;
-	
-	u32 reserved[14];
-
-	struct dlevel_state
-	{
-		i32 score;
-		i32 reserved[3];
-	}
-	levels[ NUM_CAMPAIGN_LEVELS ];	
-};
-#pragma pack(pop)
-
-static void career_serialize(void)
-{
-	struct dcareer_state encoded;
-	encoded.version = 2;
-	encoded.total_unlocked = career_local.total_unlocked;
-
-	for( int i = 0; i < vg_list_size( career_serializable ); i ++ )
-	{
-		struct serializable_set *set = &career_serializable[i];
-		
-		for( int j = 0; j < set->count; j ++ )
-		{
-			struct cmp_level *lvl = &set->pack[j];
-			struct dlevel_state *dest = &encoded.levels[lvl->serial_id];
-			
-			dest->score = lvl->completed_score;
-			dest->reserved[0] = 0;
-			dest->reserved[1] = 0;
-			dest->reserved[2] = 0;
-		}
-	}
-
-	vg_asset_write( "sav/game.sv2", &encoded, sizeof( struct dcareer_state ) );
-}
-
-static void career_load(void)
-{
-	i64 sz;
-	struct dcareer_state encoded;
-	memset( (void*)&encoded, 0, sizeof( struct dcareer_state ) );
-	
-	// Load and copy data into encoded
-	void *cr = vg_asset_read_s( "sav/game.sv2", &sz );
-	
-	if( cr )
-	{
-		if( sz > sizeof( struct dcareer_state ) )
-			vg_warn( "This save file is too big! Some levels will be lost\n" );
-		
-		if( sz <= offsetof( struct dcareer_state, levels ) )
-		{
-			vg_warn( "This save file is too small to have a header. Creating a blank one\n" );
-			free( cr );
-			return;
-		}
-		
-		memcpy( (void*)&encoded, cr, VG_MIN( sizeof( struct dcareer_state ), sz ) );
-		free( cr );
-	}
-	else
-		vg_info( "No save file... Using blank one\n" );
-		
-	// Decode everything from dstate
-	for( int i = 0; i < vg_list_size( career_serializable ); i ++ )
-	{
-		struct serializable_set *set = &career_serializable[i];
-		
-		for( int j = 0; j < set->count; j ++ )
-		{
-			struct cmp_level *lvl = &set->pack[j];
-			struct dlevel_state *src = &encoded.levels[lvl->serial_id];
-			
-			lvl->completed_score = src->score;
-			// ...
-		}
-	}
-}
-
 m3x3f m_projection;
 m3x3f m_view;
 m3x3f m_mdl;
@@ -381,7 +296,7 @@ struct world
 	u32 score;
 	u32 completed;
 	u32 time;
-} world = {};
+} world;
 
 void leaderboard_set_score( struct cmp_level *cmp_level, u32 score );
 
@@ -978,6 +893,148 @@ static int console_changelevel( int argc, char const *argv[] )
 	return 0;
 }
 
+#pragma pack(push,1)
+struct dcareer_state
+{
+	u32 version;
+	i32 in_map;
+	
+	u32 reserved[14];
+
+	struct dlevel_state
+	{
+		i32 score;
+		i32 unlocked;
+		i32 reserved[2];
+	}
+	levels[ NUM_CAMPAIGN_LEVELS ];	
+};
+#pragma pack(pop)
+
+static void career_serialize(void)
+{
+	struct dcareer_state encoded;
+	encoded.version = 2;
+	encoded.in_map = world.pCmpLevel? world.pCmpLevel->serial_id: -1;
+	
+	memset( encoded.reserved, 0, sizeof( encoded.reserved ) );
+
+	for( int i = 0; i < vg_list_size( career_serializable ); i ++ )
+	{
+		struct serializable_set *set = &career_serializable[i];
+		
+		for( int j = 0; j < set->count; j ++ )
+		{
+			struct cmp_level *lvl = &set->pack[j];
+			struct dlevel_state *dest = &encoded.levels[lvl->serial_id];
+			
+			dest->score = lvl->completed_score;
+			dest->unlocked = lvl->unlocked;
+			dest->reserved[0] = 0;
+			dest->reserved[1] = 0;
+		}
+	}
+
+	vg_asset_write( "sav/game.sv2", &encoded, sizeof( struct dcareer_state ) );
+}
+
+static void career_unlock_level( struct cmp_level *lvl );
+static void career_unlock_level( struct cmp_level *lvl )
+{
+	lvl->unlocked = 1;
+	
+	if( lvl->linked )
+		career_unlock_level( lvl->linked );
+}
+
+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 )
+		{
+			if( !lvl->is_tutorial && upload )
+				leaderboard_set_score( lvl, score );
+			
+			lvl->completed_score = score;
+		}
+		
+		if( lvl->unlock ) career_unlock_level( lvl->unlock );
+	}
+}
+
+static void career_reset_level( struct cmp_level *lvl )
+{
+	lvl->unlocked = 0;
+	lvl->completed_score = 0;
+}
+
+static void career_load(void)
+{
+	i64 sz;
+	struct dcareer_state encoded;
+
+	// Blank save state
+	memset( (void*)&encoded, 0, sizeof( struct dcareer_state ) );	
+	encoded.in_map = -1;
+	encoded.levels[0].unlocked = 1;
+	
+	// Load and copy data into encoded
+	void *cr = vg_asset_read_s( "sav/game.sv2", &sz );
+	
+	if( cr )
+	{
+		if( sz > sizeof( struct dcareer_state ) )
+			vg_warn( "This save file is too big! Some levels will be lost\n" );
+		
+		if( sz <= offsetof( struct dcareer_state, levels ) )
+		{
+			vg_warn( "This save file is too small to have a header. Creating a blank one\n" );
+			free( cr );
+			return;
+		}
+		
+		memcpy( (void*)&encoded, cr, VG_MIN( sizeof( struct dcareer_state ), sz ) );
+		free( cr );
+	}
+	else
+		vg_info( "No save file... Using blank one\n" );
+	
+	// Reset memory
+	for( int i = 0; i < vg_list_size( career_serializable ); i ++ )
+	{
+		struct serializable_set *set = &career_serializable[i];
+		
+		for( int j = 0; j < set->count; j ++ )
+			career_reset_level( &set->pack[j] );
+	}
+	
+	// Header information
+	// =================================
+	
+	// Decode everything from dstate
+	for( int i = 0; i < vg_list_size( career_serializable ); i ++ )
+	{
+		struct serializable_set *set = &career_serializable[i];
+		
+		for( int j = 0; j < set->count; j ++ )
+		{
+			struct cmp_level *lvl = &set->pack[j];
+			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( lvl->serial_id == encoded.in_map )
+			{
+				if( console_changelevel( 1, &lvl->map_name ) )
+					world.pCmpLevel = lvl;
+			}
+		}
+	}
+}
+
 void leaderboard_found( LeaderboardFindResult_t *pCallback );
 void leaderboard_downloaded( LeaderboardScoresDownloaded_t *pCallback );
 
@@ -1139,8 +1196,8 @@ void vg_start(void)
 	resource_load_main();
 	
 	// Restore gamestate
+	career_local_data_init();
 	career_load();
-	console_load_map( 1, level_pack_1 );
 }
 
 void vg_free(void)
@@ -1857,6 +1914,12 @@ void vg_update(void)
 						
 						world.score = score;
 						world.time = world.sim_frame;
+						
+						// Copy into career data
+						if( world.pCmpLevel )
+						{
+							career_pass_level( world.pCmpLevel, world.score, 1 );
+						}
 					}
 				}
 				else
@@ -1867,14 +1930,6 @@ void vg_update(void)
 					vg_error( "Level failed :(\n" );
 				}
 				
-				// Copy into career data
-				if( world.pCmpLevel )
-				{
-					if( world.score < world.pCmpLevel->completed_score || world.pCmpLevel->completed_score == 0 )
-						leaderboard_set_score( world.pCmpLevel, world.score );
-					
-					world.pCmpLevel->completed_score = world.score;
-				}
 				
 				simulation_stop(); // TODO: Async?
 				break;
@@ -2079,6 +2134,8 @@ void vg_render(void)
 	
 	
 	// Level title
+	// TODO: Fix this?
+	/*
 	ui_begin( &ui_global_ctx, 512, 256 );
 	
 	ui_global_ctx.override_colour = 0xff9a8a89;
@@ -2094,6 +2151,7 @@ void vg_render(void)
 	m3x3_scale( world_text, (v3f){0.01f,-0.01f,0.01f} );
 	
 	ui_draw( &ui_global_ctx, world_text );
+	*/
 	
 	// Main
 	// =========================================================================================
@@ -2304,12 +2362,13 @@ void vg_render(void)
 			for( int j = 0; j < term->runs[k].condition_count; j ++ )
 			{
 				float y_offset = is_input? 1.2f: -0.2f;
-				y_offset += (is_input? 0.2f: -0.2f) * (float)k;
-				
-				glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)posx + 0.2f + 0.2f * (float)j, (float)posy + y_offset, 0.1f );
+				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 );
+				
 					colour_code_v3( term->runs[k].conditions[j], dot_colour );
 					glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour );
 				
@@ -2321,6 +2380,8 @@ void vg_render(void)
 				}
 				else
 				{
+					glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), (float)posx + 0.2f + 0.2f * (float)j, (float)posy + y_offset + y_h, 0.1f );
+				
 					if( term->runs[k].recv_count > j )
 					{
 						colour_code_v3( term->runs[k].recieved[j], dot_colour );
@@ -2493,8 +2554,7 @@ void vg_ui(void)
 		{
 			struct cmp_level *levels = pack_infos[ pack_selection ].levels;
 			int count = pack_infos[ pack_selection ].level_count;
-			int unlocked = 3000;
-		
+
 			static struct ui_scrollbar sb = {
 				.bar_height = 400
 			};
@@ -2530,7 +2590,7 @@ void vg_ui(void)
 				{
 					struct cmp_level *lvl_info = &levels[i];
 				
-					if( i < unlocked )
+					if( lvl_info->unlocked )
 					{
 						if( lvl_info->completed_score != 0 )
 							gui_override_colours( i&0x1? &flcol_list_complete_a: &flcol_list_complete_b );
@@ -2540,7 +2600,7 @@ void vg_ui(void)
 					else
 						gui_override_colours( &flcol_list_locked );
 					
-					if( i < unlocked )
+					if( lvl_info->unlocked )
 					{
 						if( gui_button( 2 + i ) == k_button_click )
 						{
@@ -2648,11 +2708,13 @@ void vg_ui(void)
 				gui_override_colours( &flcol_list_complete_a );
 				if( gui_button( 3002 ) == k_button_click )
 				{
-					console_changelevel( 1, &ui_data.level_selected->map_name );
-					world.pCmpLevel = ui_data.level_selected;
+					if( console_changelevel( 1, &ui_data.level_selected->map_name ) )
+					{
+						world.pCmpLevel = ui_data.level_selected;
 
-					ui_data.level_selected = NULL;
-					ui_data.leaderboard_show = 0;
+						ui_data.level_selected = NULL;
+						ui_data.leaderboard_show = 0;
+					}
 				}
 				gui_text( "PLAY", 6, k_text_alignment_center );
 				gui_end();
diff --git a/fishladder_resources.h b/fishladder_resources.h
index c06c17d..8c8ee3b 100644
--- a/fishladder_resources.h
+++ b/fishladder_resources.h
@@ -566,129 +566,139 @@ struct cmp_level
 	const char *title;
 	const char *description;
 	
+	int unlocked;
 	int completed_score;
 	
-	int unlocks;			// When completed, unlock this many levels
-	int linked_unlocks;	// When unlocked, unlock this many levels additionally
+	int _unlock, _linked;	// When completed, unlock this level
+	struct cmp_level *unlock, *linked;
 	
 	int serial_id;
+	int is_tutorial;
 	
 	SteamLeaderboard_t steam_leaderboard;
 };
 
-struct cmp_level cmp_levels_tutorials[] = 
+static struct cmp_level cmp_levels_tutorials[] = 
 {
 	{
+		.serial_id = 0,
 		.title = "PRINCIPLE 1",
 		.map_name = "cmp_t01",
 		.description = "Utilize basic transport methods",
 		
-		.serial_id = 0,
-		.unlocks = 1
+		._unlock = 1,
+		.is_tutorial = 1
 	},
 	{
+		.serial_id = 1,
 		.title = "PRINCIPLE 2",
 		.map_name = "cmp_t02",
 		.description = "Utilize the twisty turny(TM) piece to split\n"
 							"the marble stream into two",
 		
-		.serial_id = 1,
-		.unlocks = 1
+		._unlock = 2,
+		.is_tutorial = 1,
 	},
 	{
+		.serial_id = 2,
 		.title = "PRINCIPLE 3",
 		.map_name = "cmp_t03",
 		.description = "Merge transport into one",
 		
-		.serial_id = 2,
-		.unlocks = 1,
+		._unlock = 12,
+		.is_tutorial = 1
 	},
 	{
+		.serial_id = 12,
 		.title = "PRINCIPLE 4",
 		.map_name = "cmp_t04",
 		.description = "Some stages require multiple runs to succeed\n" 
 							"in order to pass",
 		
-		.serial_id = 12,
-		.unlocks = 3
+		._unlock = 3,
+		.is_tutorial = 1
 	}
 };
 
-struct cmp_level cmp_levels_basic[] =
+static struct cmp_level cmp_levels_basic[] =
 {
 	{
+		.serial_id = 3,
 		.title = "SUBDIVISION 1",
 		.map_name = "cmp_b01",
 		.description = "Simple maths, branching required.",
 		
-		.serial_id = 3,
-		.unlocks = 1
+		._linked = 4,
+		._unlock = 6
 	},
 	{
+		.serial_id = 4,
 		.title = "SUBDIVISION 2",
 		.map_name = "cmp_b02",
 		.description = "Simple maths, except more.",
-
-		.serial_id = 4,
-		.unlocks = 1
+		
+		._linked = 5,
+		._unlock = 7
 	},
 	{
+		.serial_id = 5,
 		.title = "RESTRUCTURE",
 		.map_name = "cmp_b03",
 		.description = "Not so simple swap",
 		
-		.serial_id = 5,
-		.unlocks = 1
+		._unlock = 8
 	},
 	{
+		.serial_id = 6,
 		.title = "SERIALIZE",
 		.map_name = "cmp_b04",
 		.description = "Merge and sort",
 		
-		.serial_id = 6,
-		.unlocks = 1
+		._unlock = 7
 	},
 	{
+		.serial_id = 7,
 		.title = "PATTERNS 1",
 		.map_name = "cmp_b05",
 		.description = "Replicate",
 		
-		.serial_id = 7,
-		.unlocks = 1
+		._linked = 8
 	},
 	{
+		.serial_id = 8,
 		.title = "PATTERNS 2",
 		.map_name = "cmp_b06",
 		.description = "Replicate MORE",
 		
-		.serial_id = 8,
-		.unlocks = 1
+		._unlock = 9
 	},
 	{
+		.serial_id = 9,
 		.title = "MIGHTY CONSUMER",
 		.map_name = "cmp_b07",
 		.description = "Build a greedy system",
 		
-		.serial_id = 9,
-		.unlocks = 1
+		._linked = 10,
+		._unlock = 11
 	},
 	{
+		.serial_id = 10,
 		.title = "ENCRYPTED 1",
 		.map_name = "cmp_b08",
 		.description = "Some configurations may not be valid",
 
-		.serial_id = 10,
-		.unlocks = 1
+		._unlock = 15
 	},
 	{
+		.serial_id = 11,
 		.title = "REVERSE",
 		.map_name = "cmp_b09",
 		.description = "Reverse the incoming order. Always length 4",
 		
-		.serial_id = 11,
-		.unlocks = 1
+		._unlock = 15
 	},
 	{
+		.serial_id = 15,
 		.title = "PRINCIPLE 5",
 		.map_name = "cmp_b10",
 		.description = 
@@ -699,20 +709,20 @@ struct cmp_level cmp_levels_basic[] =
 			"of your training package, you will now be tasked to\n"
 			"complete them",
 
-		.serial_id = 15,
-		.linked_unlocks = 1
+		._unlock = 16
 	},
 	{
+		.serial_id = 16,
 		.title = "ROUTING PROBLEM",
 		.map_name = "cmp_routing",
 		.description = 
 			"Things can get a little chaotic on tight boards, do your\n"
 			"best to utilize principle 5 to get the job done\n",
 		
-		.serial_id = 16,
-		.unlocks = 1
+		._unlock = 17
 	},
 	{
+		.serial_id = 17,
 		.title = "PRINCIPLE 6",
 		.map_name = "cmp_b11",
 		.description =
@@ -733,93 +743,95 @@ struct cmp_level cmp_levels_basic[] =
 			"this results in no operation being performed, and no\n"
 			"state changes take place in the Twisty Turny(TM)\n",
 
-			.serial_id = 17,
-			.linked_unlocks = 1
+		._unlock = 18
 	},
 	{
+		.serial_id = 18,
 		.title = "NOT GATE",
 		.map_name = "cmp_not",
 		.description = 
 			"Test your knowledge of triggers, build an 'NOT GATE'\n"
 			"emulated by marble logic.",
 		
-		.serial_id = 18,
-		.unlocks = 1
+		._linked = 19,
+		._unlock = 20
 	},
 	{
+		.serial_id = 19,
 		.title = "AND GATE",
 		.map_name = "cmp_and",
 		.description = 
 			"A slightly more complicated gate, but shouldn't be\n"
 			"too difficult for your skillset.",
 		
-		.serial_id = 19,
-		.unlocks = 1
+		._unlock = 20
 	},
 	{
+		.serial_id = 20,
 		.title = "QUALIFICATION PROJECT",
 		.map_name = "cmp_grad",
 		.description =
 			"There's no instructions here, resolve and complete this\n"
 			"task to qualify yourself as an official marble engineer",
-		.serial_id = 20,
-		.unlocks = 3
+		._unlock = 13
 	}
 };
 
-struct cmp_level cmp_levels_grad[] =
+static struct cmp_level cmp_levels_grad[] =
 {
 	{
+		.serial_id = 13,
 		.title = "SORT",
 		.map_name = "cmp_i01",
 		.description = 
 			"Device a scheme to filter and sort the inputs. If you\n"
 			"believe you lack the tools required to solve this one,\n"
 			"take a harder look at the inputs.",
+		._linked = 14
 		
-		.serial_id = 13
 	},
 	{
+		.serial_id = 14,
 		.title = "THIRDS",
 		.map_name = "cmp_i02",
 		.description = 
 			"Split the inputs up into a third of their values\n"
 			"\n"
 			"Is this possible? -HG",
+		._linked = 21
 		
-		.serial_id = 14
 	},
 	{
+		.serial_id = 21,
 		.title = "XOR CHIP",
 		.map_name = "cmp_xor",
 		.description = 
 			"Significantly more complicated than an AND or NOT gate,\n"
 			"but possible.",
-		.serial_id = 21
+		._linked = 22
 	},
 	{
+		.serial_id = 22,
 		.title = "SECRET CODE",
 		.map_name = "cmp_secret",
 		.description = 
 			"Only one input should send an unlock signal marble to\n"
 			"the output.\n"
-			"The code: 100110",
-		.serial_id = 22
+			"The code: 100110"
 	}
 };
 
 #define NUM_CAMPAIGN_LEVELS (vg_list_size( cmp_levels_tutorials ) + vg_list_size( cmp_levels_basic ) + vg_list_size( cmp_levels_grad ))
 
-struct
+/*
+static struct
 {
-	int total_unlocked;
 }
 career_local = 
 {
-	.total_unlocked = 1
-};
+};*/
 
-struct serializable_set 
+static struct serializable_set 
 {
 	struct cmp_level *pack;
 	int count;
@@ -839,3 +851,31 @@ career_serializable[] =
 		.count = vg_list_size( cmp_levels_grad )
 	}
 };
+
+// Setup pointers and that
+static void career_local_data_init(void)
+{
+	struct cmp_level *level_ptrs[ NUM_CAMPAIGN_LEVELS ];
+	
+	// COllect pointers
+	for( int i = 0; i < vg_list_size( career_serializable ); i ++ )
+	{
+		struct serializable_set *set = &career_serializable[i];
+		
+		for( int j = 0; j < set->count; j ++ )
+			level_ptrs[ set->pack[j].serial_id ] = &set->pack[j];
+	}
+	
+	// Apply
+	for( int i = 0; i < vg_list_size( career_serializable ); i ++ )
+	{
+		struct serializable_set *set = &career_serializable[i];
+		
+		for( int j = 0; j < set->count; j ++ )
+		{
+			struct cmp_level *lvl = &set->pack[j];
+			lvl->unlock = lvl->_unlock? level_ptrs[ lvl->_unlock ]: NULL;
+			lvl->linked = lvl->_linked? level_ptrs[ lvl->_linked ]: NULL;
+		}
+	}
+}
diff --git a/maps/cmp_t01.map b/maps/cmp_t01.map
new file mode 100644
index 0000000..76b32b8
--- /dev/null
+++ b/maps/cmp_t01.map
@@ -0,0 +1,8 @@
+#########;
+###-#####;aa
+##     ##;
+##     ##;
+##     ##;
+##     ##;
+#####+###;aa
+#########;
diff --git a/maps/cmp_t02.map b/maps/cmp_t02.map
new file mode 100644
index 0000000..5d06111
--- /dev/null
+++ b/maps/cmp_t02.map
@@ -0,0 +1,9 @@
+#########;
+##-###-##;b,b
+##     ##;
+##     ##;
+##     ##;
+##     ##;
+##     ##;
+####+####;bb
+#########;
diff --git a/maps/cmp_t03.map b/maps/cmp_t03.map
new file mode 100644
index 0000000..46cc4e7
--- /dev/null
+++ b/maps/cmp_t03.map
@@ -0,0 +1,8 @@
+###########;
+#####-#####;bbbbb
+##       ##;
+##      ###;
+##  #    ##;
+##       ##;
+###+##+####;bbb,bb
+###########;
diff --git a/maps/cmp_t04.map b/maps/cmp_t04.map
new file mode 100644
index 0000000..48bcc5b
--- /dev/null
+++ b/maps/cmp_t04.map
@@ -0,0 +1,10 @@
+#############;
+###-#####-###;a:aa,b:bb
+##         ##;
+##         ##;
+##         ##;
+##         ##;
+##         ##;
+##         ##;
+######+######;ab:abab
+#############;
diff --git a/maps/level0.map b/maps/level0.map
deleted file mode 100644
index a30aab9..0000000
--- a/maps/level0.map
+++ /dev/null
@@ -1,9 +0,0 @@
-PRINCIPLE 1
-#########;
-###-#####;acac
-##     ##;
-##     ##;
-##     ##;
-##     ##;
-#####+###;acac
-#########;
diff --git a/maps/level1.map b/maps/level1.map
deleted file mode 100644
index 5d06111..0000000
--- a/maps/level1.map
+++ /dev/null
@@ -1,9 +0,0 @@
-#########;
-##-###-##;b,b
-##     ##;
-##     ##;
-##     ##;
-##     ##;
-##     ##;
-####+####;bb
-#########;
diff --git a/maps/level2.map b/maps/level2.map
deleted file mode 100644
index 46cc4e7..0000000
--- a/maps/level2.map
+++ /dev/null
@@ -1,8 +0,0 @@
-###########;
-#####-#####;bbbbb
-##       ##;
-##      ###;
-##  #    ##;
-##       ##;
-###+##+####;bbb,bb
-###########;
diff --git a/vg/vg_ui.h b/vg/vg_ui.h
index 7e67654..1172850 100644
--- a/vg/vg_ui.h
+++ b/vg/vg_ui.h
@@ -384,8 +384,8 @@ static void ui_new_node( ui_ctx *ctx )
 	
 	if( parent->mouse_over )
 	{
-		if( ctx->mouse[0] >= node->rect[0] && ctx->mouse[0] <= node->rect[0]+node->rect[2] &&
-			 ctx->mouse[1] >= node->rect[1] && ctx->mouse[1] <= node->rect[1]+node->rect[3] )
+		if( ctx->mouse[0] >= node->rect[0] && ctx->mouse[0] < node->rect[0]+node->rect[2] &&
+			 ctx->mouse[1] >= node->rect[1] && ctx->mouse[1] < node->rect[1]+node->rect[3] )
 			node->mouse_over = 1;
 		else
 			node->mouse_over = 0;