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