Merge branch 'master' of harrygodden.com:/home/fishladder into master
[fishladder.git] / fishladder_resources.h
1 // TEXTURES
2 // ===========================================================================================================
3
4 vg_tex2d tex_tile_data = { .path = "textures/tileset.qoi" };
5 vg_tex2d tex_tile_detail = { .path = "textures/tile_overlays.qoi" };
6 vg_tex2d tex_wood = { .path = "textures/wood.qoi" };
7 vg_tex2d tex_background = { .path = "textures/background.qoi" };
8 vg_tex2d tex_ball_noise = { .path = "textures/bnoise.qoi" };
9 vg_tex2d tex_monofur = { .path = "textures/ascii.qoi", .flags = VG_TEXTURE_NO_MIP };
10 vg_tex2d tex_unkown = { .path = "textures/unkown.qoi" };
11 vg_tex2d tex_buttons = { .path = "textures/buttons.qoi" };
12
13 vg_tex2d *texture_list[] = { &tex_tile_detail, &tex_tile_data, &tex_wood, &tex_background, &tex_ball_noise, &tex_monofur, &tex_unkown, &tex_buttons };
14
15 // AUDIO
16 // ===========================================================================================================
17
18 sfx_vol_control audio_volume_sfx = { .val = 1.0f, .name = "Sound effects" };
19 sfx_vol_control audio_volume_music = { .val = 1.0f, .name = "Music" };
20
21 sfx_system audio_system_sfx =
22 {
23 .vol = 1.f,
24 .ch = 1,
25 .vol_src = &audio_volume_sfx,
26 .name = "sfx"
27 };
28
29 sfx_set audio_tile_mod =
30 {
31 .sources = "\
32 sound/mod_01.ogg\0\
33 sound/mod_02.ogg\0\
34 sound/mod_03.ogg\0\
35 sound/mod_04.ogg\0\
36 sound/mod_05.ogg\0\
37 sound/mod_06.ogg\0",
38 .flags = 0
39 };
40
41 sfx_set audio_splitter =
42 {
43 .sources = "\
44 sound/splitter_01.ogg\0"
45 };
46
47 sfx_set audio_rolls =
48 {
49 .sources = "\
50 sound/rolling_01.ogg\0\
51 sound/rolling_02.ogg\0"
52 };
53
54 sfx_set audio_random =
55 {
56 .sources = "\
57 sound/random_01.ogg\0\
58 sound/random_02.ogg\0\
59 sound/random_03.ogg\0\
60 sound/random_04.ogg\0\
61 sound/random_05.ogg\0\
62 sound/random_06.ogg\0\
63 sound/random_07.ogg\0\
64 sound/random_08.ogg\0"
65 };
66
67 sfx_set audio_clicks =
68 {
69 .sources = "\
70 sound/click_a.ogg\0\
71 sound/click_b.ogg\0\
72 sound/click_c.ogg\0"
73 };
74
75 // One two or three layers of rolling noise
76 sfx_system audio_system_balls_rolling =
77 {
78 .vol = 1.f, .ch = 1, .vol_src = &audio_volume_sfx,
79 .name = "Balls Rolling", .flags = SFX_FLAG_REPEAT
80 };
81
82 // Various oneshots
83 sfx_system audio_system_balls_switching =
84 {
85 .vol = 1.f, .ch = 1, .vol_src = &audio_volume_sfx,
86 .name = "Balls Switching"
87 };
88
89 // Gameplay critical sounds eg. splitter sound rocking
90 sfx_system audio_system_balls_important =
91 {
92 .vol = 1.f, .ch = 1, .vol_src = &audio_volume_sfx,
93 .name = "Balls Gameplay"
94 };
95
96 // Suplemental sounds
97 sfx_system audio_system_balls_extra =
98 {
99 .vol = 1.f, .ch = 1, .vol_src = &audio_volume_sfx,
100 .name = "Balls Extra"
101 };
102
103 sfx_system audio_system_ui =
104 {
105 .vol = 1.f, .ch = 1, .vol_src = &audio_volume_sfx,
106 .name = "UI"
107 };
108
109 ui_colourset ui_fl_colours = {
110 .main = 0xff807373,
111 .hover = 0xff918484,
112 .active = 0xffad9f9e
113 };
114
115 ui_colourset ui_fl_colours_inactive = {
116 .main = 0xff655958,
117 .hover = 0xff655958,
118 .active = 0xff655958
119 };
120
121 static void resource_load_main(void)
122 {
123 // Textures
124 vg_tex2d_init( texture_list, vg_list_size( texture_list ) );
125
126 ui_override_font( tex_monofur.name, 7 );
127
128 ui_global_ctx.colours_main = &ui_fl_colours;
129 gui_reset_colours();
130
131 // Audio
132 sfx_set_init( &audio_tile_mod, NULL );
133 sfx_set_init( &audio_splitter, NULL );
134 sfx_set_init( &audio_rolls, NULL );
135 sfx_set_init( &audio_random, NULL );
136 sfx_set_init( &audio_clicks, NULL );
137 }
138
139 static void resource_free_main(void)
140 {
141 vg_tex2d_free( texture_list, vg_list_size( texture_list ) );
142
143 sfx_set_free( &audio_tile_mod );
144 sfx_set_free( &audio_splitter );
145 sfx_set_free( &audio_rolls );
146 sfx_set_free( &audio_random );
147 sfx_set_free( &audio_clicks );
148 }
149
150 // SHADERS
151 // ===========================================================================================================
152
153 SHADER_DEFINE( shader_tile_colour,
154
155 // VERTEX
156 "layout (location=0) in vec2 a_co;"
157 "uniform mat3 uPv;"
158 "uniform vec3 uOffset;"
159 ""
160 "void main()"
161 "{"
162 "gl_Position = vec4( uPv * vec3( a_co * uOffset.z + uOffset.xy, 1.0 ), 1.0 );"
163 "}",
164
165 // FRAGMENT
166 "out vec4 FragColor;"
167 "uniform vec4 uColour;"
168 ""
169 "void main()"
170 "{"
171 "FragColor = uColour;"
172 "}"
173 ,
174 UNIFORMS({ "uPv", "uOffset", "uColour" })
175 )
176
177 SHADER_DEFINE( shader_ball,
178 // VERTEX
179 "layout (location=0) in vec2 a_co;"
180 "uniform vec3 uOffset;"
181 "uniform mat3 uPv;"
182 ""
183 "out vec4 aTexCoords;"
184 ""
185 "void main()"
186 "{"
187 // Vertex transform
188 "vec3 worldpos = vec3( (a_co * 0.5 - 0.25) * uOffset.z + uOffset.xy, 1.0 );"
189 "gl_Position = vec4( uPv * worldpos, 1.0 );"
190
191 // Create texture coords
192 "aTexCoords = vec4( a_co, worldpos.xy );"
193 "}",
194
195 // FRAGMENT
196 "out vec4 FragColor;"
197 ""
198 "uniform sampler2D uTexMain;"
199 "uniform vec3 uColour;"
200 "uniform vec2 uTexOffset;"
201 ""
202 "in vec4 aTexCoords;"
203 ""
204 "void main()"
205 "{"
206 "vec2 center_coords = aTexCoords.xy - 0.5;"
207 "vec2 center_coords_sqr = center_coords*center_coords;"
208 "float circle_factor = smoothstep( 0.07, 0.0625, center_coords_sqr.x+center_coords_sqr.y );"
209
210 "float bulge_amt = center_coords_sqr.x+center_coords_sqr.y;"
211 "vec2 warped_coords = aTexCoords.zw+uTexOffset - center_coords;"
212 "vec4 noise_sample = texture( uTexMain, warped_coords );"
213
214 "float rim_light = (center_coords_sqr.x+center_coords_sqr.y)*15.0;"
215
216 "vec2 shadow_coords = center_coords + vec2(0.02,0.07);"
217 "vec2 shadow_coords_sqr = shadow_coords*shadow_coords;"
218 "float shadow = exp(-((shadow_coords_sqr.x+shadow_coords_sqr.y)-0.0125)*15.0);"
219
220 "vec3 marble_comp = uColour*0.9 + (noise_sample.x*0.7+pow(rim_light,3.0)*2.0) * 0.1;"
221 "vec4 colour_comp = mix( vec4(0.74,0.53,0.34,shadow), vec4(marble_comp,1.0), circle_factor );"
222
223 "FragColor = colour_comp;"
224 "}"
225 ,
226 UNIFORMS({ "uTexMain", "uColour", "uOffset", "uPv", "uTexOffset" })
227 )
228
229 SHADER_DEFINE( shader_tile_main,
230 // VERTEX
231 "layout (location=0) in vec2 a_co;"
232 "uniform vec4 uOffset;" // Tile x/y, uv x/y
233 "uniform mat3 uPv;"
234 "uniform mat2 uSubTransform;"
235 ""
236 "out vec4 aTexCoords;"
237 "out vec2 aWorldCoords;"
238 ""
239 "vec2 hash22(vec2 p)"
240 "{"
241 "vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));"
242 "p3 += dot(p3, p3.yzx+33.33);"
243 "return fract((p3.xx+p3.yz)*p3.zy);"
244 "}"
245 ""
246 "void main()"
247 "{"
248 // Vertex transform
249 "vec2 subtransform = uSubTransform * (a_co-0.5) + 0.5;"
250 "vec3 worldpos = vec3( subtransform + uOffset.xy, 1.0 );"
251 "gl_Position = vec4( uPv * worldpos, 1.0 );"
252
253 // Create texture coords
254 "vec2 random_offset = floor(hash22(uOffset.xy) * 4.0) * 0.25;"
255 "vec2 edge_safe_coords = a_co * 0.98 + 0.01;"
256 "aTexCoords = vec4((edge_safe_coords + uOffset.zw) * 0.25, edge_safe_coords * 0.25 + random_offset );"
257 "aWorldCoords = worldpos.xy;"
258 "}",
259
260 // FRAGMENT
261 "out vec4 FragColor;"
262 ""
263 "uniform sampler2D uTexGlyphs;"
264 "uniform sampler2D uTexWood;"
265 "uniform float uGhost;"
266 "uniform float uForeground;"
267 "uniform vec2 uMousePos;"
268 "uniform vec4 uColour;"
269 ""
270 "in vec4 aTexCoords;"
271 "in vec2 aWorldCoords;"
272 ""
273 "void main()"
274 "{"
275 "vec3 shadowing_colour = vec3( 0.93, 0.88536, 0.8184 ) * 0.97;"
276 "vec4 glyph = texture( uTexGlyphs, aTexCoords.xy );"
277 "vec4 wood = texture( uTexWood, aTexCoords.zw );"
278 "vec4 wood_secondary = texture( uTexWood, aTexCoords.zw + 0.25 );"
279 "vec3 wood_comp = mix( wood_secondary.rgb * shadowing_colour, wood.rgb, clamp( glyph.b * 2.0 - 1.0, 0.0, 1.0 ) );"
280
281 "vec3 shadows = mix( vec3( 0.85, 0.7344, 0.561 ), vec3(1.0,1.0,1.0), glyph.r );"
282
283 "vec4 output_regular = vec4( wood_comp * shadows, mix( glyph.a, glyph.b, uForeground ) );"
284
285 "float ghost_dist = clamp( 1.5 - distance(uMousePos, aWorldCoords), 0.0, 1.0 );"
286 "vec4 output_ghost = vec4( 1.0, 1.0, 1.0, glyph.g * ghost_dist );"
287
288 "FragColor = mix( output_regular, output_ghost, uGhost ) * uColour;"
289 "}"
290 ,
291 UNIFORMS({ "uPv", "uOffset", "uTexGlyphs", "uTexWood", "uSubTransform", "uGhost", "uMousePos", "uColour", "uForeground" })
292 )
293
294 SHADER_DEFINE( shader_background,
295 // VERTEX
296 "layout (location=0) in vec2 a_co;"
297 "uniform mat3 uPv;"
298 "uniform vec3 uOffset;"
299 ""
300 "out vec2 aTexCoords;"
301 ""
302 "void main()"
303 "{"
304 "vec2 world_pos = a_co * uOffset.z + uOffset.xy;"
305 "gl_Position = vec4( uPv * vec3( world_pos, 1.0 ), 1.0 );"
306 "aTexCoords = a_co;"
307 "}",
308
309 // FRAGMENT
310 "out vec4 FragColor;"
311 ""
312 "uniform sampler2D uTexMain;"
313 "uniform sampler2D uSamplerNoise;"
314 "uniform float uVariance;"
315 ""
316 "in vec2 aTexCoords;"
317 ""
318 "void main()"
319 "{"
320 "float ao_accum = 0.0;"
321 "for( int i=0; i<10; ++i )"
322 "{"
323 "vec2 random_noise = (texture( uSamplerNoise, aTexCoords * 20.0 + float(i) * 0.2 ).xy - vec2( 0.5, 0.5 )) * uVariance;"
324 "vec4 background = texture( uTexMain, aTexCoords + random_noise );"
325 "ao_accum += background.r * clamp((1.0 - length( random_noise )), 0.0, 1.0);"
326 "}"
327 "ao_accum *= 0.15;"
328
329 "vec4 data_this_tile = texture( uTexMain, aTexCoords );"
330
331 "ao_accum -= data_this_tile.r;"
332
333 "vec3 colour_main = mix( vec3( 0.369768, 0.3654, 0.42 ),vec3( 0.275, 0.388, 0.553 ), data_this_tile.g );"
334
335 "vec2 square_coords = fract( aTexCoords * 64.0 );"
336 "vec2 grid_coords = abs( square_coords - 0.5 );"
337 "float edge_contrast = (1.0-ao_accum*0.2);"
338
339 "float gridline = step( 0.49, max(grid_coords.x,grid_coords.y) );"
340 "float gridline_fadeout = min(max(edge_contrast-1.0, 0.0)*40.0 + data_this_tile.g,10.0);"
341
342 "FragColor = vec4( colour_main * edge_contrast + gridline * 0.02 * gridline_fadeout, 1.0 );"
343 "}"
344 ,
345 UNIFORMS({ "uPv", "uOffset", "uTexMain", "uVariance", "uSamplerNoise" })
346 )
347
348 SHADER_DEFINE( shader_wire,
349 // VERTEX
350 "layout (location=0) in vec2 a_co;"
351 "uniform vec3 uStart;"
352 "uniform vec3 uEnd;"
353 "uniform mat3 uPv;"
354 "uniform float uCurve;"
355 ""
356 "out vec2 aTexCoords;"
357 ""
358 "vec3 sample_curve_time( float t )"
359 "{"
360 "vec3 line_coord = mix( uStart, uEnd, t );"
361
362 "float curve_amt = 1.0-(pow((t*2.0-1.0),2.0));"
363 "return vec3( line_coord.x, line_coord.y - curve_amt*uCurve, line_coord.z );"
364 "}"
365 ""
366 "void main()"
367 "{"
368 // Vertex transform
369 "vec3 p0 = sample_curve_time( a_co.x );"
370 "vec3 p1 = sample_curve_time( a_co.x + 0.025 );"
371
372 "vec2 line_tangent = normalize(p1.xy-p0.xy);"
373 "vec2 line_normal = vec2( -line_tangent.y, line_tangent.x );"
374
375 "vec2 worldfinal = p0.xy + line_normal*a_co.y*p0.z;"
376
377 "gl_Position = vec4( uPv * vec3(worldfinal, 1.0), 1.0 );"
378
379 // Create texture coords (todo: include stretch adjusted coords?)
380 "aTexCoords = vec2( a_co.x, a_co.y + 0.5 );"
381 "}",
382
383 // FRAGMENT
384 "out vec4 FragColor;"
385 ""
386 "uniform sampler2D uTexMain;"
387 "uniform vec4 uColour;"
388 "uniform float uTime;"
389 "uniform float uGlow;"
390 ""
391 "in vec2 aTexCoords;"
392 ""
393 "void main()"
394 "{"
395 // Compute shadowing
396 "float shadow = 1.0 - abs(aTexCoords.y - 0.5) * 2.0;"
397 "float masking = smoothstep( 0.5, 0.8, shadow );"
398
399 "vec3 colour_comp = mix( vec3(0.0,0.0,0.0), uColour.rgb, masking );"
400
401 "float flow_thing = fract( aTexCoords.x + uTime );"
402 "vec3 final_comp = colour_comp + flow_thing * uGlow;"
403
404 "FragColor = vec4( final_comp, max( shadow* 0.2, masking ) * uColour.a );"
405 "}"
406 ,
407 UNIFORMS({ "uPv", "uColour", "uTexMain", "uStart", "uEnd", "uCurve", "uTime", "uGlow" })
408 )
409
410 SHADER_DEFINE( shader_buttons,
411 // VERTEX
412 "layout (location=0) in vec2 a_co;"
413 "uniform vec4 uOffset;" // Tile x/y, uv x/y
414 "uniform mat3 uPv;"
415 ""
416 "out vec2 aTexCoords;"
417 ""
418 "void main()"
419 "{"
420 // Vertex transform
421 "vec3 worldpos = vec3( a_co + uOffset.xy, 1.0 );"
422 "gl_Position = vec4( uPv * worldpos, 1.0 );"
423
424 // Create texture coords
425 "vec2 edge_safe_coords = a_co * 0.98 + 0.01;"
426 "aTexCoords = (edge_safe_coords + uOffset.zw) * 0.25;"
427 "}",
428
429 // FRAGMENT
430 "out vec4 FragColor;"
431 ""
432 "uniform sampler2D uTexMain;"
433 "uniform vec4 uColour;" // rgb, light amount
434 ""
435 "in vec2 aTexCoords;"
436 ""
437 "void main()"
438 "{"
439 "vec4 glyph = texture( uTexMain, aTexCoords.xy );"
440
441 "FragColor = vec4( uColour.rgb * (mix(glyph.r, glyph.g, uColour.a)+0.02)*2.6 + glyph.b * 0.4, glyph.a );"
442 "}"
443 ,
444 UNIFORMS({ "uPv", "uOffset", "uTexMain", "uColour" })
445 )
446
447
448 void vg_register(void)
449 {
450 SHADER_INIT( shader_tile_colour );
451 SHADER_INIT( shader_tile_main );
452 SHADER_INIT( shader_ball );
453 SHADER_INIT( shader_background );
454 SHADER_INIT( shader_wire );
455 SHADER_INIT( shader_buttons );
456 }
457
458 /*
459 0000 0 | 0001 1 | 0010 2 | 0011 3
460 | | | | |
461 X | X= | X | X=
462 | | |
463 0100 4 | 0101 5 | 0110 6 | 0111 7
464 | | | | |
465 =X | =X= | =X | =X=
466 | | |
467 1000 8 | 1001 9 | 1010 10 | 1011 11
468 | | | | |
469 X | X= | X | X=
470 | | | | | | |
471 1100 12 | 1101 13 | 1110 14 | 1111 15
472 | | | | |
473 =X | =X= | =X | =X=
474 | | | | | | |
475 */
476
477 float const MESH_NUMBER_0[] = {
478 #include "fonts/numbers/n0.h"
479 };
480
481 float const MESH_NUMBER_1[] = {
482 #include "fonts/numbers/n1.h"
483 };
484
485 float const MESH_NUMBER_2[] = {
486 #include "fonts/numbers/n2.h"
487 };
488
489 float const MESH_NUMBER_3[] = {
490 #include "fonts/numbers/n3.h"
491 };
492
493 float const MESH_NUMBER_4[] = {
494 #include "fonts/numbers/n4.h"
495 };
496
497 float const MESH_NUMBER_5[] = {
498 #include "fonts/numbers/n5.h"
499 };
500
501 float const MESH_NUMBER_6[] = {
502 #include "fonts/numbers/n6.h"
503 };
504
505 float const MESH_NUMBER_7[] = {
506 #include "fonts/numbers/n7.h"
507 };
508
509 float const MESH_NUMBER_8[] = {
510 #include "fonts/numbers/n8.h"
511 };
512
513 float const MESH_NUMBER_9[] = {
514 #include "fonts/numbers/n9.h"
515 };
516
517 float const MESH_NUMBERS_BUFFER[] =
518 {
519 #include "fonts/numbers/n0.h"
520 #include "fonts/numbers/n1.h"
521 #include "fonts/numbers/n2.h"
522 #include "fonts/numbers/n3.h"
523 #include "fonts/numbers/n4.h"
524 #include "fonts/numbers/n5.h"
525 #include "fonts/numbers/n6.h"
526 #include "fonts/numbers/n7.h"
527 #include "fonts/numbers/n8.h"
528 #include "fonts/numbers/n9.h"
529 };
530
531 #define MESH_NUMBER_DIVISOR 6
532
533 u32 const MESH_NUMBERS_OFFSETS[][2] =
534 {
535 {
536 0,
537 vg_list_size( MESH_NUMBER_0 ) / MESH_NUMBER_DIVISOR
538 },
539 {
540 vg_list_size( MESH_NUMBER_0 ) / MESH_NUMBER_DIVISOR,
541 vg_list_size( MESH_NUMBER_1 ) / MESH_NUMBER_DIVISOR
542 },
543 {
544 (
545 vg_list_size( MESH_NUMBER_0 ) +
546 vg_list_size( MESH_NUMBER_1 )
547 ) / MESH_NUMBER_DIVISOR,
548 vg_list_size( MESH_NUMBER_2 ) / MESH_NUMBER_DIVISOR
549 },
550 {
551 (
552 vg_list_size( MESH_NUMBER_0 ) +
553 vg_list_size( MESH_NUMBER_1 ) +
554 vg_list_size( MESH_NUMBER_2 )
555 ) / MESH_NUMBER_DIVISOR,
556 vg_list_size( MESH_NUMBER_3 ) / MESH_NUMBER_DIVISOR
557 },
558 {
559 (
560 vg_list_size( MESH_NUMBER_0 ) +
561 vg_list_size( MESH_NUMBER_1 ) +
562 vg_list_size( MESH_NUMBER_2 ) +
563 vg_list_size( MESH_NUMBER_3 )
564 ) / MESH_NUMBER_DIVISOR,
565 vg_list_size( MESH_NUMBER_4 ) / MESH_NUMBER_DIVISOR
566 },
567 {
568 (
569 vg_list_size( MESH_NUMBER_0 ) +
570 vg_list_size( MESH_NUMBER_1 ) +
571 vg_list_size( MESH_NUMBER_2 ) +
572 vg_list_size( MESH_NUMBER_3 ) +
573 vg_list_size( MESH_NUMBER_4 )
574 ) / MESH_NUMBER_DIVISOR,
575 vg_list_size( MESH_NUMBER_5 ) / MESH_NUMBER_DIVISOR
576 },
577 {
578 (
579 vg_list_size( MESH_NUMBER_0 ) +
580 vg_list_size( MESH_NUMBER_1 ) +
581 vg_list_size( MESH_NUMBER_2 ) +
582 vg_list_size( MESH_NUMBER_3 ) +
583 vg_list_size( MESH_NUMBER_4 ) +
584 vg_list_size( MESH_NUMBER_5 )
585 ) / MESH_NUMBER_DIVISOR,
586 vg_list_size( MESH_NUMBER_6 ) / MESH_NUMBER_DIVISOR
587 },
588 {
589 (
590 vg_list_size( MESH_NUMBER_0 ) +
591 vg_list_size( MESH_NUMBER_1 ) +
592 vg_list_size( MESH_NUMBER_2 ) +
593 vg_list_size( MESH_NUMBER_3 ) +
594 vg_list_size( MESH_NUMBER_4 ) +
595 vg_list_size( MESH_NUMBER_5 ) +
596 vg_list_size( MESH_NUMBER_6 )
597 ) / MESH_NUMBER_DIVISOR,
598 vg_list_size( MESH_NUMBER_7 ) / MESH_NUMBER_DIVISOR
599 },
600 {
601 (
602 vg_list_size( MESH_NUMBER_0 ) +
603 vg_list_size( MESH_NUMBER_1 ) +
604 vg_list_size( MESH_NUMBER_2 ) +
605 vg_list_size( MESH_NUMBER_3 ) +
606 vg_list_size( MESH_NUMBER_4 ) +
607 vg_list_size( MESH_NUMBER_5 ) +
608 vg_list_size( MESH_NUMBER_6 ) +
609 vg_list_size( MESH_NUMBER_7 )
610 ) / MESH_NUMBER_DIVISOR,
611 vg_list_size( MESH_NUMBER_8 ) / MESH_NUMBER_DIVISOR
612 },
613 {
614 (
615 vg_list_size( MESH_NUMBER_0 ) +
616 vg_list_size( MESH_NUMBER_1 ) +
617 vg_list_size( MESH_NUMBER_2 ) +
618 vg_list_size( MESH_NUMBER_3 ) +
619 vg_list_size( MESH_NUMBER_4 ) +
620 vg_list_size( MESH_NUMBER_5 ) +
621 vg_list_size( MESH_NUMBER_6 ) +
622 vg_list_size( MESH_NUMBER_7 ) +
623 vg_list_size( MESH_NUMBER_8 )
624 ) / MESH_NUMBER_DIVISOR,
625 vg_list_size( MESH_NUMBER_9 ) / MESH_NUMBER_DIVISOR
626 }
627 };
628
629 struct cmp_level
630 {
631 const char *map_name;
632 const char *title;
633 const char *description;
634
635 int unlocked;
636 int completed_score;
637
638 int _unlock, _linked; // When completed, unlock this level
639 struct cmp_level *unlock, *linked;
640
641 int serial_id;
642 int is_tutorial;
643
644 SteamLeaderboard_t steam_leaderboard;
645 };
646
647 static struct cmp_level cmp_levels_tutorials[] =
648 {
649 // r1
650 {
651 .serial_id = 0,
652 .title = "PRINCIPLE 1",
653 .map_name = "cmp_t01",
654 .description =
655 "Utilize basic transport methods",
656
657 ._unlock = 1,
658 .is_tutorial = 1
659 },
660 // r1
661 {
662 .serial_id = 1,
663 .title = "PRINCIPLE 2",
664 .map_name = "cmp_t02",
665 .description =
666 "Utilize the twisty turny(TM) piece to split the marble\n"
667 "stream into two",
668
669 ._unlock = 2,
670 .is_tutorial = 1,
671 },
672 // r1
673 {
674 .serial_id = 2,
675 .title = "PRINCIPLE 3",
676 .map_name = "cmp_t03",
677 .description =
678 "Merge transport into one",
679
680 ._unlock = 12,
681 .is_tutorial = 1
682 },
683 // r1
684 {
685 .serial_id = 12,
686 .title = "PRINCIPLE 4",
687 .map_name = "cmp_t04",
688 .description =
689 "Some stages require multiple runs to succeed in order to\n"
690 "pass",
691
692 ._unlock = 3,
693 .is_tutorial = 1
694 }
695 };
696
697 static struct cmp_level cmp_levels_basic[] =
698 {
699 // r2 GM
700 {
701 .serial_id = 6,
702 .title = "PATCH",
703 .map_name = "cmp_b04",
704 .description =
705 "For some reason, the division module our intern built\n"
706 "for us is sending twice as many yellows as needed. Send\n"
707 "the excess to be recycled!",
708
709 ._unlock = 7,
710 ._linked = 3
711 },
712 // r1 GM
713 {
714 .serial_id = 3,
715 .title = "SUBDIVISION 1",
716 .map_name = "cmp_b01",
717 .description =
718 "Sometimes getting the desired amount takes dividing up\n"
719 "the input and recombining it.",
720
721 ._linked = 4,
722 ._unlock = 5
723 },
724 // r1 GM
725 {
726 .serial_id = 4,
727 .title = "SUBDIVISION 2",
728 .map_name = "cmp_b02",
729 .description =
730 "",
731
732 ._unlock = 7
733 },
734 // r1 GM
735 {
736 .serial_id = 5,
737 .title = "RESTRUCTURE",
738 .map_name = "cmp_b03",
739 .description =
740 "It is possible to swap these values using simple\n"
741 "division and addition.",
742
743 ._unlock = 8
744 },
745 // r2 GM
746 {
747 .serial_id = 7,
748 .title = "PATTERNS 1",
749 .map_name = "cmp_b05",
750 .description =
751 "Replicate",
752
753 ._linked = 8
754 },
755 // r2 GM
756 {
757 .serial_id = 8,
758 .title = "PATTERNS 2",
759 .map_name = "cmp_b06",
760 .description =
761 "Replicate MORE",
762
763 ._unlock = 15
764 },
765 // r2 GM
766 {
767 .serial_id = 15,
768 .title = "PRINCIPLE 5",
769 .map_name = "cmp_b10",
770 .description =
771 "The eager engineers among you may have already spotted\n"
772 "and utilized these parts of the system\n"
773 "\n"
774 "We forgot to include the relevant principle tasks as\n"
775 "of your training package, you will now be tasked to\n"
776 "complete them",
777
778 ._unlock = 16,
779 .is_tutorial = 1
780 },
781 // r2 GM
782 {
783 .serial_id = 16,
784 .title = "ROUTING PROBLEM",
785 .map_name = "cmp_routing",
786 .description =
787 "Things can get a little chaotic on tight boards, do your\n"
788 "best to utilize principle 5 to get the job done\n",
789
790 ._linked = 9
791 },
792 // r2 GM
793 {
794 .serial_id = 9,
795 .title = "MIGHTY CONSUMER",
796 .map_name = "cmp_b07",
797 .description =
798 "Build a greedy system",
799
800 ._linked = 10,
801 ._unlock = 11
802 },
803 {
804 .serial_id = 10,
805 .title = "SHIFT",
806 .map_name = "cmp_b08",
807 .description =
808 "",
809
810 ._unlock = 17
811 },
812 // r2 GM
813 {
814 .serial_id = 11,
815 .title = "REVERSE",
816 .map_name = "cmp_b09",
817 .description =
818 "Reverse the incoming order. Always length 4",
819
820 ._unlock = 17
821 },
822 // r2 GM
823 {
824 .serial_id = 17,
825 .title = "PRINCIPLE 6",
826 .map_name = "cmp_b11",
827 .description =
828 "Usually the splitter piece will flip flop between left\n"
829 "and right, however it can be forced to only rotate in\n"
830 "one direction if trigger wires are attached.\n"
831 "\n"
832 "Right click and drag from a regular block, and attach it\n"
833 "to a splitter. This creates a trigger.\n"
834 "The default state is left, and once a marble hits the\n"
835 "trigger it will switch to rotating that direction.",
836
837 ._unlock = 18,
838 .is_tutorial = 1
839 },
840 // r2 GM
841 {
842 .serial_id = 18,
843 .title = "NOT GATE",
844 .map_name = "cmp_not",
845 .description =
846 "Test your knowledge of triggers, build an 'NOT GATE'\n"
847 "emulated by marble logic.",
848
849 ._linked = 19,
850 ._unlock = 20
851 },
852 // r2 GM
853 {
854 .serial_id = 19,
855 .title = "AND GATE",
856 .map_name = "cmp_and",
857 .description =
858 "A slightly more complicated gate, but shouldn't be\n"
859 "too difficult for your skillset.",
860
861 ._unlock = 20
862 },
863 // r2 GM
864 {
865 .serial_id = 20,
866 .title = "QUALIFICATION PROJECT",
867 .map_name = "cmp_xor",
868 .description =
869 "Significantly more complicated than an AND or NOT gate,\n"
870 "but possible.",
871
872 ._unlock = 13
873 }
874 };
875
876 static struct cmp_level cmp_levels_grad[] =
877 {
878 // r2
879 {
880 .serial_id = 13,
881 .title = "SORT",
882 .map_name = "cmp_i01",
883 .description =
884 "Device a scheme to filter and sort the inputs. If you\n"
885 "believe you lack the tools required to solve this one,\n"
886 "take a harder look at the inputs.",
887 ._linked = 14
888
889 },
890 // r2
891 {
892 .serial_id = 14,
893 .title = "THIRDS",
894 .map_name = "cmp_i02",
895 .description =
896 "Split the inputs up into a third of their values\n"
897 "\n"
898 "Is this possible? -HG",
899 ._linked = 21
900
901 },
902 // r2 GM
903 {
904 .serial_id = 21,
905 .title = "SIMPLE ADDITION",
906 .map_name = "cmp_grad",
907 .description =
908 "Take the amount of yellows coming in, and add them\n"
909 "together. Send your result using the stream of blues.",
910
911 ._linked = 22
912 },
913 // r2 GM
914 {
915 .serial_id = 22,
916 .title = "SECRET CODE",
917 .map_name = "cmp_secret",
918 .description =
919 ""
920 }
921 };
922
923 #define NUM_CAMPAIGN_LEVELS (vg_list_size( cmp_levels_tutorials ) + vg_list_size( cmp_levels_basic ) + vg_list_size( cmp_levels_grad ))
924
925 static struct serializable_set
926 {
927 struct cmp_level *pack;
928 int count;
929 }
930 career_serializable[] =
931 {
932 {
933 .pack = cmp_levels_tutorials,
934 .count = vg_list_size( cmp_levels_tutorials )
935 },
936 {
937 .pack = cmp_levels_basic,
938 .count = vg_list_size( cmp_levels_basic )
939 },
940 {
941 .pack = cmp_levels_grad,
942 .count = vg_list_size( cmp_levels_grad )
943 }
944 };
945
946 // Setup pointers and that
947 static void career_local_data_init(void)
948 {
949 struct cmp_level *level_ptrs[ NUM_CAMPAIGN_LEVELS ];
950
951 // COllect pointers
952 for( int i = 0; i < vg_list_size( career_serializable ); i ++ )
953 {
954 struct serializable_set *set = &career_serializable[i];
955
956 for( int j = 0; j < set->count; j ++ )
957 level_ptrs[ set->pack[j].serial_id ] = &set->pack[j];
958 }
959
960 // Apply
961 for( int i = 0; i < vg_list_size( career_serializable ); i ++ )
962 {
963 struct serializable_set *set = &career_serializable[i];
964
965 for( int j = 0; j < set->count; j ++ )
966 {
967 struct cmp_level *lvl = &set->pack[j];
968 lvl->unlock = lvl->_unlock? level_ptrs[ lvl->_unlock ]: NULL;
969 lvl->linked = lvl->_linked? level_ptrs[ lvl->_linked ]: NULL;
970 }
971 }
972 }