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