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