world text (old style) & optimisations
[fishladder.git] / fishladder.c
1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
2
3 //#define VG_CAPTURE_MODE
4 //#define VG_STEAM
5 #define VG_STEAM_APPID 1218140U
6 #include "vg/vg.h"
7
8 enum world_button_mode
9 {
10 k_world_button_mode_once,
11 k_world_button_mode_toggle
12 };
13
14 struct world_button
15 {
16 v2i position;
17
18 float light_target, light, extra_light;
19 int state;
20
21 enum world_button_mode mode;
22 };
23
24 enum world_button_status
25 {
26 k_world_button_on_enable,
27 k_world_button_on_disable
28 };
29
30 #include "fishladder_resources.h"
31
32 // #define STEAM_LEADERBOARDS
33
34 // CONSTANTS
35 // ===========================================================================================================
36
37 enum cell_type
38 {
39 k_cell_type_stub = 0,
40 k_cell_type_ramp_right = 3,
41 k_cell_type_ramp_left = 6,
42 k_cell_type_split = 7,
43 k_cell_type_merge = 13,
44 k_cell_type_con_r = 1,
45 k_cell_type_con_u = 2,
46 k_cell_type_con_l = 4,
47 k_cell_type_con_d = 8
48 };
49
50 enum e_fish_state
51 {
52 k_fish_state_soon_dead = -1,
53 k_fish_state_dead = 0,
54 k_fish_state_alive,
55 k_fish_state_bg,
56 k_fish_state_soon_alive
57 };
58
59 enum e_world_button
60 {
61 k_world_button_none = -1,
62 k_world_button_sim = 0,
63 k_world_button_pause = 1,
64 k_world_button_speedy = 2,
65 k_world_button_settings = 3
66 };
67
68 enum e_game_state
69 {
70 k_game_state_main,
71 k_game_state_settings
72 };
73
74 #define FLAG_CANAL 0x1
75 #define FLAG_IS_TRIGGER 0x2
76 #define FLAG_RESERVED0 0x4
77 #define FLAG_RESERVED1 0x8
78
79 #define FLAG_INPUT 0x10
80 #define FLAG_OUTPUT 0x20
81 #define FLAG_WALL 0x40
82 #define FLAG_EMITTER 0x80
83
84 #define FLAG_FLIP_FLOP 0x100
85 #define FLAG_TRIGGERED 0x200
86 #define FLAG_FLIP_ROTATING 0x400
87 #define FLAG_TARGETED 0x800
88
89 #define FLAG_INPUT_NICE 0x1000
90
91 /*
92 0000 0 | 0001 1 | 0010 2 | 0011 3
93 | | | | |
94 X | X= | X | X=
95 | | |
96 0100 4 | 0101 5 | 0110 6 | 0111 7
97 | | | | |
98 =X | =X= | =X | =X=
99 | | |
100 1000 8 | 1001 9 | 1010 10 | 1011 11
101 | | | | |
102 X | X= | X | X=
103 | | | | | | |
104 1100 12 | 1101 13 | 1110 14 | 1111 15
105 | | | | |
106 =X | =X= | =X | =X=
107 | | | | | | |
108 */
109
110 static struct cell_description
111 {
112 v2i start;
113 v2i end;
114
115 int is_special;
116 int is_linear;
117
118 v2f trigger_pos;
119 enum sprites_auto_combine_index trigger_sprite;
120 }
121 cell_descriptions[] =
122 {
123 // 0-3
124 {},
125 { .start = { 1, 0 }, .end = { -1, 0 }, .trigger_pos = { 0.5f, 0.25f }, .trigger_sprite = k_sprite_brk_d },
126 { .start = { 0, 1 }, .end = { 0, -1 }, .trigger_pos = { 0.25f, 0.5f }, .trigger_sprite = k_sprite_brk_l },
127 { .start = { 0, 1 }, .end = { 1, 0 }, .trigger_pos = { 0.25f, 0.5f }, .trigger_sprite = k_sprite_brk_l },
128 // 4-7
129 { .start = { -1, 0 }, .end = { 1, 0 }, .trigger_pos = { 0.5f, 0.25f }, .trigger_sprite = k_sprite_brk_d },
130 { .start = { -1, 0 }, .end = { 1, 0 }, .trigger_pos = { 0.5f, 0.25f }, .trigger_sprite = k_sprite_brk_d, .is_linear = 1 },
131 { .start = { 0, 1 }, .end = { -1, 0 }, .trigger_pos = { 0.5f, 0.25f }, .trigger_sprite = k_sprite_brk_d },
132 { .start = { 0, 1 }, .is_special = 1 },
133 // 8-11
134 { .start = { 0, -1 }, .end = { 0, 1 }, .trigger_pos = { 0.25f, 0.5f }, .trigger_sprite = k_sprite_brk_l },
135 { .start = { 1, 0 }, .end = { 0, -1 }, .trigger_pos = { 0.25f, 0.5f }, .trigger_sprite = k_sprite_brk_l },
136 { .start = { 0, 1 }, .end = { 0, -1 }, .trigger_pos = { 0.25f, 0.5f }, .trigger_sprite = k_sprite_brk_l, .is_linear = 1 },
137 { },
138 // 12-15
139 { .start = { -1, 0 }, .end = { 0, -1 }, .trigger_pos = { 0.5f, 0.75f }, .trigger_sprite = k_sprite_brk_u },
140 { .end = { 0, -1 }, .is_special = 1, .trigger_pos = { 0.5f, 0.75f }, .trigger_sprite = k_sprite_brk_u },
141 { },
142 { }
143 };
144
145 v2f const curve_3[] = {{0.5f,1.0f},{0.5f,0.625f},{0.625f,0.5f},{1.0f,0.5f}};
146 v2f const curve_6[] = {{0.5f,1.0f},{0.5f,0.625f},{0.375f,0.5f},{0.0f,0.5f}};
147 v2f const curve_9[] = {{1.0f,0.5f},{0.625f,0.5f},{0.5f,0.375f},{0.5f,0.0f}};
148 v2f const curve_12[]= {{0.0f,0.5f},{0.375f,0.5f},{0.5f,0.375f},{0.5f,0.0f}};
149
150 v2f const curve_1[] = {{1.0f,0.5f},{0.8f,0.5f},{0.3f,0.5f},{0.2f,0.5f}};
151 v2f const curve_4[] = {{0.0f,0.5f},{0.3f,0.5f},{0.5f,0.5f},{0.8f,0.5f}};
152 v2f const curve_2[] = {{0.5f,1.0f},{0.5f,0.8f},{0.5f,0.3f},{0.5f,0.2f}};
153 v2f const curve_8[] = {{0.5f,0.0f},{0.5f,0.3f},{0.5f,0.5f},{0.5f,0.8f}};
154
155 v2f const curve_7[] = {{0.5f,0.8438f},{0.875f,0.8438f},{0.625f,0.5f},{1.0f,0.5f}};
156 v2f const curve_7_1[] = {{0.5f,0.8438f},{1.0f-0.875f,0.8438f},{1.0-0.625f,0.5f},{0.0f,0.5f}};
157
158 float const curve_7_linear_section = 0.1562f;
159
160 // Types
161 // ===========================================================================================================
162
163 struct mesh
164 {
165 GLuint vao, vbo;
166 u32 elements;
167 };
168
169 struct
170 {
171 GLuint vao;
172 GLuint vbo;
173 GLuint ebo;
174
175 u32
176 title_start, title_count,
177 desc_start, desc_count,
178 score_start, score_count,
179 time_start, time_count,
180 grid_start, grid_count
181 ;
182
183 #pragma pack(push,1)
184 struct vector_glyph_vert
185 {
186 v2f co;
187 v2f uv;
188
189 u32 colour;
190 }
191 *buffer;
192 #pragma pack(pop)
193
194 u16 *indices;
195 }
196 text_buffers;
197
198 static struct world
199 {
200 // Things that are 'static', aka, initialized once
201 struct
202 {
203 struct world_button buttons[4];
204 float zoom;
205 enum e_game_state state;
206
207 struct cmp_level *lvl_to_load;
208 float lvl_load_time;
209
210 float world_transition;
211 ui_ctx world_text;
212 }
213 st;
214
215 struct cell
216 {
217 u16 state;
218 u16 links[2];
219 u8 config;
220 i8 emit[2];
221 }
222 *data;
223
224 struct render_cmd
225 {
226 struct cell *ptr;
227 v2i pos;
228 }
229 *cmd_buf_tiles, *cmd_buf_specials;
230
231 u32 tile_count, tile_special_count, max_commands;
232
233 int initialzed;
234 int sim_run, max_runs;
235
236 int sim_frame, sim_target;
237 float sim_internal_time, // current tick-time
238 sim_internal_delta, // time delta
239 sim_internal_ref, // Reference point of internal time
240 sim_delta_ref, // Reference point of vg_time when we started at current sim_speed
241 sim_delta_speed, // Rate to apply time delta
242 frame_lerp, // Fractional part of sim_internal_time
243 pause_offset_target; //
244
245 struct cell_terminal
246 {
247 struct terminal_run
248 {
249 i8 steps[8];
250 i8 recieved[8];
251
252 int step_count, recv_count;
253 }
254 runs[8];
255
256 int run_count;
257 v2i pos;
258 //int id;
259 }
260 *io;
261
262 int w, h;
263
264 struct mesh shapes;
265 struct mesh_wire
266 {
267 GLuint vao, vbo, ebo;
268 u32 em;
269 }
270 wire;
271
272 GLuint background_data;
273 GLuint random_samples;
274
275 int selected, tile_x, tile_y;
276 v2f tile_pos;
277
278 struct fish
279 {
280 v2i pos;
281 v2i dir;
282 enum e_fish_state state;
283 i8 colour;
284 int flow_reversed;
285 float death_time;
286 v2f physics_v;
287 v2f physics_co;
288 }
289 fishes[64];
290
291 int num_fishes;
292
293 char map_name[64];
294 struct cmp_level *pCmpLevel;
295
296 u32 score;
297 u32 completed;
298 u32 time;
299
300 u16 id_drag_from;
301 v2f drag_from_co;
302 v2f drag_to_co;
303 }
304 world =
305 {
306 .st =
307 {
308 .buttons = { { .mode = k_world_button_mode_toggle },
309 { .mode = k_world_button_mode_toggle },
310 { .mode = k_world_button_mode_toggle },
311 { .mode = k_world_button_mode_toggle } }
312 }
313 };
314
315 // Forward declerations
316 // --------------------
317
318 // Utility functions
319 // -----------------
320
321 static void colour_code_v3( i8 cc, v3f target );
322 static int hash21i( v2i p, u32 umod );
323
324 // Mesh functions
325 // --------------
326 static void init_mesh( struct mesh *m, float const *tris, u32 length );
327 static void free_mesh( struct mesh *m );
328 static void use_mesh( struct mesh *m );
329 static void draw_mesh( int const start, int const count );
330
331 // World buttons
332 // -------------
333 static void level_selection_buttons(void);
334
335 // Map/world interface
336 // -------------------
337 static void map_free(void);
338 static void io_reset(void);
339 static struct cell *pcell( v2i pos );
340 static void lcell( int id, v2i pos );
341 static void map_reclassify( v2i start, v2i end, int update_texbuffer );
342 static u32 gen_text_buffer( const char *str, struct sdf_font *font, v2f origin, float size, u32 start );
343 static void gen_level_text( struct cmp_level *pLevel );
344 static int map_load( const char *str, const char *name );
345 static void map_serialize( FILE *stream );
346
347 // Career
348 // ------
349 static void career_serialize(void);
350 static void career_unlock_level( struct cmp_level *lvl );
351 static void career_unlock_level( struct cmp_level *lvl );
352 static void career_pass_level( struct cmp_level *lvl, int score, int upload );
353 static void career_reset_level( struct cmp_level *lvl );
354 static void career_load(void);
355 static void clear_animation_flags(void);
356
357 // Gameplay main
358 // -------------
359 static void simulation_stop(void);
360 static void simulation_start(void);
361 static int world_check_pos_ok( v2i co, int dist );
362 static int cell_interactive( v2i co );
363
364 void vg_update(void);
365 static void render_tiles( v4f const regular_colour, v4f const selected_colour );
366 static void render_tile_block( v2i start, v2i end, v4f const regular_colour, v4f const selected_colour );
367
368 void vg_render(void);
369 void vg_ui(void);
370
371 // Leaderboard stuff
372 // -----------------
373 #ifdef STEAM_LEADERBOARDS
374 void leaderboard_set_score( struct cmp_level *cmp_level, u32 score );
375 void leaderboard_dispatch_score(void);
376 void leaderboard_found( LeaderboardFindResult_t *pCallback );
377 void leaderboard_downloaded( LeaderboardScoresDownloaded_t *pCallback );
378 void leaderboard_set_score( struct cmp_level *cmp_level, u32 score );
379 #endif
380
381 // Console commands
382 // ----------------
383 static int console_credits( int argc, char const *argv[] );
384 static int console_save_map( int argc, char const *argv[] );
385 static int console_load_map( int argc, char const *argv[] );
386 static int console_changelevel( int argc, char const *argv[] );
387
388 void vg_start(void);
389 void vg_free(void);
390
391 int main( int argc, char *argv[] );
392
393 // GLOBALS
394 // ===========================================================================================================
395
396 m3x3f m_projection;
397 m3x3f m_view;
398 m3x3f m_mdl;
399
400 // UTILITY
401 // ===========================================================================================================
402
403 static int colour_set_id = 0;
404 static int world_theme_id = 0;
405
406 static v3f colour_sets[][4] =
407 {
408 { { 1.0f, 0.9f, 0.3f }, // Yellow
409 { 0.4f, 0.8f, 1.00f }, // Blue
410 { 0.2f, 0.9f, 0.14f }, // Green
411 { 0.882f, 0.204f, 0.922f } // Pink
412 },
413 { { 1.0f, 0.9f, 0.3f }, // Yellow
414 { 0.4f, 0.8f, 1.00f }, // Blue
415 { 0.85f, 0.85f, 0.85f }, // Weiss
416 { 0.2f, 0.2f, 0.2f } // Black/Gray
417 },
418 { { 1.0f, 0.9f, 0.3f }, // Yellow
419 { 0.827f, 0.373f, 0.718f }, // Pink
420 { 0.0f, 0.353f, 0.71f }, // Blue
421 { 0.863f, 0.196f, 0.125f } // Red
422 },
423 };
424
425 static struct world_theme
426 {
427 const char *name;
428 v3f col_shadow;
429
430 vg_tex2d *tex_tiles;
431 }
432 world_themes[] =
433 {
434 {
435 "Wood",
436 { 0.89f, 0.8f, 0.7f },
437 &tex_tiles_wood
438 },
439 {
440 "Minimal",
441 { 0.8f, 0.8f, 0.8f },
442 &tex_tiles_min
443 },
444 {
445 "Lab",
446 { 0.7f, 0.7f, 0.7f },
447 &tex_tiles_lab
448 }
449 };
450
451 static void colour_code_v3( i8 cc, v3f target )
452 {
453 if( (cc >= 0) && (cc < vg_list_size( colour_sets[0] )) )
454 {
455 v3_copy( colour_sets[colour_set_id][ cc ], target );
456 return;
457 }
458
459 vg_error( "Invalid colour code used '%d'\n", (int)cc );
460 v3_copy( (v3f){0.0f,0.0f,0.0f}, target );
461 }
462
463 static int hash21i( v2i p, u32 umod )
464 {
465 static const int random_noise[] =
466 {
467 0x46,0xD5,0xB8,0xD3,0xF2,0xE5,0xCC,0x07,0xD0,0xB3,0x7A,0xA2,0xC3,0xDA,0xDC,0x7F,0xE0,0xB7,0x42,0xA0,0xBF,0x41,0x92,0x32,0x6F,0x0D,0x45,0xC7,0x54,0xDB,0x30,0xC2,
468 0xD5,0xDA,0x55,0x09,0xDE,0x74,0x48,0x20,0xE1,0x24,0x5C,0x4D,0x6F,0x36,0xD8,0xE9,0x8D,0x8F,0x54,0x99,0x98,0x51,0xFE,0xDB,0x26,0x04,0x65,0x57,0x56,0xF3,0x53,0x30,
469 0x3D,0x16,0xC0,0xB6,0xF2,0x47,0xCF,0x62,0xB0,0x6C,0x8F,0x4F,0x8C,0x4C,0x17,0xF0,0x19,0x7E,0x2D,0x81,0x8D,0xFB,0x10,0xD3,0x49,0x50,0x60,0xFD,0x38,0x15,0x3B,0xEE,
470 0x05,0xC1,0xCF,0x62,0x97,0x75,0xDF,0x4E,0x4D,0x89,0x5E,0x88,0x5C,0x30,0x8C,0x54,0x1D,0x39,0x41,0xEA,0xA2,0x63,0x12,0x1B,0x8E,0x35,0x22,0x9B,0x98,0xA3,0x7F,0x80,
471 0xD6,0x27,0x94,0x66,0xB5,0x1D,0x7E,0xDF,0x96,0x28,0x38,0x3A,0xA0,0xE8,0x71,0x09,0x62,0x5E,0x9D,0x53,0x58,0x1B,0x7D,0x0D,0x2D,0x99,0x77,0x83,0xC3,0x89,0xC2,0xA2,
472 0xA7,0x1D,0x78,0x80,0x37,0xC1,0x87,0xFF,0x65,0xBF,0x2C,0xF1,0xE5,0xB3,0x09,0xE0,0x25,0x92,0x83,0x0F,0x8A,0x57,0x3C,0x0B,0xC6,0xBC,0x44,0x16,0xE3,0xCE,0xC3,0x0D,
473 0x69,0xD3,0xC6,0x99,0xB8,0x46,0x44,0xC4,0xF3,0x1E,0xBF,0xF5,0xB4,0xDB,0xFB,0x93,0xA1,0x7B,0xC9,0x08,0x77,0x22,0xE5,0x02,0xEF,0x9E,0x90,0x94,0x8A,0xA6,0x3D,0x7E,
474 0xA2,0xA0,0x10,0x82,0x47,0x5C,0xAA,0xF8,0x2F,0x0D,0x9F,0x76,0xDA,0x99,0x0F,0xCB,0xE2,0x02,0x0C,0x75,0xCA,0x35,0x29,0xA6,0x49,0x83,0x6D,0x91,0xB4,0xEC,0x31,0x69,
475 0xBA,0x13,0xF3,0xC7,0x21,0x06,0xC8,0x79,0xEF,0xB1,0x9C,0x6A,0xEE,0x64,0x9A,0xDC,0x1E,0xC6,0x18,0x93,0xA9,0x7E,0x89,0x7D,0x96,0xE5,0x44,0xB8,0x00,0x15,0xAF,0x8C,
476 0x78,0x8F,0xA8,0x05,0xA7,0x07,0x25,0x9A,0xC8,0x5D,0x90,0x1A,0x41,0x53,0x30,0xD3,0x24,0x33,0x71,0xB4,0x50,0x6E,0xE4,0xEA,0x0D,0x2B,0x6D,0xF5,0x17,0x08,0x74,0x49,
477 0x71,0xC2,0xAC,0xF7,0xDC,0xB2,0x7E,0xCC,0xB6,0x1B,0xB8,0xA9,0x52,0xCF,0x6B,0x51,0xD2,0x4E,0xC9,0x43,0xEE,0x2E,0x92,0x24,0xBB,0x47,0x4D,0x0C,0x3E,0x21,0x53,0x19,
478 0xD4,0x82,0xE2,0xC6,0x93,0x85,0x0A,0xF8,0xFA,0x04,0x07,0xD3,0x1D,0xEC,0x03,0x66,0xFD,0xB1,0xFB,0x8F,0xC5,0xDE,0xE8,0x29,0xDF,0x23,0x09,0x9D,0x7C,0x43,0x3D,0x4D,
479 0x89,0xB9,0x6F,0xB4,0x6B,0x4A,0x51,0xC3,0x94,0xF4,0x7C,0x5E,0x19,0x87,0x79,0xC1,0x80,0x0C,0x45,0x12,0xEC,0x95,0xF3,0x31,0x68,0x42,0xE1,0x06,0x57,0x0E,0xA7,0xFB,
480 0x78,0x96,0x87,0x23,0xA5,0x20,0x7A,0x09,0x3A,0x45,0xE6,0xD9,0x5E,0x6A,0xD6,0xAA,0x29,0x50,0x92,0x4E,0xD0,0xB5,0x91,0xC2,0x9A,0xCF,0x07,0xFE,0xB2,0x15,0xEB,0xE4,
481 0x84,0x40,0x14,0x47,0xFA,0x93,0xB9,0x06,0x69,0xDB,0xBD,0x4E,0xEA,0x52,0x9B,0xDE,0x5B,0x50,0x36,0xAB,0xB3,0x1F,0xD2,0xCD,0x9C,0x13,0x07,0x7E,0x8B,0xED,0x72,0x62,
482 0x74,0x77,0x3B,0x88,0xAC,0x5B,0x6A,0xBC,0xDA,0x99,0xE8,0x24,0x90,0x5A,0xCA,0x8D,0x5C,0x2B,0xF8,0xF1,0xE1,0x1D,0x94,0x11,0xEA,0xCC,0x02,0x09,0x1E,0xA2,0x48,0x67,
483 0x87,0x5A,0x7E,0xC6,0xCC,0xA3,0xFB,0xC5,0x36,0xEB,0x5C,0xE1,0xAF,0x1E,0xBE,0xE7,0xD8,0x8F,0x70,0xAE,0x42,0x05,0xF5,0xCD,0x2D,0xA2,0xB0,0xFD,0xEF,0x65,0x2C,0x22,
484 0xCB,0x8C,0x8B,0xAA,0x3D,0x86,0xE2,0xCD,0xBE,0xC3,0x42,0x38,0xE3,0x9C,0x08,0xB5,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,0xB2,0xF1,0x0F,0xD6,0x77,0xAA,0x72,0xC0,0xAF,0x66,0xD8,0x40,0xC6,0x08,0x19,0x8C,
486 0xD9,0x8F,0x5A,0x75,0xAC,0xBE,0xC2,0x40,0x5B,0xBD,0x0D,0x1D,0x00,0xAF,0x26,0x5E,0x78,0x43,0xAA,0xC6,0x4F,0xF3,0xD8,0xE2,0x7F,0x0C,0x1E,0x77,0x4D,0x35,0x96,0x23,
487 0x32,0x44,0x03,0x8D,0x92,0xE7,0xFD,0x48,0x07,0xD0,0x58,0xFC,0x6D,0xC9,0x91,0x33,0xF0,0x23,0x45,0xA4,0x29,0xB9,0xF5,0xB0,0x68,0x8F,0x7B,0x59,0x15,0x8E,0xA6,0x66,
488 0x15,0xA0,0x76,0x9B,0x69,0xCB,0x38,0xA5,0xF4,0xB4,0x6B,0xDC,0x1F,0xAB,0xAE,0x12,0x77,0xC0,0x8C,0x4A,0x03,0xB9,0x73,0xD3,0x6D,0x52,0xC5,0xF5,0x6E,0x4E,0x4B,0xA3,
489 0x24,0x02,0x58,0xEE,0x5F,0xF9,0xD6,0xD0,0x1D,0xBC,0xF4,0xB8,0x4F,0xFD,0x4B,0x2D,0x34,0x77,0x46,0xE5,0xD4,0x33,0x7B,0x9C,0x35,0xCD,0xB0,0x5D,0x06,0x39,0x99,0xEB,
490 0x0C,0xD0,0x0F,0xF7,0x92,0xB5,0x58,0x5B,0x5E,0x79,0x12,0xF4,0x05,0xF6,0x21,0x07,0x0B,0x49,0x1A,0xFB,0xD4,0x98,0xC4,0xEF,0x7A,0xD6,0xCA,0xA1,0xDA,0xB3,0x51,0x00,
491 0x76,0xEC,0x08,0x48,0x40,0x35,0xD7,0x94,0xBE,0xF5,0x7B,0xA4,0x20,0x81,0x5F,0x82,0xF3,0x6F,0x96,0x24,0x98,0xB6,0x49,0x18,0xC8,0xC5,0x8C,0xD2,0x38,0x7F,0xC4,0xF6,
492 0xAA,0x87,0xDC,0x73,0x5B,0xA1,0xAF,0xE5,0x3D,0x37,0x6B,0x85,0xED,0x38,0x62,0x7D,0x57,0xBD,0xCF,0xB5,0x1B,0xA8,0xBB,0x32,0x33,0xD3,0x34,0x5A,0xC1,0x5D,0xFB,0x28,
493 0x6E,0xE1,0x67,0x51,0xBB,0x31,0x92,0x83,0xAC,0xAA,0x72,0x52,0xFD,0x13,0x4F,0x73,0xD3,0xF0,0x5E,0xFC,0xBA,0xB1,0x3C,0x7B,0x08,0x76,0x03,0x38,0x1E,0xD1,0xCC,0x33,
494 0xA3,0x1E,0xFC,0xE0,0x82,0x30,0x27,0x93,0x71,0x35,0x75,0x77,0xBA,0x78,0x10,0x33,0xCD,0xAB,0xCF,0x8E,0xAD,0xF9,0x32,0xC9,0x15,0x9F,0xD6,0x6D,0xA8,0xAE,0xB1,0x3F,
495 0x90,0xEB,0xD4,0xF9,0x31,0x81,0xA3,0x53,0x99,0x4B,0x3C,0x93,0x3B,0xFE,0x55,0xFF,0x25,0x9F,0xCC,0x07,0xC5,0x2C,0x14,0xA7,0xA4,0x1E,0x6C,0xB6,0x91,0x2A,0xE0,0x3E,
496 0x7F,0x39,0x0A,0xD9,0x24,0x3C,0x01,0xA0,0x30,0x99,0x8E,0xB8,0x1D,0xF9,0xA7,0x78,0x86,0x95,0x35,0x0E,0x21,0xDA,0x7A,0x7B,0xAD,0x9F,0x4E,0xF6,0x63,0x5B,0x96,0xBB,
497 0x87,0x36,0x3F,0xA7,0x1A,0x66,0x91,0xCD,0xB0,0x3B,0xC0,0x4F,0x54,0xD2,0x5F,0xBB,0x38,0x89,0x1C,0x79,0x7E,0xA2,0x02,0xE4,0x80,0x84,0x1E,0x33,0xAB,0x74,0xFA,0xBE,
498 0x31,0x46,0x2E,0xC5,0x15,0xB9,0x12,0xE9,0xD3,0x73,0x43,0xEA,0x74,0x11,0xA7,0xC0,0xD5,0xD8,0x39,0x08,0x9F,0x4F,0xC7,0x71,0x25,0x09,0x51,0x65,0xD6,0xA8,0x02,0x1F
499 };
500
501 return random_noise[ (random_noise[p[1] & 1023] + p[0]) & 1023 ] & umod;
502 }
503
504 // MESH
505 // ===========================================================================================================
506
507 static void init_mesh( struct mesh *m, float const *tris, u32 length )
508 {
509 m->elements = length/3;
510 glGenVertexArrays( 1, &m->vao );
511 glGenBuffers( 1, &m->vbo );
512
513 glBindVertexArray( m->vao );
514 glBindBuffer( GL_ARRAY_BUFFER, m->vbo );
515 glBufferData( GL_ARRAY_BUFFER, length*sizeof(float), tris, GL_STATIC_DRAW );
516
517 glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0 );
518 glEnableVertexAttribArray( 0 );
519
520 VG_CHECK_GL();
521 }
522
523 static void free_mesh( struct mesh *m )
524 {
525 glDeleteVertexArrays( 1, &m->vao );
526 glDeleteBuffers( 1, &m->vbo );
527 }
528
529 static void draw_mesh( int const start, int const count )
530 {
531 glDrawArrays( GL_TRIANGLES, start*3, count*3 );
532 }
533
534 static void use_mesh( struct mesh *m )
535 {
536 glBindVertexArray( m->vao );
537 }
538
539 void leaderboard_set_score( struct cmp_level *cmp_level, u32 score );
540
541 // WORLD/MAP
542 // ===========================================================================================================
543
544 static void map_free(void)
545 {
546 arrfree( world.data );
547 arrfree( world.io );
548
549 free( world.cmd_buf_tiles );
550 world.cmd_buf_tiles = NULL;
551
552 world.w = 0;
553 world.h = 0;
554 world.data = NULL;
555 world.io = NULL;
556 world.score = 0;
557 world.time = 0;
558 world.completed = 0;
559 world.max_runs = 0;
560 world.initialzed = 0;
561 }
562
563 static void io_reset(void)
564 {
565 for( int i = 0; i < arrlen( world.io ); i ++ )
566 {
567 struct cell_terminal *term = &world.io[i];
568
569 for( int j = 0; j < term->run_count; j ++ )
570 term->runs[j].recv_count = 0;
571 }
572 }
573
574 static struct cell *pcell( v2i pos )
575 {
576 return &world.data[ pos[1]*world.w + pos[0] ];
577 }
578
579 static void lcell( int id, v2i pos )
580 {
581 pos[0] = id % world.w;
582 pos[1] = (id - pos[0]) / world.w;
583 }
584
585 static void map_reclassify( v2i start, v2i end, int update_texbuffer )
586 {
587 v2i full_start = { 1,1 };
588 v2i full_end = { world.w-1, world.h-1 };
589
590 if( !start || !end )
591 {
592 start = full_start;
593 end = full_end;
594 }
595
596 // Texture data
597 u8 info_buffer[64*64*4];
598 u32 pixel_id = 0;
599
600 int px0 = vg_max( start[0], full_start[0] ),
601 px1 = vg_min( end[0], full_end[0] ),
602 py0 = vg_max( start[1], full_start[1] ),
603 py1 = vg_min( end[1], full_end[1] );
604
605 for( int y = py0; y < py1; y ++ )
606 {
607 for( int x = px0; x < px1; x ++ )
608 {
609 struct cell *cell = pcell((v2i){x,y});
610
611 v2i dirs[] = {{1,0},{0,1},{-1,0},{0,-1}};
612
613 u8 height = 0;
614 u8 config = 0x00;
615
616 if( cell->state & (FLAG_CANAL|FLAG_INPUT|FLAG_OUTPUT|FLAG_EMITTER) )
617 {
618 for( int i = 0; i < vg_list_size( dirs ); i ++ )
619 {
620 struct cell *neighbour = pcell((v2i){x+dirs[i][0], y+dirs[i][1]});
621 if( neighbour->state & (FLAG_CANAL|FLAG_INPUT|FLAG_OUTPUT|FLAG_EMITTER) )
622 config |= 0x1 << i;
623 }
624
625 height = 128;
626 }
627 else
628 {
629 if( cell->state & FLAG_WALL )
630 height = 0xFF-0x3F + hash21i( (v2i){x,y}, 0x3F );
631
632 config = cell->state & FLAG_INPUT_NICE? 0xB: 0xF;
633 }
634
635 pcell((v2i){x,y})->config = config;
636
637 u8 *info_px = &info_buffer[ (pixel_id ++)*4 ];
638 info_px[0] = height;
639 info_px[1] = cell->state & FLAG_WALL? 0: 255;
640 info_px[2] = 0;
641 info_px[3] = 0;
642
643 if(
644 (
645 ((cell->state & FLAG_IS_TRIGGER) && (cell->config == 0xF || cell->config == k_cell_type_split)) ||
646 ((cell->state & FLAG_TARGETED) && ((cell->config != k_cell_type_split) && !(cell->state & FLAG_EMITTER)))
647 ) && update_texbuffer
648 ){
649 cell->state &= ~(FLAG_TARGETED|FLAG_IS_TRIGGER);
650 for( u32 i = 0; i < 2; i ++ )
651 {
652 if( cell->links[i] )
653 {
654 struct cell *other_ptr = &world.data[ cell->links[i] ];
655 other_ptr->links[ i ] = 0;
656 other_ptr->state &= ~FLAG_IS_TRIGGER;
657
658 if( other_ptr->links[ i ^ 0x1 ] == 0 )
659 other_ptr->state &= ~FLAG_TARGETED;
660 }
661 }
662
663 cell->links[0] = 0;
664 cell->links[1] = 0;
665 }
666 }
667 }
668
669 if( update_texbuffer )
670 {
671 glBindTexture( GL_TEXTURE_2D, world.background_data );
672 glTexSubImage2D( GL_TEXTURE_2D, 0, px0 + 16, py0 + 16, px1-px0, py1-py0, GL_RGBA, GL_UNSIGNED_BYTE, info_buffer );
673 }
674 }
675
676 static u32 gen_text_buffer( const char *str, struct sdf_font *font, v2f origin, float size, u32 start )
677 {
678 u32 count = 0;
679
680 v2f cursor;
681 v2f invUv;
682 v2_copy( origin, cursor );
683
684 float invScale = (size / (float)font->size);
685 invUv[0] = 1.0f / (float)font->width;
686 invUv[1] = 1.0f / (float)font->height;
687
688 u16 base_idx = start * 4;
689
690 const char *_c = str;
691 char c;
692 while( (c = *(_c ++)) )
693 {
694 if( c == '\n' )
695 {
696 cursor[1] -= size * 1.25f;
697 cursor[0] = origin[0];
698 }
699 else if( c >= 32 && c <= 126 )
700 {
701 struct sdf_char *pch = &font->characters[ c - ' ' ];
702 struct vector_glyph_vert *vt = &text_buffers.buffer[ count * 4 ];
703 u16 *ind = &text_buffers.indices[ count * 6 ];
704
705 // Emit quad
706 v2f p0; v2f uv0;
707 v2f p1; v2f uv1;
708
709 v2_muladds( cursor, (v2f){ pch->originX, -pch->originY }, -invScale, p0 );
710 v2_muladds( p0, (v2f){ pch->w, -pch->h }, invScale, p1 );
711
712 v2_mul( (v2f){ pch->uvx, pch->uvy }, invUv, uv0 );
713 v2_muladd( uv0, (v2f){ pch->w, pch->h }, invUv, uv1 );
714
715 v2_copy( p0, vt[0].co );
716 v2_copy( uv0, vt[0].uv );
717 vt[0].colour = 0xffffffff;
718
719 v2_copy( (v2f){ p0[0], p1[1] }, vt[1].co );
720 v2_copy( (v2f){ uv0[0], uv1[1] }, vt[1].uv );
721 vt[1].colour = 0xffffffff;
722
723 v2_copy( p1, vt[2].co );
724 v2_copy( uv1, vt[2].uv );
725 vt[2].colour = 0xffffffff;
726
727 v2_copy( (v2f){ p1[0], p0[1] }, vt[3].co );
728 v2_copy( (v2f){ uv1[0], uv0[1] }, vt[3].uv );
729 vt[3].colour = 0xffffffff;
730
731 // Emit indices
732 ind[0] = base_idx+count*4;
733 ind[1] = base_idx+count*4+1;
734 ind[2] = base_idx+count*4+2;
735 ind[3] = base_idx+count*4;
736 ind[4] = base_idx+count*4+2;
737 ind[5] = base_idx+count*4+3;
738
739 cursor[0] += (float)pch->advance * invScale;
740 count ++;
741 }
742 }
743
744 glBindVertexArray( text_buffers.vao );
745
746 glBindBuffer( GL_ARRAY_BUFFER, text_buffers.vbo );
747 glBufferSubData( GL_ARRAY_BUFFER,
748 start*4*sizeof( struct vector_glyph_vert ),
749 count*4*sizeof( struct vector_glyph_vert ),
750 text_buffers.buffer
751 );
752
753 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, text_buffers.ebo );
754 glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, start*6*sizeof(u16), count*6*sizeof(u16), text_buffers.indices );
755
756 return count;
757 }
758
759 static void gen_level_text( struct cmp_level *pLevel )
760 {
761 text_buffers.title_count = gen_text_buffer( pLevel->title, &font_Ubuntu, (v2f){ -5.0f, -0.6f }, 0.6f, text_buffers.title_start );
762 text_buffers.desc_count = gen_text_buffer( pLevel->description, &font_Ubuntu, (v2f){ -5.0, -0.9f }, 0.25f, text_buffers.desc_start );
763
764 // Old style UI.
765 ui_px const unit_scale_px = 4*UI_GLYPH_SPACING_X; // 4 char per unit
766 ui_begin( &world.st.world_text, world.w*unit_scale_px, world.h*unit_scale_px );
767
768 for( int i = 0; i < vg_list_size( pLevel->strings ); i ++ )
769 {
770 struct world_string *wstr = &pLevel->strings[i];
771
772 if( wstr->str )
773 {
774 ui_px pos[2];
775
776 pos[0] = -UI_GLYPH_SPACING_X/2;
777
778 if( wstr->placement == k_placement_bottom )
779 pos[1] = 2*-unit_scale_px;
780 else
781 pos[1] = (world.h-1)*-unit_scale_px -6;
782
783 ui_text( &world.st.world_text, pos, wstr->str, 1, k_text_align_left );
784 }
785 }
786
787 ui_resolve( &world.st.world_text );
788 }
789
790 static int map_load( const char *str, const char *name )
791 {
792 map_free();
793
794 char const *c = str;
795
796 // Scan for width
797 for(;; world.w ++)
798 {
799 if( c[world.w] == ';' )
800 break;
801 else if( !c[world.w] )
802 {
803 vg_error( "Unexpected EOF when parsing level\n" );
804 return 0;
805 }
806 }
807
808 struct cell *row = arraddnptr( world.data, world.w );
809 int cx = 0;
810 int reg_start = 0, reg_end = 0;
811
812 u32 *links_to_make = NULL;
813 int links_satisfied = 0;
814
815 char link_id_buffer[32];
816 int link_id_n = 0;
817
818 for(;;)
819 {
820 if( !*c )
821 break;
822
823 if( *c == '\r' ) { c ++; continue; } // fuck off windows
824
825 if( *c == ';' )
826 {
827 c ++;
828
829 if( *c == '\r' ) c ++;
830
831 // Parse attribs
832 if( *c != '\n' )
833 {
834 while( *c )
835 {
836 if( *c == '\r' ) { c ++; continue; }
837
838 if( reg_start < reg_end )
839 {
840 struct cell_terminal *terminal = &world.io[ reg_start ];
841 struct terminal_run *run = &terminal->runs[ terminal->run_count-1 ];
842
843 if( (*c >= 'a' && *c <= 'z') || *c == ' ' )
844 {
845 i8 code = -1;
846 if( *c != ' ' )
847 code = *c - 'a';
848
849 run->steps[ run->step_count ++ ] = code;
850 }
851 else
852 {
853 if( *c == ',' || *c == '\n' )
854 {
855 reg_start ++;
856
857 if( *c == '\n' )
858 break;
859 }
860 else if( *c == ':' )
861 {
862 terminal->runs[ terminal->run_count ].step_count = 0;
863 terminal->run_count ++;
864 world.max_runs = vg_max( world.max_runs, terminal->run_count );
865 }
866 else
867 {
868 vg_error( "Unkown attribute '%c' (row: %u)\n", *c, world.h );
869 goto IL_REG_ERROR;
870 }
871 }
872 }
873 else
874 {
875 if( links_satisfied < arrlen( links_to_make ) )
876 {
877 struct cell *target = &world.data[ links_to_make[ links_satisfied ] ];
878
879 if( (((u32)*c >= (u32)'0') && ((u32)*c <= (u32)'9')) || *c == '-' )
880 {
881 if( link_id_n >= vg_list_size( link_id_buffer )-1 )
882 {
883 vg_error( "Number was way too long to be parsed (row: %u)\n", world.h );
884 goto IL_REG_ERROR;
885 }
886
887 link_id_buffer[ link_id_n ++ ] = *c;
888 }
889 else if( *c == ',' || *c == '\n' )
890 {
891 link_id_buffer[ link_id_n ] = 0x00;
892 int value = atoi( link_id_buffer );
893
894 target->links[value >= 0? 1:0] = abs(value);
895 links_satisfied ++;
896 link_id_n = 0;
897
898 if( *c == '\n' )
899 break;
900 }
901 else
902 {
903 vg_error( "Invalid character '%c' (row: %u)\n", *c, world.h );
904 goto IL_REG_ERROR;
905 }
906 }
907 else
908 {
909 vg_error( "Too many values to assign (row: %u)\n", world.h );
910 goto IL_REG_ERROR;
911 }
912 }
913
914 c ++;
915 }
916 }
917
918 // Registry length-error checks
919 if( reg_start != reg_end )
920 {
921 vg_error( "Not enough spawn values assigned (row: %u, %u of %u)\n", world.h, reg_start, reg_end );
922 goto IL_REG_ERROR;
923 }
924
925 if( links_satisfied != arrlen( links_to_make ) )
926 {
927 vg_error( "Not enough link values assigned (row: %u, %u of %u)\n", world.h, links_satisfied, arrlen( links_to_make ) );
928 goto IL_REG_ERROR;
929 }
930
931 if( cx != world.w )
932 {
933 vg_error( "Not enough cells to match previous row definition (row: %u, %u<%u)\n", world.h, cx, world.w );
934 goto IL_REG_ERROR;
935 }
936
937 row = arraddnptr( world.data, world.w );
938 cx = 0;
939 world.h ++;
940 reg_end = reg_start = arrlen( world.io );
941
942 arrsetlen( links_to_make, 0 );
943 links_satisfied = 0;
944 }
945 else
946 {
947 if( cx == world.w )
948 {
949 vg_error( "Too many cells to match previous row definition (row: %u, %u>%u)\n", world.h, cx, world.w );
950 goto IL_REG_ERROR;
951 }
952
953 // Tile initialization
954 // row[ cx ] .. etc
955 struct cell *cell = &row[ cx ];
956 cell->config = 0xF;
957
958 cell->links[0] = 0;
959 cell->links[1] = 0;
960
961 if( *c == '+' || *c == '-' || *c == '*' )
962 {
963 struct cell_terminal *term = arraddnptr( world.io, 1 );
964 term->pos[0] = cx;
965 term->pos[1] = world.h;
966
967 term->run_count = 1;
968 term->runs[0].step_count = 0;
969
970 switch( *c )
971 {
972 case '+': cell->state = FLAG_INPUT; break;
973 case '-': cell->state = FLAG_OUTPUT; break;
974 case '*': cell->state = FLAG_EMITTER; break;
975 }
976
977 reg_end ++;
978 }
979 else if( *c == '.' ) cell->state = FLAG_INPUT_NICE;
980 else if( *c == '#' ) cell->state = FLAG_WALL;
981 else if( ((u32)*c >= (u32)'A') && ((u32)*c <= (u32)'A'+0xf) )
982 {
983 // Canal flag bits (4bit/16 value):
984 // 0: Canal present
985 // 1: Is trigger
986 // 2: Reserved
987 // 3: Reserved
988
989 cell->state = ((u32)*c - (u32)'A') & (FLAG_CANAL|FLAG_IS_TRIGGER);
990
991 if( cell->state & FLAG_IS_TRIGGER )
992 arrpush( links_to_make, cx + world.h*world.w );
993
994 world.score ++;
995 }
996 else cell->state = 0x00;
997
998 cx ++;
999 }
1000
1001 c ++;
1002 }
1003
1004 // Assign emitter codes
1005 for( int i = 0; i < arrlen( world.io ); i ++ )
1006 {
1007 struct cell_terminal *term = &world.io[i];
1008 struct cell *cell = pcell( term->pos );
1009
1010 if( cell->state & FLAG_EMITTER )
1011 {
1012 if( (term->run_count > 0) && (term->runs[0].step_count >= 2) )
1013 {
1014 cell->emit[0] = term->runs[0].steps[0];
1015 cell->emit[1] = term->runs[0].steps[1];
1016 }
1017 else
1018 {
1019 vg_error( "Emitter was not assigned emit values\n" );
1020 goto IL_REG_ERROR;
1021 }
1022 }
1023 }
1024
1025 // Update data texture to fill out the background
1026 {
1027 u8 info_buffer[64*64*4];
1028 for( int x = 0; x < 64; x ++ )
1029 {
1030 for( int y = 0; y < 64; y ++ )
1031 {
1032 u8 *px = &info_buffer[((x*64)+y)*4];
1033
1034 // Fade out edges of world so that there isnt an obvious line
1035 int dist_x = 16 - VG_MIN( VG_MIN( x, 16 ), 16-VG_MAX( x-16-world.w, 0 ) );
1036 int dist_y = 16 - VG_MIN( VG_MIN( y, 16 ), 16-VG_MAX( y-16-world.h, 0 ) );
1037 int dist = VG_MAX( dist_x, dist_y ) * 16;
1038
1039 int value = VG_MAX( 0, 0xFF-0x3F + hash21i( (v2i){x,y}, 0x3F ) - dist );
1040
1041 px[0] = value;
1042 px[1] = 0;
1043 px[2] = 0;
1044 px[3] = 0;
1045 }
1046 }
1047
1048 // Level selection area
1049
1050 for( int i = 0; i < vg_list_size( career_packs ); i ++ )
1051 {
1052 struct career_level_pack *grid = &career_packs[ i ];
1053
1054 int j = 0;
1055
1056 for( int y = 0; y < grid->dims[1]; y ++ )
1057 {
1058 for( int x = 0; x < grid->dims[0]; x ++ )
1059 {
1060 u8 *px = &info_buffer[((y+16+grid->origin[1])*64+16+x+grid->origin[0])*4];
1061 px[0] = 0x10;
1062
1063 if( j < grid->count )
1064 {
1065 struct cmp_level *lvl = &grid->pack[ j ++ ];
1066 v2i_add( grid->origin, (v2i){x,y}, lvl->btn.position );
1067 }
1068 }
1069 }
1070 }
1071
1072 info_buffer[(((16+world.h-3)*64)+world.w+16-1)*4] = 0x30;
1073 info_buffer[(((16+world.h-2)*64)+world.w+16-1)*4] = 0x30;
1074
1075 // Random walks.. kinda
1076 for( int i = 0; i < arrlen(world.io); i ++ )
1077 {
1078 struct cell_terminal *term = &world.io[ i ];
1079
1080 v2i turtle;
1081 v2i turtle_dir;
1082 int original_y;
1083
1084 // Only make breakouts for terminals on the edge
1085 if( !(term->pos[1] == 1 || term->pos[1] == world.h-2) )
1086 continue;
1087
1088 turtle[0] = 16+term->pos[0];
1089 turtle[1] = 16+term->pos[1];
1090
1091 turtle_dir[0] = 0;
1092 turtle_dir[1] = pcell(term->pos)->state & (FLAG_INPUT|FLAG_EMITTER)? 1: -1;
1093 original_y = turtle_dir[1];
1094
1095 info_buffer[((turtle[1]*64)+turtle[0])*4] = 0;
1096 v2i_add( turtle_dir, turtle, turtle );
1097
1098 for( int i = 0; i < 100; i ++ )
1099 {
1100 info_buffer[((turtle[1]*64)+turtle[0])*4] = 0;
1101
1102 v2i_add( turtle_dir, turtle, turtle );
1103
1104 if( turtle[0] == 0 ) break;
1105 if( turtle[0] == 63 ) break;
1106 if( turtle[1] == 0 ) break;
1107 if( turtle[1] == 63 ) break;
1108
1109 int random_value = hash21i( turtle, 0xFF );
1110 if( random_value > 255-40 && !turtle_dir[0] )
1111 {
1112 turtle_dir[0] = -1;
1113 turtle_dir[1] = 0;
1114 }
1115 else if( random_value > 255-80 && !turtle_dir[0] )
1116 {
1117 turtle_dir[0] = 1;
1118 turtle_dir[1] = 0;
1119 }
1120 else if( random_value > 255-100 )
1121 {
1122 turtle_dir[0] = 0;
1123 turtle_dir[1] = original_y;
1124 }
1125 }
1126 }
1127
1128 glBindTexture( GL_TEXTURE_2D, world.background_data );
1129 glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 64, 64, GL_RGBA, GL_UNSIGNED_BYTE, info_buffer );
1130 }
1131
1132 arrfree( links_to_make );
1133
1134 map_reclassify( NULL, NULL, 1 );
1135
1136 // Validate links
1137 for( int i = 0; i < world.h*world.w; i ++ )
1138 {
1139 struct cell *src = &world.data[i];
1140 if( src->state & FLAG_IS_TRIGGER )
1141 {
1142 int link_id = src->links[0]?0:1;
1143 if( src->links[link_id] <= world.h*world.w )
1144 {
1145 struct cell *target = &world.data[ src->links[link_id] ];
1146 if( ((target->state & FLAG_CANAL) && (target->config == k_cell_type_split))
1147 || (target->state & FLAG_EMITTER) )
1148 {
1149 if( target->links[ link_id ] )
1150 {
1151 vg_error( "Link target was already targeted\n" );
1152 goto IL_REG_ERROR;
1153 }
1154 else
1155 {
1156 // Valid link
1157 target->links[ link_id ] = i;
1158 target->state |= FLAG_TARGETED;
1159 }
1160 }
1161 else
1162 {
1163 vg_error( "Link target was invalid\n" );
1164 goto IL_REG_ERROR;
1165 }
1166 }
1167 else
1168 {
1169 vg_error( "Link target out of bounds\n" );
1170 goto IL_REG_ERROR;
1171 }
1172 }
1173 }
1174
1175 // ==========================================================
1176 // Successful load
1177
1178 vg_success( "Map '%s' loaded! (%u:%u)\n", name, world.w, world.h );
1179
1180 io_reset();
1181
1182 strncpy( world.map_name, name, vg_list_size( world.map_name )-1 );
1183 world.initialzed = 1;
1184
1185 // Setup world button locations
1186 for( int i = 0; i < vg_list_size( world.st.buttons ); i ++ )
1187 {
1188 struct world_button *btn = &world.st.buttons[i];
1189 btn->position[0] = world.w -1;
1190 btn->position[1] = world.h -i -2;
1191 }
1192
1193 // Allocate buffers for render commands
1194 world.cmd_buf_tiles = malloc( world.w*world.h* sizeof( struct render_cmd ) );
1195 world.max_commands = world.w*world.h;
1196
1197 return 1;
1198
1199 IL_REG_ERROR:
1200 arrfree( links_to_make );
1201 map_free();
1202 return 0;
1203 }
1204
1205 static void map_serialize( FILE *stream )
1206 {
1207 for( int y = 0; y < world.h; y ++ )
1208 {
1209 for( int x = 0; x < world.w; x ++ )
1210 {
1211 struct cell *cell = pcell( (v2i){ x, y } );
1212
1213 if( cell->state & FLAG_WALL ) fputc( '#', stream );
1214 else if( cell->state & FLAG_INPUT_NICE ) fputc( '.', stream );
1215 else if( cell->state & FLAG_INPUT ) fputc( '+', stream );
1216 else if( cell->state & FLAG_OUTPUT ) fputc( '-', stream );
1217 else if( cell->state & FLAG_EMITTER ) fputc( '*', stream );
1218 else if( cell->state & (FLAG_CANAL|FLAG_IS_TRIGGER|FLAG_RESERVED0|FLAG_RESERVED1) )
1219 {
1220 fputc( (cell->state & (FLAG_CANAL|FLAG_IS_TRIGGER|FLAG_RESERVED0|FLAG_RESERVED1)) + (u32)'A', stream );
1221 }
1222 else fputc( ' ', stream );
1223 }
1224
1225 fputc( ';', stream );
1226
1227 int terminal_write_count = 0;
1228
1229 for( int x = 0; x < world.w; x ++ )
1230 {
1231 for( int i = 0; i < arrlen( world.io ); i ++ )
1232 {
1233 struct cell_terminal *term = &world.io[i];
1234 if( v2i_eq( term->pos, (v2i){x,y} ) )
1235 {
1236 if( terminal_write_count )
1237 fputc( ',', stream );
1238 terminal_write_count ++;
1239
1240 for( int j = 0; j < term->run_count; j ++ )
1241 {
1242 struct terminal_run *run = &term->runs[j];
1243
1244 for( int k = 0; k < run->step_count; k ++ )
1245 {
1246 i8 step = run->steps[k];
1247 fputc( step == -1? ' ': ('a' + run->steps[k]), stream );
1248 }
1249
1250 if( j < term->run_count-1 )
1251 fputc( ':', stream );
1252 }
1253 }
1254 }
1255 }
1256
1257 for( int x = 0; x < world.w; x ++ )
1258 {
1259 struct cell *cell = pcell( (v2i){ x,y } );
1260 if( cell->state & FLAG_IS_TRIGGER )
1261 {
1262 if( terminal_write_count )
1263 fputc( ',', stream );
1264 terminal_write_count ++;
1265
1266 fprintf( stream, "%d", cell->links[0]? -cell->links[0]: cell->links[1] );
1267 }
1268 }
1269
1270 fputc( '\n', stream );
1271 }
1272 }
1273
1274 // CAREER STATE
1275 // ===========================================================================================================
1276
1277 #pragma pack(push,1)
1278 struct dcareer_state
1279 {
1280 u32 version;
1281 i32 in_map;
1282
1283 u32 reserved[14];
1284
1285 struct dlevel_state
1286 {
1287 i32 score;
1288 i32 unlocked;
1289 i32 reserved[2];
1290 }
1291 levels[ NUM_CAMPAIGN_LEVELS ];
1292 };
1293 #pragma pack(pop)
1294
1295 static int career_load_success = 0;
1296
1297 static void career_serialize(void)
1298 {
1299 if( !career_load_success )
1300 return;
1301
1302 struct dcareer_state encoded;
1303 encoded.version = 2;
1304 encoded.in_map = world.pCmpLevel? world.pCmpLevel->serial_id: -1;
1305
1306 memset( encoded.reserved, 0, sizeof( encoded.reserved ) );
1307
1308 for( int i = 0; i < vg_list_size( career_packs ); i ++ )
1309 {
1310 struct career_level_pack *set = &career_packs[i];
1311
1312 for( int j = 0; j < set->count; j ++ )
1313 {
1314 struct cmp_level *lvl = &set->pack[j];
1315 struct dlevel_state *dest = &encoded.levels[lvl->serial_id];
1316
1317 dest->score = lvl->completed_score;
1318 dest->unlocked = lvl->unlocked;
1319 dest->reserved[0] = 0;
1320 dest->reserved[1] = 0;
1321 }
1322 }
1323
1324 vg_asset_write( "sav/game.sv2", &encoded, sizeof( struct dcareer_state ) );
1325 }
1326
1327 static void career_unlock_level( struct cmp_level *lvl );
1328 static void career_unlock_level( struct cmp_level *lvl )
1329 {
1330 lvl->unlocked = 1;
1331
1332 if( lvl->linked )
1333 career_unlock_level( lvl->linked );
1334 }
1335
1336 static void career_pass_level( struct cmp_level *lvl, int score, int upload )
1337 {
1338 if( score > 0 )
1339 {
1340 if( score < lvl->completed_score || lvl->completed_score == 0 )
1341 {
1342 #ifdef VG_STEAM
1343 if( !lvl->is_tutorial && upload )
1344 leaderboard_set_score( lvl, score );
1345 #endif
1346
1347 lvl->completed_score = score;
1348 }
1349
1350 if( lvl->unlock ) career_unlock_level( lvl->unlock );
1351
1352 #ifdef VG_STEAM
1353 if( lvl->achievement )
1354 {
1355 sw_set_achievement( lvl->achievement );
1356 }
1357
1358 // Check ALL maps to trigger master engineer
1359 for( int i = 0; i < vg_list_size( career_packs ); i ++ )
1360 {
1361 struct career_level_pack *set = &career_packs[i];
1362
1363 for( int j = 0; j < set->count; j ++ )
1364 {
1365 if( set->pack[j].completed_score == 0 )
1366 return;
1367 }
1368 }
1369
1370 sw_set_achievement( "MASTER_ENGINEER" );
1371 #endif
1372 }
1373 }
1374
1375 static void career_reset_level( struct cmp_level *lvl )
1376 {
1377 lvl->unlocked = 0;
1378 lvl->completed_score = 0;
1379 }
1380
1381 static void career_load(void)
1382 {
1383 i64 sz;
1384 struct dcareer_state encoded;
1385
1386 // Blank save state
1387 memset( (void*)&encoded, 0, sizeof( struct dcareer_state ) );
1388 encoded.in_map = 0;
1389 encoded.levels[0].unlocked = 1;
1390
1391 // Load and copy data into encoded
1392 void *cr = vg_asset_read_s( "sav/game.sv2", &sz );
1393
1394 if( cr )
1395 {
1396 if( sz > sizeof( struct dcareer_state ) )
1397 vg_warn( "This save file is too big! Some levels will be lost\n" );
1398
1399 if( sz <= offsetof( struct dcareer_state, levels ) )
1400 {
1401 vg_warn( "This save file is too small to have a header. Creating a blank one\n" );
1402 free( cr );
1403 }
1404
1405 memcpy( (void*)&encoded, cr, VG_MIN( sizeof( struct dcareer_state ), sz ) );
1406 free( cr );
1407 }
1408 else
1409 vg_info( "No save file... Using blank one\n" );
1410
1411 // Reset memory
1412 for( int i = 0; i < vg_list_size( career_packs ); i ++ )
1413 {
1414 struct career_level_pack *set = &career_packs[i];
1415
1416 for( int j = 0; j < set->count; j ++ )
1417 career_reset_level( &set->pack[j] );
1418 }
1419
1420 // Header information
1421 // =================================
1422
1423 struct cmp_level *lvl_to_load = &career_packs[0].pack[0];
1424
1425 // Decode everything from dstate
1426 for( int i = 0; i < vg_list_size( career_packs ); i ++ )
1427 {
1428 struct career_level_pack *set = &career_packs[i];
1429
1430 for( int j = 0; j < set->count; j ++ )
1431 {
1432 struct cmp_level *lvl = &set->pack[j];
1433 struct dlevel_state *src = &encoded.levels[lvl->serial_id];
1434
1435 if( src->unlocked ) career_unlock_level( lvl );
1436 if( src->score ) lvl->completed_score = src->score;
1437
1438 if( lvl->serial_id == encoded.in_map )
1439 lvl_to_load = lvl;
1440 }
1441 }
1442
1443 if( console_changelevel( 1, &lvl_to_load->map_name ) )
1444 {
1445 world.pCmpLevel = lvl_to_load;
1446 gen_level_text( world.pCmpLevel );
1447 }
1448
1449 career_load_success = 1;
1450 }
1451
1452 // MAIN GAMEPLAY
1453 // ===========================================================================================================
1454 static int is_simulation_running(void)
1455 {
1456 return world.st.buttons[ k_world_button_sim ].state;
1457 }
1458
1459 static void clear_animation_flags(void)
1460 {
1461 for( int i = 0; i < world.w*world.h; i ++ )
1462 world.data[ i ].state &= ~(FLAG_FLIP_FLOP|FLAG_FLIP_ROTATING);
1463 }
1464
1465 static void simulation_stop(void)
1466 {
1467 world.st.buttons[ k_world_button_sim ].state = 0;
1468 world.st.buttons[ k_world_button_pause ].state = 0;
1469
1470 world.num_fishes = 0;
1471 world.sim_frame = 0;
1472 world.sim_run = 0;
1473 world.frame_lerp = 0.0f;
1474
1475 io_reset();
1476
1477 sfx_system_fadeout( &audio_system_balls_rolling, 44100 );
1478
1479 clear_animation_flags();
1480
1481 vg_info( "Stopping simulation!\n" );
1482 }
1483
1484 static void simulation_start(void)
1485 {
1486 vg_success( "Starting simulation!\n" );
1487
1488 sfx_set_playrnd( &audio_rolls, &audio_system_balls_rolling, 0, 1 );
1489
1490 world.num_fishes = 0;
1491 world.sim_frame = 0;
1492 world.sim_run = 0;
1493
1494 world.sim_delta_speed = world.st.buttons[ k_world_button_speedy ].state? 10.0f: 2.5f;
1495 world.sim_delta_ref = vg_time;
1496 world.sim_internal_ref = 0.0f;
1497 world.sim_internal_time = 0.0f;
1498 world.pause_offset_target = 0.0f;
1499
1500 world.sim_target = 0;
1501
1502 clear_animation_flags();
1503
1504 io_reset();
1505
1506 if( world.pCmpLevel )
1507 {
1508 world.pCmpLevel->completed_score = 0;
1509 }
1510 }
1511
1512 static int world_check_pos_ok( v2i co, int dist )
1513 {
1514 return (co[0] < dist || co[0] >= world.w-dist || co[1] < dist || co[1] >= world.h-dist)? 0: 1;
1515 }
1516
1517 static int cell_interactive( v2i co )
1518 {
1519 struct cell *cell;
1520
1521 // Bounds check
1522 if( world_check_pos_ok( co, 1 ) )
1523 {
1524 cell = pcell( co );
1525 if( cell->state & FLAG_EMITTER )
1526 return 1;
1527 }
1528
1529 if( !world_check_pos_ok( co, 2 ) )
1530 return 0;
1531
1532 // Flags check
1533 if( cell->state & (FLAG_WALL|FLAG_INPUT|FLAG_OUTPUT) )
1534 return 0;
1535
1536 // List of 3x3 configurations that we do not allow
1537 static u32 invalid_src[][9] =
1538 {
1539 { 0,1,0,
1540 1,1,1,
1541 0,1,0
1542 },
1543 { 0,0,0,
1544 0,1,1,
1545 0,1,1
1546 },
1547 { 0,0,0,
1548 1,1,0,
1549 1,1,0
1550 },
1551 { 0,1,1,
1552 0,1,1,
1553 0,0,0
1554 },
1555 { 1,1,0,
1556 1,1,0,
1557 0,0,0
1558 },
1559 { 0,1,0,
1560 0,1,1,
1561 0,1,0
1562 },
1563 { 0,1,0,
1564 1,1,0,
1565 0,1,0
1566 }
1567 };
1568
1569 // Statically compile invalid configurations into bitmasks
1570 static u32 invalid[ vg_list_size(invalid_src) ];
1571
1572 for( int i = 0; i < vg_list_size(invalid_src); i ++ )
1573 {
1574 u32 comped = 0x00;
1575
1576 for( int j = 0; j < 3; j ++ )
1577 for( int k = 0; k < 3; k ++ )
1578 comped |= invalid_src[i][ j*3+k ] << ((j*5)+k);
1579
1580 invalid[i] = comped;
1581 }
1582
1583 // Extract 5x5 grid surrounding tile
1584 u32 blob = 0x1000;
1585 for( int y = co[1]-2; y < co[1]+3; y ++ )
1586 for( int x = co[0]-2; x < co[0]+3; x ++ )
1587 {
1588 struct cell *cell = pcell((v2i){x,y});
1589
1590 if( cell && (cell->state & (FLAG_CANAL|FLAG_INPUT|FLAG_OUTPUT|FLAG_EMITTER)) )
1591 blob |= 0x1 << ((y-(co[1]-2))*5 + x-(co[0]-2));
1592 }
1593
1594 // Run filter over center 3x3 grid to check for invalid configurations
1595 int kernel[] = { 0, 1, 2, 5, 6, 7, 10, 11, 12 };
1596 for( int i = 0; i < vg_list_size(kernel); i ++ )
1597 {
1598 if( blob & (0x1 << (6+kernel[i])) )
1599 {
1600 u32 window = blob >> kernel[i];
1601
1602 for( int j = 0; j < vg_list_size(invalid); j ++ )
1603 if((window & invalid[j]) == invalid[j])
1604 return 0;
1605 }
1606 }
1607
1608 return 1;
1609 }
1610
1611 static void vg_update(void)
1612 {
1613 // Async events
1614 if( world.st.lvl_to_load )
1615 {
1616 world.st.world_transition = (world.st.lvl_load_time-vg_time) * 4.0f;
1617
1618 if( vg_time > world.st.lvl_load_time )
1619 {
1620 if( console_changelevel( 1, &world.st.lvl_to_load->map_name ) )
1621 {
1622 world.pCmpLevel = world.st.lvl_to_load;
1623 gen_level_text( world.pCmpLevel );
1624 }
1625
1626 world.st.lvl_to_load = NULL;
1627 }
1628 }
1629 else
1630 {
1631 world.st.world_transition = vg_minf( 1.0f, (vg_time-world.st.lvl_load_time) * 4.0f );
1632 }
1633
1634 // Camera
1635 // ========================================================================================================
1636
1637 float r1 = (float)vg_window_y / (float)vg_window_x,
1638 r2 = (float)world.h / (float)world.w,
1639 size;
1640
1641 static float size_current = 2.0f;
1642 static v3f origin_current = { 0.0f, 0.0f, 0.0f };
1643 static v2f drag_offset = { 0.0f, 0.0f };
1644 static v2f view_point = { 0.0f, 0.0f };
1645 v2f result_view;
1646
1647 size = ( r2 < r1? (float)(world.w+5) * 0.5f: ((float)(world.h+5) * 0.5f) / r1 ) + 0.5f;
1648
1649 v2f origin;
1650 v2f vt_target;
1651
1652 origin[0] = floorf( -0.5f * ((float)world.w-4.5f) );
1653 origin[1] = floorf( -0.5f * world.h );
1654
1655 // Create and clamp result view
1656 v2_add( view_point, drag_offset, result_view );
1657 result_view[0] = vg_clampf( result_view[0], -world.st.zoom, world.st.zoom );
1658 result_view[1] = vg_clampf( result_view[1], -world.st.zoom*r1, world.st.zoom*r1 );
1659
1660 v2_add( origin, result_view, vt_target );
1661
1662 // Lerp towards target
1663 size_current = vg_lerpf( size_current, size - world.st.zoom, vg_time_delta * 6.0f );
1664 v2_lerp( origin_current, vt_target, vg_time_delta * 6.0f, origin_current );
1665
1666 m3x3_projection( m_projection, -size_current, size_current, -size_current*r1, size_current*r1 );
1667 m3x3_identity( m_view );
1668 m3x3_translate( m_view, origin_current );
1669 m3x3_mul( m_projection, m_view, vg_pv );
1670 vg_projection_update();
1671
1672 // Mouse input
1673 // ========================================================================================================
1674 v2_copy( vg_mouse_ws, world.tile_pos );
1675
1676 world.tile_x = floorf( world.tile_pos[0] );
1677 world.tile_y = floorf( world.tile_pos[1] );
1678
1679 // Camera dragging
1680 {
1681 static v2f drag_origin; // x/y pixel
1682
1683 if( vg_get_button_down( "tertiary" ) )
1684 v2_copy( vg_mouse, drag_origin );
1685 else if( vg_get_button( "tertiary" ) )
1686 {
1687 // get offset
1688 v2_sub( vg_mouse, drag_origin, drag_offset );
1689 v2_div( drag_offset, (v2f){ vg_window_x, vg_window_y }, drag_offset );
1690 v2_mul( drag_offset, (v2f){ size_current*2.0f, -size_current*r1*2.0f }, drag_offset );
1691 }
1692 else
1693 {
1694 v2_copy( result_view, view_point );
1695 v2_copy( (v2f){0.0f,0.0f}, drag_offset );
1696 }
1697 }
1698
1699 // Zooming
1700 {
1701 v2f mview_local;
1702 v2f mview_new;
1703 v2f mview_cur;
1704 v2f mview_delta;
1705 float rsize;
1706
1707 rsize = size-world.st.zoom;
1708
1709 v2_div( vg_mouse, (v2f){ vg_window_x*0.5f, vg_window_y*0.5f }, mview_local );
1710 v2_add( (v2f){ -rsize, -rsize*r1 }, (v2f){ mview_local[0]*rsize, (2.0f-mview_local[1])*rsize*r1 }, mview_cur );
1711
1712 world.st.zoom = vg_clampf( world.st.zoom + vg_mouse_wheel[1], 0.0f, size - 4.0f );
1713
1714 // Recalculate new position
1715 rsize = size-world.st.zoom;
1716 v2_add( (v2f){ -rsize, -rsize*r1 }, (v2f){ mview_local[0]*rsize, (2.0f-mview_local[1])*rsize*r1 }, mview_new );
1717
1718 // Apply offset
1719 v2_sub( mview_new, mview_cur, mview_delta );
1720 v2_add( mview_delta, view_point, view_point );
1721 }
1722
1723 // Tilemap
1724 // ========================================================================================================
1725 if( !is_simulation_running() && !gui_want_mouse() )
1726 {
1727 v2_copy( vg_mouse_ws, world.drag_to_co );
1728
1729 if( cell_interactive( (v2i){ world.tile_x, world.tile_y } ))
1730 {
1731 world.selected = world.tile_y * world.w + world.tile_x;
1732
1733 static u32 modify_state = 0;
1734 struct cell *cell_ptr = &world.data[world.selected];
1735
1736 if( !(cell_ptr->state & FLAG_EMITTER) )
1737 {
1738 if( vg_get_button_down("primary") )
1739 modify_state = (cell_ptr->state & FLAG_CANAL) ^ FLAG_CANAL;
1740
1741 if( vg_get_button("primary") && ((cell_ptr->state & FLAG_CANAL) != modify_state) )
1742 {
1743 cell_ptr->state &= ~FLAG_CANAL;
1744 cell_ptr->state |= modify_state;
1745
1746 if( cell_ptr->state & FLAG_CANAL )
1747 {
1748 cell_ptr->links[0] = 0;
1749 cell_ptr->links[1] = 0;
1750
1751 sfx_set_playrnd( &audio_tile_mod, &audio_system_sfx, 3, 6 );
1752 world.score ++;
1753 }
1754 else
1755 {
1756 sfx_set_playrnd( &audio_tile_mod, &audio_system_sfx, 0, 3 );
1757 world.score --;
1758 }
1759
1760 map_reclassify((v2i){ world.tile_x -2, world.tile_y -2 },
1761 (v2i){ world.tile_x +2, world.tile_y +2 }, 1 );
1762 }
1763
1764 if( vg_get_button_down("secondary") && (cell_ptr->state & FLAG_CANAL) && !(cell_ptr->config == k_cell_type_split) )
1765 {
1766 world.id_drag_from = world.selected;
1767
1768 struct cell_description *desc = &cell_descriptions[ world.data[world.id_drag_from].config ];
1769 v2_add( desc->trigger_pos, (v2f){ world.tile_x, world.tile_y }, world.drag_from_co );
1770 }
1771 }
1772
1773 float local_x = vg_mouse_ws[0] - (float)world.tile_x;
1774
1775 if( vg_get_button_up("secondary") && world.id_drag_from == world.selected )
1776 {
1777 u32 link_id = cell_ptr->links[ 0 ]? 0: 1;
1778
1779 // break existing connection off
1780 if( cell_ptr->links[ link_id ] )
1781 {
1782 struct cell *current_connection = &world.data[ cell_ptr->links[ link_id ]];
1783
1784 if( !current_connection->links[ link_id ^ 0x1 ] )
1785 current_connection->state &= ~FLAG_TARGETED;
1786
1787 current_connection->links[ link_id ] = 0;
1788 cell_ptr->links[ link_id ] = 0;
1789 }
1790
1791 cell_ptr->state &= ~FLAG_IS_TRIGGER;
1792 world.id_drag_from = 0;
1793 }
1794
1795 else if( world.id_drag_from && (cell_ptr->state & (FLAG_CANAL|FLAG_EMITTER)) &&
1796 ((cell_ptr->config == k_cell_type_split) || (cell_ptr->state & FLAG_EMITTER)) )
1797 {
1798 world.drag_to_co[0] = (float)world.tile_x + (local_x > 0.5f? 0.75f: 0.25f);
1799 world.drag_to_co[1] = (float)world.tile_y + 0.25f;
1800
1801 if( vg_get_button_up("secondary") )
1802 {
1803 struct cell *drag_ptr = &world.data[world.id_drag_from];
1804 u32 link_id = local_x > 0.5f? 1: 0;
1805
1806 // Cleanup existing connections
1807 if( cell_ptr->links[ link_id ] )
1808 {
1809 vg_warn( "Destroying existing connection on link %u (%hu)\n", link_id, cell_ptr->links[ link_id ] );
1810
1811 struct cell *current_connection = &world.data[ cell_ptr->links[ link_id ]];
1812 current_connection->state &= ~FLAG_IS_TRIGGER;
1813 current_connection->links[ link_id ] = 0;
1814 }
1815
1816 for( u32 i = 0; i < 2; i ++ )
1817 {
1818 if( drag_ptr->links[ i ] )
1819 {
1820 vg_warn( "Destroying link %u (%hu)\n", i, drag_ptr->links[ i ] );
1821
1822 struct cell *current_connection = &world.data[ drag_ptr->links[ i ]];
1823 if( current_connection->links[ i ^ 0x1 ] == 0 )
1824 current_connection->state &= ~FLAG_TARGETED;
1825
1826 current_connection->links[ i ] = 0;
1827 drag_ptr->links[ i ] = 0;
1828 }
1829 }
1830
1831 // Create the new connection
1832 vg_success( "Creating connection on link %u (%hu)\n", link_id, world.id_drag_from );
1833
1834 cell_ptr->links[ link_id ] = world.id_drag_from;
1835 drag_ptr->links[ link_id ] = world.selected;
1836
1837 cell_ptr->state |= FLAG_TARGETED;
1838 drag_ptr->state |= FLAG_IS_TRIGGER;
1839 world.id_drag_from = 0;
1840 }
1841 }
1842 }
1843 else
1844 {
1845 world.selected = -1;
1846 }
1847
1848 if( !(vg_get_button("secondary") && world.id_drag_from) )
1849 world.id_drag_from = 0;
1850 }
1851 else
1852 {
1853 world.selected = -1;
1854 world.id_drag_from = 0;
1855 }
1856
1857 // Marble state updates
1858 // ========================================================================================================
1859 if( is_simulation_running() )
1860 {
1861 float old_time = world.sim_internal_time;
1862
1863 if( !world.st.buttons[ k_world_button_pause ].state )
1864 world.sim_internal_time = world.sim_internal_ref + (vg_time-world.sim_delta_ref) * world.sim_delta_speed;
1865 else
1866 world.sim_internal_time = vg_lerpf( world.sim_internal_time, world.sim_internal_ref + world.pause_offset_target, vg_time_delta*15.0f );
1867 world.sim_internal_delta = world.sim_internal_time-old_time;
1868
1869 world.sim_target = (int)floorf(world.sim_internal_time);
1870
1871 int success_this_frame = 0;
1872 int failure_this_frame = 0;
1873
1874 while( world.sim_frame < world.sim_target )
1875 {
1876 sfx_set_playrnd( &audio_random, &audio_system_balls_switching, 0, 8 );
1877
1878 // Update splitter deltas
1879 for( int i = 0; i < world.h*world.w; i ++ )
1880 {
1881 struct cell *cell = &world.data[i];
1882 if( cell->config == k_cell_type_split )
1883 {
1884 cell->state &= ~FLAG_FLIP_ROTATING;
1885 }
1886 if( cell->state & (FLAG_IS_TRIGGER|FLAG_EMITTER) )
1887 cell->state &= ~FLAG_TRIGGERED;
1888 }
1889
1890 int alive_count = 0;
1891
1892 // Update fish positions
1893 for( int i = 0; i < world.num_fishes; i ++ )
1894 {
1895 struct fish *fish = &world.fishes[i];
1896
1897 if( fish->state == k_fish_state_soon_dead )
1898 fish->state = k_fish_state_dead;
1899
1900 if( fish->state == k_fish_state_soon_alive )
1901 fish->state = k_fish_state_alive;
1902
1903 if( fish->state < k_fish_state_alive )
1904 continue;
1905
1906 struct cell *cell_current = pcell( fish->pos );
1907
1908 if( fish->state == k_fish_state_alive )
1909 {
1910 // Apply to output
1911 if( cell_current->state & FLAG_OUTPUT )
1912 {
1913 for( int j = 0; j < arrlen( world.io ); j ++ )
1914 {
1915 struct cell_terminal *term = &world.io[j];
1916
1917 if( v2i_eq( term->pos, fish->pos ) )
1918 {
1919 struct terminal_run *run = &term->runs[ world.sim_run ];
1920 if( run->recv_count < vg_list_size( run->recieved ) )
1921 {
1922 if( fish->colour == run->steps[ run->recv_count ] )
1923 success_this_frame = 1;
1924 else
1925 failure_this_frame = 1;
1926
1927 run->recieved[ run->recv_count ++ ] = fish->colour;
1928 }
1929 else
1930 failure_this_frame = 1;
1931
1932 break;
1933 }
1934 }
1935
1936 fish->state = k_fish_state_dead;
1937 fish->death_time = -1000.0f;
1938 continue;
1939 }
1940
1941
1942 if( cell_current->config == k_cell_type_merge )
1943 {
1944 // Can only move up
1945 fish->dir[0] = 0;
1946 fish->dir[1] = -1;
1947 fish->flow_reversed = 0;
1948 }
1949 else
1950 {
1951 if( cell_current->config == k_cell_type_split )
1952 {
1953 // Flip flop L/R
1954 fish->dir[0] = cell_current->state&FLAG_FLIP_FLOP?1:-1;
1955 fish->dir[1] = 0;
1956
1957 if( !(cell_current->state & FLAG_TARGETED) )
1958 cell_current->state ^= FLAG_FLIP_FLOP;
1959 }
1960 else
1961 {
1962 // Apply cell out-flow
1963 struct cell_description *desc = &cell_descriptions[ cell_current->config ];
1964
1965 v2i_copy( fish->flow_reversed? desc->start: desc->end, fish->dir );
1966 }
1967
1968 v2i pos_next;
1969 v2i_add( fish->pos, fish->dir, pos_next );
1970
1971 struct cell *cell_next = pcell( pos_next );
1972
1973 if( cell_next->state & (FLAG_CANAL|FLAG_OUTPUT) )
1974 {
1975 struct cell_description *desc = &cell_descriptions[ cell_next->config ];
1976
1977 if( cell_next->config == k_cell_type_merge )
1978 {
1979 if( fish->dir[0] == 0 )
1980 {
1981 fish->state = k_fish_state_dead;
1982 fish->death_time = world.sim_internal_time;
1983 }
1984 else
1985 fish->flow_reversed = 0;
1986 }
1987 else
1988 {
1989 if( cell_next->config == k_cell_type_split )
1990 {
1991 if( fish->dir[0] == 0 )
1992 {
1993 sfx_set_playrnd( &audio_splitter, &audio_system_balls_important, 0, 1 );
1994 cell_next->state |= FLAG_FLIP_ROTATING;
1995
1996 fish->flow_reversed = 0;
1997 }
1998 else
1999 {
2000 fish->state = k_fish_state_dead;
2001 fish->death_time = world.sim_internal_time;
2002 }
2003 }
2004 else
2005 fish->flow_reversed = ( fish->dir[0] != -desc->start[0] ||
2006 fish->dir[1] != -desc->start[1] )? 1: 0;
2007 }
2008 }
2009 else
2010 {
2011 if( world_check_pos_ok( fish->pos, 2 ) )
2012 fish->state = k_fish_state_bg;
2013 else
2014 {
2015 fish->state = k_fish_state_dead;
2016 fish->death_time = world.sim_internal_time;
2017 }
2018 }
2019 }
2020
2021 //v2i_add( fish->pos, fish->dir, fish->pos );
2022 }
2023 else if( fish->state == k_fish_state_bg )
2024 {
2025 v2i_add( fish->pos, fish->dir, fish->pos );
2026
2027 if( !world_check_pos_ok( fish->pos, 2 ) )
2028 {
2029 fish->state = k_fish_state_dead;
2030 fish->death_time = -1000.0f;
2031 }
2032 else
2033 {
2034 struct cell *cell_entry = pcell( fish->pos );
2035
2036 if( cell_entry->state & FLAG_CANAL )
2037 {
2038 if( cell_entry->config == k_cell_type_con_r || cell_entry->config == k_cell_type_con_u
2039 || cell_entry->config == k_cell_type_con_l || cell_entry->config == k_cell_type_con_d )
2040 {
2041 #ifdef VG_STEAM
2042 sw_set_achievement( "CAN_DO_THAT" );
2043 #endif
2044
2045 fish->state = k_fish_state_soon_alive;
2046
2047 fish->dir[0] = 0;
2048 fish->dir[1] = 0;
2049 fish->flow_reversed = 1;
2050
2051 switch( cell_entry->config )
2052 {
2053 case k_cell_type_con_r: fish->dir[0] = 1; break;
2054 case k_cell_type_con_l: fish->dir[0] = -1; break;
2055 case k_cell_type_con_u: fish->dir[1] = 1; break;
2056 case k_cell_type_con_d: fish->dir[1] = -1; break;
2057 }
2058 }
2059 }
2060 }
2061 }
2062 else { vg_error( "fish behaviour unimplemented for behaviour type (%d)\n" ); }
2063
2064 if( fish->state >= k_fish_state_alive )
2065 alive_count ++;
2066 }
2067
2068 // Second pass (triggers)
2069 for( int i = 0; i < world.num_fishes; i ++ )
2070 {
2071 struct fish *fish = &world.fishes[i];
2072
2073 // Apply movement
2074 if( fish->state == k_fish_state_alive )
2075 v2i_add( fish->pos, fish->dir, fish->pos );
2076
2077 if( fish->state == k_fish_state_alive || fish->state == k_fish_state_soon_alive )
2078 {
2079 struct cell *cell_current = pcell( fish->pos );
2080
2081 if( cell_current->state & FLAG_IS_TRIGGER )
2082 {
2083 int trigger_id = cell_current->links[0]?0:1;
2084
2085 struct cell *target_peice = &world.data[ cell_current->links[trigger_id] ];
2086
2087 // Spawn new marble
2088 if( (target_peice->state & FLAG_EMITTER) && !(target_peice->state & FLAG_TRIGGERED))
2089 {
2090 struct fish *fish = &world.fishes[ world.num_fishes ];
2091 lcell( cell_current->links[trigger_id], fish->pos );
2092
2093 fish->state = k_fish_state_soon_alive;
2094 fish->colour = target_peice->emit[ trigger_id ];
2095
2096 if( target_peice->config != k_cell_type_stub )
2097 {
2098 struct cell_description *desc = &cell_descriptions[ target_peice->config ];
2099 v2i_copy( desc->start, fish->dir );
2100 fish->flow_reversed = 1;
2101
2102 world.num_fishes ++;
2103 alive_count ++;
2104 }
2105 }
2106 else
2107 {
2108 if( trigger_id )
2109 target_peice->state |= FLAG_FLIP_FLOP;
2110 else
2111 target_peice->state &= ~FLAG_FLIP_FLOP;
2112 }
2113
2114 cell_current->state |= FLAG_TRIGGERED;
2115 }
2116 }
2117 }
2118
2119 // Third pass (collisions)
2120 struct fish *fi, *fj;
2121
2122 for( int i = 0; i < world.num_fishes; i ++ )
2123 {
2124 fi = &world.fishes[i];
2125
2126 if( fi->state == k_fish_state_alive )
2127 {
2128 int continue_again = 0;
2129
2130 for( int j = i+1; j < world.num_fishes; j ++ )
2131 {
2132 fj = &world.fishes[j];
2133
2134 if( fj->state == k_fish_state_alive )
2135 {
2136 v2i fi_prev;
2137 v2i fj_prev;
2138
2139 v2i_sub( fi->pos, fi->dir, fi_prev );
2140 v2i_sub( fj->pos, fj->dir, fj_prev );
2141
2142 int
2143 collide_next_frame = (
2144 (fi->pos[0] == fj->pos[0]) &&
2145 (fi->pos[1] == fj->pos[1]))? 1: 0,
2146 collide_this_frame = (
2147 (fi_prev[0] == fj->pos[0]) &&
2148 (fi_prev[1] == fj->pos[1]) &&
2149 (fj_prev[0] == fi->pos[0]) &&
2150 (fj_prev[1] == fi->pos[1])
2151 )? 1: 0;
2152
2153 if( collide_next_frame || collide_this_frame )
2154 {
2155 #ifdef VG_STEAM
2156 sw_set_achievement( "BANG" );
2157 #endif
2158
2159 // Shatter death (+0.5s)
2160 float death_time = world.sim_internal_time + ( collide_this_frame? 0.0f: 0.5f );
2161
2162 fi->state = k_fish_state_soon_dead;
2163 fj->state = k_fish_state_soon_dead;
2164 fi->death_time = death_time;
2165 fj->death_time = death_time;
2166
2167 continue_again = 1;
2168 break;
2169 }
2170 }
2171 }
2172 if( continue_again )
2173 continue;
2174 }
2175 }
2176
2177 // Spawn fishes
2178 for( int i = 0; i < arrlen( world.io ); i ++ )
2179 {
2180 struct cell_terminal *term = &world.io[ i ];
2181 int is_input = pcell(term->pos)->state & FLAG_INPUT;
2182
2183 if( is_input )
2184 {
2185 if( world.sim_frame < term->runs[ world.sim_run ].step_count )
2186 {
2187 i8 emit = term->runs[ world.sim_run ].steps[ world.sim_frame ];
2188 if( emit == -1 )
2189 continue;
2190
2191 struct fish *fish = &world.fishes[ world.num_fishes ];
2192 v2i_copy( term->pos, fish->pos );
2193
2194 fish->state = k_fish_state_alive;
2195 fish->colour = emit;
2196
2197 struct cell *cell_ptr = pcell( fish->pos );
2198
2199 if( cell_ptr->config != k_cell_type_stub )
2200 {
2201 struct cell_description *desc = &cell_descriptions[ cell_ptr->config ];
2202
2203 v2i_copy( desc->start, fish->dir );
2204 fish->flow_reversed = 1;
2205
2206 world.num_fishes ++;
2207 alive_count ++;
2208 }
2209 }
2210 }
2211 }
2212
2213 if( alive_count == 0 )
2214 {
2215 world.completed = 1;
2216
2217 for( int i = 0; i < arrlen( world.io ); i ++ )
2218 {
2219 struct cell_terminal *term = &world.io[ i ];
2220
2221 if( pcell(term->pos)->state & FLAG_OUTPUT )
2222 {
2223 struct terminal_run *run = &term->runs[ world.sim_run ];
2224
2225 if( run->recv_count == run->step_count )
2226 {
2227 for( int j = 0; j < run->step_count; j ++ )
2228 {
2229 if( run->recieved[j] != run->steps[j] )
2230 {
2231 world.completed = 0;
2232 break;
2233 }
2234 }
2235 }
2236 else
2237 {
2238 world.completed = 0;
2239 break;
2240 }
2241 }
2242 }
2243
2244 if( world.completed )
2245 {
2246 if( world.sim_run < world.max_runs-1 )
2247 {
2248 vg_success( "Run passed, starting next\n" );
2249 world.sim_run ++;
2250 world.sim_frame = 0;
2251 world.sim_target = 0;
2252 world.num_fishes = 0;
2253
2254 // Reset timing reference points
2255 world.sim_delta_ref = vg_time;
2256 world.sim_internal_ref = 0.0f;
2257
2258 if( world.st.buttons[ k_world_button_pause ].state )
2259 world.pause_offset_target = 0.5f;
2260 else
2261 world.pause_offset_target = 0.0f;
2262
2263 world.sim_internal_time = 0.0f;
2264
2265 for( int i = 0; i < world.w*world.h; i ++ )
2266 world.data[ i ].state &= ~FLAG_FLIP_FLOP;
2267
2268 continue;
2269 }
2270 else
2271 {
2272 vg_success( "Level passed!\n" );
2273
2274 u32 score = 0;
2275 for( int i = 0; i < world.w*world.h; i ++ )
2276 if( world.data[ i ].state & FLAG_CANAL )
2277 score ++;
2278
2279 world.score = score;
2280 world.time = world.sim_frame;
2281
2282 // Copy into career data
2283 if( world.pCmpLevel )
2284 {
2285 career_pass_level( world.pCmpLevel, world.score, 1 );
2286 }
2287
2288 sfx_set_play( &audio_tones, &audio_system_balls_extra, 9 );
2289 failure_this_frame = 0;
2290 success_this_frame = 0;
2291 }
2292 }
2293 else
2294 {
2295 #ifdef VG_STEAM
2296 if( world.sim_run > 0 )
2297 sw_set_achievement( "GOOD_ENOUGH" );
2298 #endif
2299
2300 vg_error( "Level failed :(\n" );
2301 }
2302
2303 simulation_stop();
2304 break;
2305 }
2306
2307 world.sim_frame ++;
2308 }
2309
2310 // Sounds
2311 if( failure_this_frame )
2312 {
2313 sfx_set_play( &audio_tones, &audio_system_balls_extra, 0 );
2314 }
2315 else if( success_this_frame )
2316 {
2317 static int succes_counter = 0;
2318
2319 sfx_set_play( &audio_tones, &audio_system_balls_extra, 1+(succes_counter++) );
2320
2321 if( succes_counter == 7 )
2322 succes_counter = 0;
2323 }
2324
2325 // Position update
2326 // =====================================================================================================
2327
2328 world.frame_lerp = world.sim_internal_time - floorf( world.sim_internal_time );
2329
2330 for( int i = 0; i < world.num_fishes; i ++ )
2331 {
2332 struct fish *fish = &world.fishes[i];
2333
2334 if( fish->state == k_fish_state_dead )
2335 continue;
2336
2337 if( fish->state == k_fish_state_soon_dead && (world.sim_internal_time > fish->death_time) )
2338 continue; // Todo: particle thing?
2339
2340 struct cell *cell = pcell(fish->pos);
2341 struct cell_description *desc = &cell_descriptions[ cell->config ];
2342
2343 v2f const *curve;
2344
2345 float t = world.frame_lerp;
2346 if( fish->flow_reversed && !desc->is_linear )
2347 t = 1.0f-t;
2348
2349 v2_copy( fish->physics_co, fish->physics_v );
2350
2351 switch( cell->config )
2352 {
2353 case k_cell_type_merge:
2354 if( fish->dir[0] == 1 )
2355 curve = curve_12;
2356 else
2357 curve = curve_9;
2358 break;
2359 case k_cell_type_con_r: curve = curve_1; break;
2360 case k_cell_type_con_l: curve = curve_4; break;
2361 case k_cell_type_con_u: curve = curve_2; break;
2362 case k_cell_type_con_d: curve = curve_8; break;
2363 case 3: curve = curve_3; break;
2364 case 6: curve = curve_6; break;
2365 case 9: curve = curve_9; break;
2366 case 12: curve = curve_12; break;
2367 case 7:
2368 if( t > curve_7_linear_section )
2369 {
2370 t -= curve_7_linear_section;
2371 t *= (1.0f/(1.0f-curve_7_linear_section));
2372
2373 curve = cell->state & FLAG_FLIP_FLOP? curve_7: curve_7_1;
2374 }
2375 else curve = NULL;
2376 break;
2377 default: curve = NULL; break;
2378 }
2379
2380 if( curve )
2381 {
2382 float t2 = t * t;
2383 float t3 = t * t * t;
2384
2385 float cA = 3.0f*t2 - 3.0f*t3;
2386 float cB = 3.0f*t3 - 6.0f*t2 + 3.0f*t;
2387 float cC = 3.0f*t2 - t3 - 3.0f*t + 1.0f;
2388
2389 fish->physics_co[0] = t3*curve[3][0] + cA*curve[2][0] + cB*curve[1][0] + cC*curve[0][0];
2390 fish->physics_co[1] = t3*curve[3][1] + cA*curve[2][1] + cB*curve[1][1] + cC*curve[0][1];
2391 fish->physics_co[0] += (float)fish->pos[0];
2392 fish->physics_co[1] += (float)fish->pos[1];
2393 }
2394 else
2395 {
2396 v2f origin;
2397 origin[0] = (float)fish->pos[0] + (float)fish->dir[0]*-0.5f + 0.5f;
2398 origin[1] = (float)fish->pos[1] + (float)fish->dir[1]*-0.5f + 0.5f;
2399
2400 fish->physics_co[0] = origin[0] + (float)fish->dir[0]*t;
2401 fish->physics_co[1] = origin[1] + (float)fish->dir[1]*t;
2402 }
2403
2404 v2_sub( fish->physics_co, fish->physics_v, fish->physics_v );
2405 v2_divs( fish->physics_v, world.sim_internal_delta, fish->physics_v );
2406 }
2407 }
2408 }
2409
2410 static void render_tile( v2i pos, struct cell *ptr, v4f const regular_colour, v4f const selected_colour )
2411 {
2412 int selected = world.selected == pos[1]*world.w + pos[0];
2413 int uv[2];
2414
2415 uv[0] = ptr->config & 0x3;
2416 uv[1] = ptr->config >> 2;
2417
2418 glUniform4f( SHADER_UNIFORM( shader_tile_main, "uOffset" ),
2419 (float)pos[0],
2420 (float)pos[1],
2421 uv[0],
2422 uv[1]
2423 );
2424
2425 if( selected )
2426 {
2427 glUniform4fv( SHADER_UNIFORM( shader_tile_main, "uColour" ), 1, selected_colour );
2428 draw_mesh( 0, 2 );
2429 glUniform4fv( SHADER_UNIFORM( shader_tile_main, "uColour" ), 1, regular_colour );
2430 }
2431 else
2432 draw_mesh( 0, 2 );
2433 }
2434
2435 // Renders specific chunk of tiles
2436 static void render_tile_block( v2i start, v2i end, v4f const regular_colour, v4f const selected_colour )
2437 {
2438 v2i full_start = { 0,0 };
2439 v2i full_end = { world.w, world.h };
2440
2441 if( !start || !end )
2442 {
2443 start = full_start;
2444 end = full_end;
2445 }
2446
2447 for( int y = start[1]; y < end[1]; y ++ )
2448 {
2449 for( int x = start[0]; x < end[0]; x ++ )
2450 {
2451 v2i pos = { x, y };
2452 struct cell *cell = pcell( pos );
2453
2454 if( cell->state & (FLAG_CANAL|FLAG_INPUT|FLAG_OUTPUT|FLAG_EMITTER) )
2455 render_tile( pos, cell, regular_colour, selected_colour );
2456 }
2457 }
2458 }
2459
2460 // Renders all tiles in the command list
2461 static void render_tiles( v4f const regular_colour, v4f const selected_colour )
2462 {
2463 glUniform4fv( SHADER_UNIFORM( shader_tile_main, "uColour" ), 1, regular_colour );
2464
2465 struct render_list
2466 {
2467 struct render_cmd *arr;
2468 u32 count;
2469 }
2470 render_lists[] = {
2471 { world.cmd_buf_tiles, world.tile_count },
2472 { world.cmd_buf_specials, world.tile_special_count }
2473 };
2474
2475 for( int i = 0; i < vg_list_size( render_lists ); i ++ )
2476 {
2477 struct render_list *list = &render_lists[i];
2478 for( int j = 0; j < list->count; j ++ )
2479 {
2480 struct render_cmd *cmd = &list->arr[j];
2481 struct cell *cell = cmd->ptr;
2482
2483 render_tile( cmd->pos, cell, regular_colour, selected_colour );
2484 }
2485 }
2486 }
2487
2488 static int world_button_exec( struct world_button *btn, v2f texture, v3f colour, enum world_button_status *status )
2489 {
2490 static v2i click_grab = { -9999, -9999 };
2491
2492 // Reset click_grab
2493 if( !btn )
2494 {
2495 click_grab[0] = -9999;
2496 click_grab[1] = -9999;
2497 return 0;
2498 }
2499
2500 v2i click_tile = { world.tile_x, world.tile_y };
2501
2502 int triggered = 0;
2503 int is_hovering = v2i_eq( click_tile, btn->position );
2504
2505 // Set up light targets before logic runs
2506 if( btn->state )
2507 btn->light_target = is_hovering? 0.9f: 0.8f;
2508 else
2509 btn->light_target = is_hovering? 0.2f: 0.0f;
2510
2511 if( vg_get_button( "primary" ) && is_hovering )
2512 btn->light_target = 1.0f;
2513
2514 // Process click action
2515 if( is_hovering )
2516 {
2517 if( vg_get_button_down( "primary" ) && is_hovering )
2518 v2i_copy( click_tile, click_grab );
2519 else if( v2i_eq( click_grab, click_tile ) && vg_get_button_up( "primary" ) )
2520 {
2521 // Click event
2522 *status = btn->state? k_world_button_on_disable: k_world_button_on_enable;
2523
2524 if( btn->mode == k_world_button_mode_toggle )
2525 btn->state ^= 0x1;
2526
2527 sfx_set_play( &audio_clicks, &audio_system_ui, btn->state? 1:0 );
2528 triggered = 1;
2529 }
2530 }
2531
2532 // Drawing stage
2533 v4f final_colour;
2534
2535 btn->light = vg_lerpf( btn->light, btn->light_target + btn->extra_light, vg_time_delta*26.0f );
2536
2537 v3_copy( colour, final_colour );
2538 final_colour[3] = btn->light;
2539
2540 glUniform4f( SHADER_UNIFORM( shader_buttons, "uOffset" ),
2541 btn->position[0],
2542 btn->position[1],
2543 texture[0],
2544 texture[1]
2545 );
2546 glUniform4fv( SHADER_UNIFORM( shader_buttons, "uColour" ), 1, final_colour );
2547 draw_mesh( 0, 2 );
2548
2549 return triggered;
2550 }
2551
2552 static void level_selection_buttons(void)
2553 {
2554 v3f tutorial_colour = { 0.204f, 0.345f, 0.553f };
2555 v3f locked_colour = { 0.2f, 0.2f, 0.2f };
2556
2557 struct cmp_level *switch_level_to = NULL;
2558
2559 for( int i = 0; i < vg_list_size( career_packs ); i ++ )
2560 {
2561 struct career_level_pack *grid = &career_packs[i];
2562
2563 for( int j = 0; j < grid->count; j ++ )
2564 {
2565 struct cmp_level *lvl = &grid->pack[ j ];
2566
2567 if( world.pCmpLevel == lvl )
2568 lvl->btn.extra_light = 0.35f + fabsf(sinf( vg_time * 2.0f )) * 0.05f;
2569 else lvl->btn.extra_light = 0.2f;
2570
2571 if( lvl->completed_score )
2572 lvl->btn.extra_light += 0.8f;
2573
2574 enum world_button_status status;
2575 if( world_button_exec(
2576 &lvl->btn,
2577 (v2f){0.0f,0.0f},
2578 lvl->unlocked? (lvl->is_tutorial? tutorial_colour: grid->primary_colour): locked_colour,
2579 &status
2580 ))
2581 {
2582 if( status == k_world_button_on_enable && lvl->unlocked )
2583 switch_level_to = lvl;
2584 }
2585 }
2586 }
2587
2588 if( switch_level_to )
2589 {
2590 world.st.lvl_to_load = switch_level_to;
2591 world.st.lvl_load_time = vg_time + 0.25f;
2592 world.st.world_transition = 1.0f;
2593
2594 /*
2595 if( console_changelevel( 1, &switch_level_to->map_name ) )
2596 {
2597 world.pCmpLevel = switch_level_to;
2598 gen_level_text( world.pCmpLevel );
2599 }
2600 */
2601 }
2602 }
2603
2604 static void render_sprite( enum sprites_auto_combine_index id, v3f pos )
2605 {
2606 struct vg_sprite *sp = &sprites_auto_combine[ id ];
2607
2608 glUniform4fv( SHADER_UNIFORM( shader_sprite, "uUv" ), 1, sp->uv_xywh );
2609 glUniform3f( SHADER_UNIFORM( shader_sprite, "uPos" ), pos[0], pos[1], pos[2] * world.st.world_transition );
2610
2611 draw_mesh( 0, 2 );
2612 }
2613
2614 void vg_render(void)
2615 {
2616 glViewport( 0,0, vg_window_x, vg_window_y );
2617
2618 glDisable( GL_DEPTH_TEST );
2619 glClearColor( 0.369768f, 0.3654f, 0.42f, 1.0f );
2620 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
2621
2622 v4f const colour_default = {1.0f, 1.0f, 1.0f, 1.0f};
2623 v4f const colour_selected = {0.90f, 0.92f, 1.0f, 1.0f};
2624
2625 int const circle_base = 6;
2626 int const filled_start = circle_base+0;
2627 int const filled_count = circle_base+32;
2628 int const empty_start = circle_base+32;
2629 int const empty_count = circle_base+32*2;
2630
2631 struct world_theme *theme = &world_themes[ world_theme_id ];
2632
2633 if( !world.initialzed )
2634 return;
2635
2636 // Extract render commands
2637 world.tile_count = 0;
2638 world.tile_special_count = 0;
2639
2640 for( int y = 1; y < world.h-1; y ++ )
2641 {
2642 for( int x = 1; x < world.w-1; x ++ )
2643 {
2644 struct cell *cell = pcell((v2i){x,y});
2645
2646 if( cell->state & (FLAG_CANAL|FLAG_INPUT|FLAG_OUTPUT|FLAG_EMITTER|FLAG_INPUT_NICE) )
2647 {
2648 struct render_cmd *cmd;
2649
2650 if(
2651 (cell->config == k_cell_type_split && (cell->state & FLAG_CANAL))
2652 || (cell->state & (FLAG_EMITTER|FLAG_IS_TRIGGER))
2653 )
2654 cmd = &world.cmd_buf_tiles[ world.max_commands - (++ world.tile_special_count) ];
2655 else
2656 cmd = &world.cmd_buf_tiles[ world.tile_count ++ ];
2657
2658 cmd->pos[0] = x;
2659 cmd->pos[1] = y;
2660 cmd->ptr = cell;
2661 }
2662 }
2663 }
2664
2665 world.cmd_buf_specials = &world.cmd_buf_tiles[ world.max_commands - world.tile_special_count ];
2666
2667 // BACKGROUND
2668 // ========================================================================================================
2669 use_mesh( &world.shapes );
2670
2671 SHADER_USE( shader_background );
2672 glUniformMatrix3fv( SHADER_UNIFORM( shader_background, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
2673
2674 glActiveTexture( GL_TEXTURE0 );
2675 glBindTexture( GL_TEXTURE_2D, world.background_data );
2676 glUniform1i( SHADER_UNIFORM( shader_background, "uTexMain" ), 0 );
2677
2678 glUniform3f( SHADER_UNIFORM( shader_background, "uOffset" ), -16, -16, 64 );
2679 glUniform1f( SHADER_UNIFORM( shader_background, "uVariance" ), 0.02f );
2680
2681 glActiveTexture( GL_TEXTURE1 );
2682 glBindTexture( GL_TEXTURE_2D, world.random_samples );
2683 glUniform1i( SHADER_UNIFORM( shader_background, "uSamplerNoise" ), 1 );
2684 glUniform1f( SHADER_UNIFORM( shader_background, "uVisibility" ), 1.0f ); //world.st.world_transition );
2685
2686 draw_mesh( 0, 2 );
2687
2688 // TILESET BACKGROUND LAYER
2689 // ========================================================================================================
2690 use_mesh( &world.shapes );
2691 SHADER_USE( shader_tile_main );
2692
2693 m2x2f subtransform;
2694 m2x2_identity( subtransform );
2695 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main, "uSubTransform" ), 1, GL_FALSE, (float *)subtransform );
2696 glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_main, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
2697 glUniform1f( SHADER_UNIFORM( shader_tile_main, "uGhost" ), 0.0f );
2698 glUniform1f( SHADER_UNIFORM( shader_tile_main, "uForeground" ), 0.0f );
2699 glUniform1f( SHADER_UNIFORM( shader_tile_main, "uVisibility" ), world.st.world_transition * 2.0f );
2700
2701 glEnable(GL_BLEND);
2702 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2703 glBlendEquation(GL_FUNC_ADD);
2704
2705 // rebind textures
2706 vg_tex2d_bind( &tex_tile_data, 0 );
2707 vg_tex2d_bind( theme->tex_tiles, 1 );
2708
2709 glUniform3fv( SHADER_UNIFORM( shader_tile_main, "uShadowing" ), 1, theme->col_shadow );
2710
2711 render_tiles( colour_default, colour_default );
2712
2713 // MARBLES
2714 // ========================================================================================================
2715 SHADER_USE( shader_ball );
2716 glUniformMatrix3fv( SHADER_UNIFORM( shader_ball, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
2717
2718 vg_tex2d_bind( &tex_ball_noise, 0 );
2719 glUniform1i( SHADER_UNIFORM( shader_ball, "uTexMain" ), 0 );
2720
2721 if( world.st.buttons[ k_world_button_sim ].state )
2722 {
2723 for( int i = 0; i < world.num_fishes; i ++ )
2724 {
2725 struct fish *fish = &world.fishes[i];
2726 v3f render_pos;
2727 render_pos[2] = 1.0f;
2728
2729 if( fish->state == k_fish_state_dead || fish->state == k_fish_state_soon_dead )
2730 {
2731 float death_anim_time = world.sim_internal_time - fish->death_time;
2732
2733 // Death animation
2734 if( death_anim_time > 0.0f && death_anim_time < 1.0f )
2735 {
2736 float amt = 1.0f-death_anim_time*death_anim_time;
2737
2738 v2_muladds( fish->physics_co, fish->physics_v, -1.0f * world.sim_internal_delta * amt, fish->physics_co );
2739 render_pos[2] = amt;
2740 }
2741 else if( world.sim_internal_time > fish->death_time )
2742 continue;
2743 }
2744 else if( fish->state == k_fish_state_bg )
2745 continue;
2746
2747 v2_copy( fish->physics_co, render_pos );
2748
2749 v4f dot_colour = { 0.0f, 0.0f, 0.0f, 1.0f };
2750 colour_code_v3( fish->colour, dot_colour );
2751
2752 glUniform3fv( SHADER_UNIFORM( shader_ball, "uColour" ), 1, dot_colour );
2753 glUniform3fv( SHADER_UNIFORM( shader_ball, "uOffset" ), 1, render_pos );
2754 glUniform2f( SHADER_UNIFORM( shader_ball, "uTexOffset" ), (float)i * 1.2334, (float)i * -0.3579f );
2755 draw_mesh( 0, 2 );
2756 }
2757 }
2758
2759 // TILESET FOREGROUND LAYER
2760 // ========================================================================================================
2761 SHADER_USE( shader_tile_main );
2762
2763 // Bind textures
2764 vg_tex2d_bind( &tex_tile_data, 0 );
2765 glUniform1i( SHADER_UNIFORM( shader_tile_main, "uTexGlyphs" ), 0 );
2766
2767 // TODO: is this needed to be rebinded?
2768 vg_tex2d_bind( theme->tex_tiles, 1 );
2769 glUniform1i( SHADER_UNIFORM( shader_tile_main, "uTexWood" ), 1 );
2770
2771 glUniform1f( SHADER_UNIFORM( shader_tile_main, "uForeground" ), 1.0f );
2772 render_tiles( colour_default, colour_selected );
2773
2774 // Draw splitters
2775 for( int i = 0; i < world.tile_special_count; i ++ )
2776 {
2777 struct render_cmd *cmd = &world.cmd_buf_specials[i];
2778 struct cell *cell = cmd->ptr;
2779
2780 if( cell->config == k_cell_type_split )
2781 {
2782 float rotation = cell->state & FLAG_FLIP_FLOP? vg_rad( -45.0f ): vg_rad( 45.0f );
2783
2784 if( cell->state & FLAG_FLIP_ROTATING )
2785 {
2786 if( (world.frame_lerp > curve_7_linear_section) )
2787 {
2788 float const rotation_speed = 0.4f;
2789 if( (world.frame_lerp < 1.0f-rotation_speed) )
2790 {
2791 float t = world.frame_lerp - curve_7_linear_section;
2792 t *= -2.0f * (1.0f/(1.0f-(curve_7_linear_section+rotation_speed)));
2793 t += 1.0f;
2794
2795 rotation *= t;
2796 }
2797 else
2798 rotation *= -1.0f;
2799 }
2800 }
2801
2802 m2x2_create_rotation( subtransform, rotation );
2803
2804 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main, "uSubTransform" ), 1, GL_FALSE, (float *)subtransform );
2805 glUniform4f( SHADER_UNIFORM( shader_tile_main, "uOffset" ),
2806 (float)cmd->pos[0],
2807 (float)cmd->pos[1] + 0.125f,
2808 cell->state & FLAG_TARGETED? 3.0f: 2.0f,
2809 3.0f
2810 );
2811 draw_mesh( 0, 2 );
2812 }
2813 }
2814
2815 // EDIT OVERLAY
2816 // ========================================================================================================
2817 if( world.selected != -1 && !(world.data[ world.selected ].state & FLAG_CANAL) && !world.id_drag_from )
2818 {
2819 v2i new_begin = { world.tile_x - 2, world.tile_y - 2 };
2820 v2i new_end = { world.tile_x + 2, world.tile_y + 2 };
2821
2822 world.data[ world.selected ].state ^= FLAG_CANAL;
2823 map_reclassify( new_begin, new_end, 0 );
2824
2825 m2x2_identity( subtransform );
2826 glUniform1f( SHADER_UNIFORM( shader_tile_main, "uGhost" ), 1.0f );
2827 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main, "uSubTransform" ), 1, GL_FALSE, (float *)subtransform );
2828 glUniform2fv( SHADER_UNIFORM( shader_tile_main, "uMousePos" ), 1, world.tile_pos );
2829
2830 render_tile_block( new_begin, new_end, colour_default, colour_default );
2831
2832 world.data[ world.selected ].state ^= FLAG_CANAL;
2833 map_reclassify( new_begin, new_end, 0 );
2834 }
2835
2836 // BUTTONS
2837 // ========================================================================================================
2838 SHADER_USE( shader_buttons );
2839 glUniformMatrix3fv( SHADER_UNIFORM( shader_buttons, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
2840
2841 vg_tex2d_bind( &tex_buttons, 0 );
2842 glUniform1i( SHADER_UNIFORM( shader_buttons, "uTexMain" ), 0 );
2843
2844 enum world_button_status stat;
2845 int world_paused = world.st.buttons[k_world_button_pause].state;
2846 int world_running = world.st.buttons[k_world_button_sim].state;
2847
2848 float sim_icon_x = world_paused? 3.0f: (world_running? 2.0f: 0.0f);
2849
2850 v3f btn_dark_blue = { 0.204f, 0.345f, 0.553f };
2851 v3f btn_orange = { 0.553f, 0.345f, 0.204f };
2852
2853 if( world_button_exec( &world.st.buttons[k_world_button_sim], (v2f){ sim_icon_x, 3.0f }, btn_dark_blue, &stat ))
2854 {
2855 if( stat == k_world_button_on_enable )
2856 {
2857 simulation_start();
2858
2859 if( world_paused )
2860 world.pause_offset_target = 0.5f;
2861 }
2862 else
2863 {
2864 if( world_paused )
2865 {
2866 // Trigger single step
2867 world.pause_offset_target += 1.0f;
2868 world.st.buttons[k_world_button_sim].state = 1;
2869 }
2870 else
2871 {
2872 simulation_stop();
2873 }
2874 }
2875 }
2876
2877 if( world_button_exec( &world.st.buttons[k_world_button_pause], (v2f){ 1.0f, 3.0f }, btn_dark_blue, &stat ))
2878 {
2879 world.sim_internal_ref = world.sim_internal_time;
2880 world.sim_delta_ref = vg_time;
2881
2882 if( stat == k_world_button_on_enable )
2883 {
2884 float time_frac = world.sim_internal_time-floorf(world.sim_internal_time);
2885 world.pause_offset_target = 0.5f - time_frac;
2886 }
2887 else
2888 world.pause_offset_target = 0.0f;
2889 }
2890
2891 if( world_button_exec( &world.st.buttons[k_world_button_speedy], (v2f){ 0.0f, 2.0f }, btn_orange, &stat ))
2892 {
2893 world.sim_delta_speed = stat == k_world_button_on_enable? 10.0f: 2.5f;
2894
2895 if( !world_paused )
2896 {
2897 world.sim_delta_ref = vg_time;
2898 world.sim_internal_ref = world.sim_internal_time;
2899 }
2900 }
2901
2902 if( world_button_exec( &world.st.buttons[k_world_button_settings], (v2f){ 1.0f, 2.0f }, btn_orange, &stat ))
2903 {
2904 world.st.state = stat == k_world_button_on_enable? k_game_state_settings: k_game_state_main;
2905 }
2906
2907 level_selection_buttons();
2908
2909 if( vg_get_button_up( "primary" ) )
2910 world_button_exec( NULL, NULL, NULL, NULL );
2911
2912 // I/O ARRAYS
2913 // ========================================================================================================
2914
2915 //glEnable(GL_BLEND);
2916 SHADER_USE( shader_tile_colour );
2917 glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
2918
2919 for( int i = 0; i < arrlen( world.io ); i ++ )
2920 {
2921 struct cell_terminal *term = &world.io[ i ];
2922 struct cell *cell = pcell(term->pos);
2923
2924 int is_input = cell->state & FLAG_INPUT;
2925 v4f dot_colour = { 0.0f, 0.0f, 0.0f, 1.0f };
2926
2927 if( cell->state & FLAG_EMITTER )
2928 {
2929 for( int j = 0; j < 2; j ++ )
2930 {
2931 if( cell->emit[j] != -1 )
2932 {
2933 colour_code_v3( cell->emit[j], dot_colour );
2934
2935 glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ),
2936 term->pos[0] + 0.25f + (float)j * 0.5f,
2937 term->pos[1] + 0.25f,
2938 0.12f
2939 );
2940
2941 glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour );
2942 draw_mesh( filled_start, filled_count );
2943 }
2944 }
2945 continue;
2946 }
2947
2948 for( int k = 0; k < term->run_count; k ++ )
2949 {
2950 float arr_base = is_input? 1.2f: -0.2f,
2951 run_offset = (is_input? 0.2f: -0.2f) * (float)k,
2952 y_position = is_input?
2953 (arr_base + (float)term->pos[1] + (float)(term->run_count-1)*0.2f) - run_offset:
2954 (float)term->pos[1] + arr_base + run_offset;
2955
2956 v4f bar_colour;
2957 int bar_draw = 0;
2958
2959 if( is_simulation_running() )
2960 {
2961 if( k == world.sim_run )
2962 {
2963 float a = fabsf(sinf( vg_time * 2.0f )) * 0.075f + 0.075f;
2964
2965 v4_copy( (v4f){ 1.0f, 1.0f, 1.0f, a }, bar_colour );
2966 }
2967 else
2968 v4_copy( (v4f){ 0.0f, 0.0f, 0.0f, 0.13f }, bar_colour );
2969
2970 bar_draw = 1;
2971 }
2972 else if( 1 || k & 0x1 )
2973 {
2974 if( k & 0x1 )
2975 v4_copy( (v4f){ 1.0f, 1.0f, 1.0f, 0.07f }, bar_colour );
2976 else
2977 v4_copy( (v4f){ 0.0f, 0.0f, 0.0f, 0.13f }, bar_colour );
2978
2979 bar_draw = 1;
2980 }
2981
2982 if( bar_draw )
2983 {
2984 glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, bar_colour );
2985 glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ),
2986 (float)term->pos[0], y_position - 0.1f, 1.0f );
2987
2988 draw_mesh( 2, 2 );
2989 }
2990
2991 for( int j = 0; j < term->runs[k].step_count; j ++ )
2992 {
2993 glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ),
2994 (float)term->pos[0] + 0.2f + 0.2f * (float)j,
2995 y_position,
2996 0.1f
2997 );
2998
2999 if( is_input )
3000 {
3001 i8 colour = term->runs[k].steps[j];
3002 if( colour != -1 )
3003 {
3004 colour_code_v3( colour, dot_colour );
3005 glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour );
3006
3007 // Draw filled if tick not passed, draw empty if empty
3008 if( (world.sim_frame > j && world.sim_run >= k) || world.sim_run > k )
3009 draw_mesh( empty_start, empty_count );
3010 else
3011 draw_mesh( filled_start, filled_count );
3012 }
3013 }
3014 else
3015 {
3016
3017 if( term->runs[k].recv_count > j )
3018 {
3019 colour_code_v3( term->runs[k].recieved[j], dot_colour );
3020 v3_muls( dot_colour, 0.8f, dot_colour );
3021 glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour );
3022
3023 draw_mesh( filled_start, filled_count );
3024 }
3025
3026 colour_code_v3( term->runs[k].steps[j], dot_colour );
3027 glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, dot_colour );
3028
3029 draw_mesh( empty_start, empty_count );
3030 }
3031 }
3032 }
3033 }
3034
3035 // SPRITES
3036 // ========================================================================================================
3037 SHADER_USE( shader_sprite );
3038 glUniformMatrix3fv( SHADER_UNIFORM( shader_sprite, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
3039
3040 vg_tex2d_bind( &tex_sprites, 0 );
3041 glUniform1i( SHADER_UNIFORM( shader_sprite, "uTexMain" ), 0 );
3042
3043 for( int i = 0; i < world.tile_special_count; i ++ )
3044 {
3045 struct render_cmd *cmd = &world.cmd_buf_specials[i];
3046 struct cell *cell = cmd->ptr;
3047
3048 if( (cell->config == k_cell_type_split) || (cell->state & FLAG_EMITTER) )
3049 {
3050 v2f center = { cmd->pos[0] + 0.5f, cmd->pos[1] + 0.5f };
3051
3052 v3f p0 = { 0.0f, 0.0f, 4.0f };
3053 v3f p1 = { 0.0f, 0.0f, 4.0f };
3054
3055 v2_add( center, (v2f){ -0.25f, -0.25f }, p0 );
3056 v2_add( center, (v2f){ 0.25f, -0.25f }, p1 );
3057
3058 render_sprite( k_sprite_jack_1, p0 );
3059 render_sprite( k_sprite_jack_2, p1 );
3060 }
3061 else if( cell->state & FLAG_IS_TRIGGER )
3062 {
3063 v3f p0 = { 0.0f, 0.0f, 4.0f };
3064
3065 struct cell_description *desc = &cell_descriptions[ cell->config ];
3066
3067 v2_add( (v2f){ cmd->pos[0], cmd->pos[1] }, desc->trigger_pos, p0 );
3068 render_sprite( desc->trigger_sprite, p0 );
3069 }
3070 }
3071
3072 // TEXT ELEMENTS
3073 // ========================================================================================================
3074 SHADER_USE( shader_sdf );
3075 glBindVertexArray( text_buffers.vao );
3076 glUniformMatrix3fv( SHADER_UNIFORM( shader_sdf, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
3077
3078 vg_tex2d_bind( &tex_ubuntu, 0 );
3079 glUniform1i( SHADER_UNIFORM( shader_sdf, "uTexGlyphs" ), 0 );
3080
3081 glUniform4f( SHADER_UNIFORM( shader_sdf, "uColour" ), 1.0f, 1.0f, 1.0f, 1.0f );
3082 glDrawElements( GL_TRIANGLES, text_buffers.title_count*6, GL_UNSIGNED_SHORT, (void*)( text_buffers.title_start*6*sizeof(u16) ) );
3083 glDrawElements( GL_TRIANGLES, text_buffers.desc_count*6, GL_UNSIGNED_SHORT, (void*)( text_buffers.desc_start*6*sizeof(u16) ) );
3084
3085 glUniform4f( SHADER_UNIFORM( shader_sdf, "uColour" ), 1.0f, 1.0f, 1.0f, 0.17f );
3086 glDrawElements( GL_TRIANGLES, text_buffers.grid_count*6, GL_UNSIGNED_SHORT, (void*)( text_buffers.grid_start*6*sizeof(u16) ) );
3087
3088 // Old style
3089 m3x3f mvp_text;
3090 m3x3_identity( mvp_text );
3091 m3x3_scale( mvp_text, (v3f){
3092 1.0f/ ((float)UI_GLYPH_SPACING_X*4.0f),
3093 1.0f/ -((float)UI_GLYPH_SPACING_X*4.0f),
3094 1.0f
3095 });
3096
3097 m3x3_mul( vg_pv, mvp_text, mvp_text );
3098 ui_draw( &world.st.world_text, mvp_text );
3099
3100 // WIRES
3101 // ========================================================================================================
3102 glEnable(GL_BLEND);
3103
3104 SHADER_USE( shader_wire );
3105 glBindVertexArray( world.wire.vao );
3106
3107 glUniformMatrix3fv( SHADER_UNIFORM( shader_wire, "uPv" ), 1, GL_FALSE, (float *)vg_pv );
3108
3109 v4f const wire_left_colour = { 0.5f, 0.5f, 0.5f, 1.0f };
3110 v4f const wire_right_colour = { 0.2f, 0.2f, 0.2f, 1.0f };
3111 v4f const wire_drag_colour = { 0.2f, 0.2f, 0.2f, 0.6f };
3112
3113 glUniform1f( SHADER_UNIFORM( shader_wire, "uTime" ), world.frame_lerp );
3114 glUniform1f( SHADER_UNIFORM( shader_wire, "uGlow" ), 0.0f );
3115
3116 if( world.id_drag_from )
3117 {
3118 glUniform4fv( SHADER_UNIFORM( shader_wire, "uColour" ), 1, wire_drag_colour );
3119 glUniform1f( SHADER_UNIFORM( shader_wire, "uCurve" ), 0.4f );
3120 glUniform3f( SHADER_UNIFORM( shader_wire, "uStart" ), world.drag_from_co[0], world.drag_from_co[1], 0.20f*world.st.world_transition );
3121 glUniform3f( SHADER_UNIFORM( shader_wire, "uEnd" ), world.drag_to_co[0], world.drag_to_co[1], 0.20f*world.st.world_transition );
3122 glDrawElements( GL_TRIANGLES, world.wire.em, GL_UNSIGNED_SHORT, (void*)(0) );
3123 }
3124
3125 // Pulling animation
3126 float rp_x1 = world.frame_lerp*9.0f;
3127 float rp_xa = rp_x1*expf(1.0f-rp_x1)* 0.36f;
3128 float rp_x2 = 1.0f-rp_xa;
3129
3130 for( int i = 0; i < world.tile_special_count; i ++ )
3131 {
3132 struct render_cmd *cmd = &world.cmd_buf_specials[i];
3133 struct cell *cell = cmd->ptr;
3134
3135 if( cell->state & FLAG_TARGETED )
3136 {
3137 for( int j = 0; j < 2; j ++ )
3138 {
3139 if( !cell->links[j] )
3140 continue;
3141
3142 struct cell *other_cell = &world.data[ cell->links[ j ]];
3143 struct cell_description *desc = &cell_descriptions[ other_cell->config ];
3144
3145 int x2 = cell->links[j] % world.w;
3146 int y2 = (cell->links[j] - x2) / world.w;
3147
3148 v2f startpoint;
3149 v2f endpoint;
3150
3151 endpoint[0] = (float)cmd->pos[0] + (j? 0.75f: 0.25f);
3152 endpoint[1] = (float)cmd->pos[1] + 0.25f;
3153
3154 startpoint[0] = x2;
3155 startpoint[1] = y2;
3156
3157 v2_add( desc->trigger_pos, startpoint, startpoint );
3158
3159 if( cmd->ptr->state & FLAG_EMITTER )
3160 {
3161 v4f wire_colour;
3162 colour_code_v3( cmd->ptr->emit[j], wire_colour );
3163 wire_colour[3] = 1.0f;
3164
3165 glUniform4fv( SHADER_UNIFORM( shader_wire, "uColour" ), 1, wire_colour );
3166 }
3167 else
3168 glUniform4fv( SHADER_UNIFORM( shader_wire, "uColour" ), 1, j? wire_right_colour: wire_left_colour );
3169
3170 glUniform1f( SHADER_UNIFORM( shader_wire, "uCurve" ), other_cell->state & FLAG_TRIGGERED? rp_x2 * 0.4f: 0.4f );
3171 glUniform1f( SHADER_UNIFORM( shader_wire, "uGlow" ), other_cell->state & FLAG_TRIGGERED? rp_xa: 0.0f );
3172 glUniform3f( SHADER_UNIFORM( shader_wire, "uEnd" ), startpoint[0], startpoint[1], 0.18f*world.st.world_transition );
3173 glUniform3f( SHADER_UNIFORM( shader_wire, "uStart" ), endpoint[0], endpoint[1], 0.18f*world.st.world_transition );
3174 glDrawElements( GL_TRIANGLES, world.wire.em, GL_UNSIGNED_SHORT, (void*)(0) );
3175 }
3176 }
3177 }
3178
3179 // WIRE ENDPOINTS
3180 // ========================================================================================================
3181
3182 SHADER_USE( shader_tile_colour );
3183 use_mesh( &world.shapes );
3184
3185 for( int i = 0; i < world.tile_special_count; i ++ )
3186 {
3187 struct render_cmd *cmd = &world.cmd_buf_specials[i];
3188 struct cell *cell = cmd->ptr;
3189
3190 if( cell->state & FLAG_TARGETED )
3191 {
3192 for( int j = 0; j < 2; j ++ )
3193 {
3194 if( !cell->links[j] )
3195 continue;
3196
3197 struct cell *other_cell = &world.data[ cell->links[ j ]];
3198 struct cell_description *desc = &cell_descriptions[ other_cell->config ];
3199
3200 int x2 = cell->links[j] % world.w;
3201 int y2 = (cell->links[j] - x2) / world.w;
3202
3203 v2f pts[2];
3204
3205 pts[0][0] = (float)cmd->pos[0] + (j? 0.75f: 0.25f);
3206 pts[0][1] = (float)cmd->pos[1] + 0.25f;
3207
3208 pts[1][0] = x2;
3209 pts[1][1] = y2;
3210
3211 v2_add( desc->trigger_pos, pts[1], pts[1] );
3212
3213 if( cell->state & FLAG_EMITTER )
3214 {
3215 v4f wire_colour;
3216 colour_code_v3( cell->emit[j], wire_colour );
3217
3218 v3_muls( wire_colour, 0.8f, wire_colour );
3219 wire_colour[3] = 1.0f;
3220
3221 glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1, wire_colour );
3222 }
3223 else
3224 glUniform4fv( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 1,j?wire_right_colour: wire_left_colour );
3225
3226 for( int i = 0; i < 2; i ++ )
3227 {
3228 glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ),
3229 pts[i][0],
3230 pts[i][1],
3231 0.08f * world.st.world_transition
3232 );
3233 draw_mesh( filled_start, filled_count );
3234 }
3235 }
3236 }
3237 }
3238
3239 // SUB SPLITTER DIRECTION
3240 // ========================================================================================================
3241
3242 /*
3243 glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.9f, 0.35f, 0.1f, 0.75f );
3244
3245 for( int i = 0; i < world.tile_special_count; i ++ )
3246 {
3247 struct render_cmd *cmd = &world.cmd_buf_specials[i];
3248 struct cell *cell = cmd->ptr;
3249
3250 if( cell->state & FLAG_TARGETED && cell->config == k_cell_type_split )
3251 {
3252 glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), cmd->pos[0], cmd->pos[1], 1.0f );
3253 draw_mesh( cell->state & FLAG_FLIP_FLOP? 5: 4, 1 );
3254 }
3255 }
3256 */
3257
3258 // LIGHT FLARES
3259 // ========================================================================================================
3260 glBlendFunc(GL_ONE, GL_ONE);
3261 glBlendEquation(GL_FUNC_ADD);
3262
3263 SHADER_USE( shader_sprite );
3264
3265 vg_tex2d_bind( &tex_sprites, 0 );
3266 glUniform1i( SHADER_UNIFORM( shader_sprite, "uTexMain" ), 0 );
3267
3268 for( int i = 0; i < world.tile_special_count; i ++ )
3269 {
3270 struct render_cmd *cmd = &world.cmd_buf_specials[i];
3271 struct cell *cell = cmd->ptr;
3272
3273 if( cell->config == k_cell_type_split )
3274 {
3275 v2f center = { cmd->pos[0] + 0.5f, cmd->pos[1] + 0.5f };
3276
3277 v3f p0 = { 0.0f, 0.0f, 12.0f };
3278 v3f p1 = { 0.0f, 0.0f, 12.0f };
3279
3280 v2_add( center, (v2f){ -0.25f, -0.25f }, p0 );
3281 v2_add( center, (v2f){ 0.25f, -0.25f }, p1 );
3282
3283 if( cell->state & FLAG_FLIP_FLOP )
3284 render_sprite( k_sprite_flare_y, p1 );
3285 else
3286 render_sprite( k_sprite_flare_b, p0 );
3287 }
3288 }
3289
3290 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3291 glBlendEquation(GL_FUNC_ADD);
3292
3293
3294 glDisable(GL_BLEND);
3295
3296 // Draw score
3297 /*
3298 float const score_bright = 1.25f;
3299 glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ),
3300 0.4f*score_bright, 0.39f*score_bright, 0.45f*score_bright, 1.0f );
3301
3302 use_mesh( &world.numbers );
3303 draw_numbers( (v3f){ 2.0f, (float)world.h-1.875f, 0.3333f }, world.score );
3304 */
3305 }
3306
3307 void vg_ui(void)
3308 {
3309 // Drawing world name
3310 gui_text( (ui_px [2]){ vg_window_x / 2, 4 }, "THIS IS A WORLD NAME", 2, k_text_align_center );
3311 gui_text( (ui_px [2]){ vg_window_x / 2, 28 }, "And here is its cool description yo", 1, k_text_align_center );
3312
3313 if( world.st.state == k_game_state_settings )
3314 {
3315 gui_group_id( 35 );
3316
3317 ui_global_ctx.cursor[2] = 225;
3318 gui_fill_y();
3319 gui_align_right();
3320
3321 gui_new_node();
3322 {
3323 gui_capture_mouse( 200 );
3324
3325 gui_fill_rect( ui_global_ctx.cursor, 0xC0202020 );
3326 ui_rect_pad( ui_global_ctx.cursor, 8 );
3327
3328 ui_global_ctx.cursor[3] = 25;
3329
3330 gui_new_node();
3331 {
3332 gui_text( ui_global_ctx.cursor, "SETTINGS", 2, 0 );
3333 }
3334 gui_end();
3335
3336 // Colour scheme selection
3337 ui_global_ctx.cursor[1] += 30;
3338
3339 gui_text( ui_global_ctx.cursor, "Colour Scheme", 1, 0 );
3340 ui_global_ctx.cursor[1] += 25;
3341
3342 gui_new_node();
3343 {
3344 ui_global_ctx.cursor[2] = 50;
3345
3346 for( int i = 0; i < 4; i ++ )
3347 {
3348 gui_new_node();
3349 {
3350 // Convert to RGB
3351 u32 rgb = 0xff000000;
3352
3353 for( int j = 0; j < 3; j ++ )
3354 rgb |= (u32)(colour_sets[ colour_set_id ][i][j]*255.0f) << j * 8;
3355
3356 gui_fill_rect( ui_global_ctx.cursor, rgb );
3357 }
3358 gui_end_right();
3359 }
3360 }
3361 gui_end_down();
3362
3363 gui_new_node();
3364 {
3365 ui_global_ctx.cursor[2] = 25;
3366 if( gui_button( 0 ) == k_button_click )
3367 {
3368 if( colour_set_id > 0 )
3369 colour_set_id --;
3370 }
3371 gui_text( ui_global_ctx.cursor, "<", 1, 0 );
3372 gui_end_right();
3373
3374 ui_global_ctx.cursor[2] = 150;
3375 gui_new_node();
3376 {
3377 gui_fill_rect( ui_global_ctx.cursor, 0x33ffffff );
3378 gui_text(
3379 (ui_px [2]){ ui_global_ctx.cursor[0] + 75, ui_global_ctx.cursor[1] + 6 },
3380 (const char *[]){ "Normal", "Extra1", "Extra2" }[ colour_set_id ],
3381 1, k_text_align_center
3382 );
3383 }
3384 gui_end_right();
3385
3386 ui_global_ctx.cursor[2] = 25;
3387 if( gui_button( 1 ) == k_button_click )
3388 {
3389 if( colour_set_id < vg_list_size( colour_sets )-1 )
3390 colour_set_id ++;
3391 }
3392 gui_text( ui_global_ctx.cursor, ">", 1, 0 );
3393 gui_end_down();
3394 }
3395 gui_end_down();
3396
3397 // Theme select
3398 // TODO: remove code dupe
3399 ui_global_ctx.cursor[1] += 16;
3400
3401 gui_text( ui_global_ctx.cursor, "Tile Theme", 1, 0 );
3402 ui_global_ctx.cursor[1] += 20;
3403
3404 gui_new_node();
3405 {
3406 ui_global_ctx.cursor[2] = 25;
3407 if( gui_button( 0 ) == k_button_click )
3408 {
3409 if( world_theme_id > 0 )
3410 world_theme_id --;
3411 }
3412 gui_text( ui_global_ctx.cursor, "<", 1, 0 );
3413 gui_end_right();
3414
3415 ui_global_ctx.cursor[2] = 150;
3416 gui_new_node();
3417 {
3418 gui_fill_rect( ui_global_ctx.cursor, 0x33ffffff );
3419 gui_text(
3420 (ui_px [2]){ ui_global_ctx.cursor[0] + 75, ui_global_ctx.cursor[1] + 6 },
3421 world_themes[ world_theme_id ].name, 1, k_text_align_center
3422 );
3423 }
3424 gui_end_right();
3425
3426 ui_global_ctx.cursor[2] = 25;
3427 if( gui_button( 1 ) == k_button_click )
3428 {
3429 if( world_theme_id < vg_list_size( world_themes )-1 )
3430 world_theme_id ++;
3431 }
3432 gui_text( ui_global_ctx.cursor, ">", 1, 0 );
3433 gui_end_down();
3434 }
3435 gui_end_down();
3436 }
3437 gui_end();
3438 }
3439 }
3440
3441 #if STEAM_LEADERBOARDS
3442 void leaderboard_dispatch_score(void)
3443 {
3444
3445 sw_upload_leaderboard_score(
3446 ui_data.upload_request.level->steam_leaderboard,
3447 k_ELeaderboardUploadScoreMethodKeepBest,
3448 ui_data.upload_request.score,
3449 NULL,
3450 0
3451 );
3452
3453 ui_data.upload_request.is_waiting = 0;
3454
3455 vg_success( "Dispatched leaderboard score\n" );
3456 }
3457
3458 void leaderboard_found( LeaderboardFindResult_t *pCallback )
3459 {
3460 if( !pCallback->m_bLeaderboardFound )
3461 {
3462 vg_error( "Leaderboard could not be found\n" );
3463 ui_data.steam_leaderboard = 0;
3464 }
3465 else
3466 {
3467 const char *recieved_name = sw_get_leaderboard_name( pCallback->m_hSteamLeaderboard );
3468
3469 // Update UI state and request entries if this callback found the current UI level
3470 if( ui_data.level_selected )
3471 {
3472 if( !strcmp( recieved_name, ui_data.level_selected->map_name ) )
3473 {
3474 sw_download_leaderboard_entries( pCallback->m_hSteamLeaderboard, k_ELeaderboardDataRequestFriends, 0, 8 );
3475 ui_data.level_selected->steam_leaderboard = pCallback->m_hSteamLeaderboard;
3476 }
3477 }
3478
3479 // Dispatch the waiting request if there was one
3480 if( ui_data.upload_request.is_waiting )
3481 {
3482 if( !strcmp( recieved_name, ui_data.upload_request.level->map_name ) )
3483 {
3484 ui_data.upload_request.level->steam_leaderboard = pCallback->m_hSteamLeaderboard;
3485 leaderboard_dispatch_score();
3486 }
3487 }
3488 }
3489 }
3490
3491 void leaderboard_downloaded( LeaderboardScoresDownloaded_t *pCallback )
3492 {
3493 // Update UI if this leaderboard matches what we currently have in view
3494 if( ui_data.level_selected->steam_leaderboard == pCallback->m_hSteamLeaderboard )
3495 {
3496 vg_info( "Recieved %d entries\n", pCallback->m_cEntryCount );
3497 ui_data.leaderboard_count = VG_MIN( pCallback->m_cEntryCount, 8 );
3498
3499 u64_steamid local_player = sw_get_steamid();
3500
3501 for( int i = 0; i < ui_data.leaderboard_count; i ++ )
3502 {
3503 LeaderboardEntry_t entry;
3504 sw_get_downloaded_entry( pCallback->m_hSteamLeaderboardEntries, i, &entry, NULL, 0 );
3505
3506 struct leaderboard_player *player = &ui_data.leaderboard_players[i];
3507
3508 player->id = entry.m_steamIDUser.m_unAll64Bits;
3509 strncpy( player->player_name, sw_get_friend_persona_name( player->id ), vg_list_size( player->player_name )-1 );
3510 player->score = entry.m_nScore;
3511
3512 snprintf( player->score_text, vg_list_size(player->score_text), "%d", player->score );
3513 player->texture = sw_get_player_image( player->id );
3514
3515 if( player->texture == 0 )
3516 player->texture = tex_unkown.name;
3517
3518 player->is_local_player = local_player == player->id? 1: 0;
3519 }
3520
3521 if( ui_data.leaderboard_count )
3522 ui_data.leaderboard_show = 1;
3523 else
3524 ui_data.leaderboard_show = 0;
3525 }
3526 else vg_warn( "Downloaded leaderboard does not match requested!\n" );
3527 }
3528
3529 void leaderboard_set_score( struct cmp_level *cmp_level, u32 score )
3530 {
3531 if( ui_data.upload_request.is_waiting )
3532 vg_warn( "You are uploading leaderboard entries too quickly!\n" );
3533
3534 ui_data.upload_request.level = cmp_level;
3535 ui_data.upload_request.score = score;
3536 ui_data.upload_request.is_waiting = 1;
3537
3538 // If leaderboard ID has been downloaded already then just immediately dispatch this
3539 if( cmp_level->steam_leaderboard )
3540 leaderboard_dispatch_score();
3541 else
3542 sw_find_leaderboard( cmp_level->map_name );
3543 }
3544 #endif
3545
3546 // CONSOLE COMMANDS
3547 // ===========================================================================================================
3548
3549 static int console_credits( int argc, char const *argv[] )
3550 {
3551 vg_info( "Aknowledgements:\n" );
3552 vg_info( " GLFW zlib/libpng glfw.org\n" );
3553 vg_info( " miniaudio MIT0 miniaud.io\n" );
3554 vg_info( " QOI MIT phoboslab.org\n" );
3555 vg_info( " STB library MIT nothings.org\n" );
3556 vg_info( " Ubuntu Regular ubuntu.com\n" );
3557 return 0;
3558 }
3559
3560 static int console_save_map( int argc, char const *argv[] )
3561 {
3562 if( !world.initialzed )
3563 {
3564 vg_error( "Tried to save uninitialized map!\n" );
3565 return 0;
3566 }
3567
3568 char map_path[ 256 ];
3569
3570 strcpy( map_path, "sav/" );
3571 strcat( map_path, world.map_name );
3572 strcat( map_path, ".map" );
3573
3574 FILE *test_writer = fopen( map_path, "wb" );
3575 if( test_writer )
3576 {
3577 vg_info( "Saving map to '%s'\n", map_path );
3578 map_serialize( test_writer );
3579
3580 fclose( test_writer );
3581 return 1;
3582 }
3583 else
3584 {
3585 vg_error( "Unable to open stream for writing\n" );
3586 return 0;
3587 }
3588 }
3589
3590 static int console_load_map( int argc, char const *argv[] )
3591 {
3592 char map_path[ 256 ];
3593
3594 if( argc >= 1 )
3595 {
3596 // try from saves
3597 strcpy( map_path, "sav/" );
3598 strcat( map_path, argv[0] );
3599 strcat( map_path, ".map" );
3600
3601 char *text_source = vg_textasset_read( map_path );
3602
3603 if( !text_source )
3604 {
3605 strcpy( map_path, "maps/" );
3606 strcat( map_path, argv[0] );
3607 strcat( map_path, ".map" );
3608
3609 text_source = vg_textasset_read( map_path );
3610 }
3611
3612 if( text_source )
3613 {
3614 vg_info( "Loading map: '%s'\n", map_path );
3615 world.pCmpLevel = NULL;
3616
3617 if( !map_load( text_source, argv[0] ) )
3618 {
3619 free( text_source );
3620 return 0;
3621 }
3622
3623 free( text_source );
3624 return 1;
3625 }
3626 else
3627 {
3628 vg_error( "Missing maps '%s'\n", argv[0] );
3629 return 0;
3630 }
3631 }
3632 else
3633 {
3634 vg_error( "Missing argument <map_path>\n" );
3635 return 0;
3636 }
3637 }
3638
3639 static int console_changelevel( int argc, char const *argv[] )
3640 {
3641 if( argc >= 1 )
3642 {
3643 // Save current level
3644 console_save_map( 0, NULL );
3645
3646 if( console_load_map( argc, argv ) )
3647 {
3648 world.st.zoom = 0.0f;
3649 simulation_stop();
3650 return 1;
3651 }
3652 }
3653 else
3654 {
3655 vg_error( "Missing argument <map_path>\n" );
3656 }
3657
3658 return 0;
3659 }
3660
3661 // START UP / SHUTDOWN
3662 // ===========================================================================================================
3663
3664 #define TRANSFORM_TRI_2D( S, OX, OY, X1, Y1, X2, Y2, X3, Y3 ) \
3665 X1*S+OX, Y1*S+OY, X2*S+OX, Y2*S+OY, X3*S+OX, Y3*S+OY
3666
3667 void vg_start(void)
3668 {
3669 // Steamworks callbacks
3670 #ifdef STEAM_LEADERBOARDS
3671 sw_leaderboard_found = &leaderboard_found;
3672 sw_leaderboard_downloaded = &leaderboard_downloaded;
3673 #endif
3674
3675 vg_function_push( (struct vg_cmd){
3676 .name = "_map_write",
3677 .function = console_save_map
3678 });
3679
3680 vg_function_push( (struct vg_cmd){
3681 .name = "_map_load",
3682 .function = console_load_map
3683 });
3684
3685 vg_function_push( (struct vg_cmd){
3686 .name = "map",
3687 .function = console_changelevel
3688 });
3689
3690 vg_function_push( (struct vg_cmd){
3691 .name = "credits",
3692 .function = console_credits
3693 });
3694
3695 vg_convar_push( (struct vg_convar){
3696 .name = "colours",
3697 .data = &colour_set_id,
3698 .data_type = k_convar_dtype_i32,
3699 .opt_i32 = { .min = 0, .max = 2, .clamp = 1 },
3700 .persistent = 1
3701 });
3702
3703 vg_convar_push( (struct vg_convar){
3704 .name = "theme",
3705 .data = &world_theme_id,
3706 .data_type = k_convar_dtype_i32,
3707 .opt_i32 = { .min = 0, .max = vg_list_size( world_themes )-1, .clamp = 1 },
3708 .persistent = 1,
3709 });
3710
3711 // Combined quad, long quad / empty circle / filled circle mesh
3712 {
3713 float combined_mesh[6*6 + 32*6*3] = {
3714 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
3715 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f,
3716
3717 0.0f, 0.0f, 0.0f, 0.2f, 1.0f, 0.2f,
3718 0.0f, 0.0f, 1.0f, 0.2f, 1.0f, 0.0f,
3719
3720 TRANSFORM_TRI_2D( 0.15f,0.05f,0.4f, 0.0f, 1.0f, 1.0f, 2.0f, 1.0f, 0.0f ),
3721 TRANSFORM_TRI_2D( 0.15f,0.80f,0.4f, 0.0f, 0.0f, 0.0f, 2.0f, 1.0f, 1.0f )
3722 };
3723
3724 float *circle_mesh = combined_mesh + 6*6;
3725 int const res = 32;
3726
3727 for( int i = 0; i < res; i ++ )
3728 {
3729 v2f v0 = { sinf( ((float)i/(float)res)*VG_TAUf ), cosf( ((float)i/(float)res)*VG_TAUf ) };
3730 v2f v1 = { sinf( ((float)(i+1)/(float)res)*VG_TAUf ), cosf( ((float)(i+1)/(float)res)*VG_TAUf ) };
3731
3732 circle_mesh[ i*6+0 ] = 0.0f;
3733 circle_mesh[ i*6+1 ] = 0.0f;
3734
3735 v2_copy( v0, circle_mesh + 32*6 + i*12 );
3736 v2_muls( v0, 0.8f, circle_mesh + 32*6 + i*12+2 );
3737 v2_copy( v1, circle_mesh + 32*6 + i*12+4 );
3738
3739 v2_copy( v1, circle_mesh + 32*6 + i*12+6 );
3740 v2_muls( v1, 0.8f, circle_mesh + 32*6 + i*12+8 );
3741 v2_muls( v0, 0.8f, circle_mesh + 32*6 + i*12+10 );
3742
3743 v2_copy( v0, circle_mesh + i*6+4 );
3744 v2_copy( v1, circle_mesh + i*6+2 );
3745 v2_copy( v0, circle_mesh+i*6+4 );
3746 v2_copy( v1, circle_mesh+i*6+2 );
3747 }
3748
3749 init_mesh( &world.shapes, combined_mesh, vg_list_size( combined_mesh ) );
3750 }
3751
3752 // Create wire mesh
3753 {
3754 int const num_segments = 64;
3755
3756 struct mesh_wire *mw = &world.wire;
3757
3758 v2f wire_points[ num_segments * 2 ];
3759 u16 wire_indices[ 6*(num_segments-1) ];
3760
3761 for( int i = 0; i < num_segments; i ++ )
3762 {
3763 float l = (float)i / (float)(num_segments-1);
3764
3765 v2_copy( (v2f){ l, -0.5f }, wire_points[i*2+0] );
3766 v2_copy( (v2f){ l, 0.5f }, wire_points[i*2+1] );
3767
3768 if( i < num_segments-1 )
3769 {
3770 wire_indices[ i*6+0 ] = i*2 + 0;
3771 wire_indices[ i*6+1 ] = i*2 + 1;
3772 wire_indices[ i*6+2 ] = i*2 + 3;
3773 wire_indices[ i*6+3 ] = i*2 + 0;
3774 wire_indices[ i*6+4 ] = i*2 + 3;
3775 wire_indices[ i*6+5 ] = i*2 + 2;
3776 }
3777 }
3778
3779 glGenVertexArrays( 1, &mw->vao );
3780 glGenBuffers( 1, &mw->vbo );
3781 glGenBuffers( 1, &mw->ebo );
3782 glBindVertexArray( mw->vao );
3783
3784 glBindBuffer( GL_ARRAY_BUFFER, mw->vbo );
3785
3786 glBufferData( GL_ARRAY_BUFFER, sizeof( wire_points ), wire_points, GL_STATIC_DRAW );
3787 glBindVertexArray( mw->vao );
3788
3789 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mw->ebo );
3790 glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( wire_indices ), wire_indices, GL_STATIC_DRAW );
3791
3792 // XY
3793 glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), (void*)0 );
3794 glEnableVertexAttribArray( 0 );
3795
3796 VG_CHECK_GL();
3797
3798 mw->em = vg_list_size( wire_indices );
3799 }
3800
3801 // Create info data texture
3802 {
3803 glGenTextures( 1, &world.background_data );
3804 glBindTexture( GL_TEXTURE_2D, world.background_data );
3805 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
3806 vg_tex2d_nearest();
3807 }
3808
3809 // Create random smaples texture
3810 {
3811 u8 *data = malloc(512*512*2);
3812 for( int i = 0; i < 512*512*2; i ++ )
3813 data[ i ] = rand()/(RAND_MAX/255);
3814
3815 glGenTextures( 1, &world.random_samples );
3816 glBindTexture( GL_TEXTURE_2D, world.random_samples );
3817 glTexImage2D( GL_TEXTURE_2D, 0, GL_RG, 512, 512, 0, GL_RG, GL_UNSIGNED_BYTE, data );
3818 vg_tex2d_linear();
3819 vg_tex2d_repeat();
3820
3821 free( data );
3822 }
3823
3824 resource_load_main();
3825
3826 // Init world text
3827 {
3828 ui_init_context( &world.st.world_text, 15000 );
3829 }
3830
3831 // Create text buffers
3832 {
3833 // Work out the counts for each 'segment'
3834 u32 desc_max_size = 0, title_max_size = 0,
3835 score_max_size = 10,
3836 time_max_size = 10,
3837
3838 size_level_texts = 6*9*7
3839 ;
3840
3841 for( int i = 0; i < vg_list_size( career_packs ); i ++ )
3842 {
3843 struct career_level_pack *set = &career_packs[i];
3844 for( int j = 0; j < set->count; j ++ )
3845 {
3846 struct cmp_level *lvl = &set->pack[j];
3847
3848 desc_max_size = VG_MAX( desc_max_size, strlen( lvl->description ) );
3849 title_max_size = VG_MAX( title_max_size, strlen( lvl->title ) );
3850 }
3851 }
3852
3853 // Full buffer
3854 u32 total_characters =
3855 title_max_size +
3856 desc_max_size +
3857 score_max_size +
3858 time_max_size +
3859 size_level_texts;
3860
3861 u32 total_faces = total_characters * 2,
3862 total_vertices = total_characters * 4,
3863 total_indices = total_faces * 3;
3864
3865 // Working buffer
3866 u32 work_buffer_total_chars =
3867 VG_MAX( 7, VG_MAX( VG_MAX( desc_max_size, title_max_size ), VG_MAX( score_max_size, time_max_size ) ) );
3868
3869 u32 total_work_faces = work_buffer_total_chars * 2,
3870 total_work_vertices = work_buffer_total_chars * 4,
3871 total_work_indices = total_work_faces * 3;
3872
3873 text_buffers.title_count = 0;
3874 text_buffers.desc_count = 0;
3875 text_buffers.score_count = 0;
3876 text_buffers.time_count = 0;
3877 text_buffers.grid_count = size_level_texts;
3878
3879 // Calculate offsets
3880 text_buffers.title_start = 0;
3881 text_buffers.desc_start = text_buffers.title_start + title_max_size;
3882 text_buffers.score_start = text_buffers.desc_start + desc_max_size;
3883 text_buffers.time_start = text_buffers.score_start + score_max_size;
3884 text_buffers.grid_start = text_buffers.time_start + time_max_size;
3885
3886 // Opengl
3887 glGenVertexArrays(1, &text_buffers.vao);
3888 glGenBuffers( 1, &text_buffers.vbo );
3889 glGenBuffers( 1, &text_buffers.ebo );
3890 glBindVertexArray( text_buffers.vao );
3891
3892 glBindBuffer( GL_ARRAY_BUFFER, text_buffers.vbo );
3893 glBufferData( GL_ARRAY_BUFFER, total_vertices * sizeof( struct vector_glyph_vert ), NULL, GL_DYNAMIC_DRAW );
3894
3895 glBindVertexArray( text_buffers.vao );
3896
3897 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, text_buffers.ebo );
3898 glBufferData( GL_ELEMENT_ARRAY_BUFFER, total_indices * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
3899
3900 u32 const stride = sizeof( struct vector_glyph_vert );
3901
3902 // XY
3903 glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, stride, (void *)offsetof( struct vector_glyph_vert, co ) );
3904 glEnableVertexAttribArray( 0 );
3905
3906 // UV
3907 glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, stride, (void *)offsetof( struct vector_glyph_vert, uv ) );
3908 glEnableVertexAttribArray( 1 );
3909
3910 // COLOUR
3911 glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void *)offsetof( struct vector_glyph_vert, colour ) );
3912 glEnableVertexAttribArray( 2 );
3913
3914 // Offline memory
3915 text_buffers.buffer = (struct vector_glyph_vert *)malloc( total_work_vertices * sizeof(struct vector_glyph_vert) );
3916 text_buffers.indices = (u16*)malloc( total_work_indices * sizeof(u16) );
3917
3918 char label[8];
3919 for( int i = 1; i < 7; i ++ )
3920 label[i] = ' ';
3921 label[7] = 0x00;
3922
3923 // Reset grid
3924 for( int x = 0; x < 6; x ++ )
3925 {
3926 for( int y = 0; y < 9; y ++ )
3927 {
3928 label[0] = ' ';
3929
3930 if( x == 0 )
3931 {
3932 if( y != 8 )
3933 label[0] = 'A' + y;
3934 }
3935 else if( y == 8 )
3936 {
3937 label[0] = '0' + x;
3938 }
3939
3940 gen_text_buffer( label, &font_Ubuntu, (v2f){ -6.0f + x + (x == 0? 0.6f: 0.2f), y + 0.2f }, 0.35f, text_buffers.grid_start+(y*6+x)*7 );
3941 }
3942 }
3943 }
3944
3945 // Restore gamestate
3946 career_local_data_init();
3947 career_load();
3948 }
3949
3950 void vg_free(void)
3951 {
3952 #ifdef VG_STEAM
3953 sw_free_opengl();
3954 #endif
3955
3956 console_save_map( 0, NULL );
3957 career_serialize();
3958
3959 glDeleteVertexArrays( 1, &text_buffers.vao );
3960 glDeleteBuffers( 1, &text_buffers.vbo );
3961 glDeleteBuffers( 1, &text_buffers.ebo );
3962
3963 free( text_buffers.buffer );
3964 free( text_buffers.indices );
3965
3966 resource_free_main();
3967
3968 glDeleteTextures( 1, &world.background_data );
3969 glDeleteTextures( 1, &world.random_samples );
3970
3971 glDeleteVertexArrays( 1, &world.wire.vao );
3972 glDeleteBuffers( 1, &world.wire.vbo );
3973 glDeleteBuffers( 1, &world.wire.ebo );
3974
3975 free_mesh( &world.shapes );
3976
3977 ui_context_free( &world.st.world_text );
3978
3979 map_free();
3980 }
3981
3982 int main( int argc, char *argv[] )
3983 {
3984 vg_init( argc, argv, "Marble Computing" );
3985 return 0;
3986 }