1 #define MARBLE_COMP_VERSION 4
5 k_world_button_mode_once
,
6 k_world_button_mode_toggle
13 float light_target
, light
, extra_light
;
16 enum world_button_mode mode
;
19 enum world_button_status
21 k_world_button_on_enable
,
22 k_world_button_on_disable
25 #include "fishladder_resources_vg1.h"
28 #define STEAM_LEADERBOARDS
34 k_cell_type_ramp_right
= 3,
35 k_cell_type_ramp_left
= 6,
36 k_cell_type_split
= 7,
37 k_cell_type_merge
= 13,
38 k_cell_type_con_r
= 1,
39 k_cell_type_con_u
= 2,
40 k_cell_type_con_l
= 4,
46 k_fish_state_soon_dead
= -1,
47 k_fish_state_dead
= 0,
50 k_fish_state_soon_alive
55 k_world_button_none
= -1,
56 k_world_button_sim
= 0,
57 k_world_button_pause
= 1,
58 k_world_button_speedy
= 2,
59 k_world_button_settings
= 3
65 k_game_state_settings
,
69 #define FLAG_CANAL 0x1
70 #define FLAG_IS_TRIGGER 0x2
71 #define FLAG_RESERVED0 0x4
72 #define FLAG_RESERVED1 0x8
74 #define FLAG_4B_GROUP (FLAG_CANAL|FLAG_IS_TRIGGER|FLAG_RESERVED0|FLAG_RESERVED1)
76 #define FLAG_INPUT 0x10
77 #define FLAG_OUTPUT 0x20
78 #define FLAG_WALL 0x40
79 #define FLAG_EMITTER 0x80
81 #define FLAG_FLIP_FLOP 0x100
82 #define FLAG_TRIGGERED 0x200
83 #define FLAG_FLIP_ROTATING 0x400
84 #define FLAG_TARGETED 0x800
86 #define FLAG_INPUT_NICE 0x1000
89 0000 0 | 0001 1 | 0010 2 | 0011 3
93 0100 4 | 0101 5 | 0110 6 | 0111 7
97 1000 8 | 1001 9 | 1010 10 | 1011 11
101 1100 12 | 1101 13 | 1110 14 | 1111 15
107 static struct cell_description
116 enum sprites_auto_combine_index trigger_sprite
;
118 cell_descriptions
[] =
121 { .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
},
122 { .start
= { 1, 0 }, .end
= { -1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
},
123 .trigger_sprite
= k_sprite_brk_d
},
124 { .start
= { 0, 1 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
},
125 .trigger_sprite
= k_sprite_brk_l
},
126 { .start
= { 0, 1 }, .end
= { 1, 0 }, .trigger_pos
= { 0.25f
, 0.5f
},
127 .trigger_sprite
= k_sprite_brk_l
},
129 { .start
= { -1, 0 }, .end
= { 1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
},
130 .trigger_sprite
= k_sprite_brk_d
},
131 { .start
= { -1, 0 }, .end
= { 1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
},
132 .trigger_sprite
= k_sprite_brk_d
, .is_linear
= 1 },
133 { .start
= { 0, 1 }, .end
= { -1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
},
134 .trigger_sprite
= k_sprite_brk_d
},
135 { .start
= { 0, 1 }, .is_special
= 1 },
137 { .start
= { 0, -1 }, .end
= { 0, 1 }, .trigger_pos
= { 0.25f
, 0.5f
},
138 .trigger_sprite
= k_sprite_brk_l
},
139 { .start
= { 1, 0 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
},
140 .trigger_sprite
= k_sprite_brk_l
},
141 { .start
= { 0, 1 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
},
142 .trigger_sprite
= k_sprite_brk_l
, .is_linear
= 1 },
145 { .start
= { -1, 0 }, .end
= { 0, -1 }, .trigger_pos
= { 0.5f
, 0.75f
},
146 .trigger_sprite
= k_sprite_brk_u
},
147 { .end
= { 0, -1 }, .is_special
= 1, .trigger_pos
= { 0.5f
, 0.75f
},
148 .trigger_sprite
= k_sprite_brk_u
},
153 v2f
const curve_3
[] = {{0.5f
,1.0f
},{0.5f
,0.625f
},{0.625f
,0.5f
},{1.0f
,0.5f
}};
154 v2f
const curve_6
[] = {{0.5f
,1.0f
},{0.5f
,0.625f
},{0.375f
,0.5f
},{0.0f
,0.5f
}};
155 v2f
const curve_9
[] = {{1.0f
,0.5f
},{0.625f
,0.5f
},{0.5f
,0.375f
},{0.5f
,0.0f
}};
156 v2f
const curve_12
[]= {{0.0f
,0.5f
},{0.375f
,0.5f
},{0.5f
,0.375f
},{0.5f
,0.0f
}};
158 v2f
const curve_1
[] = {{1.0f
,0.5f
},{0.8f
,0.5f
},{0.3f
,0.5f
},{0.2f
,0.5f
}};
159 v2f
const curve_4
[] = {{0.0f
,0.5f
},{0.3f
,0.5f
},{0.5f
,0.5f
},{0.8f
,0.5f
}};
160 v2f
const curve_2
[] = {{0.5f
,1.0f
},{0.5f
,0.8f
},{0.5f
,0.3f
},{0.5f
,0.2f
}};
161 v2f
const curve_8
[] = {{0.5f
,0.0f
},{0.5f
,0.3f
},{0.5f
,0.5f
},{0.5f
,0.8f
}};
163 v2f
const curve_7
[] =
164 {{0.5f
,0.8438f
},{0.875f
,0.8438f
},{0.625f
,0.5f
},{1.0f
,0.5f
}};
165 v2f
const curve_7_1
[] =
166 {{0.5f
,0.8438f
},{1.0f
-0.875f
,0.8438f
},{1.0-0.625f
,0.5f
},{0.0f
,0.5f
}};
168 float const curve_7_linear_section
= 0.1562f
;
176 #define EFFECT_BUFFER_RATIO 4
180 /* Things that are 'static', aka, initialized once */
183 struct world_button buttons
[4];
185 enum e_game_state state
;
187 struct cmp_level
*lvl_to_load
;
190 float world_transition
;
197 bloomframebuffer
[2], /* Quater res */
198 bloomcolourbuffer
[2];
218 *cmd_buf_tiles
, *cmd_buf_specials
;
220 u32 tile_count
, tile_special_count
, max_commands
;
223 int sim_run
, max_runs
;
225 int sim_frame
, sim_target
;
226 float sim_internal_time
,
241 int step_count
, recv_count
;
255 GLuint vao
, vbo
, ebo
;
260 GLuint background_data
;
261 GLuint random_samples
;
263 int selected
, tile_x
, tile_y
;
270 enum e_fish_state state
;
282 struct cmp_level
*pCmpLevel
;
296 .buttons
= { { .mode
= k_world_button_mode_toggle
},
297 { .mode
= k_world_button_mode_toggle
},
298 { .mode
= k_world_button_mode_toggle
},
299 { .mode
= k_world_button_mode_toggle
} }
304 static void colour_code_v3( i8 cc
, v3f target
);
305 static int hash21i( v2i p
, u32 umod
);
310 static void init_mesh( struct mesh
*m
, float const *tris
, u32 length
);
311 static void free_mesh( struct mesh
*m
);
312 static void use_mesh( struct mesh
*m
);
313 static void draw_mesh( int const start
, int const count
);
318 static void level_selection_buttons(void);
321 * Map/world interface
323 static void map_free(void);
324 static void io_reset(void);
325 static struct cell
*pcell( v2i pos
);
326 static void lcell( int id
, v2i pos
);
327 static void map_reclassify( v2i start
, v2i end
, int update_texbuffer
);
328 static void gen_level_text(void);
329 static int map_load( const char *str
, const char *name
);
330 static void map_serialize( FILE *stream
);
335 static void career_serialize(void);
336 static void career_unlock_level( struct cmp_level
*lvl
);
337 static void career_unlock_level( struct cmp_level
*lvl
);
338 static void career_pass_level( struct cmp_level
*lvl
, int score
, int upload
);
339 static void career_reset_level( struct cmp_level
*lvl
);
340 static void career_load(void);
341 static void clear_animation_flags(void);
346 static void simulation_stop(void);
347 static void simulation_start(void);
348 static int world_check_pos_ok( v2i co
, int dist
);
349 static int cell_interactive( v2i co
);
351 static void render_tiles( v4f
const regular_colour
,
352 v4f
const selected_colour
, int with_glow
);
353 static void render_tile_block( v2i start
, v2i end
, v4f
const regular_colour
,
354 v4f
const selected_colour
);
356 #ifdef STEAM_LEADERBOARDS
357 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
358 void leaderboard_dispatch_score(void);
359 void leaderboard_found( LeaderboardFindResult_t
*pCallback
);
360 void leaderboard_downloaded( LeaderboardScoresDownloaded_t
*pCallback
);
361 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
364 static int console_credits( int argc
, char const *argv
[] );
365 static int console_save_map( int argc
, char const *argv
[] );
366 static int console_load_map( int argc
, char const *argv
[] );
367 static int console_changelevel( int argc
, char const *argv
[] );
376 static int colour_set_id
= 0;
377 static int world_theme_id
= 0;
378 static int enable_bloom
= 1;
379 static int enable_vignette
= 1;
380 static float music_volume
= 1.0f
;
382 static void music_volume_update(void)
385 sfx_vol_fset( &audio_volume_music
, music_volume
);
389 static v3f colour_sets
[][4] =
391 { { 1.0f
, 0.9f
, 0.3f
},
392 { 0.4f
, 0.8f
, 1.00f
},
393 { 0.2f
, 0.9f
, 0.14f
},
394 { 0.882f
, 0.204f
, 0.922f
}
396 { { 1.0f
, 0.9f
, 0.3f
},
397 { 0.4f
, 0.8f
, 1.00f
},
398 { 0.85f
, 0.85f
, 0.85f
},
401 { { 1.0f
, 0.9f
, 0.3f
},
402 { 0.827f
, 0.373f
, 0.718f
},
403 { 0.0f
, 0.353f
, 0.71f
},
404 { 0.863f
, 0.196f
, 0.125f
}
408 static struct world_theme
413 vg1_tex2d
*tex_tiles
;
419 { 0.6f
, 0.6f
, 0.6f
},
424 { 0.89f
, 0.8f
, 0.7f
},
429 { 0.7f
, 0.7f
, 0.7f
},
434 static void colour_code_v3( i8 cc
, v3f target
)
436 if( (cc
>= 0) && (cc
< vg_list_size( colour_sets
[0] )) )
438 v3_copy( colour_sets
[colour_set_id
][ cc
], target
);
442 vg_error( "Invalid colour code used '%d'\n", (int)cc
);
443 v3_copy( (v3f
){0.0f
,0.0f
,0.0f
}, target
);
446 static int hash21i( v2i p
, u32 umod
)
448 static const int random_noise
[] = {
449 0x46,0xD5,0xB8,0xD3,0xF2,0xE5,0xCC,0x07,0xD0,0xB3,0x7A,0xA2,0xC3,0xDA,0xDC,0x7F,
450 0xE0,0xB7,0x42,0xA0,0xBF,0x41,0x92,0x32,0x6F,0x0D,0x45,0xC7,0x54,0xDB,0x30,0xC2,
451 0xD5,0xDA,0x55,0x09,0xDE,0x74,0x48,0x20,0xE1,0x24,0x5C,0x4D,0x6F,0x36,0xD8,0xE9,
452 0x8D,0x8F,0x54,0x99,0x98,0x51,0xFE,0xDB,0x26,0x04,0x65,0x57,0x56,0xF3,0x53,0x30,
453 0x3D,0x16,0xC0,0xB6,0xF2,0x47,0xCF,0x62,0xB0,0x6C,0x8F,0x4F,0x8C,0x4C,0x17,0xF0,
454 0x19,0x7E,0x2D,0x81,0x8D,0xFB,0x10,0xD3,0x49,0x50,0x60,0xFD,0x38,0x15,0x3B,0xEE,
455 0x05,0xC1,0xCF,0x62,0x97,0x75,0xDF,0x4E,0x4D,0x89,0x5E,0x88,0x5C,0x30,0x8C,0x54,
456 0x1D,0x39,0x41,0xEA,0xA2,0x63,0x12,0x1B,0x8E,0x35,0x22,0x9B,0x98,0xA3,0x7F,0x80,
457 0xD6,0x27,0x94,0x66,0xB5,0x1D,0x7E,0xDF,0x96,0x28,0x38,0x3A,0xA0,0xE8,0x71,0x09,
458 0x62,0x5E,0x9D,0x53,0x58,0x1B,0x7D,0x0D,0x2D,0x99,0x77,0x83,0xC3,0x89,0xC2,0xA2,
459 0xA7,0x1D,0x78,0x80,0x37,0xC1,0x87,0xFF,0x65,0xBF,0x2C,0xF1,0xE5,0xB3,0x09,0xE0,
460 0x25,0x92,0x83,0x0F,0x8A,0x57,0x3C,0x0B,0xC6,0xBC,0x44,0x16,0xE3,0xCE,0xC3,0x0D,
461 0x69,0xD3,0xC6,0x99,0xB8,0x46,0x44,0xC4,0xF3,0x1E,0xBF,0xF5,0xB4,0xDB,0xFB,0x93,
462 0xA1,0x7B,0xC9,0x08,0x77,0x22,0xE5,0x02,0xEF,0x9E,0x90,0x94,0x8A,0xA6,0x3D,0x7E,
463 0xA2,0xA0,0x10,0x82,0x47,0x5C,0xAA,0xF8,0x2F,0x0D,0x9F,0x76,0xDA,0x99,0x0F,0xCB,
464 0xE2,0x02,0x0C,0x75,0xCA,0x35,0x29,0xA6,0x49,0x83,0x6D,0x91,0xB4,0xEC,0x31,0x69,
465 0xBA,0x13,0xF3,0xC7,0x21,0x06,0xC8,0x79,0xEF,0xB1,0x9C,0x6A,0xEE,0x64,0x9A,0xDC,
466 0x1E,0xC6,0x18,0x93,0xA9,0x7E,0x89,0x7D,0x96,0xE5,0x44,0xB8,0x00,0x15,0xAF,0x8C,
467 0x78,0x8F,0xA8,0x05,0xA7,0x07,0x25,0x9A,0xC8,0x5D,0x90,0x1A,0x41,0x53,0x30,0xD3,
468 0x24,0x33,0x71,0xB4,0x50,0x6E,0xE4,0xEA,0x0D,0x2B,0x6D,0xF5,0x17,0x08,0x74,0x49,
469 0x71,0xC2,0xAC,0xF7,0xDC,0xB2,0x7E,0xCC,0xB6,0x1B,0xB8,0xA9,0x52,0xCF,0x6B,0x51,
470 0xD2,0x4E,0xC9,0x43,0xEE,0x2E,0x92,0x24,0xBB,0x47,0x4D,0x0C,0x3E,0x21,0x53,0x19,
471 0xD4,0x82,0xE2,0xC6,0x93,0x85,0x0A,0xF8,0xFA,0x04,0x07,0xD3,0x1D,0xEC,0x03,0x66,
472 0xFD,0xB1,0xFB,0x8F,0xC5,0xDE,0xE8,0x29,0xDF,0x23,0x09,0x9D,0x7C,0x43,0x3D,0x4D,
473 0x89,0xB9,0x6F,0xB4,0x6B,0x4A,0x51,0xC3,0x94,0xF4,0x7C,0x5E,0x19,0x87,0x79,0xC1,
474 0x80,0x0C,0x45,0x12,0xEC,0x95,0xF3,0x31,0x68,0x42,0xE1,0x06,0x57,0x0E,0xA7,0xFB,
475 0x78,0x96,0x87,0x23,0xA5,0x20,0x7A,0x09,0x3A,0x45,0xE6,0xD9,0x5E,0x6A,0xD6,0xAA,
476 0x29,0x50,0x92,0x4E,0xD0,0xB5,0x91,0xC2,0x9A,0xCF,0x07,0xFE,0xB2,0x15,0xEB,0xE4,
477 0x84,0x40,0x14,0x47,0xFA,0x93,0xB9,0x06,0x69,0xDB,0xBD,0x4E,0xEA,0x52,0x9B,0xDE,
478 0x5B,0x50,0x36,0xAB,0xB3,0x1F,0xD2,0xCD,0x9C,0x13,0x07,0x7E,0x8B,0xED,0x72,0x62,
479 0x74,0x77,0x3B,0x88,0xAC,0x5B,0x6A,0xBC,0xDA,0x99,0xE8,0x24,0x90,0x5A,0xCA,0x8D,
480 0x5C,0x2B,0xF8,0xF1,0xE1,0x1D,0x94,0x11,0xEA,0xCC,0x02,0x09,0x1E,0xA2,0x48,0x67,
481 0x87,0x5A,0x7E,0xC6,0xCC,0xA3,0xFB,0xC5,0x36,0xEB,0x5C,0xE1,0xAF,0x1E,0xBE,0xE7,
482 0xD8,0x8F,0x70,0xAE,0x42,0x05,0xF5,0xCD,0x2D,0xA2,0xB0,0xFD,0xEF,0x65,0x2C,0x22,
483 0xCB,0x8C,0x8B,0xAA,0x3D,0x86,0xE2,0xCD,0xBE,0xC3,0x42,0x38,0xE3,0x9C,0x08,0xB5,
484 0xAE,0xBD,0x54,0x73,0x83,0x70,0x24,0x47,0xCA,0x4C,0x04,0xC4,0xE0,0x1D,0x40,0xED,
485 0xF4,0x2B,0x50,0x8E,0x97,0xB3,0xF0,0xA6,0x76,0xDB,0x49,0x30,0xE5,0xD9,0x71,0x07,
486 0xB2,0xF1,0x0F,0xD6,0x77,0xAA,0x72,0xC0,0xAF,0x66,0xD8,0x40,0xC6,0x08,0x19,0x8C,
487 0xD9,0x8F,0x5A,0x75,0xAC,0xBE,0xC2,0x40,0x5B,0xBD,0x0D,0x1D,0x00,0xAF,0x26,0x5E,
488 0x78,0x43,0xAA,0xC6,0x4F,0xF3,0xD8,0xE2,0x7F,0x0C,0x1E,0x77,0x4D,0x35,0x96,0x23,
489 0x32,0x44,0x03,0x8D,0x92,0xE7,0xFD,0x48,0x07,0xD0,0x58,0xFC,0x6D,0xC9,0x91,0x33,
490 0xF0,0x23,0x45,0xA4,0x29,0xB9,0xF5,0xB0,0x68,0x8F,0x7B,0x59,0x15,0x8E,0xA6,0x66,
491 0x15,0xA0,0x76,0x9B,0x69,0xCB,0x38,0xA5,0xF4,0xB4,0x6B,0xDC,0x1F,0xAB,0xAE,0x12,
492 0x77,0xC0,0x8C,0x4A,0x03,0xB9,0x73,0xD3,0x6D,0x52,0xC5,0xF5,0x6E,0x4E,0x4B,0xA3,
493 0x24,0x02,0x58,0xEE,0x5F,0xF9,0xD6,0xD0,0x1D,0xBC,0xF4,0xB8,0x4F,0xFD,0x4B,0x2D,
494 0x34,0x77,0x46,0xE5,0xD4,0x33,0x7B,0x9C,0x35,0xCD,0xB0,0x5D,0x06,0x39,0x99,0xEB,
495 0x0C,0xD0,0x0F,0xF7,0x92,0xB5,0x58,0x5B,0x5E,0x79,0x12,0xF4,0x05,0xF6,0x21,0x07,
496 0x0B,0x49,0x1A,0xFB,0xD4,0x98,0xC4,0xEF,0x7A,0xD6,0xCA,0xA1,0xDA,0xB3,0x51,0x00,
497 0x76,0xEC,0x08,0x48,0x40,0x35,0xD7,0x94,0xBE,0xF5,0x7B,0xA4,0x20,0x81,0x5F,0x82,
498 0xF3,0x6F,0x96,0x24,0x98,0xB6,0x49,0x18,0xC8,0xC5,0x8C,0xD2,0x38,0x7F,0xC4,0xF6,
499 0xAA,0x87,0xDC,0x73,0x5B,0xA1,0xAF,0xE5,0x3D,0x37,0x6B,0x85,0xED,0x38,0x62,0x7D,
500 0x57,0xBD,0xCF,0xB5,0x1B,0xA8,0xBB,0x32,0x33,0xD3,0x34,0x5A,0xC1,0x5D,0xFB,0x28,
501 0x6E,0xE1,0x67,0x51,0xBB,0x31,0x92,0x83,0xAC,0xAA,0x72,0x52,0xFD,0x13,0x4F,0x73,
502 0xD3,0xF0,0x5E,0xFC,0xBA,0xB1,0x3C,0x7B,0x08,0x76,0x03,0x38,0x1E,0xD1,0xCC,0x33,
503 0xA3,0x1E,0xFC,0xE0,0x82,0x30,0x27,0x93,0x71,0x35,0x75,0x77,0xBA,0x78,0x10,0x33,
504 0xCD,0xAB,0xCF,0x8E,0xAD,0xF9,0x32,0xC9,0x15,0x9F,0xD6,0x6D,0xA8,0xAE,0xB1,0x3F,
505 0x90,0xEB,0xD4,0xF9,0x31,0x81,0xA3,0x53,0x99,0x4B,0x3C,0x93,0x3B,0xFE,0x55,0xFF,
506 0x25,0x9F,0xCC,0x07,0xC5,0x2C,0x14,0xA7,0xA4,0x1E,0x6C,0xB6,0x91,0x2A,0xE0,0x3E,
507 0x7F,0x39,0x0A,0xD9,0x24,0x3C,0x01,0xA0,0x30,0x99,0x8E,0xB8,0x1D,0xF9,0xA7,0x78,
508 0x86,0x95,0x35,0x0E,0x21,0xDA,0x7A,0x7B,0xAD,0x9F,0x4E,0xF6,0x63,0x5B,0x96,0xBB,
509 0x87,0x36,0x3F,0xA7,0x1A,0x66,0x91,0xCD,0xB0,0x3B,0xC0,0x4F,0x54,0xD2,0x5F,0xBB,
510 0x38,0x89,0x1C,0x79,0x7E,0xA2,0x02,0xE4,0x80,0x84,0x1E,0x33,0xAB,0x74,0xFA,0xBE,
511 0x31,0x46,0x2E,0xC5,0x15,0xB9,0x12,0xE9,0xD3,0x73,0x43,0xEA,0x74,0x11,0xA7,0xC0,
512 0xD5,0xD8,0x39,0x08,0x9F,0x4F,0xC7,0x71,0x25,0x09,0x51,0x65,0xD6,0xA8,0x02,0x1F
515 return random_noise
[ (random_noise
[p
[1] & 1023] + p
[0]) & 1023 ] & umod
;
518 static void init_mesh( struct mesh
*m
, float const *tris
, u32 length
){
519 m
->elements
= length
/3;
520 glGenVertexArrays( 1, &m
->vao
);
521 glGenBuffers( 1, &m
->vbo
);
523 glBindVertexArray( m
->vao
);
524 glBindBuffer( GL_ARRAY_BUFFER
, m
->vbo
);
525 glBufferData( GL_ARRAY_BUFFER
, length
*sizeof(float), tris
, GL_STATIC_DRAW
);
527 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
, 2*sizeof(float), (void*)0 );
528 glEnableVertexAttribArray( 0 );
531 static void free_mesh( struct mesh
*m
){
532 glDeleteVertexArrays( 1, &m
->vao
);
533 glDeleteBuffers( 1, &m
->vbo
);
536 static void draw_mesh( int const start
, int const count
){
537 glDrawArrays( GL_TRIANGLES
, start
*3, count
*3 );
540 static void use_mesh( struct mesh
*m
){
541 glBindVertexArray( m
->vao
);
544 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
546 static void map_free(void)
548 arrfree( world
.data
);
551 free( world
.cmd_buf_tiles
);
552 world
.cmd_buf_tiles
= NULL
;
562 world
.initialzed
= 0;
565 static void io_reset(void)
567 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
569 struct cell_terminal
*term
= &world
.io
[i
];
571 for( int j
= 0; j
< term
->run_count
; j
++ )
572 term
->runs
[j
].recv_count
= 0;
576 static struct cell
*pcell( v2i pos
)
578 return &world
.data
[ pos
[1]*world
.w
+ pos
[0] ];
581 static void lcell( int id
, v2i pos
)
583 pos
[0] = id
% world
.w
;
584 pos
[1] = (id
- pos
[0]) / world
.w
;
587 static void map_reclassify( v2i start
, v2i end
, int update_texbuffer
)
589 v2i full_start
= { 1,1 };
590 v2i full_end
= { world
.w
-1, world
.h
-1 };
599 u8 info_buffer
[64*64*4];
602 int px0
= vg_max( start
[0], full_start
[0] ),
603 px1
= vg_min( end
[0], full_end
[0] ),
604 py0
= vg_max( start
[1], full_start
[1] ),
605 py1
= vg_min( end
[1], full_end
[1] );
607 for( int y
= py0
; y
< py1
; y
++ )
609 for( int x
= px0
; x
< px1
; x
++ )
611 struct cell
*cell
= pcell((v2i
){x
,y
});
613 v2i dirs
[] = {{1,0},{0,1},{-1,0},{0,-1}};
618 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
620 for( int i
= 0; i
< vg_list_size( dirs
); i
++ )
622 struct cell
*neighbour
=
623 pcell((v2i
){ x
+dirs
[i
][0], y
+dirs
[i
][1] });
625 if( neighbour
->state
&
626 (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
636 if( cell
->state
& FLAG_WALL
)
637 height
= 0xFF; /*-0x3F + hash21i( (v2i){x,y}, 0x3F );*/
639 config
= cell
->state
& FLAG_INPUT_NICE
? 0xB: 0xF;
642 pcell((v2i
){x
,y
})->config
= config
;
644 u8
*info_px
= &info_buffer
[ (pixel_id
++)*4 ];
646 info_px
[1] = cell
->state
& FLAG_WALL
? 0: 255;
651 * Detecting hanging links that should be removed
653 int is_trigger
= cell
->state
& FLAG_IS_TRIGGER
;
654 int trigger_invalid
= cell
->config
== 0xF ||
655 cell
->config
== k_cell_type_split
;
656 int is_targeted
= cell
->state
& FLAG_TARGETED
;
657 int target_invalid
= (cell
->config
!= k_cell_type_split
) &&
658 !(cell
->state
& FLAG_EMITTER
);
661 (is_trigger
&& trigger_invalid
) ||
662 (is_targeted
&& target_invalid
)
663 ) && update_texbuffer
)
665 cell
->state
&= ~(FLAG_TARGETED
|FLAG_IS_TRIGGER
);
666 for( u32 i
= 0; i
< 2; i
++ )
670 struct cell
*other_ptr
= &world
.data
[ cell
->links
[i
] ];
671 other_ptr
->links
[ i
] = 0;
672 other_ptr
->state
&= ~FLAG_IS_TRIGGER
;
674 if( other_ptr
->links
[ i
^ 0x1 ] == 0 )
675 other_ptr
->state
&= ~FLAG_TARGETED
;
685 if( update_texbuffer
)
687 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
688 glTexSubImage2D( GL_TEXTURE_2D
, 0,
690 px1
-px0
, py1
-py0
, GL_RGBA
, GL_UNSIGNED_BYTE
, info_buffer
);
694 static void gen_level_text(void)
697 ui_px
const unit_scale_px
= 4*UI_GLYPH_SPACING_X
;
698 ui_begin( &world
.st
.world_text
, world
.w
*unit_scale_px
,
699 world
.h
*unit_scale_px
);
701 if( world
.pCmpLevel
)
703 for( int i
= 0; i
< vg_list_size( world
.pCmpLevel
->strings
); i
++ )
705 struct world_string
*wstr
= &world
.pCmpLevel
->strings
[i
];
711 pos
[0] = -UI_GLYPH_SPACING_X
/2;
713 if( wstr
->placement
== k_placement_bottom
)
714 pos
[1] = 2*-unit_scale_px
;
716 pos
[1] = (world
.h
-1)*-unit_scale_px
-6;
718 ui_text( &world
.st
.world_text
, pos
, wstr
->str
,
719 1, k_text_align_left
);
724 // re-create level scores
725 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
727 struct career_level_pack
*set
= &career_packs
[i
];
730 ui_text( &world.st.world_text,
732 set->origin[0]*unit_scale_px,
733 -(set->origin[1]+set->dims[1]+1)*unit_scale_px + 18
735 set->title, 1, k_text_align_left );
738 for( int j
= 0; j
< set
->count
; j
++ )
740 struct cmp_level
*lvl
= &set
->pack
[j
];
742 if( lvl
->completed_score
&& !lvl
->is_tutorial
)
745 snprintf( num
, 9, "%d", lvl
->completed_score
);
747 ui_text( &world
.st
.world_text
,
749 lvl
->btn
.position
[0]*unit_scale_px
+ unit_scale_px
/2,
750 -lvl
->btn
.position
[1]*unit_scale_px
- unit_scale_px
/2
752 num
, 1, k_text_align_center
);
757 ui_resolve( &world
.st
.world_text
);
761 /* Usually for ignoring windows crap */
762 static int map_load_char_ignore( char c
)
764 if( c
== '\r' ) return 1;
768 static int map_load_sequence_char( struct terminal_run
*run
, char c
)
770 if( (c
>= 'a' && c
<= 'z') || c
== ' ' )
776 run
->steps
[ run
->step_count
++ ] = code
;
784 static int map_load_is_digit( char c
)
786 if( (((u32
)c
>= (u32
)'0') && ((u32
)c
<= (u32
)'9')) || c
== '-' )
794 static int map_load_is_terminal( char c
)
796 if( c
== '+' || c
== '-' || c
== '*' )
801 static int map_load_apply_emitter_codes( struct cell_terminal
*term
)
803 struct cell
*cell
= pcell( term
->pos
);
805 if( cell
->state
& FLAG_EMITTER
)
807 if( (term
->run_count
> 0) && (term
->runs
[0].step_count
>= 2) )
809 cell
->emit
[0] = term
->runs
[0].steps
[0];
810 cell
->emit
[1] = term
->runs
[0].steps
[1];
814 vg_error( "Emitter was not assigned emit values\n" );
822 static void map_load_cell( struct cell
*cell
, char c
, int cx
)
829 v3_zero( cell
->glow
[0] );
830 v3_zero( cell
->glow
[1] );
832 /* Input, output, emitter */
833 if( map_load_is_terminal(c
) )
835 struct cell_terminal
*term
= arraddnptr( world
.io
, 1 );
837 term
->pos
[1] = world
.h
;
840 term
->runs
[0].step_count
= 0;
844 case '+': cell
->state
= FLAG_INPUT
; break;
845 case '-': cell
->state
= FLAG_OUTPUT
; break;
846 case '*': cell
->state
= FLAG_EMITTER
; break;
849 else if( c
== '.' ) cell
->state
= FLAG_INPUT_NICE
;
850 else if( c
== '#' ) cell
->state
= FLAG_WALL
;
851 else if( ((u32
)c
>= (u32
)'A') && ((u32
)c
<= (u32
)'A'+0xf) )
854 * Canal flag bits (4bit/16 value):
860 cell
->state
= ((u32
)c
- (u32
)'A') & (FLAG_CANAL
|FLAG_IS_TRIGGER
);
863 else cell
->state
= 0x00;
866 static void map_load_draw_background(void)
868 u8 info_buffer
[64*64*4];
870 for( int x
= 0; x
< 64; x
++ )
872 for( int y
= 0; y
< 64; y
++ )
874 u8
*px
= &info_buffer
[((x
*64)+y
)*4];
877 /* Fade out edges of world so that there isnt an obvious line */
878 int dx
= 16 - VG_MIN( VG_MIN( x
, 16 ), 16-VG_MAX( x
-16-world
.w
, 0 ) ),
879 dy
= 16 - VG_MIN( VG_MIN( y
, 16 ), 16-VG_MAX( y
-16-world
.h
, 0 ) ),
881 dist
= VG_MAX( dx
, dy
) * 16,
882 value
= VG_MAX( 0, 0xFF-0x3F + hash21i( (v2i
){x
,y
}, 0x3F ) - dist
);
893 * Level selection indentation, to make it look like the buttons are in a
894 * recessed area of the map.
896 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
898 struct career_level_pack
*grid
= &career_packs
[ i
];
902 for( int y
= 0; y
< grid
->dims
[1]; y
++ )
904 for( int x
= 0; x
< grid
->dims
[0]; x
++ )
906 int cy
= y
+16+grid
->origin
[1],
907 cx
= 16+x
+grid
->origin
[0];
909 u8
*px
= &info_buffer
[(cy
*64+cx
)*4];
912 if( j
< grid
->count
)
914 struct cmp_level
*lvl
= &grid
->pack
[ j
++ ];
915 v2i_add( grid
->origin
, (v2i
){x
,y
}, lvl
->btn
.position
);
922 * Recess the UI buttons, this adds a little bit of a (subtle) outline
923 * to them when the background shader is run
925 for( int i
=0; i
<4; i
++ )
926 info_buffer
[(((16+world
.h
-(i
+2))*64)+world
.w
+16-1)*4] = 0x30;
929 * Digging 'wires' from the output/input terminals, to make it look like
930 * there is something connecting our world.
932 for( int i
= 0; i
< arrlen(world
.io
); i
++ )
934 struct cell_terminal
*term
= &world
.io
[ i
];
941 * Only make breakouts for terminals on the edge,
942 * starting them from every position leads to some weird overlapping
943 * and confusing lines.
945 if( !(term
->pos
[1] == 1 || term
->pos
[1] == world
.h
-2) )
948 turtle
[0] = 16+term
->pos
[0];
949 turtle
[1] = 16+term
->pos
[1];
953 pcell(term
->pos
)->state
& (FLAG_INPUT
|FLAG_EMITTER
)? 1: -1;
954 original_y
= turtle_dir
[1];
956 info_buffer
[((turtle
[1]*64)+turtle
[0])*4] = 0;
957 v2i_add( turtle_dir
, turtle
, turtle
);
959 for( int i
= 0; i
< 100; i
++ )
961 info_buffer
[((turtle
[1]*64)+turtle
[0])*4] = 0;
963 v2i_add( turtle_dir
, turtle
, turtle
);
965 if( turtle
[0] == 0 ) break;
966 if( turtle
[0] == 63 ) break;
967 if( turtle
[1] == 0 ) break;
968 if( turtle
[1] == 63 ) break;
970 int random_value
= hash21i( turtle
, 0xFF );
971 if( random_value
> 255-40 && !turtle_dir
[0] )
976 else if( random_value
> 255-80 && !turtle_dir
[0] )
981 else if( random_value
> 255-100 )
984 turtle_dir
[1] = original_y
;
989 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
990 glTexSubImage2D( GL_TEXTURE_2D
, 0, 0, 0, 64, 64,
991 GL_RGBA
, GL_UNSIGNED_BYTE
, info_buffer
);
994 static int map_load_validate(void)
996 for( int i
= 0; i
< world
.h
*world
.w
; i
++ )
998 struct cell
*src
= &world
.data
[i
];
1000 if( src
->state
& FLAG_IS_TRIGGER
)
1002 int link_id
= src
->links
[0]?0:1;
1003 if( src
->links
[link_id
] <= world
.h
*world
.w
)
1005 struct cell
*target
= &world
.data
[ src
->links
[link_id
] ];
1007 int is_canal
= target
->state
& FLAG_CANAL
,
1008 is_splitter
= target
->config
== k_cell_type_split
,
1009 is_emitter
= target
->state
& FLAG_EMITTER
;
1011 if((is_canal
&& is_splitter
) || is_emitter
)
1013 if( target
->links
[ link_id
] )
1015 vg_error( "Link target was already targeted\n" );
1021 * Valid link, apply reverse mapping to other cell so it
1022 * knows who is linking to it
1024 target
->links
[ link_id
] = i
;
1025 target
->state
|= FLAG_TARGETED
;
1030 vg_error( "Link target was invalid\n" );
1036 vg_error( "Link target out of bounds\n" );
1045 static int map_load( const char *str
, const char *name
)
1049 char const *c
= str
;
1051 /* Predetermine width */
1054 if( c
[world
.w
] == ';' )
1056 else if( !c
[world
.w
] )
1058 vg_error( "Unexpected EOF when parsing level\n" );
1063 struct cell
*row
= arraddnptr( world
.data
, world
.w
);
1065 int reg_start
= 0, reg_end
= 0;
1067 u32
*links_to_make
= NULL
;
1068 int links_satisfied
= 0;
1070 char link_id_buffer
[32];
1078 if( map_load_char_ignore( *c
) ) { c
++; continue; }
1084 if( *c
== '\r' ) c
++;
1091 if( map_load_char_ignore( *c
) ) { c
++; continue; }
1093 if( reg_start
< reg_end
)
1095 struct cell_terminal
*terminal
= &world
.io
[ reg_start
];
1096 struct terminal_run
*run
=
1097 &terminal
->runs
[ terminal
->run_count
-1 ];
1099 if( !map_load_sequence_char( run
, *c
) )
1101 /* Control characters */
1102 if( *c
== ',' || *c
== '\n' ) /* Next terminal */
1109 else if( *c
== ':' ) /* New run starting */
1111 terminal
->runs
[ terminal
->run_count
].step_count
= 0;
1112 terminal
->run_count
++;
1114 vg_max( world
.max_runs
, terminal
->run_count
);
1118 vg_error( "Unkown attribute '%c' (row: %u)\n",
1126 if( links_satisfied
< arrlen( links_to_make
) )
1128 struct cell
*target
=
1129 &world
.data
[ links_to_make
[ links_satisfied
] ];
1131 if( map_load_is_digit( *c
) )
1133 if( link_id_n
>= vg_list_size( link_id_buffer
)-1 )
1135 vg_error( "Number was way too long to be parsed"
1136 " (row: %u)\n", world
.h
);
1140 link_id_buffer
[ link_id_n
++ ] = *c
;
1142 else if( *c
== ',' || *c
== '\n' )
1144 link_id_buffer
[ link_id_n
] = 0x00;
1145 int value
= atoi( link_id_buffer
);
1147 target
->links
[value
>= 0? 1:0] = abs(value
);
1156 vg_error( "Invalid character '%c'"
1157 " (row: %u)\n", *c
, world
.h
);
1163 vg_error( "Too many values to assign"
1164 " (row: %u)\n", world
.h
);
1173 /* Registry length-error checks */
1174 if( reg_start
!= reg_end
)
1176 vg_error( "Not enough spawn values assigned (row: %u, %u of %u)\n",
1177 world
.h
, reg_start
, reg_end
);
1181 if( links_satisfied
!= arrlen( links_to_make
) )
1183 vg_error( "Not enough link values assigned (row: %u, %u of %u)\n",
1184 world
.h
, links_satisfied
, arrlen( links_to_make
) );
1190 vg_error( "Not enough cells to match previous row definition"
1191 " (row: %u, %u<%u)\n", world
.h
, cx
, world
.w
);
1195 row
= arraddnptr( world
.data
, world
.w
);
1198 reg_end
= reg_start
= arrlen( world
.io
);
1200 arrsetlen( links_to_make
, 0 );
1201 links_satisfied
= 0;
1207 vg_error( "Too many cells to match previous row definition"
1208 " (row: %u, %u>%u)\n", world
.h
, cx
, world
.w
);
1212 struct cell
*cell
= &row
[ cx
];
1213 map_load_cell( cell
, *c
, cx
);
1215 if( map_load_is_terminal(*c
) )
1218 if( cell
->state
& FLAG_IS_TRIGGER
)
1219 arrpush( links_to_make
, cx
+ world
.h
*world
.w
);
1227 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
1229 if( !map_load_apply_emitter_codes( &world
.io
[i
] ) )
1233 map_load_draw_background();
1234 map_reclassify( NULL
, NULL
, 1 );
1236 if( !map_load_validate() )
1240 * At this point the world is in a fully loaded and complete state
1243 arrfree( links_to_make
);
1245 vg_success( "Map '%s' loaded! (%u:%u)\n", name
, world
.w
, world
.h
);
1249 strncpy( world
.map_name
, name
, vg_list_size( world
.map_name
)-1 );
1250 world
.initialzed
= 1;
1252 /* Setup world button locations */
1253 for( int i
= 0; i
< vg_list_size( world
.st
.buttons
); i
++ )
1255 struct world_button
*btn
= &world
.st
.buttons
[i
];
1256 btn
->position
[0] = world
.w
-1;
1257 btn
->position
[1] = world
.h
-i
-2;
1260 /* Allocate buffers for render commands */
1261 world
.cmd_buf_tiles
= malloc( world
.w
*world
.h
* sizeof( struct render_cmd
) );
1262 world
.max_commands
= world
.w
*world
.h
;
1266 arrfree( links_to_make
);
1271 static void map_serialize( FILE *stream
)
1273 for( int y
= 0; y
< world
.h
; y
++ )
1275 for( int x
= 0; x
< world
.w
; x
++ )
1277 struct cell
*cell
= pcell( (v2i
){ x
, y
} );
1279 if( cell
->state
& FLAG_WALL
) fputc( '#', stream
);
1280 else if( cell
->state
& FLAG_INPUT_NICE
) fputc( '.', stream
);
1281 else if( cell
->state
& FLAG_INPUT
) fputc( '+', stream
);
1282 else if( cell
->state
& FLAG_OUTPUT
) fputc( '-', stream
);
1283 else if( cell
->state
& FLAG_EMITTER
) fputc( '*', stream
);
1284 else if( cell
->state
& FLAG_4B_GROUP
)
1287 * Serialize the '4 bit group' into ABCD...
1289 fputc( (cell
->state
& FLAG_4B_GROUP
) + (u32
)'A', stream
);
1291 else fputc( ' ', stream
);
1294 fputc( ';', stream
);
1295 int terminal_write_count
= 0;
1297 for( int x
= 0; x
< world
.w
; x
++ )
1299 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
1301 struct cell_terminal
*term
= &world
.io
[i
];
1302 if( v2i_eq( term
->pos
, (v2i
){x
,y
} ) )
1304 if( terminal_write_count
)
1305 fputc( ',', stream
);
1306 terminal_write_count
++;
1308 for( int j
= 0; j
< term
->run_count
; j
++ )
1310 struct terminal_run
*run
= &term
->runs
[j
];
1312 for( int k
= 0; k
< run
->step_count
; k
++ )
1314 i8 step
= run
->steps
[k
];
1315 fputc( step
== -1? ' ': ('a' + run
->steps
[k
]), stream
);
1318 if( j
< term
->run_count
-1 )
1319 fputc( ':', stream
);
1325 for( int x
= 0; x
< world
.w
; x
++ )
1327 struct cell
*cell
= pcell( (v2i
){ x
,y
} );
1328 if( cell
->state
& FLAG_IS_TRIGGER
)
1330 if( terminal_write_count
)
1331 fputc( ',', stream
);
1332 terminal_write_count
++;
1334 fprintf( stream
, "%d",
1335 cell
->links
[0]? -cell
->links
[0]: cell
->links
[1] );
1339 fputc( '\n', stream
);
1346 #pragma pack(push,1)
1347 struct dcareer_state
1361 levels
[ NUM_CAMPAIGN_LEVELS
];
1365 static int career_load_success
= 0;
1367 static void career_serialize(void)
1369 if( !career_load_success
)
1372 struct dcareer_state encoded
;
1373 encoded
.version
= MARBLE_COMP_VERSION
;
1374 encoded
.in_map
= world
.pCmpLevel
? world
.pCmpLevel
->serial_id
: -1;
1376 memset( encoded
.reserved
, 0, sizeof( encoded
.reserved
) );
1378 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1380 struct career_level_pack
*set
= &career_packs
[i
];
1382 for( int j
= 0; j
< set
->count
; j
++ )
1384 struct cmp_level
*lvl
= &set
->pack
[j
];
1385 struct dlevel_state
*dest
= &encoded
.levels
[lvl
->serial_id
];
1387 dest
->score
= lvl
->completed_score
;
1388 dest
->unlocked
= lvl
->unlocked
;
1389 dest
->reserved
[0] = 0;
1390 dest
->reserved
[1] = 0;
1394 vg_asset_write( "sav/game.sv2", &encoded
, sizeof( struct dcareer_state
) );
1397 static void career_unlock_level( struct cmp_level
*lvl
);
1398 static void career_unlock_level( struct cmp_level
*lvl
)
1403 career_unlock_level( lvl
->linked
);
1406 static void career_pass_level( struct cmp_level
*lvl
, int score
, int upload
)
1410 lvl
->completed_score
= score
;
1414 career_unlock_level( lvl
->unlock
);
1417 if( lvl
->achievement
)
1418 sw_set_achievement( lvl
->achievement
);
1420 /* Check ALL maps to trigger master engineer */
1421 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1423 struct career_level_pack
*set
= &career_packs
[i
];
1425 for( int j
= 0; j
< set
->count
; j
++ )
1427 if( set
->pack
[j
].completed_score
== 0 )
1432 sw_set_achievement( "MASTER_ENGINEER" );
1437 static void career_reset_level( struct cmp_level
*lvl
)
1440 lvl
->completed_score
= 0;
1443 static void career_load(void)
1445 struct dcareer_state encoded
;
1447 /* Blank save state */
1448 memset( (void*)&encoded
, 0, sizeof( struct dcareer_state
) );
1450 encoded
.levels
[0].unlocked
= 1;
1453 * Load and copy, this step is just to ensure old/newer saves can be loaded
1454 * without crashing. Old saves will load fine, too new saves will lose data,
1455 * such a situation should rarely (never) happen with the steam version.
1458 void *cr
= vg_file_read( NULL
, "sav/game.sv2", &sz
);
1462 if( sz
> sizeof( struct dcareer_state
) )
1463 vg_warn( "This save file is too big! Some levels will be lost\n" );
1465 if( sz
<= offsetof( struct dcareer_state
, levels
) )
1467 vg_warn( "This save file is too small to have a header. "
1468 "Creating a blank one\n" );
1472 memcpy( (void*)&encoded
, cr
, VG_MIN( sizeof( struct dcareer_state
), sz
));
1476 vg_info( "No save file... Using blank one\n" );
1479 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1481 struct career_level_pack
*set
= &career_packs
[i
];
1483 for( int j
= 0; j
< set
->count
; j
++ )
1484 career_reset_level( &set
->pack
[j
] );
1487 /* Header information */
1489 struct cmp_level
*lvl_to_load
= &career_packs
[0].pack
[0];
1491 /* Decode everything from dstate */
1492 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1494 struct career_level_pack
*set
= &career_packs
[i
];
1496 for( int j
= 0; j
< set
->count
; j
++ )
1498 struct cmp_level
*lvl
= &set
->pack
[j
];
1499 struct dlevel_state
*src
= &encoded
.levels
[lvl
->serial_id
];
1501 if( src
->unlocked
) career_unlock_level( lvl
);
1504 lvl
->completed_score
= src
->score
;
1507 * Apply unlocking trigger to next levels,
1508 * in case the level graph was updated in the future
1511 career_unlock_level( lvl
->unlock
);
1514 if( lvl
->serial_id
== encoded
.in_map
)
1519 if( console_changelevel( 1, &lvl_to_load
->map_name
) )
1521 world
.pCmpLevel
= lvl_to_load
;
1525 career_load_success
= 1;
1528 if( encoded
.version
< MARBLE_COMP_VERSION
)
1529 world
.st
.state
= k_game_state_update
;
1536 static int is_simulation_running(void)
1538 return world
.st
.buttons
[ k_world_button_sim
].state
;
1541 static void clear_animation_flags(void)
1543 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
1544 world
.data
[ i
].state
&= ~(FLAG_FLIP_FLOP
|FLAG_FLIP_ROTATING
);
1547 static void simulation_stop(void)
1549 world
.st
.buttons
[ k_world_button_sim
].state
= 0;
1550 world
.st
.buttons
[ k_world_button_pause
].state
= 0;
1552 world
.num_fishes
= 0;
1553 world
.sim_frame
= 0;
1555 world
.frame_lerp
= 0.0f
;
1560 sfx_system_fadeout( &audio_system_balls_rolling
, 44100 );
1563 clear_animation_flags();
1565 vg_info( "Stopping simulation!\n" );
1568 static void simulation_start(void)
1570 vg_success( "Starting simulation!\n" );
1572 audio_oneshot( &audio_rolls
[ vg_randu32(&vg
.rand
) % 2 ], 1.0f
, 0.0f
);
1575 world
.num_fishes
= 0;
1576 world
.sim_frame
= 0;
1579 world
.sim_delta_speed
= world
.st
.buttons
[ k_world_button_speedy
].state
?
1581 world
.sim_delta_ref
= vg
.time
;
1582 world
.sim_internal_ref
= 0.0f
;
1583 world
.sim_internal_time
= 0.0f
;
1584 world
.pause_offset_target
= 0.0f
;
1586 world
.sim_target
= 0;
1588 clear_animation_flags();
1592 if( world
.pCmpLevel
)
1594 world
.pCmpLevel
->completed_score
= 0;
1599 static int world_check_pos_ok( v2i co
, int dist
)
1601 return (co
[0] < dist
|| co
[0] >= world
.w
-dist
|| co
[1] < dist
|| co
[1] >= world
.h
-dist
)? 0: 1;
1604 static int cell_interactive( v2i co
)
1606 struct cell
*cell
= NULL
;
1609 if( world_check_pos_ok( co
, 1 ) )
1612 if( cell
->state
& FLAG_EMITTER
)
1616 if( !world_check_pos_ok( co
, 2 ) )
1620 if( cell
->state
& (FLAG_WALL
|FLAG_INPUT
|FLAG_OUTPUT
) )
1623 // List of 3x3 configurations that we do not allow
1624 static u32 invalid_src
[][9] =
1656 // Statically compile invalid configurations into bitmasks
1657 static u32 invalid
[ vg_list_size(invalid_src
) ];
1659 for( int i
= 0; i
< vg_list_size(invalid_src
); i
++ )
1663 for( int j
= 0; j
< 3; j
++ )
1664 for( int k
= 0; k
< 3; k
++ )
1665 comped
|= invalid_src
[i
][ j
*3+k
] << ((j
*5)+k
);
1667 invalid
[i
] = comped
;
1670 // Extract 5x5 grid surrounding tile
1672 for( int y
= co
[1]-2; y
< co
[1]+3; y
++ )
1673 for( int x
= co
[0]-2; x
< co
[0]+3; x
++ )
1675 struct cell
*cell
= pcell((v2i
){x
,y
});
1677 if( cell
&& (cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
)) )
1678 blob
|= 0x1 << ((y
-(co
[1]-2))*5 + x
-(co
[0]-2));
1681 // Run filter over center 3x3 grid to check for invalid configurations
1682 int kernel
[] = { 0, 1, 2, 5, 6, 7, 10, 11, 12 };
1683 for( int i
= 0; i
< vg_list_size(kernel
); i
++ )
1685 if( blob
& (0x1 << (6+kernel
[i
])) )
1687 u32 window
= blob
>> kernel
[i
];
1689 for( int j
= 0; j
< vg_list_size(invalid
); j
++ )
1690 if((window
& invalid
[j
]) == invalid
[j
])
1698 static void _mc_vg1_update(void)
1701 if( world
.st
.lvl_to_load
)
1703 world
.st
.world_transition
= (world
.st
.lvl_load_time
-vg
.time
) * 4.0f
;
1705 if( vg
.time
> world
.st
.lvl_load_time
)
1707 if( console_changelevel( 1, &world
.st
.lvl_to_load
->map_name
) )
1709 world
.pCmpLevel
= world
.st
.lvl_to_load
;
1713 world
.st
.lvl_to_load
= NULL
;
1718 world
.st
.world_transition
= vg_minf( 1.0f
, (vg
.time
-world
.st
.lvl_load_time
) * 4.0f
);
1722 // ========================================================================================================
1724 float r1
= (float)vg
.window_y
/ (float)vg
.window_x
,
1725 r2
= (float)world
.h
/ (float)world
.w
,
1728 static float size_current
= 2.0f
;
1729 static v3f origin_current
= { 0.0f
, 0.0f
, 0.0f
};
1730 static v2f drag_offset
= { 0.0f
, 0.0f
};
1731 static v2f view_point
= { 0.0f
, 0.0f
};
1734 size
= ( r2
< r1
? (float)(world
.w
+5) * 0.5f
: ((float)(world
.h
+5) * 0.5f
) / r1
) + 0.5f
;
1739 origin
[0] = floorf( -0.5f
* ((float)world
.w
-4.5f
) );
1740 origin
[1] = floorf( -0.5f
* world
.h
);
1742 // Create and clamp result view
1743 v2_add( view_point
, drag_offset
, result_view
);
1744 result_view
[0] = vg_clampf( result_view
[0], -world
.st
.zoom
, world
.st
.zoom
);
1745 result_view
[1] = vg_clampf( result_view
[1], -world
.st
.zoom
*r1
, world
.st
.zoom
*r1
);
1747 v2_add( origin
, result_view
, vt_target
);
1749 // Lerp towards target
1750 size_current
= vg_lerpf( size_current
, size
- world
.st
.zoom
, vg
.time_delta
* 6.0f
);
1751 v2_lerp( origin_current
, vt_target
, vg
.time_delta
* 6.0f
, origin_current
);
1753 m3x3_projection( m_projection
, -size_current
, size_current
, -size_current
*r1
, size_current
*r1
);
1754 m3x3_identity( m_view
);
1755 m3x3_translate( m_view
, origin_current
);
1756 m3x3_mul( m_projection
, m_view
, vg
.pv
);
1759 vg_projection_update();
1762 if( world
.st
.state
== k_game_state_update
)
1766 // ========================================================================================================
1767 v2_copy( marblecomp
.mouse_ws
, world
.tile_pos
);
1768 world
.tile_x
= floorf( world
.tile_pos
[0] );
1769 world
.tile_y
= floorf( world
.tile_pos
[1] );
1770 v2f vg_mouse
= { vg
.mouse_pos
[0], vg
.mouse_pos
[1] };
1774 static v2f drag_origin
; // x/y pixel
1776 if( button_down( k_srbind_tertiary
) ){
1777 v2_copy( vg_mouse
, drag_origin
);
1779 else if( button_press( k_srbind_tertiary
) ){
1781 v2_sub( vg_mouse
, drag_origin
, drag_offset
);
1782 v2_div( drag_offset
, (v2f
){ vg
.window_x
, vg
.window_y
}, drag_offset
);
1783 v2_mul( drag_offset
, (v2f
){ size_current
*2.0f
, -size_current
*r1
*2.0f
}, drag_offset
);
1786 v2_copy( result_view
, view_point
);
1787 v2_copy( (v2f
){0.0f
,0.0f
}, drag_offset
);
1799 rsize
= size
-world
.st
.zoom
;
1801 v2_div( vg_mouse
, (v2f
){ vg
.window_x
*0.5f
, vg
.window_y
*0.5f
}, mview_local
);
1802 v2_add( (v2f
){ -rsize
, -rsize
*r1
}, (v2f
){ mview_local
[0]*rsize
, (2.0f
-mview_local
[1])*rsize
*r1
}, mview_cur
);
1804 world
.st
.zoom
= vg_clampf( world
.st
.zoom
+ vg
.mouse_wheel
[1], 0.0f
, size
- 4.0f
);
1806 // Recalculate new position
1807 rsize
= size
-world
.st
.zoom
;
1808 v2_add( (v2f
){ -rsize
, -rsize
*r1
}, (v2f
){ mview_local
[0]*rsize
, (2.0f
-mview_local
[1])*rsize
*r1
}, mview_new
);
1811 v2_sub( mview_new
, mview_cur
, mview_delta
);
1812 v2_add( mview_delta
, view_point
, view_point
);
1816 // ========================================================================================================
1817 if( !is_simulation_running() ){
1818 v2_copy( marblecomp
.mouse_ws
, world
.drag_to_co
);
1820 if( cell_interactive( (v2i
){ world
.tile_x
, world
.tile_y
} ))
1822 world
.selected
= world
.tile_y
* world
.w
+ world
.tile_x
;
1824 static u32 modify_state
= 0;
1825 struct cell
*cell_ptr
= &world
.data
[world
.selected
];
1827 if( !(cell_ptr
->state
& FLAG_EMITTER
) )
1829 if( button_down(k_srbind_primary
) )
1830 modify_state
= (cell_ptr
->state
& FLAG_CANAL
) ^ FLAG_CANAL
;
1832 if( button_press(k_srbind_primary
) && ((cell_ptr
->state
& FLAG_CANAL
) != modify_state
) )
1834 cell_ptr
->state
&= ~FLAG_CANAL
;
1835 cell_ptr
->state
|= modify_state
;
1838 if( cell_ptr
->state
& FLAG_CANAL
)
1840 cell_ptr
->links
[0] = 0;
1841 cell_ptr
->links
[1] = 0;
1843 audio_oneshot( &audio_tile_mod
[ vg_randu32(&vg
.rand
)%4+2 ],
1849 audio_oneshot( &audio_tile_mod
[ vg_randu32(&vg
.rand
)%3 ],
1855 map_reclassify((v2i
){ world
.tile_x
-2, world
.tile_y
-2 },
1856 (v2i
){ world
.tile_x
+2, world
.tile_y
+2 }, 1 );
1859 if( button_down(k_srbind_secondary
) && (cell_ptr
->state
& FLAG_CANAL
) && !(cell_ptr
->config
== k_cell_type_split
) )
1861 world
.id_drag_from
= world
.selected
;
1863 struct cell_description
*desc
= &cell_descriptions
[ world
.data
[world
.id_drag_from
].config
];
1864 v2_add( desc
->trigger_pos
, (v2f
){ world
.tile_x
, world
.tile_y
}, world
.drag_from_co
);
1868 float local_x
= marblecomp
.mouse_ws
[0] - (float)world
.tile_x
;
1870 if( button_up(k_srbind_secondary
) && world
.id_drag_from
== world
.selected
)
1872 u32 link_id
= cell_ptr
->links
[ 0 ]? 0: 1;
1874 // break existing connection off
1875 if( cell_ptr
->links
[ link_id
] )
1877 struct cell
*current_connection
= &world
.data
[ cell_ptr
->links
[ link_id
]];
1879 if( !current_connection
->links
[ link_id
^ 0x1 ] )
1880 current_connection
->state
&= ~FLAG_TARGETED
;
1882 current_connection
->links
[ link_id
] = 0;
1883 cell_ptr
->links
[ link_id
] = 0;
1886 cell_ptr
->state
&= ~FLAG_IS_TRIGGER
;
1887 world
.id_drag_from
= 0;
1890 else if( world
.id_drag_from
&& (cell_ptr
->state
& (FLAG_CANAL
|FLAG_EMITTER
)) &&
1891 ((cell_ptr
->config
== k_cell_type_split
) || (cell_ptr
->state
& FLAG_EMITTER
)) )
1893 world
.drag_to_co
[0] = (float)world
.tile_x
+ (local_x
> 0.5f
? 0.75f
: 0.25f
);
1894 world
.drag_to_co
[1] = (float)world
.tile_y
+ 0.25f
;
1896 if( button_up( k_srbind_secondary
) )
1898 struct cell
*drag_ptr
= &world
.data
[world
.id_drag_from
];
1899 u32 link_id
= local_x
> 0.5f
? 1: 0;
1901 // Cleanup existing connections
1902 if( cell_ptr
->links
[ link_id
] )
1904 vg_warn( "Destroying existing connection on link %u (%hu)\n", link_id
, cell_ptr
->links
[ link_id
] );
1906 struct cell
*current_connection
= &world
.data
[ cell_ptr
->links
[ link_id
]];
1907 current_connection
->state
&= ~FLAG_IS_TRIGGER
;
1908 current_connection
->links
[ link_id
] = 0;
1911 for( u32 i
= 0; i
< 2; i
++ )
1913 if( drag_ptr
->links
[ i
] )
1915 vg_warn( "Destroying link %u (%hu)\n", i
, drag_ptr
->links
[ i
] );
1917 struct cell
*current_connection
= &world
.data
[ drag_ptr
->links
[ i
]];
1918 if( current_connection
->links
[ i
^ 0x1 ] == 0 )
1919 current_connection
->state
&= ~FLAG_TARGETED
;
1921 current_connection
->links
[ i
] = 0;
1922 drag_ptr
->links
[ i
] = 0;
1926 // Create the new connection
1927 vg_success( "Creating connection on link %u (%hu)\n", link_id
, world
.id_drag_from
);
1929 cell_ptr
->links
[ link_id
] = world
.id_drag_from
;
1930 drag_ptr
->links
[ link_id
] = world
.selected
;
1932 cell_ptr
->state
|= FLAG_TARGETED
;
1933 drag_ptr
->state
|= FLAG_IS_TRIGGER
;
1934 world
.id_drag_from
= 0;
1940 world
.selected
= -1;
1943 if( !(button_press(k_srbind_secondary
) && world
.id_drag_from
) )
1944 world
.id_drag_from
= 0;
1948 world
.selected
= -1;
1949 world
.id_drag_from
= 0;
1952 // Marble state updates
1953 // ========================================================================================================
1954 if( is_simulation_running() )
1956 float old_time
= world
.sim_internal_time
;
1958 if( !world
.st
.buttons
[ k_world_button_pause
].state
)
1959 world
.sim_internal_time
= world
.sim_internal_ref
+ (vg
.time
-world
.sim_delta_ref
) * world
.sim_delta_speed
;
1961 world
.sim_internal_time
= vg_lerpf( world
.sim_internal_time
, world
.sim_internal_ref
+ world
.pause_offset_target
, vg
.time_delta
*15.0f
);
1962 world
.sim_internal_delta
= world
.sim_internal_time
-old_time
;
1964 world
.sim_target
= (int)floorf(world
.sim_internal_time
);
1966 int success_this_frame
= 0;
1967 int failure_this_frame
= 0;
1969 while( world
.sim_frame
< world
.sim_target
)
1972 audio_oneshot( &audio_random
[ vg_randu32(&vg
.rand
) % 8 ], 1.0f
, 0.0f
);
1975 // Update splitter deltas
1976 for( int i
= 0; i
< world
.h
*world
.w
; i
++ )
1978 struct cell
*cell
= &world
.data
[i
];
1979 if( cell
->config
== k_cell_type_split
)
1981 cell
->state
&= ~FLAG_FLIP_ROTATING
;
1983 if( cell
->state
& (FLAG_IS_TRIGGER
|FLAG_EMITTER
) )
1984 cell
->state
&= ~FLAG_TRIGGERED
;
1987 int alive_count
= 0;
1989 // Update fish positions
1990 for( int i
= 0; i
< world
.num_fishes
; i
++ )
1992 struct fish
*fish
= &world
.fishes
[i
];
1994 if( fish
->state
== k_fish_state_soon_dead
)
1995 fish
->state
= k_fish_state_dead
;
1997 if( fish
->state
== k_fish_state_soon_alive
)
1998 fish
->state
= k_fish_state_alive
;
2000 if( fish
->state
< k_fish_state_alive
)
2003 struct cell
*cell_current
= pcell( fish
->pos
);
2005 if( fish
->state
== k_fish_state_alive
)
2008 if( cell_current
->state
& FLAG_OUTPUT
)
2010 for( int j
= 0; j
< arrlen( world
.io
); j
++ )
2012 struct cell_terminal
*term
= &world
.io
[j
];
2014 if( v2i_eq( term
->pos
, fish
->pos
) )
2016 struct terminal_run
*run
= &term
->runs
[ world
.sim_run
];
2017 if( run
->recv_count
< vg_list_size( run
->recieved
) )
2019 if( fish
->colour
== run
->steps
[ run
->recv_count
] )
2020 success_this_frame
= 1;
2022 failure_this_frame
= 1;
2024 run
->recieved
[ run
->recv_count
++ ] = fish
->colour
;
2027 failure_this_frame
= 1;
2033 fish
->state
= k_fish_state_dead
;
2034 fish
->death_time
= -1000.0f
;
2039 if( cell_current
->config
== k_cell_type_merge
)
2044 fish
->flow_reversed
= 0;
2048 if( cell_current
->config
== k_cell_type_split
)
2051 fish
->dir
[0] = cell_current
->state
&FLAG_FLIP_FLOP
?1:-1;
2054 if( !(cell_current
->state
& FLAG_TARGETED
) )
2055 cell_current
->state
^= FLAG_FLIP_FLOP
;
2059 // Apply cell out-flow
2060 struct cell_description
*desc
= &cell_descriptions
[ cell_current
->config
];
2062 v2i_copy( fish
->flow_reversed
? desc
->start
: desc
->end
, fish
->dir
);
2066 v2i_add( fish
->pos
, fish
->dir
, pos_next
);
2068 struct cell
*cell_next
= pcell( pos_next
);
2070 if( cell_next
->state
& (FLAG_CANAL
|FLAG_OUTPUT
) )
2072 struct cell_description
*desc
= &cell_descriptions
[ cell_next
->config
];
2074 if( cell_next
->config
== k_cell_type_merge
)
2076 if( fish
->dir
[0] == 0 )
2078 fish
->state
= k_fish_state_dead
;
2079 fish
->death_time
= world
.sim_internal_time
;
2082 fish
->flow_reversed
= 0;
2086 if( cell_next
->config
== k_cell_type_split
)
2088 if( fish
->dir
[0] == 0 )
2091 audio_oneshot( &audio_splitter
[0], 1.0f
, 0.0f
);
2093 cell_next
->state
|= FLAG_FLIP_ROTATING
;
2095 fish
->flow_reversed
= 0;
2099 fish
->state
= k_fish_state_dead
;
2100 fish
->death_time
= world
.sim_internal_time
;
2104 fish
->flow_reversed
= ( fish
->dir
[0] != -desc
->start
[0] ||
2105 fish
->dir
[1] != -desc
->start
[1] )? 1: 0;
2110 if( world_check_pos_ok( fish
->pos
, 2 ) )
2111 fish
->state
= k_fish_state_bg
;
2114 fish
->state
= k_fish_state_dead
;
2115 fish
->death_time
= world
.sim_internal_time
;
2120 //v2i_add( fish->pos, fish->dir, fish->pos );
2122 else if( fish
->state
== k_fish_state_bg
)
2124 v2i_add( fish
->pos
, fish
->dir
, fish
->pos
);
2126 if( !world_check_pos_ok( fish
->pos
, 2 ) )
2128 fish
->state
= k_fish_state_dead
;
2129 fish
->death_time
= -1000.0f
;
2133 struct cell
*cell_entry
= pcell( fish
->pos
);
2135 if( cell_entry
->state
& FLAG_CANAL
)
2137 if( cell_entry
->config
== k_cell_type_con_r
|| cell_entry
->config
== k_cell_type_con_u
2138 || cell_entry
->config
== k_cell_type_con_l
|| cell_entry
->config
== k_cell_type_con_d
)
2141 sw_set_achievement( "CAN_DO_THAT" );
2144 fish
->state
= k_fish_state_soon_alive
;
2148 fish
->flow_reversed
= 1;
2150 switch( cell_entry
->config
)
2152 case k_cell_type_con_r
: fish
->dir
[0] = 1; break;
2153 case k_cell_type_con_l
: fish
->dir
[0] = -1; break;
2154 case k_cell_type_con_u
: fish
->dir
[1] = 1; break;
2155 case k_cell_type_con_d
: fish
->dir
[1] = -1; break;
2161 else { vg_error( "fish behaviour unimplemented for behaviour type (%d)\n" ); }
2163 if( fish
->state
>= k_fish_state_alive
)
2167 // Second pass (triggers)
2168 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2170 struct fish
*fish
= &world
.fishes
[i
];
2173 if( fish
->state
== k_fish_state_alive
)
2174 v2i_add( fish
->pos
, fish
->dir
, fish
->pos
);
2176 if( fish
->state
== k_fish_state_alive
|| fish
->state
== k_fish_state_soon_alive
)
2178 struct cell
*cell_current
= pcell( fish
->pos
);
2180 if( cell_current
->state
& FLAG_IS_TRIGGER
)
2182 int trigger_id
= cell_current
->links
[0]?0:1;
2184 struct cell
*target_peice
= &world
.data
[ cell_current
->links
[trigger_id
] ];
2187 if( (target_peice
->state
& FLAG_EMITTER
) && !(target_peice
->state
& FLAG_TRIGGERED
))
2189 if( world
.num_fishes
< vg_list_size( world
.fishes
) )
2191 struct fish
*fish
= &world
.fishes
[ world
.num_fishes
];
2192 lcell( cell_current
->links
[trigger_id
], fish
->pos
);
2194 fish
->state
= k_fish_state_soon_alive
;
2195 fish
->colour
= target_peice
->emit
[ trigger_id
];
2197 if( target_peice
->config
!= k_cell_type_stub
)
2199 struct cell_description
*desc
= &cell_descriptions
[ target_peice
->config
];
2200 v2i_copy( desc
->start
, fish
->dir
);
2201 fish
->flow_reversed
= 1;
2203 world
.num_fishes
++;
2208 vg_warn( "Max marbles exceeded\n" );
2213 target_peice
->state
|= FLAG_FLIP_FLOP
;
2215 target_peice
->state
&= ~FLAG_FLIP_FLOP
;
2218 cell_current
->state
|= FLAG_TRIGGERED
;
2223 // Third pass (collisions)
2224 struct fish
*fi
, *fj
;
2226 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2228 fi
= &world
.fishes
[i
];
2230 if( (fi
->state
== k_fish_state_alive
) |
2231 (fi
->state
== k_fish_state_soon_alive
) )
2233 int continue_again
= 0;
2235 for( int j
= i
+1; j
< world
.num_fishes
; j
++ )
2237 fj
= &world
.fishes
[j
];
2239 if( (fj
->state
== k_fish_state_alive
) |
2240 (fj
->state
== k_fish_state_soon_alive
) )
2245 v2i_sub( fi
->pos
, fi
->dir
, fi_prev
);
2246 v2i_sub( fj
->pos
, fj
->dir
, fj_prev
);
2249 collide_next_frame
= (
2250 (fi
->pos
[0] == fj
->pos
[0]) &&
2251 (fi
->pos
[1] == fj
->pos
[1]))? 1: 0,
2252 collide_this_frame
= (
2253 (fi_prev
[0] == fj
->pos
[0]) &&
2254 (fi_prev
[1] == fj
->pos
[1]) &&
2255 (fj_prev
[0] == fi
->pos
[0]) &&
2256 (fj_prev
[1] == fi
->pos
[1])
2259 if( collide_next_frame
|| collide_this_frame
)
2262 sw_set_achievement( "BANG" );
2265 // Shatter death (+0.5s)
2266 float death_time
= world
.sim_internal_time
+ ( collide_this_frame
? 0.0f
: 0.5f
);
2268 fi
->state
= k_fish_state_soon_dead
;
2269 fj
->state
= k_fish_state_soon_dead
;
2270 fi
->death_time
= death_time
;
2271 fj
->death_time
= death_time
;
2278 if( continue_again
)
2284 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
2286 struct cell_terminal
*term
= &world
.io
[ i
];
2287 int is_input
= pcell(term
->pos
)->state
& FLAG_INPUT
;
2291 if( world
.sim_frame
< term
->runs
[ world
.sim_run
].step_count
)
2293 i8 emit
= term
->runs
[ world
.sim_run
].steps
[ world
.sim_frame
];
2297 struct fish
*fish
= &world
.fishes
[ world
.num_fishes
];
2298 v2i_copy( term
->pos
, fish
->pos
);
2300 fish
->state
= k_fish_state_alive
;
2301 fish
->colour
= emit
;
2303 struct cell
*cell_ptr
= pcell( fish
->pos
);
2305 if( cell_ptr
->config
!= k_cell_type_stub
)
2307 if( world
.num_fishes
< vg_list_size(world
.fishes
))
2309 struct cell_description
*desc
= &cell_descriptions
[ cell_ptr
->config
];
2311 v2i_copy( desc
->start
, fish
->dir
);
2312 fish
->flow_reversed
= 1;
2314 world
.num_fishes
++;
2318 vg_warn( "Max marbles exceeded\n" );
2324 if( alive_count
== 0 )
2326 world
.completed
= 1;
2328 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
2330 struct cell_terminal
*term
= &world
.io
[ i
];
2332 if( pcell(term
->pos
)->state
& FLAG_OUTPUT
)
2334 struct terminal_run
*run
= &term
->runs
[ world
.sim_run
];
2336 if( run
->recv_count
== run
->step_count
)
2338 for( int j
= 0; j
< run
->step_count
; j
++ )
2340 if( run
->recieved
[j
] != run
->steps
[j
] )
2342 world
.completed
= 0;
2349 world
.completed
= 0;
2355 if( world
.completed
)
2357 if( world
.sim_run
< world
.max_runs
-1 )
2359 vg_success( "Run passed, starting next\n" );
2361 world
.sim_frame
= 0;
2362 world
.sim_target
= 0;
2363 world
.num_fishes
= 0;
2365 // Reset timing reference points
2366 world
.sim_delta_ref
= vg
.time
;
2367 world
.sim_internal_ref
= 0.0f
;
2369 if( world
.st
.buttons
[ k_world_button_pause
].state
)
2370 world
.pause_offset_target
= 0.5f
;
2372 world
.pause_offset_target
= 0.0f
;
2374 world
.sim_internal_time
= 0.0f
;
2376 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
2377 world
.data
[ i
].state
&= ~FLAG_FLIP_FLOP
;
2383 vg_success( "Level passed!\n" );
2386 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
2387 if( world
.data
[ i
].state
& FLAG_CANAL
)
2390 world
.score
= score
;
2391 world
.time
= world
.sim_frame
;
2393 // Copy into career data
2394 if( world
.pCmpLevel
)
2396 career_pass_level( world
.pCmpLevel
, world
.score
, 1 );
2400 audio_oneshot( &audio_tones
[9], 1.0f
, 0.0f
);
2402 failure_this_frame
= 0;
2403 success_this_frame
= 0;
2409 if( world
.sim_run
> 0 )
2410 sw_set_achievement( "GOOD_ENOUGH" );
2413 vg_error( "Level failed :(\n" );
2424 if( failure_this_frame
)
2427 audio_oneshot( &audio_tones
[0], 1.0f
, 0.0f
);
2430 else if( success_this_frame
)
2432 static int succes_counter
= 0;
2435 audio_oneshot( &audio_tones
[1+(succes_counter
++)], 1.0f
, 0.0f
);
2438 if( succes_counter
== 7 )
2443 // =====================================================================================================
2445 world
.frame_lerp
= world
.sim_internal_time
- floorf( world
.sim_internal_time
);
2447 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2449 struct fish
*fish
= &world
.fishes
[i
];
2451 if( fish
->state
== k_fish_state_dead
)
2454 if( fish
->state
== k_fish_state_soon_dead
&& (world
.sim_internal_time
> fish
->death_time
) )
2455 continue; // Todo: particle thing?
2457 struct cell
*cell
= pcell(fish
->pos
);
2458 struct cell_description
*desc
= &cell_descriptions
[ cell
->config
];
2462 float t
= world
.frame_lerp
;
2463 if( fish
->flow_reversed
&& !desc
->is_linear
)
2466 v2_copy( fish
->physics_co
, fish
->physics_v
);
2468 switch( cell
->config
)
2470 case k_cell_type_merge
:
2471 if( fish
->dir
[0] == 1 )
2476 case k_cell_type_con_r
: curve
= curve_1
; break;
2477 case k_cell_type_con_l
: curve
= curve_4
; break;
2478 case k_cell_type_con_u
: curve
= curve_2
; break;
2479 case k_cell_type_con_d
: curve
= curve_8
; break;
2480 case 3: curve
= curve_3
; break;
2481 case 6: curve
= curve_6
; break;
2482 case 9: curve
= curve_9
; break;
2483 case 12: curve
= curve_12
; break;
2485 if( t
> curve_7_linear_section
)
2487 t
-= curve_7_linear_section
;
2488 t
*= (1.0f
/(1.0f
-curve_7_linear_section
));
2490 curve
= cell
->state
& FLAG_FLIP_FLOP
? curve_7
: curve_7_1
;
2494 default: curve
= NULL
; break;
2500 float t3
= t
* t
* t
;
2502 float cA
= 3.0f
*t2
- 3.0f
*t3
;
2503 float cB
= 3.0f
*t3
- 6.0f
*t2
+ 3.0f
*t
;
2504 float cC
= 3.0f
*t2
- t3
- 3.0f
*t
+ 1.0f
;
2506 fish
->physics_co
[0] = t3
*curve
[3][0] + cA
*curve
[2][0] + cB
*curve
[1][0] + cC
*curve
[0][0];
2507 fish
->physics_co
[1] = t3
*curve
[3][1] + cA
*curve
[2][1] + cB
*curve
[1][1] + cC
*curve
[0][1];
2508 fish
->physics_co
[0] += (float)fish
->pos
[0];
2509 fish
->physics_co
[1] += (float)fish
->pos
[1];
2514 origin
[0] = (float)fish
->pos
[0] + (float)fish
->dir
[0]*-0.5f
+ 0.5f
;
2515 origin
[1] = (float)fish
->pos
[1] + (float)fish
->dir
[1]*-0.5f
+ 0.5f
;
2517 fish
->physics_co
[0] = origin
[0] + (float)fish
->dir
[0]*t
;
2518 fish
->physics_co
[1] = origin
[1] + (float)fish
->dir
[1]*t
;
2521 v2_sub( fish
->physics_co
, fish
->physics_v
, fish
->physics_v
);
2522 v2_divs( fish
->physics_v
, world
.sim_internal_delta
, fish
->physics_v
);
2527 static void render_tile( v2i pos
, struct cell
*ptr
, v4f
const regular_colour
,
2528 v4f
const selected_colour
, int with_glow
)
2530 int selected
= world
.selected
== pos
[1]*world
.w
+ pos
[0];
2533 uv
[0] = ptr
->config
& 0x3;
2534 uv
[1] = ptr
->config
>> 2;
2536 shader_tile_main_uOffset( (v4f
){
2544 shader_tile_main_uGlowA( ptr
->glow
[0] );
2545 shader_tile_main_uGlowB( ptr
->glow
[1] );
2548 shader_tile_main_uGlowA( (v3f
){0,0,0} );
2549 shader_tile_main_uGlowB( (v3f
){0,0,0} );
2553 shader_tile_main_uColour( selected_colour
);
2555 shader_tile_main_uColour( regular_colour
);
2561 // Renders specific chunk of tiles
2562 static void render_tile_block( v2i start
, v2i end
, v4f
const regular_colour
, v4f
const selected_colour
)
2564 v2i full_start
= { 0,0 };
2565 v2i full_end
= { world
.w
, world
.h
};
2567 if( !start
|| !end
)
2573 for( int y
= start
[1]; y
< end
[1]; y
++ )
2575 for( int x
= start
[0]; x
< end
[0]; x
++ )
2578 struct cell
*cell
= pcell( pos
);
2580 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
2581 render_tile( pos
, cell
, regular_colour
, selected_colour
, 0 );
2586 // Renders all tiles in the command list
2587 static void render_tiles( v4f
const regular_colour
, v4f
const selected_colour
,
2590 shader_tile_main_uColour( regular_colour
);
2594 struct render_cmd
*arr
;
2598 { world
.cmd_buf_tiles
, world
.tile_count
},
2599 { world
.cmd_buf_specials
, world
.tile_special_count
}
2602 int world_paused
= world
.st
.buttons
[k_world_button_pause
].state
;
2603 if( with_glow
&& !world_paused
)
2605 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2607 struct fish
*fish
= &world
.fishes
[i
];
2609 if( !(fish
->state
== k_fish_state_alive
||
2610 fish
->state
== k_fish_state_soon_alive
) ) continue;
2612 struct cell
*cell_x
= pcell( fish
->pos
);
2614 colour_code_v3( fish
->colour
, glow_colour
);
2617 if( cell_x
->config
== k_cell_type_split
)
2618 c
= cell_x
->state
& FLAG_FLIP_FLOP
? 1:0;
2620 if( cell_x
->config
== k_cell_type_merge
)
2621 c
= fish
->dir
[0]==-1?1:0;
2623 v3_muladds( cell_x
->glow
[c
], glow_colour
,
2624 powf(world
.frame_lerp
,2.0f
)*0.03f
* world
.sim_delta_speed
,
2629 for( int i
= 0; i
< vg_list_size( render_lists
); i
++ )
2631 struct render_list
*list
= &render_lists
[i
];
2632 for( int j
= 0; j
< list
->count
; j
++ )
2634 struct render_cmd
*cmd
= &list
->arr
[j
];
2635 struct cell
*cell
= cmd
->ptr
;
2637 render_tile( cmd
->pos
, cell
, regular_colour
, selected_colour
, with_glow
);
2642 static int world_button_exec( struct world_button
*btn
, v2f texture
, v3f colour
, enum world_button_status
*status
)
2644 static v2i click_grab
= { -9999, -9999 };
2649 click_grab
[0] = -9999;
2650 click_grab
[1] = -9999;
2654 v2i click_tile
= { world
.tile_x
, world
.tile_y
};
2657 int is_hovering
= v2i_eq( click_tile
, btn
->position
);
2659 // Set up light targets before logic runs
2661 btn
->light_target
= is_hovering
? 0.7f
: 0.6f
;
2663 btn
->light_target
= is_hovering
? 0.2f
: 0.0f
;
2665 if( button_press( k_srbind_primary
) && is_hovering
)
2666 btn
->light_target
= 1.0f
;
2668 // Process click action
2671 if( button_down( k_srbind_primary
) && is_hovering
)
2672 v2i_copy( click_tile
, click_grab
);
2673 else if( v2i_eq( click_grab
, click_tile
) && button_up(k_srbind_primary
))
2676 *status
= btn
->state
? k_world_button_on_disable
: k_world_button_on_enable
;
2678 if( btn
->mode
== k_world_button_mode_toggle
)
2682 audio_oneshot( &audio_clicks
[ btn
->state
?1:0 ], 1.0f
, 0.0f
);
2691 btn
->light
= vg_lerpf( btn
->light
, btn
->light_target
+ btn
->extra_light
, vg
.time_delta
*26.0f
);
2693 v3_copy( colour
, final_colour
);
2694 final_colour
[3] = btn
->light
;
2696 shader_button_uOffset( (v4f
){
2702 shader_button_uColour( final_colour
);
2708 static void level_selection_buttons(void)
2710 v3f tutorial_colour
= { 0.204f
, 0.345f
, 0.553f
};
2711 v3f locked_colour
= { 0.2f
, 0.2f
, 0.2f
};
2713 struct cmp_level
*switch_level_to
= NULL
;
2715 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
2717 struct career_level_pack
*grid
= &career_packs
[i
];
2719 for( int j
= 0; j
< grid
->count
; j
++ )
2721 struct cmp_level
*lvl
= &grid
->pack
[ j
];
2723 if( world
.pCmpLevel
== lvl
)
2724 lvl
->btn
.extra_light
= 0.35f
+ fabsf(sinf( vg
.time
* 2.0f
)) * 0.05f
;
2725 else lvl
->btn
.extra_light
= 0.2f
;
2727 if( lvl
->completed_score
)
2728 lvl
->btn
.extra_light
+= 0.6f
;
2730 enum world_button_status status
;
2731 if( world_button_exec(
2734 lvl
->unlocked
? (lvl
->is_tutorial
? tutorial_colour
: grid
->primary_colour
): locked_colour
,
2738 if( status
== k_world_button_on_enable
&& lvl
->unlocked
)
2739 switch_level_to
= lvl
;
2744 if( switch_level_to
)
2746 world
.st
.lvl_to_load
= switch_level_to
;
2747 world
.st
.lvl_load_time
= vg
.time
+ 0.25f
;
2748 world
.st
.world_transition
= 1.0f
;
2751 if( console_changelevel( 1, &switch_level_to->map_name ) )
2753 world.pCmpLevel = switch_level_to;
2754 gen_level_text( world.pCmpLevel );
2760 static void render_sprite( enum sprites_auto_combine_index id
, v3f pos
){
2761 struct vg_sprite
*sp
= &sprites_auto_combine
[ id
];
2763 shader_sprite_uUv( sp
->uv_xywh
);
2764 shader_sprite_uPos( (v3f
){ pos
[0],
2766 pos
[2] * world
.st
.world_transition
} );
2770 static void _mc_vg1_framebuffer_resize(int w
, int h
){
2771 glBindTexture( GL_TEXTURE_2D
, world
.st
.colourbuffer
);
2772 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGB
, w
, h
,
2773 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
2775 for( int i
=0; i
<2; i
++ )
2777 glBindTexture( GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[i
] );
2778 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGB
,
2779 w
/EFFECT_BUFFER_RATIO
, h
/EFFECT_BUFFER_RATIO
,
2780 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
2784 static void _mc_vg1_render(void){
2785 if( enable_bloom
|| enable_vignette
)
2786 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.framebuffer
);
2788 glBindFramebuffer( GL_FRAMEBUFFER
, 0 );
2790 glViewport( 0,0, vg
.window_x
, vg
.window_y
);
2792 glDisable( GL_DEPTH_TEST
);
2793 glClearColor( 0.14f
, 0.14f
, 0.14f
, 1.0f
);
2794 glClear( GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
2796 v4f
const colour_default
= {1.0f
, 1.0f
, 1.0f
, 1.0f
};
2797 v4f
const colour_selected
= {0.90f
, 0.92f
, 1.0f
, 1.0f
};
2799 int const circle_base
= 6;
2800 int const filled_start
= circle_base
+0;
2801 int const filled_count
= circle_base
+32;
2802 int const empty_start
= circle_base
+32;
2803 int const empty_count
= circle_base
+32*2;
2806 struct world_theme
*theme
= &world_themes
[ world_theme_id
];
2808 struct world_theme
*theme
= &world_themes
[ 0 ];
2811 if( !world
.initialzed
)
2814 // Extract render commands
2815 world
.tile_count
= 0;
2816 world
.tile_special_count
= 0;
2818 for( int y
= 1; y
< world
.h
-1; y
++ )
2820 for( int x
= 1; x
< world
.w
-1; x
++ )
2822 struct cell
*cell
= pcell((v2i
){x
,y
});
2824 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
|FLAG_INPUT_NICE
) )
2826 struct render_cmd
*cmd
;
2829 (cell
->config
== k_cell_type_split
&& (cell
->state
& FLAG_CANAL
))
2830 || (cell
->state
& (FLAG_EMITTER
|FLAG_IS_TRIGGER
))
2832 cmd
= &world
.cmd_buf_tiles
[ world
.max_commands
- (++ world
.tile_special_count
) ];
2834 cmd
= &world
.cmd_buf_tiles
[ world
.tile_count
++ ];
2840 int world_paused
= world
.st
.buttons
[k_world_button_pause
].state
;
2843 float decay
= 1.0f
- world
.sim_delta_speed
*0.005f
;
2844 v3_muls( cell
->glow
[0], decay
, cell
->glow
[0] );
2845 v3_muls( cell
->glow
[1], decay
, cell
->glow
[1] );
2851 world
.cmd_buf_specials
= &world
.cmd_buf_tiles
[ world
.max_commands
- world
.tile_special_count
];
2854 // ========================================================================================================
2855 use_mesh( &world
.shapes
);
2857 shader_background_use();
2858 shader_background_uPv( vg
.pv
);
2860 glActiveTexture( GL_TEXTURE0
);
2861 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
2862 shader_background_uTexMain( 0 );
2864 shader_background_uOffset( (v3f
){ -16, -16, 64 } );
2865 shader_background_uVariance( 0.05f
);
2867 glActiveTexture( GL_TEXTURE1
);
2868 glBindTexture( GL_TEXTURE_2D
, world
.random_samples
);
2869 shader_background_uSamplerNoise( 1 );
2870 shader_background_uVisibility( 1.0f
);
2874 // TILESET BACKGROUND LAYER
2875 // ========================================================================================================
2876 use_mesh( &world
.shapes
);
2877 shader_tile_main_use();
2879 m2x2_identity( subtransform
);
2880 shader_tile_main_uSubTransform( subtransform
);
2881 shader_tile_main_uPv( vg
.pv
);
2882 shader_tile_main_uGhost( 0.0f
);
2883 shader_tile_main_uForeground( 0.0f
);
2884 shader_tile_main_uVisibility( world
.st
.world_transition
* 2.0f
);
2887 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
2888 glBlendEquation(GL_FUNC_ADD
);
2891 glActiveTexture( GL_TEXTURE0
);
2892 glBindTexture( GL_TEXTURE_2D
, tex_tile_data
.name
);
2894 glActiveTexture( GL_TEXTURE1
);
2895 glBindTexture( GL_TEXTURE_2D
, theme
->tex_tiles
->name
);
2897 glActiveTexture( GL_TEXTURE2
);
2898 glBindTexture( GL_TEXTURE_2D
, tex_tile_glow
.name
);
2900 shader_tile_main_uTexGlyphs(0);
2901 shader_tile_main_uTexWood(1);
2902 shader_tile_main_uTexGlow(2);
2903 shader_tile_main_uShadowing( theme
->col_shadow
);
2904 render_tiles( colour_default
, colour_default
, 1 );
2907 // ========================================================================================================
2909 shader_ball_uPv( vg
.pv
);
2910 glActiveTexture( GL_TEXTURE0
);
2911 glBindTexture( GL_TEXTURE_2D
, tex_ball_noise
.name
);
2912 shader_ball_uTexMain( 0 );
2914 if( world
.st
.buttons
[ k_world_button_sim
].state
)
2916 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2918 struct fish
*fish
= &world
.fishes
[i
];
2920 render_pos
[2] = 1.0f
;
2922 if( fish
->state
== k_fish_state_dead
|| fish
->state
== k_fish_state_soon_dead
)
2924 float death_anim_time
= world
.sim_internal_time
- fish
->death_time
;
2927 if( death_anim_time
> 0.0f
&& death_anim_time
< 1.0f
)
2929 float amt
= 1.0f
-death_anim_time
*death_anim_time
;
2931 v2_muladds( fish
->physics_co
, fish
->physics_v
, -1.0f
* world
.sim_internal_delta
* amt
, fish
->physics_co
);
2932 render_pos
[2] = amt
;
2934 else if( world
.sim_internal_time
> fish
->death_time
)
2937 else if( fish
->state
== k_fish_state_bg
)
2940 v2_copy( fish
->physics_co
, render_pos
);
2942 v4f dot_colour
= { 0.0f
, 0.0f
, 0.0f
, 1.0f
};
2943 colour_code_v3( fish
->colour
, dot_colour
);
2945 shader_ball_uColour( dot_colour
);
2946 shader_ball_uOffset( render_pos
);
2947 shader_ball_uTexOffset( (v2f
){ (f32
)i
*1.2334f
, (f32
)i
*-0.3579f
} );
2952 // TILESET FOREGROUND LAYER
2953 // ========================================================================================================
2954 shader_tile_main_use();
2958 glActiveTexture( GL_TEXTURE0
);
2959 glBindTexture( GL_TEXTURE_2D
, tex_tile_data
.name
);
2961 glActiveTexture( GL_TEXTURE1
);
2962 glBindTexture( GL_TEXTURE_2D
, theme
->tex_tiles
->name
);
2964 glActiveTexture( GL_TEXTURE2
);
2965 glBindTexture( GL_TEXTURE_2D
, tex_tile_glow
.name
);
2967 shader_tile_main_uForeground(1.0f
);
2968 render_tiles( colour_default
, colour_selected
, 0 );
2971 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
2973 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
2974 struct cell
*cell
= cmd
->ptr
;
2976 if( cell
->config
== k_cell_type_split
)
2978 float rotation
= cell
->state
& FLAG_FLIP_FLOP
? vg_rad( -45.0f
): vg_rad( 45.0f
);
2980 if( cell
->state
& FLAG_FLIP_ROTATING
)
2982 if( (world
.frame_lerp
> curve_7_linear_section
) )
2984 float const rotation_speed
= 0.4f
;
2985 if( (world
.frame_lerp
< 1.0f
-rotation_speed
) )
2987 float t
= world
.frame_lerp
- curve_7_linear_section
;
2988 t
*= -2.0f
* (1.0f
/(1.0f
-(curve_7_linear_section
+rotation_speed
)));
2998 m2x2_create_rotation( subtransform
, rotation
);
3000 shader_tile_main_uSubTransform( subtransform
);
3001 shader_tile_main_uOffset( (v4f
){
3003 (f32
)cmd
->pos
[1] + 0.125f
,
3004 cell
->state
& FLAG_TARGETED
? 3.0f
: 2.0f
,
3012 // ========================================================================================================
3013 if( world
.selected
!= -1 && !(world
.data
[ world
.selected
].state
& FLAG_CANAL
) && !world
.id_drag_from
)
3015 v2i new_begin
= { world
.tile_x
- 2, world
.tile_y
- 2 };
3016 v2i new_end
= { world
.tile_x
+ 2, world
.tile_y
+ 2 };
3018 world
.data
[ world
.selected
].state
^= FLAG_CANAL
;
3019 map_reclassify( new_begin
, new_end
, 0 );
3021 m2x2_identity( subtransform
);
3022 shader_tile_main_uGhost(1.0f
);
3023 shader_tile_main_uSubTransform( subtransform
);
3024 shader_tile_main_uMousePos( world
.tile_pos
);
3025 render_tile_block( new_begin
, new_end
, colour_default
, colour_default
);
3027 world
.data
[ world
.selected
].state
^= FLAG_CANAL
;
3028 map_reclassify( new_begin
, new_end
, 0 );
3032 // ========================================================================================================
3033 shader_button_use();
3034 shader_button_uPv( vg
.pv
);
3036 glActiveTexture(GL_TEXTURE0
);
3037 glBindTexture( GL_TEXTURE_2D
, tex_buttons
.name
);
3038 shader_button_uTexMain(0);
3040 enum world_button_status stat
;
3041 int world_paused
= world
.st
.buttons
[k_world_button_pause
].state
;
3042 int world_running
= world
.st
.buttons
[k_world_button_sim
].state
;
3044 float sim_icon_x
= world_paused
? 3.0f
: (world_running
? 2.0f
: 0.0f
);
3046 v3f btn_dark_blue
= { 0.204f
, 0.345f
, 0.553f
};
3047 v3f btn_orange
= { 0.553f
, 0.345f
, 0.204f
};
3049 if( world_button_exec( &world
.st
.buttons
[k_world_button_sim
], (v2f
){ sim_icon_x
, 3.0f
}, btn_dark_blue
, &stat
))
3051 if( stat
== k_world_button_on_enable
)
3056 world
.pause_offset_target
= 0.5f
;
3062 // Trigger single step
3063 world
.pause_offset_target
+= 1.0f
;
3064 world
.st
.buttons
[k_world_button_sim
].state
= 1;
3073 if( world_button_exec( &world
.st
.buttons
[k_world_button_pause
], (v2f
){ 1.0f
, 3.0f
}, btn_dark_blue
, &stat
))
3075 world
.sim_internal_ref
= world
.sim_internal_time
;
3076 world
.sim_delta_ref
= vg
.time
;
3078 if( stat
== k_world_button_on_enable
)
3080 float time_frac
= world
.sim_internal_time
-floorf(world
.sim_internal_time
);
3081 world
.pause_offset_target
= 0.5f
- time_frac
;
3084 world
.pause_offset_target
= 0.0f
;
3087 if( world_button_exec( &world
.st
.buttons
[k_world_button_speedy
], (v2f
){ 0.0f
, 2.0f
}, btn_orange
, &stat
))
3089 world
.sim_delta_speed
= stat
== k_world_button_on_enable
? 10.0f
: 2.5f
;
3093 world
.sim_delta_ref
= vg
.time
;
3094 world
.sim_internal_ref
= world
.sim_internal_time
;
3098 if( world_button_exec( &world
.st
.buttons
[k_world_button_settings
], (v2f
){ 1.0f
, 2.0f
}, btn_orange
, &stat
))
3100 world
.st
.state
= stat
== k_world_button_on_enable
?
3101 k_game_state_settings
: k_game_state_main
;
3104 level_selection_buttons();
3106 if( button_up( k_srbind_primary
) )
3107 world_button_exec( NULL
, NULL
, NULL
, NULL
);
3110 // ========================================================================================================
3112 //glEnable(GL_BLEND);
3113 shader_tile_colour_use();
3114 shader_tile_colour_uPv( vg
.pv
);
3116 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
3118 struct cell_terminal
*term
= &world
.io
[ i
];
3119 struct cell
*cell
= pcell(term
->pos
);
3121 int is_input
= cell
->state
& FLAG_INPUT
;
3122 v4f dot_colour
= { 0.0f
, 0.0f
, 0.0f
, 1.0f
};
3124 if( cell
->state
& FLAG_EMITTER
)
3126 for( int j
= 0; j
< 2; j
++ )
3128 if( cell
->emit
[j
] != -1 )
3130 colour_code_v3( cell
->emit
[j
], dot_colour
);
3132 shader_tile_colour_uOffset( (v3f
){
3133 term
->pos
[0] + 0.25f
+ (float)j
* 0.5f
,
3134 term
->pos
[1] + 0.25f
,
3138 shader_tile_colour_uColour( dot_colour
);
3139 draw_mesh( filled_start
, filled_count
);
3145 for( int k
= 0; k
< term
->run_count
; k
++ )
3147 float arr_base
= is_input
? 1.2f
: -0.2f
,
3148 run_offset
= (is_input
? 0.2f
: -0.2f
) * (float)k
,
3149 y_position
= is_input
?
3150 (arr_base
+ (float)term
->pos
[1] + (float)(term
->run_count
-1)*0.2f
) - run_offset
:
3151 (float)term
->pos
[1] + arr_base
+ run_offset
;
3156 if( is_simulation_running() )
3158 if( k
== world
.sim_run
)
3160 float a
= fabsf(sinf( vg
.time
* 2.0f
)) * 0.075f
+ 0.075f
;
3162 v4_copy( (v4f
){ 1.0f
, 1.0f
, 1.0f
, a
}, bar_colour
);
3165 v4_copy( (v4f
){ 0.0f
, 0.0f
, 0.0f
, 0.13f
}, bar_colour
);
3169 else if( 1 || k
& 0x1 )
3172 v4_copy( (v4f
){ 1.0f
, 1.0f
, 1.0f
, 0.07f
}, bar_colour
);
3174 v4_copy( (v4f
){ 0.0f
, 0.0f
, 0.0f
, 0.13f
}, bar_colour
);
3181 shader_tile_colour_uColour( bar_colour
);
3182 shader_tile_colour_uOffset( (v3f
){
3183 (float)term
->pos
[0], y_position
- 0.1f
, 1.0f
});
3188 for( int j
= 0; j
< term
->runs
[k
].step_count
; j
++ )
3190 shader_tile_colour_uOffset( (v3f
){
3191 (float)term
->pos
[0] + 0.2f
+ 0.2f
* (float)j
,
3198 i8 colour
= term
->runs
[k
].steps
[j
];
3201 colour_code_v3( colour
, dot_colour
);
3202 shader_tile_colour_uColour( dot_colour
);
3204 // Draw filled if tick not passed, draw empty if empty
3205 if( (world
.sim_frame
> j
&& world
.sim_run
>= k
) || world
.sim_run
> k
)
3206 draw_mesh( empty_start
, empty_count
);
3208 draw_mesh( filled_start
, filled_count
);
3214 if( term
->runs
[k
].recv_count
> j
)
3216 colour_code_v3( term
->runs
[k
].recieved
[j
], dot_colour
);
3217 v3_muls( dot_colour
, 0.8f
, dot_colour
);
3218 shader_tile_colour_uColour( dot_colour
);
3219 draw_mesh( filled_start
, filled_count
);
3222 colour_code_v3( term
->runs
[k
].steps
[j
], dot_colour
);
3223 shader_tile_colour_uColour( dot_colour
);
3225 draw_mesh( empty_start
, empty_count
);
3232 // ========================================================================================================
3233 shader_sprite_use();
3234 shader_sprite_uPv( vg
.pv
);
3236 glActiveTexture( GL_TEXTURE0
);
3237 glBindTexture( GL_TEXTURE_2D
, tex_sprites
.name
);
3238 shader_sprite_uTexMain(0);
3240 for( int i
= 0; i
< world
.tile_special_count
; i
++ ){
3241 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3242 struct cell
*cell
= cmd
->ptr
;
3244 if( (cell
->config
== k_cell_type_split
) || (cell
->state
& FLAG_EMITTER
) )
3246 v2f center
= { cmd
->pos
[0] + 0.5f
, cmd
->pos
[1] + 0.5f
};
3248 v3f p0
= { 0.0f
, 0.0f
, 4.0f
};
3249 v3f p1
= { 0.0f
, 0.0f
, 4.0f
};
3251 v2_add( center
, (v2f
){ -0.25f
, -0.25f
}, p0
);
3252 v2_add( center
, (v2f
){ 0.25f
, -0.25f
}, p1
);
3254 render_sprite( k_sprite_jack_1
, p0
);
3255 render_sprite( k_sprite_jack_2
, p1
);
3257 else if( cell
->state
& FLAG_IS_TRIGGER
)
3259 v3f p0
= { 0.0f
, 0.0f
, 4.0f
};
3261 struct cell_description
*desc
= &cell_descriptions
[ cell
->config
];
3263 v2_add( (v2f
){ cmd
->pos
[0], cmd
->pos
[1] }, desc
->trigger_pos
, p0
);
3264 render_sprite( desc
->trigger_sprite
, p0
);
3269 // ========================================================================================================
3272 m3x3_identity( mvp_text
);
3273 m3x3_scale( mvp_text
, (v3f
){
3274 1.0f
/ ((float)UI_GLYPH_SPACING_X
*4.0f
),
3275 1.0f
/ -((float)UI_GLYPH_SPACING_X
*4.0f
),
3279 m3x3_mul( vg
.pv
, mvp_text
, mvp_text
);
3283 ui_draw( &world
.st
.world_text
, mvp_text
);
3287 // ========================================================================================================
3291 glBindVertexArray( world
.wire
.vao
);
3292 shader_wire_uPv(vg
.pv
);
3294 v4f
const wire_left_colour
= { 0.9f
, 0.9f
, 0.9f
, 1.0f
};
3295 v4f
const wire_right_colour
= { 0.5f
, 0.5f
, 0.5f
, 1.0f
};
3296 v4f
const wire_drag_colour
= { 0.3f
, 0.3f
, 0.3f
, 0.6f
};
3298 shader_wire_uTime( world
.frame_lerp
);
3299 shader_wire_uGlow( 0.0f
);
3301 if( world
.id_drag_from
)
3303 shader_wire_uColour( wire_drag_colour
);
3304 shader_wire_uCurve( 0.4f
);
3305 shader_wire_uStart( (v3f
){ world
.drag_from_co
[0],
3306 world
.drag_from_co
[1],
3307 0.20f
*world
.st
.world_transition
} );
3308 shader_wire_uEnd( (v3f
){ world
.drag_to_co
[0],
3309 world
.drag_to_co
[1],
3310 0.20f
*world
.st
.world_transition
} );
3311 glDrawElements( GL_TRIANGLES
, world
.wire
.em
,
3312 GL_UNSIGNED_SHORT
, (void*)(0) );
3315 // Pulling animation
3316 float rp_x1
= world
.frame_lerp
*9.0f
;
3317 float rp_xa
= rp_x1
*expf(1.0f
-rp_x1
)* 0.36f
;
3318 float rp_x2
= 1.0f
-rp_xa
;
3320 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3322 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3323 struct cell
*cell
= cmd
->ptr
;
3325 if( cell
->state
& FLAG_TARGETED
)
3327 for( int j
= 0; j
< 2; j
++ )
3329 if( !cell
->links
[j
] )
3332 struct cell
*other_cell
= &world
.data
[ cell
->links
[ j
]];
3333 struct cell_description
*desc
= &cell_descriptions
[ other_cell
->config
];
3335 int x2
= cell
->links
[j
] % world
.w
;
3336 int y2
= (cell
->links
[j
] - x2
) / world
.w
;
3341 endpoint
[0] = (float)cmd
->pos
[0] + (j
? 0.75f
: 0.25f
);
3342 endpoint
[1] = (float)cmd
->pos
[1] + 0.25f
;
3347 v2_add( desc
->trigger_pos
, startpoint
, startpoint
);
3349 if( cmd
->ptr
->state
& FLAG_EMITTER
)
3352 colour_code_v3( cmd
->ptr
->emit
[j
], wire_colour
);
3353 wire_colour
[3] = 1.0f
;
3355 shader_wire_uColour( wire_colour
);
3358 shader_wire_uColour( j
? wire_right_colour
: wire_left_colour
);
3361 other_cell
->state
& FLAG_TRIGGERED
? rp_x2
* 0.4f
: 0.4f
);
3363 other_cell
->state
& FLAG_TRIGGERED
? rp_xa
: 0.0f
);
3364 shader_wire_uEnd( (v3f
){
3365 startpoint
[0], startpoint
[1],
3366 0.18f
*world
.st
.world_transition
} );
3367 shader_wire_uStart( (v3f
){
3368 endpoint
[0], endpoint
[1], 0.18f
*world
.st
.world_transition
} );
3369 glDrawElements( GL_TRIANGLES
, world
.wire
.em
,
3370 GL_UNSIGNED_SHORT
, (void*)(0) );
3376 // ========================================================================================================
3378 shader_tile_colour_use();
3379 use_mesh( &world
.shapes
);
3381 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3383 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3384 struct cell
*cell
= cmd
->ptr
;
3386 if( cell
->state
& FLAG_TARGETED
)
3388 for( int j
= 0; j
< 2; j
++ )
3390 if( !cell
->links
[j
] )
3393 struct cell
*other_cell
= &world
.data
[ cell
->links
[ j
]];
3394 struct cell_description
*desc
= &cell_descriptions
[ other_cell
->config
];
3396 int x2
= cell
->links
[j
] % world
.w
;
3397 int y2
= (cell
->links
[j
] - x2
) / world
.w
;
3401 pts
[0][0] = (float)cmd
->pos
[0] + (j
? 0.75f
: 0.25f
);
3402 pts
[0][1] = (float)cmd
->pos
[1] + 0.25f
;
3407 v2_add( desc
->trigger_pos
, pts
[1], pts
[1] );
3409 if( cell
->state
& FLAG_EMITTER
)
3412 colour_code_v3( cell
->emit
[j
], wire_colour
);
3414 v3_muls( wire_colour
, 0.8f
, wire_colour
);
3415 wire_colour
[3] = 1.0f
;
3417 shader_tile_colour_uColour( wire_colour
);
3420 shader_tile_colour_uColour(
3421 j
?wire_right_colour
: wire_left_colour
);
3423 for( int i
= 0; i
< 2; i
++ ){
3424 shader_tile_colour_uOffset( (v3f
){
3427 0.08f
* world
.st
.world_transition
3429 draw_mesh( filled_start
, filled_count
);
3435 // SUB SPLITTER DIRECTION
3436 // ========================================================================================================
3439 glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.9f, 0.35f, 0.1f, 0.75f );
3441 for( int i = 0; i < world.tile_special_count; i ++ )
3443 struct render_cmd *cmd = &world.cmd_buf_specials[i];
3444 struct cell *cell = cmd->ptr;
3446 if( cell->state & FLAG_TARGETED && cell->config == k_cell_type_split )
3448 glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), cmd->pos[0], cmd->pos[1], 1.0f );
3449 draw_mesh( cell->state & FLAG_FLIP_FLOP? 5: 4, 1 );
3455 // ========================================================================================================
3456 glBlendFunc(GL_ONE
, GL_ONE
);
3457 glBlendEquation(GL_FUNC_ADD
);
3459 shader_sprite_use();
3460 glActiveTexture( GL_TEXTURE0
);
3461 glBindTexture( GL_TEXTURE_2D
, tex_sprites
.name
);
3462 shader_sprite_uTexMain(0);
3464 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3466 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3467 struct cell
*cell
= cmd
->ptr
;
3469 if( cell
->config
== k_cell_type_split
)
3471 v2f center
= { cmd
->pos
[0] + 0.5f
, cmd
->pos
[1] + 0.5f
};
3473 v3f p0
= { 0.0f
, 0.0f
, 12.0f
};
3474 v3f p1
= { 0.0f
, 0.0f
, 12.0f
};
3476 v2_add( center
, (v2f
){ -0.25f
, -0.25f
}, p0
);
3477 v2_add( center
, (v2f
){ 0.25f
, -0.25f
}, p1
);
3479 if( cell
->state
& FLAG_TARGETED
)
3481 if( cell
->state
& FLAG_FLIP_FLOP
)
3482 render_sprite( k_sprite_flare_y
, p1
);
3484 render_sprite( k_sprite_flare_b
, p0
);
3487 render_sprite( k_sprite_flare_w
, cell
->state
&FLAG_FLIP_FLOP
? p1
: p0
);
3491 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
3492 glBlendEquation(GL_FUNC_ADD
);
3494 glDisable(GL_BLEND
);
3498 float const score_bright = 1.25f;
3499 glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ),
3500 0.4f*score_bright, 0.39f*score_bright, 0.45f*score_bright, 1.0f );
3502 use_mesh( &world.numbers );
3503 draw_numbers( (v3f){ 2.0f, (float)world.h-1.875f, 0.3333f }, world.score );
3508 if( enable_vignette
)
3509 goto image_composite
;
3514 /* Scale down image and remap colour values */
3516 vg
.window_x
/EFFECT_BUFFER_RATIO
, vg
.window_y
/EFFECT_BUFFER_RATIO
);
3517 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.bloomframebuffer
[0] );
3519 shader_post_darken_use();
3520 glActiveTexture( GL_TEXTURE0
);
3521 glBindTexture( GL_TEXTURE_2D
, world
.st
.colourbuffer
);
3522 shader_post_darken_uTexMain(0);
3527 v2f res_inv
, blur_dir
;
3528 res_inv
[0] = 1.0f
/ (float)( vg
.window_x
/EFFECT_BUFFER_RATIO
);
3529 res_inv
[1] = 1.0f
/ (float)( vg
.window_y
/EFFECT_BUFFER_RATIO
);
3531 shader_post_blur_use();
3532 shader_post_blur_uTexMain(0);
3534 for( int i
=0; i
<1; i
++ )
3536 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.bloomframebuffer
[1] );
3538 v2_mul( (v2f
){ 1.0f
*(float)(i
+1), 0.0f
}, res_inv
, blur_dir
);
3540 shader_post_blur_uDir( blur_dir
);
3541 glActiveTexture( GL_TEXTURE0
);
3542 glBindTexture( GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[0] );
3546 v2_mul( (v2f
){ 0.0f
, 1.0f
*(float)(i
+1) }, res_inv
, blur_dir
);
3548 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.bloomframebuffer
[0] );
3549 shader_post_blur_uDir( blur_dir
);
3550 glBindTexture( GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[1] );
3554 /* Scene composite */
3555 glViewport( 0,0, vg
.window_x
, vg
.window_y
);
3558 glBindFramebuffer( GL_FRAMEBUFFER
, 0 );
3560 shader_post_comp_use();
3562 glActiveTexture( GL_TEXTURE0
);
3563 glBindTexture( GL_TEXTURE_2D
, world
.st
.colourbuffer
);
3564 shader_post_comp_uTexMain(0);
3566 glActiveTexture( GL_TEXTURE1
);
3567 glBindTexture( GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[0] );
3568 shader_post_comp_uTexBloom(1);
3570 shader_post_comp_uComp( (v2f
){
3571 enable_bloom
? 1.0f
: 0.0f
,
3572 enable_vignette
? 0.0f
: 1.0f
3578 void _mc_vg1_ui(void) {
3579 // Drawing world name
3581 if( world
.pCmpLevel
)
3583 gui_text( (ui_px
[2]){ vg
.window_x
/ 2, 4 }, world
.pCmpLevel
->title
, 2, k_text_align_center
);
3584 gui_text( (ui_px
[2]){ vg
.window_x
/ 2, 28 }, world
.pCmpLevel
->description
, 1, k_text_align_center
);
3589 if( world
.st
.state
== k_game_state_update
)
3593 ui_global_ctx
.cursor
[2] = 458;
3594 ui_global_ctx
.cursor
[3] = 316;
3595 ui_global_ctx
.cursor
[0] = vg
.window_x
/ 2 - 229;
3596 ui_global_ctx
.cursor
[1] = vg
.window_y
/ 2 - 158;
3600 gui_capture_mouse( 200 );
3601 gui_fill_rect( ui_global_ctx
.cursor
, 0xE8303030 );
3604 title_pos
[0] = ui_global_ctx
.cursor
[0] + 229;
3605 title_pos
[1] = ui_global_ctx
.cursor
[1] + 16;
3607 gui_text( title_pos
, "Update 1.5", 2, k_text_align_center
);
3609 gui_text( (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 16, title_pos
[1] + 45 },
3610 "Welcome to the first update to marble computing!"
3612 "New features have been added:\n"
3614 " - Settings menu\n"
3616 " - More levels and a new block type\n"
3617 " - Scores for each level\n"
3618 " - Zooming and panning (mousewheel)\n"
3620 "There is much more in the works, such as a\n"
3621 "soundtrack, and the rest of the levels for the\n"
3624 "Thank you everyone for enjoying my game :)\n",
3625 1, k_text_align_left
3628 ui_global_ctx
.cursor
[2] = 100;
3629 ui_global_ctx
.cursor
[3] = 30;
3630 ui_global_ctx
.cursor
[0] += 229 - 50;
3631 ui_global_ctx
.cursor
[1] += 316 - 30 - 16;
3633 if( gui_button( 1 ) )
3635 world
.st
.state
= k_game_state_main
;
3637 gui_text( (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 50,
3638 ui_global_ctx
.cursor
[1] + 4 }, "OK", 1, k_text_align_center
);
3647 if( world
.st
.state
== k_game_state_settings
)
3651 ui_global_ctx
.cursor
[2] = 225;
3657 gui_capture_mouse( 200 );
3659 gui_fill_rect( ui_global_ctx
.cursor
, 0xC0202020 );
3660 ui_rect_pad( ui_global_ctx
.cursor
, 8 );
3662 ui_global_ctx
.cursor
[3] = 25;
3666 gui_text( ui_global_ctx
.cursor
, "SETTINGS", 2, 0 );
3668 ui_global_ctx
.cursor
[2] = 25;
3671 if( gui_button(4) == k_button_click
)
3673 world
.st
.buttons
[ k_world_button_settings
].state
= 0;
3674 world
.st
.state
= k_game_state_main
;
3675 vg_info( "exit\n" );
3677 ui_global_ctx
.cursor
[0] += 4;
3678 ui_global_ctx
.cursor
[1] -= 4;
3679 gui_text( ui_global_ctx
.cursor
, "x", 2, 0 );
3684 // Colour scheme selection
3685 ui_global_ctx
.cursor
[1] += 30;
3687 gui_text( ui_global_ctx
.cursor
, "Colour Scheme", 1, 0 );
3688 ui_global_ctx
.cursor
[1] += 25;
3692 ui_global_ctx
.cursor
[2] = 50;
3694 for( int i
= 0; i
< 4; i
++ )
3699 u32 rgb
= 0xff000000;
3701 for( int j
= 0; j
< 3; j
++ )
3702 rgb
|= (u32
)(colour_sets
[ colour_set_id
][i
][j
]*255.0f
) << j
* 8;
3704 gui_fill_rect( ui_global_ctx
.cursor
, rgb
);
3713 ui_global_ctx
.cursor
[2] = 25;
3714 if( gui_button( 0 ) == k_button_click
)
3716 if( colour_set_id
> 0 )
3719 gui_text( ui_global_ctx
.cursor
, "<", 2, 0 );
3722 ui_global_ctx
.cursor
[2] = 150;
3725 gui_fill_rect( ui_global_ctx
.cursor
, 0x33ffffff );
3727 (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 75, ui_global_ctx
.cursor
[1] + 6 },
3728 (const char *[]){ "Normal", "Extra1", "Extra2" }[ colour_set_id
],
3729 1, k_text_align_center
3734 ui_global_ctx
.cursor
[2] = 25;
3735 if( gui_button( 1 ) == k_button_click
)
3737 if( colour_set_id
< vg_list_size( colour_sets
)-1 )
3740 gui_text( ui_global_ctx
.cursor
, ">", 2, 0 );
3746 ui_global_ctx
.cursor
[1] += 16;
3749 gui_text( ui_global_ctx
.cursor
, "Tile Theme", 1, 0 );
3750 ui_global_ctx
.cursor
[1] += 20;
3754 ui_global_ctx
.cursor
[2] = 25;
3755 if( gui_button( 2 ) == k_button_click
)
3757 if( world_theme_id
> 0 )
3760 gui_text( ui_global_ctx
.cursor
, "<", 2, 0 );
3763 ui_global_ctx
.cursor
[2] = 150;
3766 gui_fill_rect( ui_global_ctx
.cursor
, 0x33ffffff );
3768 (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 75, ui_global_ctx
.cursor
[1] + 6 },
3769 world_themes
[ world_theme_id
].name
, 1, k_text_align_center
3774 ui_global_ctx
.cursor
[2] = 25;
3775 if( gui_button( 3 ) == k_button_click
)
3777 if( world_theme_id
< vg_list_size( world_themes
)-1 )
3780 gui_text( ui_global_ctx
.cursor
, ">", 2, 0 );
3786 gui_text( ui_global_ctx
.cursor
, "Graphics", 1, 0 );
3787 ui_global_ctx
.cursor
[1] += 20;
3791 ui_global_ctx
.cursor
[2] = 200;
3792 if( gui_button( 5 ) == k_button_click
)
3794 enable_bloom
^= 0x1;
3796 ui_global_ctx
.cursor
[0] += 4;
3797 ui_global_ctx
.cursor
[1] += 4;
3798 gui_text( ui_global_ctx
.cursor
, enable_bloom
?
3800 "Bloom: DISABLED", 1, 0 );
3805 ui_global_ctx
.cursor
[1] += 10;
3808 ui_global_ctx
.cursor
[2] = 200;
3809 if( gui_button( 6 ) == k_button_click
)
3811 enable_vignette
^= 0x1;
3813 ui_global_ctx
.cursor
[0] += 4;
3814 ui_global_ctx
.cursor
[1] += 4;
3815 gui_text( ui_global_ctx
.cursor
, enable_vignette
?
3816 "Vignette: ENABLED":
3817 "Vignette: DISABLED", 1, 0 );
3822 ui_global_ctx
.cursor
[1] += 16;
3823 gui_text( ui_global_ctx
.cursor
, "Music Volume", 1, 0 );
3824 ui_global_ctx
.cursor
[1] += 20;
3828 ui_px slider_start
= ui_global_ctx
.cursor
[0];
3830 float const bar_width
= 45.0f
,
3832 bar_movement
= bar_total
-bar_width
,
3833 bar_start
= bar_width
* 0.5f
;
3835 ui_global_ctx
.cursor
[2] = bar_total
;
3836 ui_fill_rect( &ui_global_ctx
,
3837 ui_global_ctx
.cursor
,
3840 ui_global_ctx
.cursor
[2] = bar_width
;
3841 ui_global_ctx
.cursor
[0] = slider_start
+ music_volume
* bar_movement
;
3843 int status
= gui_button( 7 );
3845 static ui_px drag_start
= 0.0f
;
3847 if( status
== k_button_start_click
)
3848 drag_start
= ui_global_ctx
.mouse
[0];
3849 else if( ui_global_ctx
.capture_lock
&&
3850 (ui_global_ctx
.capture_mouse_id
== ui_group_id(&ui_global_ctx
,7)))
3852 ui_px drag_offset
= ui_global_ctx
.mouse
[0] - drag_start
;
3853 float offset_local
= (drag_start
+ drag_offset
- slider_start
- bar_start
) / bar_movement
;
3855 music_volume
= vg_minf( vg_maxf( offset_local
, 0.0f
), 1.0f
);
3856 music_volume_update();
3859 ui_global_ctx
.cursor
[0] += 4;
3860 ui_global_ctx
.cursor
[1] += 4;
3863 snprintf( volbuf
, 12, "%.2f", music_volume
);
3864 gui_text( ui_global_ctx
.cursor
, volbuf
, 1, 0 );
3875 // ===========================================================================================================
3877 static int console_credits( int argc
, char const *argv
[] )
3879 vg_info( "Aknowledgements:\n" );
3880 vg_info( " GLFW zlib/libpng glfw.org\n" );
3881 vg_info( " miniaudio MIT0 miniaud.io\n" );
3882 vg_info( " QOI MIT phoboslab.org\n" );
3883 vg_info( " STB library MIT nothings.org\n" );
3887 static int console_save_map( int argc
, char const *argv
[] )
3889 if( !world
.initialzed
)
3891 vg_error( "Tried to save uninitialized map!\n" );
3895 char map_path
[ 256 ];
3897 strcpy( map_path
, "sav/" );
3898 strcat( map_path
, world
.map_name
);
3899 strcat( map_path
, ".map" );
3901 FILE *test_writer
= fopen( map_path
, "wb" );
3904 vg_info( "Saving map to '%s'\n", map_path
);
3905 map_serialize( test_writer
);
3907 fclose( test_writer
);
3912 vg_error( "Unable to open stream for writing\n" );
3917 static int console_load_map( int argc
, char const *argv
[] )
3919 char map_path
[ 256 ];
3924 strcpy( map_path
, "sav/" );
3925 strcat( map_path
, argv
[0] );
3926 strcat( map_path
, ".map" );
3929 char *text_source
= vg_file_read_text( NULL
, map_path
, &sz
);
3933 strcpy( map_path
, "maps/" );
3934 strcat( map_path
, argv
[0] );
3935 strcat( map_path
, ".map" );
3937 text_source
= vg_file_read_text( NULL
, map_path
, &sz
);
3942 vg_info( "Loading map: '%s'\n", map_path
);
3943 world
.pCmpLevel
= NULL
;
3945 if( !map_load( text_source
, argv
[0] ) )
3947 free( text_source
);
3951 free( text_source
);
3956 vg_error( "Missing maps '%s'\n", argv
[0] );
3962 vg_error( "Missing argument <map_path>\n" );
3967 static int console_changelevel( int argc
, char const *argv
[] )
3971 // Save current level
3972 console_save_map( 0, NULL
);
3974 if( console_load_map( argc
, argv
) )
3976 world
.st
.zoom
= 0.0f
;
3983 vg_error( "Missing argument <map_path>\n" );
3989 // START UP / SHUTDOWN
3990 // ===========================================================================================================
3992 #define TRANSFORM_TRI_2D( S, OX, OY, X1, Y1, X2, Y2, X3, Y3 ) \
3993 X1*S+OX, Y1*S+OY, X2*S+OX, Y2*S+OY, X3*S+OX, Y3*S+OY
3995 void _mc_vg1_start(void){
3996 // Steamworks callbacks
3997 #ifdef STEAM_LEADERBOARDS
3998 sw_leaderboard_found
= &leaderboard_found
;
3999 sw_leaderboard_downloaded
= &leaderboard_downloaded
;
4003 vg_function_push( (struct vg_cmd
){
4004 .name
= "_map_write",
4005 .function
= console_save_map
4008 vg_function_push( (struct vg_cmd
){
4009 .name
= "_map_load",
4010 .function
= console_load_map
4013 vg_function_push( (struct vg_cmd
){
4015 .function
= console_changelevel
4018 vg_function_push( (struct vg_cmd
){
4020 .function
= console_credits
4023 vg_convar_push( (struct vg_convar
){
4025 .data
= &colour_set_id
,
4026 .data_type
= k_convar_dtype_i32
,
4027 .opt_i32
= { .min
= 0, .max
= 2, .clamp
= 1 },
4031 vg_convar_push( (struct vg_convar
){
4033 .data
= &world_theme_id
,
4034 .data_type
= k_convar_dtype_i32
,
4035 .opt_i32
= { .min
= 0, .max
= vg_list_size( world_themes
)-1, .clamp
= 1 },
4040 vg_convar_push( (struct vg_convar
){
4041 .name
= "enable_bloom",
4042 .data
= &enable_bloom
,
4043 .data_type
= k_convar_dtype_i32
,
4044 .opt_i32
= { .min
= 0, .max
= 1, .clamp
= 1 },
4049 vg_convar_push( (struct vg_convar
){
4050 .name
= "enable_vignette",
4051 .data
= &enable_vignette
,
4052 .data_type
= k_convar_dtype_i32
,
4053 .opt_i32
= { .min
= 0, .max
= 1, .clamp
= 1 },
4058 vg_convar_push( (struct vg_convar
){
4059 .name
= "music_volume",
4060 .data
= &music_volume
,
4061 .data_type
= k_convar_dtype_f32
,
4062 .opt_f32
= { .min
= 0.0f
, .max
= 1.0f
, .clamp
= 1 },
4064 .update
= music_volume_update
4068 // Combined quad, long quad / empty circle / filled circle mesh
4070 float combined_mesh
[6*6 + 32*6*3] = {
4071 0.0f
, 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
,
4072 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
, 0.0f
,
4074 0.0f
, 0.0f
, 0.0f
, 0.2f
, 1.0f
, 0.2f
,
4075 0.0f
, 0.0f
, 1.0f
, 0.2f
, 1.0f
, 0.0f
,
4077 TRANSFORM_TRI_2D( 0.15f
,0.05f
,0.4f
, 0.0f
, 1.0f
, 1.0f
, 2.0f
, 1.0f
, 0.0f
),
4078 TRANSFORM_TRI_2D( 0.15f
,0.80f
,0.4f
, 0.0f
, 0.0f
, 0.0f
, 2.0f
, 1.0f
, 1.0f
)
4081 float *circle_mesh
= combined_mesh
+ 6*6;
4084 for( int i
= 0; i
< res
; i
++ )
4086 v2f v0
= { sinf( ((float)i
/(float)res
)*VG_TAUf
), cosf( ((float)i
/(float)res
)*VG_TAUf
) };
4087 v2f v1
= { sinf( ((float)(i
+1)/(float)res
)*VG_TAUf
), cosf( ((float)(i
+1)/(float)res
)*VG_TAUf
) };
4089 circle_mesh
[ i
*6+0 ] = 0.0f
;
4090 circle_mesh
[ i
*6+1 ] = 0.0f
;
4092 v2_copy( v0
, circle_mesh
+ 32*6 + i
*12 );
4093 v2_muls( v0
, 0.8f
, circle_mesh
+ 32*6 + i
*12+2 );
4094 v2_copy( v1
, circle_mesh
+ 32*6 + i
*12+4 );
4096 v2_copy( v1
, circle_mesh
+ 32*6 + i
*12+6 );
4097 v2_muls( v1
, 0.8f
, circle_mesh
+ 32*6 + i
*12+8 );
4098 v2_muls( v0
, 0.8f
, circle_mesh
+ 32*6 + i
*12+10 );
4100 v2_copy( v0
, circle_mesh
+ i
*6+4 );
4101 v2_copy( v1
, circle_mesh
+ i
*6+2 );
4102 v2_copy( v0
, circle_mesh
+i
*6+4 );
4103 v2_copy( v1
, circle_mesh
+i
*6+2 );
4106 init_mesh( &world
.shapes
, combined_mesh
, vg_list_size( combined_mesh
) );
4111 int const num_segments
= 64;
4113 struct mesh_wire
*mw
= &world
.wire
;
4115 v2f wire_points
[ num_segments
* 2 ];
4116 u16 wire_indices
[ 6*(num_segments
-1) ];
4118 for( int i
= 0; i
< num_segments
; i
++ )
4120 float l
= (float)i
/ (float)(num_segments
-1);
4122 v2_copy( (v2f
){ l
, -0.5f
}, wire_points
[i
*2+0] );
4123 v2_copy( (v2f
){ l
, 0.5f
}, wire_points
[i
*2+1] );
4125 if( i
< num_segments
-1 )
4127 wire_indices
[ i
*6+0 ] = i
*2 + 0;
4128 wire_indices
[ i
*6+1 ] = i
*2 + 1;
4129 wire_indices
[ i
*6+2 ] = i
*2 + 3;
4130 wire_indices
[ i
*6+3 ] = i
*2 + 0;
4131 wire_indices
[ i
*6+4 ] = i
*2 + 3;
4132 wire_indices
[ i
*6+5 ] = i
*2 + 2;
4136 glGenVertexArrays( 1, &mw
->vao
);
4137 glGenBuffers( 1, &mw
->vbo
);
4138 glGenBuffers( 1, &mw
->ebo
);
4139 glBindVertexArray( mw
->vao
);
4141 glBindBuffer( GL_ARRAY_BUFFER
, mw
->vbo
);
4143 glBufferData( GL_ARRAY_BUFFER
, sizeof( wire_points
), wire_points
, GL_STATIC_DRAW
);
4144 glBindVertexArray( mw
->vao
);
4146 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, mw
->ebo
);
4147 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, sizeof( wire_indices
), wire_indices
, GL_STATIC_DRAW
);
4150 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
, 2*sizeof(float), (void*)0 );
4151 glEnableVertexAttribArray( 0 );
4153 mw
->em
= vg_list_size( wire_indices
);
4156 // Create info data texture
4158 glGenTextures( 1, &world
.background_data
);
4159 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
4160 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGBA
, 64, 64, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, NULL
);
4161 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
4162 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
4165 // Create random smaples texture
4167 u8
*data
= malloc(512*512*2);
4168 for( int i
= 0; i
< 512*512*2; i
++ )
4169 data
[ i
] = rand()/(RAND_MAX
/255);
4171 glGenTextures( 1, &world
.random_samples
);
4172 glBindTexture( GL_TEXTURE_2D
, world
.random_samples
);
4173 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RG
, 512, 512, 0, GL_RG
, GL_UNSIGNED_BYTE
, data
);
4174 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
4175 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
4176 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_REPEAT
);
4177 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_REPEAT
);
4186 ui_init_context( &world
.st
.world_text
, 15000 );
4190 // Restore gamestate
4191 career_local_data_init();
4194 /* Create framebuffers */
4195 glGenFramebuffers( 1, &world
.st
.framebuffer
);
4196 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.framebuffer
);
4198 glGenTextures( 1, &world
.st
.colourbuffer
);
4199 glBindTexture( GL_TEXTURE_2D
, world
.st
.colourbuffer
);
4200 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGB
, vg
.window_x
, vg
.window_y
,
4201 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
4203 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
4204 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
4205 glFramebufferTexture2D( GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
, GL_TEXTURE_2D
,
4206 world
.st
.colourbuffer
, 0);
4208 /* Bloom framebuffer (quater res) */
4209 glGenFramebuffers( 2, world
.st
.bloomframebuffer
);
4210 glGenTextures( 2, world
.st
.bloomcolourbuffer
);
4212 for( int i
=0; i
<2; i
++ )
4214 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.bloomframebuffer
[i
] );
4216 glBindTexture( GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[i
] );
4217 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGB
,
4218 vg
.window_x
/EFFECT_BUFFER_RATIO
, vg
.window_y
/EFFECT_BUFFER_RATIO
,
4219 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
4220 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
4221 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
4223 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
4224 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
4225 glFramebufferTexture2D( GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
4226 GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[i
], 0);
4230 /* FIXME: run this at vg exit */
4237 console_save_map( 0, NULL
);