1 #define MARBLE_COMP_VERSION 4
5 k_world_button_mode_once
,
6 k_world_button_mode_toggle
13 float light_target
, light
, extra_light
;
16 enum world_button_mode mode
;
19 enum world_button_status
21 k_world_button_on_enable
,
22 k_world_button_on_disable
25 #include "fishladder_resources_vg1.h"
28 #define STEAM_LEADERBOARDS
34 k_cell_type_ramp_right
= 3,
35 k_cell_type_ramp_left
= 6,
36 k_cell_type_split
= 7,
37 k_cell_type_merge
= 13,
38 k_cell_type_con_r
= 1,
39 k_cell_type_con_u
= 2,
40 k_cell_type_con_l
= 4,
46 k_fish_state_soon_dead
= -1,
47 k_fish_state_dead
= 0,
50 k_fish_state_soon_alive
55 k_world_button_none
= -1,
56 k_world_button_sim
= 0,
57 k_world_button_pause
= 1,
58 k_world_button_speedy
= 2,
59 k_world_button_settings
= 3
65 k_game_state_settings
,
69 #define FLAG_CANAL 0x1
70 #define FLAG_IS_TRIGGER 0x2
71 #define FLAG_RESERVED0 0x4
72 #define FLAG_RESERVED1 0x8
74 #define FLAG_4B_GROUP (FLAG_CANAL|FLAG_IS_TRIGGER|FLAG_RESERVED0|FLAG_RESERVED1)
76 #define FLAG_INPUT 0x10
77 #define FLAG_OUTPUT 0x20
78 #define FLAG_WALL 0x40
79 #define FLAG_EMITTER 0x80
81 #define FLAG_FLIP_FLOP 0x100
82 #define FLAG_TRIGGERED 0x200
83 #define FLAG_FLIP_ROTATING 0x400
84 #define FLAG_TARGETED 0x800
86 #define FLAG_INPUT_NICE 0x1000
89 0000 0 | 0001 1 | 0010 2 | 0011 3
93 0100 4 | 0101 5 | 0110 6 | 0111 7
97 1000 8 | 1001 9 | 1010 10 | 1011 11
101 1100 12 | 1101 13 | 1110 14 | 1111 15
107 static struct cell_description
116 enum sprites_auto_combine_index trigger_sprite
;
118 cell_descriptions
[] =
121 { .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
},
122 { .start
= { 1, 0 }, .end
= { -1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
},
123 .trigger_sprite
= k_sprite_brk_d
},
124 { .start
= { 0, 1 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
},
125 .trigger_sprite
= k_sprite_brk_l
},
126 { .start
= { 0, 1 }, .end
= { 1, 0 }, .trigger_pos
= { 0.25f
, 0.5f
},
127 .trigger_sprite
= k_sprite_brk_l
},
129 { .start
= { -1, 0 }, .end
= { 1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
},
130 .trigger_sprite
= k_sprite_brk_d
},
131 { .start
= { -1, 0 }, .end
= { 1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
},
132 .trigger_sprite
= k_sprite_brk_d
, .is_linear
= 1 },
133 { .start
= { 0, 1 }, .end
= { -1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
},
134 .trigger_sprite
= k_sprite_brk_d
},
135 { .start
= { 0, 1 }, .is_special
= 1 },
137 { .start
= { 0, -1 }, .end
= { 0, 1 }, .trigger_pos
= { 0.25f
, 0.5f
},
138 .trigger_sprite
= k_sprite_brk_l
},
139 { .start
= { 1, 0 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
},
140 .trigger_sprite
= k_sprite_brk_l
},
141 { .start
= { 0, 1 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
},
142 .trigger_sprite
= k_sprite_brk_l
, .is_linear
= 1 },
145 { .start
= { -1, 0 }, .end
= { 0, -1 }, .trigger_pos
= { 0.5f
, 0.75f
},
146 .trigger_sprite
= k_sprite_brk_u
},
147 { .end
= { 0, -1 }, .is_special
= 1, .trigger_pos
= { 0.5f
, 0.75f
},
148 .trigger_sprite
= k_sprite_brk_u
},
153 v2f
const curve_3
[] = {{0.5f
,1.0f
},{0.5f
,0.625f
},{0.625f
,0.5f
},{1.0f
,0.5f
}};
154 v2f
const curve_6
[] = {{0.5f
,1.0f
},{0.5f
,0.625f
},{0.375f
,0.5f
},{0.0f
,0.5f
}};
155 v2f
const curve_9
[] = {{1.0f
,0.5f
},{0.625f
,0.5f
},{0.5f
,0.375f
},{0.5f
,0.0f
}};
156 v2f
const curve_12
[]= {{0.0f
,0.5f
},{0.375f
,0.5f
},{0.5f
,0.375f
},{0.5f
,0.0f
}};
158 v2f
const curve_1
[] = {{1.0f
,0.5f
},{0.8f
,0.5f
},{0.3f
,0.5f
},{0.2f
,0.5f
}};
159 v2f
const curve_4
[] = {{0.0f
,0.5f
},{0.3f
,0.5f
},{0.5f
,0.5f
},{0.8f
,0.5f
}};
160 v2f
const curve_2
[] = {{0.5f
,1.0f
},{0.5f
,0.8f
},{0.5f
,0.3f
},{0.5f
,0.2f
}};
161 v2f
const curve_8
[] = {{0.5f
,0.0f
},{0.5f
,0.3f
},{0.5f
,0.5f
},{0.5f
,0.8f
}};
163 v2f
const curve_7
[] =
164 {{0.5f
,0.8438f
},{0.875f
,0.8438f
},{0.625f
,0.5f
},{1.0f
,0.5f
}};
165 v2f
const curve_7_1
[] =
166 {{0.5f
,0.8438f
},{1.0f
-0.875f
,0.8438f
},{1.0-0.625f
,0.5f
},{0.0f
,0.5f
}};
168 float const curve_7_linear_section
= 0.1562f
;
176 #define EFFECT_BUFFER_RATIO 4
180 /* Things that are 'static', aka, initialized once */
183 struct world_button buttons
[4];
185 enum e_game_state state
;
187 struct cmp_level
*lvl_to_load
;
190 float world_transition
;
195 bloomframebuffer
[2], /* Quater res */
196 bloomcolourbuffer
[2];
216 *cmd_buf_tiles
, *cmd_buf_specials
;
218 u32 tile_count
, tile_special_count
, max_commands
;
221 int sim_run
, max_runs
;
223 int sim_frame
, sim_target
;
224 float sim_internal_time
,
239 int step_count
, recv_count
;
253 GLuint vao
, vbo
, ebo
;
258 GLuint background_data
;
259 GLuint random_samples
;
261 int selected
, tile_x
, tile_y
;
268 enum e_fish_state state
;
280 struct cmp_level
*pCmpLevel
;
294 .buttons
= { { .mode
= k_world_button_mode_toggle
},
295 { .mode
= k_world_button_mode_toggle
},
296 { .mode
= k_world_button_mode_toggle
},
297 { .mode
= k_world_button_mode_toggle
} }
302 static void colour_code_v3( i8 cc
, v3f target
);
303 static int hash21i( v2i p
, u32 umod
);
308 static void init_mesh( struct mesh
*m
, float const *tris
, u32 length
);
309 static void free_mesh( struct mesh
*m
);
310 static void use_mesh( struct mesh
*m
);
311 static void draw_mesh( int const start
, int const count
);
316 static void level_selection_buttons(void);
319 * Map/world interface
321 static void map_free(void);
322 static void io_reset(void);
323 static struct cell
*pcell( v2i pos
);
324 static void lcell( int id
, v2i pos
);
325 static void map_reclassify( v2i start
, v2i end
, int update_texbuffer
);
326 static void gen_level_text(void);
327 static int map_load( const char *str
, const char *name
);
328 static void map_serialize( FILE *stream
);
333 static void career_serialize(void);
334 static void career_unlock_level( struct cmp_level
*lvl
);
335 static void career_unlock_level( struct cmp_level
*lvl
);
336 static void career_pass_level( struct cmp_level
*lvl
, int score
, int upload
);
337 static void career_reset_level( struct cmp_level
*lvl
);
338 static void career_load(void);
339 static void clear_animation_flags(void);
344 static void simulation_stop(void);
345 static void simulation_start(void);
346 static int world_check_pos_ok( v2i co
, int dist
);
347 static int cell_interactive( v2i co
);
349 static void render_tiles( v4f
const regular_colour
,
350 v4f
const selected_colour
, int with_glow
);
351 static void render_tile_block( v2i start
, v2i end
, v4f
const regular_colour
,
352 v4f
const selected_colour
);
354 #ifdef STEAM_LEADERBOARDS
355 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
356 void leaderboard_dispatch_score(void);
357 void leaderboard_found( LeaderboardFindResult_t
*pCallback
);
358 void leaderboard_downloaded( LeaderboardScoresDownloaded_t
*pCallback
);
359 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
362 static int console_credits( int argc
, char const *argv
[] );
363 static int console_save_map( int argc
, char const *argv
[] );
364 static int console_load_map( int argc
, char const *argv
[] );
365 static int console_changelevel( int argc
, char const *argv
[] );
367 void vg_render(void);
369 void vg_update(void);
372 int main( int argc
, char *argv
[] );
381 static int colour_set_id
= 0;
382 static int world_theme_id
= 0;
383 static int enable_bloom
= 1;
384 static int enable_vignette
= 1;
385 static float music_volume
= 1.0f
;
387 static void music_volume_update(void)
389 sfx_vol_fset( &audio_volume_music
, music_volume
);
392 static v3f colour_sets
[][4] =
394 { { 1.0f
, 0.9f
, 0.3f
},
395 { 0.4f
, 0.8f
, 1.00f
},
396 { 0.2f
, 0.9f
, 0.14f
},
397 { 0.882f
, 0.204f
, 0.922f
}
399 { { 1.0f
, 0.9f
, 0.3f
},
400 { 0.4f
, 0.8f
, 1.00f
},
401 { 0.85f
, 0.85f
, 0.85f
},
404 { { 1.0f
, 0.9f
, 0.3f
},
405 { 0.827f
, 0.373f
, 0.718f
},
406 { 0.0f
, 0.353f
, 0.71f
},
407 { 0.863f
, 0.196f
, 0.125f
}
411 static struct world_theme
422 { 0.6f
, 0.6f
, 0.6f
},
427 { 0.89f
, 0.8f
, 0.7f
},
432 { 0.7f
, 0.7f
, 0.7f
},
437 static void colour_code_v3( i8 cc
, v3f target
)
439 if( (cc
>= 0) && (cc
< vg_list_size( colour_sets
[0] )) )
441 v3_copy( colour_sets
[colour_set_id
][ cc
], target
);
445 vg_error( "Invalid colour code used '%d'\n", (int)cc
);
446 v3_copy( (v3f
){0.0f
,0.0f
,0.0f
}, target
);
449 static int hash21i( v2i p
, u32 umod
)
451 static const int random_noise
[] = {
452 0x46,0xD5,0xB8,0xD3,0xF2,0xE5,0xCC,0x07,0xD0,0xB3,0x7A,0xA2,0xC3,0xDA,0xDC,0x7F,
453 0xE0,0xB7,0x42,0xA0,0xBF,0x41,0x92,0x32,0x6F,0x0D,0x45,0xC7,0x54,0xDB,0x30,0xC2,
454 0xD5,0xDA,0x55,0x09,0xDE,0x74,0x48,0x20,0xE1,0x24,0x5C,0x4D,0x6F,0x36,0xD8,0xE9,
455 0x8D,0x8F,0x54,0x99,0x98,0x51,0xFE,0xDB,0x26,0x04,0x65,0x57,0x56,0xF3,0x53,0x30,
456 0x3D,0x16,0xC0,0xB6,0xF2,0x47,0xCF,0x62,0xB0,0x6C,0x8F,0x4F,0x8C,0x4C,0x17,0xF0,
457 0x19,0x7E,0x2D,0x81,0x8D,0xFB,0x10,0xD3,0x49,0x50,0x60,0xFD,0x38,0x15,0x3B,0xEE,
458 0x05,0xC1,0xCF,0x62,0x97,0x75,0xDF,0x4E,0x4D,0x89,0x5E,0x88,0x5C,0x30,0x8C,0x54,
459 0x1D,0x39,0x41,0xEA,0xA2,0x63,0x12,0x1B,0x8E,0x35,0x22,0x9B,0x98,0xA3,0x7F,0x80,
460 0xD6,0x27,0x94,0x66,0xB5,0x1D,0x7E,0xDF,0x96,0x28,0x38,0x3A,0xA0,0xE8,0x71,0x09,
461 0x62,0x5E,0x9D,0x53,0x58,0x1B,0x7D,0x0D,0x2D,0x99,0x77,0x83,0xC3,0x89,0xC2,0xA2,
462 0xA7,0x1D,0x78,0x80,0x37,0xC1,0x87,0xFF,0x65,0xBF,0x2C,0xF1,0xE5,0xB3,0x09,0xE0,
463 0x25,0x92,0x83,0x0F,0x8A,0x57,0x3C,0x0B,0xC6,0xBC,0x44,0x16,0xE3,0xCE,0xC3,0x0D,
464 0x69,0xD3,0xC6,0x99,0xB8,0x46,0x44,0xC4,0xF3,0x1E,0xBF,0xF5,0xB4,0xDB,0xFB,0x93,
465 0xA1,0x7B,0xC9,0x08,0x77,0x22,0xE5,0x02,0xEF,0x9E,0x90,0x94,0x8A,0xA6,0x3D,0x7E,
466 0xA2,0xA0,0x10,0x82,0x47,0x5C,0xAA,0xF8,0x2F,0x0D,0x9F,0x76,0xDA,0x99,0x0F,0xCB,
467 0xE2,0x02,0x0C,0x75,0xCA,0x35,0x29,0xA6,0x49,0x83,0x6D,0x91,0xB4,0xEC,0x31,0x69,
468 0xBA,0x13,0xF3,0xC7,0x21,0x06,0xC8,0x79,0xEF,0xB1,0x9C,0x6A,0xEE,0x64,0x9A,0xDC,
469 0x1E,0xC6,0x18,0x93,0xA9,0x7E,0x89,0x7D,0x96,0xE5,0x44,0xB8,0x00,0x15,0xAF,0x8C,
470 0x78,0x8F,0xA8,0x05,0xA7,0x07,0x25,0x9A,0xC8,0x5D,0x90,0x1A,0x41,0x53,0x30,0xD3,
471 0x24,0x33,0x71,0xB4,0x50,0x6E,0xE4,0xEA,0x0D,0x2B,0x6D,0xF5,0x17,0x08,0x74,0x49,
472 0x71,0xC2,0xAC,0xF7,0xDC,0xB2,0x7E,0xCC,0xB6,0x1B,0xB8,0xA9,0x52,0xCF,0x6B,0x51,
473 0xD2,0x4E,0xC9,0x43,0xEE,0x2E,0x92,0x24,0xBB,0x47,0x4D,0x0C,0x3E,0x21,0x53,0x19,
474 0xD4,0x82,0xE2,0xC6,0x93,0x85,0x0A,0xF8,0xFA,0x04,0x07,0xD3,0x1D,0xEC,0x03,0x66,
475 0xFD,0xB1,0xFB,0x8F,0xC5,0xDE,0xE8,0x29,0xDF,0x23,0x09,0x9D,0x7C,0x43,0x3D,0x4D,
476 0x89,0xB9,0x6F,0xB4,0x6B,0x4A,0x51,0xC3,0x94,0xF4,0x7C,0x5E,0x19,0x87,0x79,0xC1,
477 0x80,0x0C,0x45,0x12,0xEC,0x95,0xF3,0x31,0x68,0x42,0xE1,0x06,0x57,0x0E,0xA7,0xFB,
478 0x78,0x96,0x87,0x23,0xA5,0x20,0x7A,0x09,0x3A,0x45,0xE6,0xD9,0x5E,0x6A,0xD6,0xAA,
479 0x29,0x50,0x92,0x4E,0xD0,0xB5,0x91,0xC2,0x9A,0xCF,0x07,0xFE,0xB2,0x15,0xEB,0xE4,
480 0x84,0x40,0x14,0x47,0xFA,0x93,0xB9,0x06,0x69,0xDB,0xBD,0x4E,0xEA,0x52,0x9B,0xDE,
481 0x5B,0x50,0x36,0xAB,0xB3,0x1F,0xD2,0xCD,0x9C,0x13,0x07,0x7E,0x8B,0xED,0x72,0x62,
482 0x74,0x77,0x3B,0x88,0xAC,0x5B,0x6A,0xBC,0xDA,0x99,0xE8,0x24,0x90,0x5A,0xCA,0x8D,
483 0x5C,0x2B,0xF8,0xF1,0xE1,0x1D,0x94,0x11,0xEA,0xCC,0x02,0x09,0x1E,0xA2,0x48,0x67,
484 0x87,0x5A,0x7E,0xC6,0xCC,0xA3,0xFB,0xC5,0x36,0xEB,0x5C,0xE1,0xAF,0x1E,0xBE,0xE7,
485 0xD8,0x8F,0x70,0xAE,0x42,0x05,0xF5,0xCD,0x2D,0xA2,0xB0,0xFD,0xEF,0x65,0x2C,0x22,
486 0xCB,0x8C,0x8B,0xAA,0x3D,0x86,0xE2,0xCD,0xBE,0xC3,0x42,0x38,0xE3,0x9C,0x08,0xB5,
487 0xAE,0xBD,0x54,0x73,0x83,0x70,0x24,0x47,0xCA,0x4C,0x04,0xC4,0xE0,0x1D,0x40,0xED,
488 0xF4,0x2B,0x50,0x8E,0x97,0xB3,0xF0,0xA6,0x76,0xDB,0x49,0x30,0xE5,0xD9,0x71,0x07,
489 0xB2,0xF1,0x0F,0xD6,0x77,0xAA,0x72,0xC0,0xAF,0x66,0xD8,0x40,0xC6,0x08,0x19,0x8C,
490 0xD9,0x8F,0x5A,0x75,0xAC,0xBE,0xC2,0x40,0x5B,0xBD,0x0D,0x1D,0x00,0xAF,0x26,0x5E,
491 0x78,0x43,0xAA,0xC6,0x4F,0xF3,0xD8,0xE2,0x7F,0x0C,0x1E,0x77,0x4D,0x35,0x96,0x23,
492 0x32,0x44,0x03,0x8D,0x92,0xE7,0xFD,0x48,0x07,0xD0,0x58,0xFC,0x6D,0xC9,0x91,0x33,
493 0xF0,0x23,0x45,0xA4,0x29,0xB9,0xF5,0xB0,0x68,0x8F,0x7B,0x59,0x15,0x8E,0xA6,0x66,
494 0x15,0xA0,0x76,0x9B,0x69,0xCB,0x38,0xA5,0xF4,0xB4,0x6B,0xDC,0x1F,0xAB,0xAE,0x12,
495 0x77,0xC0,0x8C,0x4A,0x03,0xB9,0x73,0xD3,0x6D,0x52,0xC5,0xF5,0x6E,0x4E,0x4B,0xA3,
496 0x24,0x02,0x58,0xEE,0x5F,0xF9,0xD6,0xD0,0x1D,0xBC,0xF4,0xB8,0x4F,0xFD,0x4B,0x2D,
497 0x34,0x77,0x46,0xE5,0xD4,0x33,0x7B,0x9C,0x35,0xCD,0xB0,0x5D,0x06,0x39,0x99,0xEB,
498 0x0C,0xD0,0x0F,0xF7,0x92,0xB5,0x58,0x5B,0x5E,0x79,0x12,0xF4,0x05,0xF6,0x21,0x07,
499 0x0B,0x49,0x1A,0xFB,0xD4,0x98,0xC4,0xEF,0x7A,0xD6,0xCA,0xA1,0xDA,0xB3,0x51,0x00,
500 0x76,0xEC,0x08,0x48,0x40,0x35,0xD7,0x94,0xBE,0xF5,0x7B,0xA4,0x20,0x81,0x5F,0x82,
501 0xF3,0x6F,0x96,0x24,0x98,0xB6,0x49,0x18,0xC8,0xC5,0x8C,0xD2,0x38,0x7F,0xC4,0xF6,
502 0xAA,0x87,0xDC,0x73,0x5B,0xA1,0xAF,0xE5,0x3D,0x37,0x6B,0x85,0xED,0x38,0x62,0x7D,
503 0x57,0xBD,0xCF,0xB5,0x1B,0xA8,0xBB,0x32,0x33,0xD3,0x34,0x5A,0xC1,0x5D,0xFB,0x28,
504 0x6E,0xE1,0x67,0x51,0xBB,0x31,0x92,0x83,0xAC,0xAA,0x72,0x52,0xFD,0x13,0x4F,0x73,
505 0xD3,0xF0,0x5E,0xFC,0xBA,0xB1,0x3C,0x7B,0x08,0x76,0x03,0x38,0x1E,0xD1,0xCC,0x33,
506 0xA3,0x1E,0xFC,0xE0,0x82,0x30,0x27,0x93,0x71,0x35,0x75,0x77,0xBA,0x78,0x10,0x33,
507 0xCD,0xAB,0xCF,0x8E,0xAD,0xF9,0x32,0xC9,0x15,0x9F,0xD6,0x6D,0xA8,0xAE,0xB1,0x3F,
508 0x90,0xEB,0xD4,0xF9,0x31,0x81,0xA3,0x53,0x99,0x4B,0x3C,0x93,0x3B,0xFE,0x55,0xFF,
509 0x25,0x9F,0xCC,0x07,0xC5,0x2C,0x14,0xA7,0xA4,0x1E,0x6C,0xB6,0x91,0x2A,0xE0,0x3E,
510 0x7F,0x39,0x0A,0xD9,0x24,0x3C,0x01,0xA0,0x30,0x99,0x8E,0xB8,0x1D,0xF9,0xA7,0x78,
511 0x86,0x95,0x35,0x0E,0x21,0xDA,0x7A,0x7B,0xAD,0x9F,0x4E,0xF6,0x63,0x5B,0x96,0xBB,
512 0x87,0x36,0x3F,0xA7,0x1A,0x66,0x91,0xCD,0xB0,0x3B,0xC0,0x4F,0x54,0xD2,0x5F,0xBB,
513 0x38,0x89,0x1C,0x79,0x7E,0xA2,0x02,0xE4,0x80,0x84,0x1E,0x33,0xAB,0x74,0xFA,0xBE,
514 0x31,0x46,0x2E,0xC5,0x15,0xB9,0x12,0xE9,0xD3,0x73,0x43,0xEA,0x74,0x11,0xA7,0xC0,
515 0xD5,0xD8,0x39,0x08,0x9F,0x4F,0xC7,0x71,0x25,0x09,0x51,0x65,0xD6,0xA8,0x02,0x1F
518 return random_noise
[ (random_noise
[p
[1] & 1023] + p
[0]) & 1023 ] & umod
;
521 static void init_mesh( struct mesh
*m
, float const *tris
, u32 length
)
523 m
->elements
= length
/3;
524 glGenVertexArrays( 1, &m
->vao
);
525 glGenBuffers( 1, &m
->vbo
);
527 glBindVertexArray( m
->vao
);
528 glBindBuffer( GL_ARRAY_BUFFER
, m
->vbo
);
529 glBufferData( GL_ARRAY_BUFFER
, length
*sizeof(float), tris
, GL_STATIC_DRAW
);
531 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
, 2*sizeof(float), (void*)0 );
532 glEnableVertexAttribArray( 0 );
537 static void free_mesh( struct mesh
*m
)
539 glDeleteVertexArrays( 1, &m
->vao
);
540 glDeleteBuffers( 1, &m
->vbo
);
543 static void draw_mesh( int const start
, int const count
)
545 glDrawArrays( GL_TRIANGLES
, start
*3, count
*3 );
548 static void use_mesh( struct mesh
*m
)
550 glBindVertexArray( m
->vao
);
553 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
555 static void map_free(void)
557 arrfree( world
.data
);
560 free( world
.cmd_buf_tiles
);
561 world
.cmd_buf_tiles
= NULL
;
571 world
.initialzed
= 0;
574 static void io_reset(void)
576 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
578 struct cell_terminal
*term
= &world
.io
[i
];
580 for( int j
= 0; j
< term
->run_count
; j
++ )
581 term
->runs
[j
].recv_count
= 0;
585 static struct cell
*pcell( v2i pos
)
587 return &world
.data
[ pos
[1]*world
.w
+ pos
[0] ];
590 static void lcell( int id
, v2i pos
)
592 pos
[0] = id
% world
.w
;
593 pos
[1] = (id
- pos
[0]) / world
.w
;
596 static void map_reclassify( v2i start
, v2i end
, int update_texbuffer
)
598 v2i full_start
= { 1,1 };
599 v2i full_end
= { world
.w
-1, world
.h
-1 };
608 u8 info_buffer
[64*64*4];
611 int px0
= vg_max( start
[0], full_start
[0] ),
612 px1
= vg_min( end
[0], full_end
[0] ),
613 py0
= vg_max( start
[1], full_start
[1] ),
614 py1
= vg_min( end
[1], full_end
[1] );
616 for( int y
= py0
; y
< py1
; y
++ )
618 for( int x
= px0
; x
< px1
; x
++ )
620 struct cell
*cell
= pcell((v2i
){x
,y
});
622 v2i dirs
[] = {{1,0},{0,1},{-1,0},{0,-1}};
627 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
629 for( int i
= 0; i
< vg_list_size( dirs
); i
++ )
631 struct cell
*neighbour
=
632 pcell((v2i
){ x
+dirs
[i
][0], y
+dirs
[i
][1] });
634 if( neighbour
->state
&
635 (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
645 if( cell
->state
& FLAG_WALL
)
646 height
= 0xFF; /*-0x3F + hash21i( (v2i){x,y}, 0x3F );*/
648 config
= cell
->state
& FLAG_INPUT_NICE
? 0xB: 0xF;
651 pcell((v2i
){x
,y
})->config
= config
;
653 u8
*info_px
= &info_buffer
[ (pixel_id
++)*4 ];
655 info_px
[1] = cell
->state
& FLAG_WALL
? 0: 255;
660 * Detecting hanging links that should be removed
662 int is_trigger
= cell
->state
& FLAG_IS_TRIGGER
;
663 int trigger_invalid
= cell
->config
== 0xF ||
664 cell
->config
== k_cell_type_split
;
665 int is_targeted
= cell
->state
& FLAG_TARGETED
;
666 int target_invalid
= (cell
->config
!= k_cell_type_split
) &&
667 !(cell
->state
& FLAG_EMITTER
);
670 (is_trigger
&& trigger_invalid
) ||
671 (is_targeted
&& target_invalid
)
672 ) && update_texbuffer
)
674 cell
->state
&= ~(FLAG_TARGETED
|FLAG_IS_TRIGGER
);
675 for( u32 i
= 0; i
< 2; i
++ )
679 struct cell
*other_ptr
= &world
.data
[ cell
->links
[i
] ];
680 other_ptr
->links
[ i
] = 0;
681 other_ptr
->state
&= ~FLAG_IS_TRIGGER
;
683 if( other_ptr
->links
[ i
^ 0x1 ] == 0 )
684 other_ptr
->state
&= ~FLAG_TARGETED
;
694 if( update_texbuffer
)
696 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
697 glTexSubImage2D( GL_TEXTURE_2D
, 0,
699 px1
-px0
, py1
-py0
, GL_RGBA
, GL_UNSIGNED_BYTE
, info_buffer
);
703 static void gen_level_text(void)
705 ui_px
const unit_scale_px
= 4*UI_GLYPH_SPACING_X
;
706 ui_begin( &world
.st
.world_text
, world
.w
*unit_scale_px
,
707 world
.h
*unit_scale_px
);
709 if( world
.pCmpLevel
)
711 for( int i
= 0; i
< vg_list_size( world
.pCmpLevel
->strings
); i
++ )
713 struct world_string
*wstr
= &world
.pCmpLevel
->strings
[i
];
719 pos
[0] = -UI_GLYPH_SPACING_X
/2;
721 if( wstr
->placement
== k_placement_bottom
)
722 pos
[1] = 2*-unit_scale_px
;
724 pos
[1] = (world
.h
-1)*-unit_scale_px
-6;
726 ui_text( &world
.st
.world_text
, pos
, wstr
->str
,
727 1, k_text_align_left
);
732 // re-create level scores
733 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
735 struct career_level_pack
*set
= &career_packs
[i
];
738 ui_text( &world.st.world_text,
740 set->origin[0]*unit_scale_px,
741 -(set->origin[1]+set->dims[1]+1)*unit_scale_px + 18
743 set->title, 1, k_text_align_left );
746 for( int j
= 0; j
< set
->count
; j
++ )
748 struct cmp_level
*lvl
= &set
->pack
[j
];
750 if( lvl
->completed_score
&& !lvl
->is_tutorial
)
753 snprintf( num
, 9, "%d", lvl
->completed_score
);
755 ui_text( &world
.st
.world_text
,
757 lvl
->btn
.position
[0]*unit_scale_px
+ unit_scale_px
/2,
758 -lvl
->btn
.position
[1]*unit_scale_px
- unit_scale_px
/2
760 num
, 1, k_text_align_center
);
765 ui_resolve( &world
.st
.world_text
);
768 /* Usually for ignoring windows crap */
769 static int map_load_char_ignore( char c
)
771 if( c
== '\r' ) return 1;
775 static int map_load_sequence_char( struct terminal_run
*run
, char c
)
777 if( (c
>= 'a' && c
<= 'z') || c
== ' ' )
783 run
->steps
[ run
->step_count
++ ] = code
;
791 static int map_load_is_digit( char c
)
793 if( (((u32
)c
>= (u32
)'0') && ((u32
)c
<= (u32
)'9')) || c
== '-' )
801 static int map_load_is_terminal( char c
)
803 if( c
== '+' || c
== '-' || c
== '*' )
808 static int map_load_apply_emitter_codes( struct cell_terminal
*term
)
810 struct cell
*cell
= pcell( term
->pos
);
812 if( cell
->state
& FLAG_EMITTER
)
814 if( (term
->run_count
> 0) && (term
->runs
[0].step_count
>= 2) )
816 cell
->emit
[0] = term
->runs
[0].steps
[0];
817 cell
->emit
[1] = term
->runs
[0].steps
[1];
821 vg_error( "Emitter was not assigned emit values\n" );
829 static void map_load_cell( struct cell
*cell
, char c
, int cx
)
836 v3_zero( cell
->glow
[0] );
837 v3_zero( cell
->glow
[1] );
839 /* Input, output, emitter */
840 if( map_load_is_terminal(c
) )
842 struct cell_terminal
*term
= arraddnptr( world
.io
, 1 );
844 term
->pos
[1] = world
.h
;
847 term
->runs
[0].step_count
= 0;
851 case '+': cell
->state
= FLAG_INPUT
; break;
852 case '-': cell
->state
= FLAG_OUTPUT
; break;
853 case '*': cell
->state
= FLAG_EMITTER
; break;
856 else if( c
== '.' ) cell
->state
= FLAG_INPUT_NICE
;
857 else if( c
== '#' ) cell
->state
= FLAG_WALL
;
858 else if( ((u32
)c
>= (u32
)'A') && ((u32
)c
<= (u32
)'A'+0xf) )
861 * Canal flag bits (4bit/16 value):
867 cell
->state
= ((u32
)c
- (u32
)'A') & (FLAG_CANAL
|FLAG_IS_TRIGGER
);
870 else cell
->state
= 0x00;
873 static void map_load_draw_background(void)
875 u8 info_buffer
[64*64*4];
877 for( int x
= 0; x
< 64; x
++ )
879 for( int y
= 0; y
< 64; y
++ )
881 u8
*px
= &info_buffer
[((x
*64)+y
)*4];
884 /* Fade out edges of world so that there isnt an obvious line */
885 int dx
= 16 - VG_MIN( VG_MIN( x
, 16 ), 16-VG_MAX( x
-16-world
.w
, 0 ) ),
886 dy
= 16 - VG_MIN( VG_MIN( y
, 16 ), 16-VG_MAX( y
-16-world
.h
, 0 ) ),
888 dist
= VG_MAX( dx
, dy
) * 16,
889 value
= VG_MAX( 0, 0xFF-0x3F + hash21i( (v2i
){x
,y
}, 0x3F ) - dist
);
900 * Level selection indentation, to make it look like the buttons are in a
901 * recessed area of the map.
903 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
905 struct career_level_pack
*grid
= &career_packs
[ i
];
909 for( int y
= 0; y
< grid
->dims
[1]; y
++ )
911 for( int x
= 0; x
< grid
->dims
[0]; x
++ )
913 int cy
= y
+16+grid
->origin
[1],
914 cx
= 16+x
+grid
->origin
[0];
916 u8
*px
= &info_buffer
[(cy
*64+cx
)*4];
919 if( j
< grid
->count
)
921 struct cmp_level
*lvl
= &grid
->pack
[ j
++ ];
922 v2i_add( grid
->origin
, (v2i
){x
,y
}, lvl
->btn
.position
);
929 * Recess the UI buttons, this adds a little bit of a (subtle) outline
930 * to them when the background shader is run
932 for( int i
=0; i
<4; i
++ )
933 info_buffer
[(((16+world
.h
-(i
+2))*64)+world
.w
+16-1)*4] = 0x30;
936 * Digging 'wires' from the output/input terminals, to make it look like
937 * there is something connecting our world.
939 for( int i
= 0; i
< arrlen(world
.io
); i
++ )
941 struct cell_terminal
*term
= &world
.io
[ i
];
948 * Only make breakouts for terminals on the edge,
949 * starting them from every position leads to some weird overlapping
950 * and confusing lines.
952 if( !(term
->pos
[1] == 1 || term
->pos
[1] == world
.h
-2) )
955 turtle
[0] = 16+term
->pos
[0];
956 turtle
[1] = 16+term
->pos
[1];
960 pcell(term
->pos
)->state
& (FLAG_INPUT
|FLAG_EMITTER
)? 1: -1;
961 original_y
= turtle_dir
[1];
963 info_buffer
[((turtle
[1]*64)+turtle
[0])*4] = 0;
964 v2i_add( turtle_dir
, turtle
, turtle
);
966 for( int i
= 0; i
< 100; i
++ )
968 info_buffer
[((turtle
[1]*64)+turtle
[0])*4] = 0;
970 v2i_add( turtle_dir
, turtle
, turtle
);
972 if( turtle
[0] == 0 ) break;
973 if( turtle
[0] == 63 ) break;
974 if( turtle
[1] == 0 ) break;
975 if( turtle
[1] == 63 ) break;
977 int random_value
= hash21i( turtle
, 0xFF );
978 if( random_value
> 255-40 && !turtle_dir
[0] )
983 else if( random_value
> 255-80 && !turtle_dir
[0] )
988 else if( random_value
> 255-100 )
991 turtle_dir
[1] = original_y
;
996 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
997 glTexSubImage2D( GL_TEXTURE_2D
, 0, 0, 0, 64, 64,
998 GL_RGBA
, GL_UNSIGNED_BYTE
, info_buffer
);
1001 static int map_load_validate(void)
1003 for( int i
= 0; i
< world
.h
*world
.w
; i
++ )
1005 struct cell
*src
= &world
.data
[i
];
1007 if( src
->state
& FLAG_IS_TRIGGER
)
1009 int link_id
= src
->links
[0]?0:1;
1010 if( src
->links
[link_id
] <= world
.h
*world
.w
)
1012 struct cell
*target
= &world
.data
[ src
->links
[link_id
] ];
1014 int is_canal
= target
->state
& FLAG_CANAL
,
1015 is_splitter
= target
->config
== k_cell_type_split
,
1016 is_emitter
= target
->state
& FLAG_EMITTER
;
1018 if((is_canal
&& is_splitter
) || is_emitter
)
1020 if( target
->links
[ link_id
] )
1022 vg_error( "Link target was already targeted\n" );
1028 * Valid link, apply reverse mapping to other cell so it
1029 * knows who is linking to it
1031 target
->links
[ link_id
] = i
;
1032 target
->state
|= FLAG_TARGETED
;
1037 vg_error( "Link target was invalid\n" );
1043 vg_error( "Link target out of bounds\n" );
1052 static int map_load( const char *str
, const char *name
)
1056 char const *c
= str
;
1058 /* Predetermine width */
1061 if( c
[world
.w
] == ';' )
1063 else if( !c
[world
.w
] )
1065 vg_error( "Unexpected EOF when parsing level\n" );
1070 struct cell
*row
= arraddnptr( world
.data
, world
.w
);
1072 int reg_start
= 0, reg_end
= 0;
1074 u32
*links_to_make
= NULL
;
1075 int links_satisfied
= 0;
1077 char link_id_buffer
[32];
1085 if( map_load_char_ignore( *c
) ) { c
++; continue; }
1091 if( *c
== '\r' ) c
++;
1098 if( map_load_char_ignore( *c
) ) { c
++; continue; }
1100 if( reg_start
< reg_end
)
1102 struct cell_terminal
*terminal
= &world
.io
[ reg_start
];
1103 struct terminal_run
*run
=
1104 &terminal
->runs
[ terminal
->run_count
-1 ];
1106 if( !map_load_sequence_char( run
, *c
) )
1108 /* Control characters */
1109 if( *c
== ',' || *c
== '\n' ) /* Next terminal */
1116 else if( *c
== ':' ) /* New run starting */
1118 terminal
->runs
[ terminal
->run_count
].step_count
= 0;
1119 terminal
->run_count
++;
1121 vg_max( world
.max_runs
, terminal
->run_count
);
1125 vg_error( "Unkown attribute '%c' (row: %u)\n",
1133 if( links_satisfied
< arrlen( links_to_make
) )
1135 struct cell
*target
=
1136 &world
.data
[ links_to_make
[ links_satisfied
] ];
1138 if( map_load_is_digit( *c
) )
1140 if( link_id_n
>= vg_list_size( link_id_buffer
)-1 )
1142 vg_error( "Number was way too long to be parsed"
1143 " (row: %u)\n", world
.h
);
1147 link_id_buffer
[ link_id_n
++ ] = *c
;
1149 else if( *c
== ',' || *c
== '\n' )
1151 link_id_buffer
[ link_id_n
] = 0x00;
1152 int value
= atoi( link_id_buffer
);
1154 target
->links
[value
>= 0? 1:0] = abs(value
);
1163 vg_error( "Invalid character '%c'"
1164 " (row: %u)\n", *c
, world
.h
);
1170 vg_error( "Too many values to assign"
1171 " (row: %u)\n", world
.h
);
1180 /* Registry length-error checks */
1181 if( reg_start
!= reg_end
)
1183 vg_error( "Not enough spawn values assigned (row: %u, %u of %u)\n",
1184 world
.h
, reg_start
, reg_end
);
1188 if( links_satisfied
!= arrlen( links_to_make
) )
1190 vg_error( "Not enough link values assigned (row: %u, %u of %u)\n",
1191 world
.h
, links_satisfied
, arrlen( links_to_make
) );
1197 vg_error( "Not enough cells to match previous row definition"
1198 " (row: %u, %u<%u)\n", world
.h
, cx
, world
.w
);
1202 row
= arraddnptr( world
.data
, world
.w
);
1205 reg_end
= reg_start
= arrlen( world
.io
);
1207 arrsetlen( links_to_make
, 0 );
1208 links_satisfied
= 0;
1214 vg_error( "Too many cells to match previous row definition"
1215 " (row: %u, %u>%u)\n", world
.h
, cx
, world
.w
);
1219 struct cell
*cell
= &row
[ cx
];
1220 map_load_cell( cell
, *c
, cx
);
1222 if( map_load_is_terminal(*c
) )
1225 if( cell
->state
& FLAG_IS_TRIGGER
)
1226 arrpush( links_to_make
, cx
+ world
.h
*world
.w
);
1234 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
1236 if( !map_load_apply_emitter_codes( &world
.io
[i
] ) )
1240 map_load_draw_background();
1241 map_reclassify( NULL
, NULL
, 1 );
1243 if( !map_load_validate() )
1247 * At this point the world is in a fully loaded and complete state
1250 arrfree( links_to_make
);
1252 vg_success( "Map '%s' loaded! (%u:%u)\n", name
, world
.w
, world
.h
);
1256 strncpy( world
.map_name
, name
, vg_list_size( world
.map_name
)-1 );
1257 world
.initialzed
= 1;
1259 /* Setup world button locations */
1260 for( int i
= 0; i
< vg_list_size( world
.st
.buttons
); i
++ )
1262 struct world_button
*btn
= &world
.st
.buttons
[i
];
1263 btn
->position
[0] = world
.w
-1;
1264 btn
->position
[1] = world
.h
-i
-2;
1267 /* Allocate buffers for render commands */
1268 world
.cmd_buf_tiles
= malloc( world
.w
*world
.h
* sizeof( struct render_cmd
) );
1269 world
.max_commands
= world
.w
*world
.h
;
1273 arrfree( links_to_make
);
1278 static void map_serialize( FILE *stream
)
1280 for( int y
= 0; y
< world
.h
; y
++ )
1282 for( int x
= 0; x
< world
.w
; x
++ )
1284 struct cell
*cell
= pcell( (v2i
){ x
, y
} );
1286 if( cell
->state
& FLAG_WALL
) fputc( '#', stream
);
1287 else if( cell
->state
& FLAG_INPUT_NICE
) fputc( '.', stream
);
1288 else if( cell
->state
& FLAG_INPUT
) fputc( '+', stream
);
1289 else if( cell
->state
& FLAG_OUTPUT
) fputc( '-', stream
);
1290 else if( cell
->state
& FLAG_EMITTER
) fputc( '*', stream
);
1291 else if( cell
->state
& FLAG_4B_GROUP
)
1294 * Serialize the '4 bit group' into ABCD...
1296 fputc( (cell
->state
& FLAG_4B_GROUP
) + (u32
)'A', stream
);
1298 else fputc( ' ', stream
);
1301 fputc( ';', stream
);
1302 int terminal_write_count
= 0;
1304 for( int x
= 0; x
< world
.w
; x
++ )
1306 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
1308 struct cell_terminal
*term
= &world
.io
[i
];
1309 if( v2i_eq( term
->pos
, (v2i
){x
,y
} ) )
1311 if( terminal_write_count
)
1312 fputc( ',', stream
);
1313 terminal_write_count
++;
1315 for( int j
= 0; j
< term
->run_count
; j
++ )
1317 struct terminal_run
*run
= &term
->runs
[j
];
1319 for( int k
= 0; k
< run
->step_count
; k
++ )
1321 i8 step
= run
->steps
[k
];
1322 fputc( step
== -1? ' ': ('a' + run
->steps
[k
]), stream
);
1325 if( j
< term
->run_count
-1 )
1326 fputc( ':', stream
);
1332 for( int x
= 0; x
< world
.w
; x
++ )
1334 struct cell
*cell
= pcell( (v2i
){ x
,y
} );
1335 if( cell
->state
& FLAG_IS_TRIGGER
)
1337 if( terminal_write_count
)
1338 fputc( ',', stream
);
1339 terminal_write_count
++;
1341 fprintf( stream
, "%d",
1342 cell
->links
[0]? -cell
->links
[0]: cell
->links
[1] );
1346 fputc( '\n', stream
);
1353 #pragma pack(push,1)
1354 struct dcareer_state
1368 levels
[ NUM_CAMPAIGN_LEVELS
];
1372 static int career_load_success
= 0;
1374 static void career_serialize(void)
1376 if( !career_load_success
)
1379 struct dcareer_state encoded
;
1380 encoded
.version
= MARBLE_COMP_VERSION
;
1381 encoded
.in_map
= world
.pCmpLevel
? world
.pCmpLevel
->serial_id
: -1;
1383 memset( encoded
.reserved
, 0, sizeof( encoded
.reserved
) );
1385 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1387 struct career_level_pack
*set
= &career_packs
[i
];
1389 for( int j
= 0; j
< set
->count
; j
++ )
1391 struct cmp_level
*lvl
= &set
->pack
[j
];
1392 struct dlevel_state
*dest
= &encoded
.levels
[lvl
->serial_id
];
1394 dest
->score
= lvl
->completed_score
;
1395 dest
->unlocked
= lvl
->unlocked
;
1396 dest
->reserved
[0] = 0;
1397 dest
->reserved
[1] = 0;
1401 vg_asset_write( "sav/game.sv2", &encoded
, sizeof( struct dcareer_state
) );
1404 static void career_unlock_level( struct cmp_level
*lvl
);
1405 static void career_unlock_level( struct cmp_level
*lvl
)
1410 career_unlock_level( lvl
->linked
);
1413 static void career_pass_level( struct cmp_level
*lvl
, int score
, int upload
)
1417 lvl
->completed_score
= score
;
1421 career_unlock_level( lvl
->unlock
);
1424 if( lvl
->achievement
)
1425 sw_set_achievement( lvl
->achievement
);
1427 /* Check ALL maps to trigger master engineer */
1428 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1430 struct career_level_pack
*set
= &career_packs
[i
];
1432 for( int j
= 0; j
< set
->count
; j
++ )
1434 if( set
->pack
[j
].completed_score
== 0 )
1439 sw_set_achievement( "MASTER_ENGINEER" );
1444 static void career_reset_level( struct cmp_level
*lvl
)
1447 lvl
->completed_score
= 0;
1450 static void career_load(void)
1453 struct dcareer_state encoded
;
1455 /* Blank save state */
1456 memset( (void*)&encoded
, 0, sizeof( struct dcareer_state
) );
1458 encoded
.levels
[0].unlocked
= 1;
1461 * Load and copy, this step is just to ensure old/newer saves can be loaded
1462 * without crashing. Old saves will load fine, too new saves will lose data,
1463 * such a situation should rarely (never) happen with the steam version.
1465 void *cr
= vg_asset_read_s( "sav/game.sv2", &sz
);
1469 if( sz
> sizeof( struct dcareer_state
) )
1470 vg_warn( "This save file is too big! Some levels will be lost\n" );
1472 if( sz
<= offsetof( struct dcareer_state
, levels
) )
1474 vg_warn( "This save file is too small to have a header. "
1475 "Creating a blank one\n" );
1479 memcpy( (void*)&encoded
, cr
, VG_MIN( sizeof( struct dcareer_state
), sz
));
1483 vg_info( "No save file... Using blank one\n" );
1486 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1488 struct career_level_pack
*set
= &career_packs
[i
];
1490 for( int j
= 0; j
< set
->count
; j
++ )
1491 career_reset_level( &set
->pack
[j
] );
1494 /* Header information */
1496 struct cmp_level
*lvl_to_load
= &career_packs
[0].pack
[0];
1498 /* Decode everything from dstate */
1499 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1501 struct career_level_pack
*set
= &career_packs
[i
];
1503 for( int j
= 0; j
< set
->count
; j
++ )
1505 struct cmp_level
*lvl
= &set
->pack
[j
];
1506 struct dlevel_state
*src
= &encoded
.levels
[lvl
->serial_id
];
1508 if( src
->unlocked
) career_unlock_level( lvl
);
1511 lvl
->completed_score
= src
->score
;
1514 * Apply unlocking trigger to next levels,
1515 * in case the level graph was updated in the future
1518 career_unlock_level( lvl
->unlock
);
1521 if( lvl
->serial_id
== encoded
.in_map
)
1526 if( console_changelevel( 1, &lvl_to_load
->map_name
) )
1528 world
.pCmpLevel
= lvl_to_load
;
1532 career_load_success
= 1;
1535 if( encoded
.version
< MARBLE_COMP_VERSION
)
1536 world
.st
.state
= k_game_state_update
;
1543 static int is_simulation_running(void)
1545 return world
.st
.buttons
[ k_world_button_sim
].state
;
1548 static void clear_animation_flags(void)
1550 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
1551 world
.data
[ i
].state
&= ~(FLAG_FLIP_FLOP
|FLAG_FLIP_ROTATING
);
1554 static void simulation_stop(void)
1556 world
.st
.buttons
[ k_world_button_sim
].state
= 0;
1557 world
.st
.buttons
[ k_world_button_pause
].state
= 0;
1559 world
.num_fishes
= 0;
1560 world
.sim_frame
= 0;
1562 world
.frame_lerp
= 0.0f
;
1566 sfx_system_fadeout( &audio_system_balls_rolling
, 44100 );
1568 clear_animation_flags();
1570 vg_info( "Stopping simulation!\n" );
1573 static void simulation_start(void)
1575 vg_success( "Starting simulation!\n" );
1577 sfx_set_playrnd( &audio_rolls
, &audio_system_balls_rolling
, 0, 1 );
1579 world
.num_fishes
= 0;
1580 world
.sim_frame
= 0;
1583 world
.sim_delta_speed
= world
.st
.buttons
[ k_world_button_speedy
].state
? 10.0f
: 2.5f
;
1584 world
.sim_delta_ref
= vg_time
;
1585 world
.sim_internal_ref
= 0.0f
;
1586 world
.sim_internal_time
= 0.0f
;
1587 world
.pause_offset_target
= 0.0f
;
1589 world
.sim_target
= 0;
1591 clear_animation_flags();
1595 if( world
.pCmpLevel
)
1597 world
.pCmpLevel
->completed_score
= 0;
1602 static int world_check_pos_ok( v2i co
, int dist
)
1604 return (co
[0] < dist
|| co
[0] >= world
.w
-dist
|| co
[1] < dist
|| co
[1] >= world
.h
-dist
)? 0: 1;
1607 static int cell_interactive( v2i co
)
1609 struct cell
*cell
= NULL
;
1612 if( world_check_pos_ok( co
, 1 ) )
1615 if( cell
->state
& FLAG_EMITTER
)
1619 if( !world_check_pos_ok( co
, 2 ) )
1623 if( cell
->state
& (FLAG_WALL
|FLAG_INPUT
|FLAG_OUTPUT
) )
1626 // List of 3x3 configurations that we do not allow
1627 static u32 invalid_src
[][9] =
1659 // Statically compile invalid configurations into bitmasks
1660 static u32 invalid
[ vg_list_size(invalid_src
) ];
1662 for( int i
= 0; i
< vg_list_size(invalid_src
); i
++ )
1666 for( int j
= 0; j
< 3; j
++ )
1667 for( int k
= 0; k
< 3; k
++ )
1668 comped
|= invalid_src
[i
][ j
*3+k
] << ((j
*5)+k
);
1670 invalid
[i
] = comped
;
1673 // Extract 5x5 grid surrounding tile
1675 for( int y
= co
[1]-2; y
< co
[1]+3; y
++ )
1676 for( int x
= co
[0]-2; x
< co
[0]+3; x
++ )
1678 struct cell
*cell
= pcell((v2i
){x
,y
});
1680 if( cell
&& (cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
)) )
1681 blob
|= 0x1 << ((y
-(co
[1]-2))*5 + x
-(co
[0]-2));
1684 // Run filter over center 3x3 grid to check for invalid configurations
1685 int kernel
[] = { 0, 1, 2, 5, 6, 7, 10, 11, 12 };
1686 for( int i
= 0; i
< vg_list_size(kernel
); i
++ )
1688 if( blob
& (0x1 << (6+kernel
[i
])) )
1690 u32 window
= blob
>> kernel
[i
];
1692 for( int j
= 0; j
< vg_list_size(invalid
); j
++ )
1693 if((window
& invalid
[j
]) == invalid
[j
])
1701 static void vg_update(void)
1704 if( world
.st
.lvl_to_load
)
1706 world
.st
.world_transition
= (world
.st
.lvl_load_time
-vg_time
) * 4.0f
;
1708 if( vg_time
> world
.st
.lvl_load_time
)
1710 if( console_changelevel( 1, &world
.st
.lvl_to_load
->map_name
) )
1712 world
.pCmpLevel
= world
.st
.lvl_to_load
;
1716 world
.st
.lvl_to_load
= NULL
;
1721 world
.st
.world_transition
= vg_minf( 1.0f
, (vg_time
-world
.st
.lvl_load_time
) * 4.0f
);
1725 // ========================================================================================================
1727 float r1
= (float)vg_window_y
/ (float)vg_window_x
,
1728 r2
= (float)world
.h
/ (float)world
.w
,
1731 static float size_current
= 2.0f
;
1732 static v3f origin_current
= { 0.0f
, 0.0f
, 0.0f
};
1733 static v2f drag_offset
= { 0.0f
, 0.0f
};
1734 static v2f view_point
= { 0.0f
, 0.0f
};
1737 size
= ( r2
< r1
? (float)(world
.w
+5) * 0.5f
: ((float)(world
.h
+5) * 0.5f
) / r1
) + 0.5f
;
1742 origin
[0] = floorf( -0.5f
* ((float)world
.w
-4.5f
) );
1743 origin
[1] = floorf( -0.5f
* world
.h
);
1745 // Create and clamp result view
1746 v2_add( view_point
, drag_offset
, result_view
);
1747 result_view
[0] = vg_clampf( result_view
[0], -world
.st
.zoom
, world
.st
.zoom
);
1748 result_view
[1] = vg_clampf( result_view
[1], -world
.st
.zoom
*r1
, world
.st
.zoom
*r1
);
1750 v2_add( origin
, result_view
, vt_target
);
1752 // Lerp towards target
1753 size_current
= vg_lerpf( size_current
, size
- world
.st
.zoom
, vg_time_delta
* 6.0f
);
1754 v2_lerp( origin_current
, vt_target
, vg_time_delta
* 6.0f
, origin_current
);
1756 m3x3_projection( m_projection
, -size_current
, size_current
, -size_current
*r1
, size_current
*r1
);
1757 m3x3_identity( m_view
);
1758 m3x3_translate( m_view
, origin_current
);
1759 m3x3_mul( m_projection
, m_view
, vg_pv
);
1760 vg_projection_update();
1762 if( world
.st
.state
== k_game_state_update
)
1766 // ========================================================================================================
1767 v2_copy( vg_mouse_ws
, world
.tile_pos
);
1769 world
.tile_x
= floorf( world
.tile_pos
[0] );
1770 world
.tile_y
= floorf( world
.tile_pos
[1] );
1774 static v2f drag_origin
; // x/y pixel
1776 if( vg_get_button_down( "tertiary" ) )
1777 v2_copy( vg_mouse
, drag_origin
);
1778 else if( vg_get_button( "tertiary" ) )
1781 v2_sub( vg_mouse
, drag_origin
, drag_offset
);
1782 v2_div( drag_offset
, (v2f
){ vg_window_x
, vg_window_y
}, drag_offset
);
1783 v2_mul( drag_offset
, (v2f
){ size_current
*2.0f
, -size_current
*r1
*2.0f
}, drag_offset
);
1787 v2_copy( result_view
, view_point
);
1788 v2_copy( (v2f
){0.0f
,0.0f
}, drag_offset
);
1800 rsize
= size
-world
.st
.zoom
;
1802 v2_div( vg_mouse
, (v2f
){ vg_window_x
*0.5f
, vg_window_y
*0.5f
}, mview_local
);
1803 v2_add( (v2f
){ -rsize
, -rsize
*r1
}, (v2f
){ mview_local
[0]*rsize
, (2.0f
-mview_local
[1])*rsize
*r1
}, mview_cur
);
1805 world
.st
.zoom
= vg_clampf( world
.st
.zoom
+ vg_mouse_wheel
[1], 0.0f
, size
- 4.0f
);
1807 // Recalculate new position
1808 rsize
= size
-world
.st
.zoom
;
1809 v2_add( (v2f
){ -rsize
, -rsize
*r1
}, (v2f
){ mview_local
[0]*rsize
, (2.0f
-mview_local
[1])*rsize
*r1
}, mview_new
);
1812 v2_sub( mview_new
, mview_cur
, mview_delta
);
1813 v2_add( mview_delta
, view_point
, view_point
);
1817 // ========================================================================================================
1818 if( !is_simulation_running() && !gui_want_mouse() )
1820 v2_copy( vg_mouse_ws
, world
.drag_to_co
);
1822 if( cell_interactive( (v2i
){ world
.tile_x
, world
.tile_y
} ))
1824 world
.selected
= world
.tile_y
* world
.w
+ world
.tile_x
;
1826 static u32 modify_state
= 0;
1827 struct cell
*cell_ptr
= &world
.data
[world
.selected
];
1829 if( !(cell_ptr
->state
& FLAG_EMITTER
) )
1831 if( vg_get_button_down("primary") )
1832 modify_state
= (cell_ptr
->state
& FLAG_CANAL
) ^ FLAG_CANAL
;
1834 if( vg_get_button("primary") && ((cell_ptr
->state
& FLAG_CANAL
) != modify_state
) )
1836 cell_ptr
->state
&= ~FLAG_CANAL
;
1837 cell_ptr
->state
|= modify_state
;
1839 if( cell_ptr
->state
& FLAG_CANAL
)
1841 cell_ptr
->links
[0] = 0;
1842 cell_ptr
->links
[1] = 0;
1844 sfx_set_playrnd( &audio_tile_mod
, &audio_system_sfx
, 3, 6 );
1849 sfx_set_playrnd( &audio_tile_mod
, &audio_system_sfx
, 0, 3 );
1853 map_reclassify((v2i
){ world
.tile_x
-2, world
.tile_y
-2 },
1854 (v2i
){ world
.tile_x
+2, world
.tile_y
+2 }, 1 );
1857 if( vg_get_button_down("secondary") && (cell_ptr
->state
& FLAG_CANAL
) && !(cell_ptr
->config
== k_cell_type_split
) )
1859 world
.id_drag_from
= world
.selected
;
1861 struct cell_description
*desc
= &cell_descriptions
[ world
.data
[world
.id_drag_from
].config
];
1862 v2_add( desc
->trigger_pos
, (v2f
){ world
.tile_x
, world
.tile_y
}, world
.drag_from_co
);
1866 float local_x
= vg_mouse_ws
[0] - (float)world
.tile_x
;
1868 if( vg_get_button_up("secondary") && world
.id_drag_from
== world
.selected
)
1870 u32 link_id
= cell_ptr
->links
[ 0 ]? 0: 1;
1872 // break existing connection off
1873 if( cell_ptr
->links
[ link_id
] )
1875 struct cell
*current_connection
= &world
.data
[ cell_ptr
->links
[ link_id
]];
1877 if( !current_connection
->links
[ link_id
^ 0x1 ] )
1878 current_connection
->state
&= ~FLAG_TARGETED
;
1880 current_connection
->links
[ link_id
] = 0;
1881 cell_ptr
->links
[ link_id
] = 0;
1884 cell_ptr
->state
&= ~FLAG_IS_TRIGGER
;
1885 world
.id_drag_from
= 0;
1888 else if( world
.id_drag_from
&& (cell_ptr
->state
& (FLAG_CANAL
|FLAG_EMITTER
)) &&
1889 ((cell_ptr
->config
== k_cell_type_split
) || (cell_ptr
->state
& FLAG_EMITTER
)) )
1891 world
.drag_to_co
[0] = (float)world
.tile_x
+ (local_x
> 0.5f
? 0.75f
: 0.25f
);
1892 world
.drag_to_co
[1] = (float)world
.tile_y
+ 0.25f
;
1894 if( vg_get_button_up("secondary") )
1896 struct cell
*drag_ptr
= &world
.data
[world
.id_drag_from
];
1897 u32 link_id
= local_x
> 0.5f
? 1: 0;
1899 // Cleanup existing connections
1900 if( cell_ptr
->links
[ link_id
] )
1902 vg_warn( "Destroying existing connection on link %u (%hu)\n", link_id
, cell_ptr
->links
[ link_id
] );
1904 struct cell
*current_connection
= &world
.data
[ cell_ptr
->links
[ link_id
]];
1905 current_connection
->state
&= ~FLAG_IS_TRIGGER
;
1906 current_connection
->links
[ link_id
] = 0;
1909 for( u32 i
= 0; i
< 2; i
++ )
1911 if( drag_ptr
->links
[ i
] )
1913 vg_warn( "Destroying link %u (%hu)\n", i
, drag_ptr
->links
[ i
] );
1915 struct cell
*current_connection
= &world
.data
[ drag_ptr
->links
[ i
]];
1916 if( current_connection
->links
[ i
^ 0x1 ] == 0 )
1917 current_connection
->state
&= ~FLAG_TARGETED
;
1919 current_connection
->links
[ i
] = 0;
1920 drag_ptr
->links
[ i
] = 0;
1924 // Create the new connection
1925 vg_success( "Creating connection on link %u (%hu)\n", link_id
, world
.id_drag_from
);
1927 cell_ptr
->links
[ link_id
] = world
.id_drag_from
;
1928 drag_ptr
->links
[ link_id
] = world
.selected
;
1930 cell_ptr
->state
|= FLAG_TARGETED
;
1931 drag_ptr
->state
|= FLAG_IS_TRIGGER
;
1932 world
.id_drag_from
= 0;
1938 world
.selected
= -1;
1941 if( !(vg_get_button("secondary") && world
.id_drag_from
) )
1942 world
.id_drag_from
= 0;
1946 world
.selected
= -1;
1947 world
.id_drag_from
= 0;
1950 // Marble state updates
1951 // ========================================================================================================
1952 if( is_simulation_running() )
1954 float old_time
= world
.sim_internal_time
;
1956 if( !world
.st
.buttons
[ k_world_button_pause
].state
)
1957 world
.sim_internal_time
= world
.sim_internal_ref
+ (vg_time
-world
.sim_delta_ref
) * world
.sim_delta_speed
;
1959 world
.sim_internal_time
= vg_lerpf( world
.sim_internal_time
, world
.sim_internal_ref
+ world
.pause_offset_target
, vg_time_delta
*15.0f
);
1960 world
.sim_internal_delta
= world
.sim_internal_time
-old_time
;
1962 world
.sim_target
= (int)floorf(world
.sim_internal_time
);
1964 int success_this_frame
= 0;
1965 int failure_this_frame
= 0;
1967 while( world
.sim_frame
< world
.sim_target
)
1969 sfx_set_playrnd( &audio_random
, &audio_system_balls_switching
, 0, 8 );
1971 // Update splitter deltas
1972 for( int i
= 0; i
< world
.h
*world
.w
; i
++ )
1974 struct cell
*cell
= &world
.data
[i
];
1975 if( cell
->config
== k_cell_type_split
)
1977 cell
->state
&= ~FLAG_FLIP_ROTATING
;
1979 if( cell
->state
& (FLAG_IS_TRIGGER
|FLAG_EMITTER
) )
1980 cell
->state
&= ~FLAG_TRIGGERED
;
1983 int alive_count
= 0;
1985 // Update fish positions
1986 for( int i
= 0; i
< world
.num_fishes
; i
++ )
1988 struct fish
*fish
= &world
.fishes
[i
];
1990 if( fish
->state
== k_fish_state_soon_dead
)
1991 fish
->state
= k_fish_state_dead
;
1993 if( fish
->state
== k_fish_state_soon_alive
)
1994 fish
->state
= k_fish_state_alive
;
1996 if( fish
->state
< k_fish_state_alive
)
1999 struct cell
*cell_current
= pcell( fish
->pos
);
2001 if( fish
->state
== k_fish_state_alive
)
2004 if( cell_current
->state
& FLAG_OUTPUT
)
2006 for( int j
= 0; j
< arrlen( world
.io
); j
++ )
2008 struct cell_terminal
*term
= &world
.io
[j
];
2010 if( v2i_eq( term
->pos
, fish
->pos
) )
2012 struct terminal_run
*run
= &term
->runs
[ world
.sim_run
];
2013 if( run
->recv_count
< vg_list_size( run
->recieved
) )
2015 if( fish
->colour
== run
->steps
[ run
->recv_count
] )
2016 success_this_frame
= 1;
2018 failure_this_frame
= 1;
2020 run
->recieved
[ run
->recv_count
++ ] = fish
->colour
;
2023 failure_this_frame
= 1;
2029 fish
->state
= k_fish_state_dead
;
2030 fish
->death_time
= -1000.0f
;
2035 if( cell_current
->config
== k_cell_type_merge
)
2040 fish
->flow_reversed
= 0;
2044 if( cell_current
->config
== k_cell_type_split
)
2047 fish
->dir
[0] = cell_current
->state
&FLAG_FLIP_FLOP
?1:-1;
2050 if( !(cell_current
->state
& FLAG_TARGETED
) )
2051 cell_current
->state
^= FLAG_FLIP_FLOP
;
2055 // Apply cell out-flow
2056 struct cell_description
*desc
= &cell_descriptions
[ cell_current
->config
];
2058 v2i_copy( fish
->flow_reversed
? desc
->start
: desc
->end
, fish
->dir
);
2062 v2i_add( fish
->pos
, fish
->dir
, pos_next
);
2064 struct cell
*cell_next
= pcell( pos_next
);
2066 if( cell_next
->state
& (FLAG_CANAL
|FLAG_OUTPUT
) )
2068 struct cell_description
*desc
= &cell_descriptions
[ cell_next
->config
];
2070 if( cell_next
->config
== k_cell_type_merge
)
2072 if( fish
->dir
[0] == 0 )
2074 fish
->state
= k_fish_state_dead
;
2075 fish
->death_time
= world
.sim_internal_time
;
2078 fish
->flow_reversed
= 0;
2082 if( cell_next
->config
== k_cell_type_split
)
2084 if( fish
->dir
[0] == 0 )
2086 sfx_set_playrnd( &audio_splitter
, &audio_system_balls_important
, 0, 1 );
2087 cell_next
->state
|= FLAG_FLIP_ROTATING
;
2089 fish
->flow_reversed
= 0;
2093 fish
->state
= k_fish_state_dead
;
2094 fish
->death_time
= world
.sim_internal_time
;
2098 fish
->flow_reversed
= ( fish
->dir
[0] != -desc
->start
[0] ||
2099 fish
->dir
[1] != -desc
->start
[1] )? 1: 0;
2104 if( world_check_pos_ok( fish
->pos
, 2 ) )
2105 fish
->state
= k_fish_state_bg
;
2108 fish
->state
= k_fish_state_dead
;
2109 fish
->death_time
= world
.sim_internal_time
;
2114 //v2i_add( fish->pos, fish->dir, fish->pos );
2116 else if( fish
->state
== k_fish_state_bg
)
2118 v2i_add( fish
->pos
, fish
->dir
, fish
->pos
);
2120 if( !world_check_pos_ok( fish
->pos
, 2 ) )
2122 fish
->state
= k_fish_state_dead
;
2123 fish
->death_time
= -1000.0f
;
2127 struct cell
*cell_entry
= pcell( fish
->pos
);
2129 if( cell_entry
->state
& FLAG_CANAL
)
2131 if( cell_entry
->config
== k_cell_type_con_r
|| cell_entry
->config
== k_cell_type_con_u
2132 || cell_entry
->config
== k_cell_type_con_l
|| cell_entry
->config
== k_cell_type_con_d
)
2135 sw_set_achievement( "CAN_DO_THAT" );
2138 fish
->state
= k_fish_state_soon_alive
;
2142 fish
->flow_reversed
= 1;
2144 switch( cell_entry
->config
)
2146 case k_cell_type_con_r
: fish
->dir
[0] = 1; break;
2147 case k_cell_type_con_l
: fish
->dir
[0] = -1; break;
2148 case k_cell_type_con_u
: fish
->dir
[1] = 1; break;
2149 case k_cell_type_con_d
: fish
->dir
[1] = -1; break;
2155 else { vg_error( "fish behaviour unimplemented for behaviour type (%d)\n" ); }
2157 if( fish
->state
>= k_fish_state_alive
)
2161 // Second pass (triggers)
2162 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2164 struct fish
*fish
= &world
.fishes
[i
];
2167 if( fish
->state
== k_fish_state_alive
)
2168 v2i_add( fish
->pos
, fish
->dir
, fish
->pos
);
2170 if( fish
->state
== k_fish_state_alive
|| fish
->state
== k_fish_state_soon_alive
)
2172 struct cell
*cell_current
= pcell( fish
->pos
);
2174 if( cell_current
->state
& FLAG_IS_TRIGGER
)
2176 int trigger_id
= cell_current
->links
[0]?0:1;
2178 struct cell
*target_peice
= &world
.data
[ cell_current
->links
[trigger_id
] ];
2181 if( (target_peice
->state
& FLAG_EMITTER
) && !(target_peice
->state
& FLAG_TRIGGERED
))
2183 if( world
.num_fishes
< vg_list_size( world
.fishes
) )
2185 struct fish
*fish
= &world
.fishes
[ world
.num_fishes
];
2186 lcell( cell_current
->links
[trigger_id
], fish
->pos
);
2188 fish
->state
= k_fish_state_soon_alive
;
2189 fish
->colour
= target_peice
->emit
[ trigger_id
];
2191 if( target_peice
->config
!= k_cell_type_stub
)
2193 struct cell_description
*desc
= &cell_descriptions
[ target_peice
->config
];
2194 v2i_copy( desc
->start
, fish
->dir
);
2195 fish
->flow_reversed
= 1;
2197 world
.num_fishes
++;
2202 vg_warn( "Max marbles exceeded\n" );
2207 target_peice
->state
|= FLAG_FLIP_FLOP
;
2209 target_peice
->state
&= ~FLAG_FLIP_FLOP
;
2212 cell_current
->state
|= FLAG_TRIGGERED
;
2217 // Third pass (collisions)
2218 struct fish
*fi
, *fj
;
2220 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2222 fi
= &world
.fishes
[i
];
2224 if( (fi
->state
== k_fish_state_alive
) |
2225 (fi
->state
== k_fish_state_soon_alive
) )
2227 int continue_again
= 0;
2229 for( int j
= i
+1; j
< world
.num_fishes
; j
++ )
2231 fj
= &world
.fishes
[j
];
2233 if( (fj
->state
== k_fish_state_alive
) |
2234 (fj
->state
== k_fish_state_soon_alive
) )
2239 v2i_sub( fi
->pos
, fi
->dir
, fi_prev
);
2240 v2i_sub( fj
->pos
, fj
->dir
, fj_prev
);
2243 collide_next_frame
= (
2244 (fi
->pos
[0] == fj
->pos
[0]) &&
2245 (fi
->pos
[1] == fj
->pos
[1]))? 1: 0,
2246 collide_this_frame
= (
2247 (fi_prev
[0] == fj
->pos
[0]) &&
2248 (fi_prev
[1] == fj
->pos
[1]) &&
2249 (fj_prev
[0] == fi
->pos
[0]) &&
2250 (fj_prev
[1] == fi
->pos
[1])
2253 if( collide_next_frame
|| collide_this_frame
)
2256 sw_set_achievement( "BANG" );
2259 // Shatter death (+0.5s)
2260 float death_time
= world
.sim_internal_time
+ ( collide_this_frame
? 0.0f
: 0.5f
);
2262 fi
->state
= k_fish_state_soon_dead
;
2263 fj
->state
= k_fish_state_soon_dead
;
2264 fi
->death_time
= death_time
;
2265 fj
->death_time
= death_time
;
2272 if( continue_again
)
2278 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
2280 struct cell_terminal
*term
= &world
.io
[ i
];
2281 int is_input
= pcell(term
->pos
)->state
& FLAG_INPUT
;
2285 if( world
.sim_frame
< term
->runs
[ world
.sim_run
].step_count
)
2287 i8 emit
= term
->runs
[ world
.sim_run
].steps
[ world
.sim_frame
];
2291 struct fish
*fish
= &world
.fishes
[ world
.num_fishes
];
2292 v2i_copy( term
->pos
, fish
->pos
);
2294 fish
->state
= k_fish_state_alive
;
2295 fish
->colour
= emit
;
2297 struct cell
*cell_ptr
= pcell( fish
->pos
);
2299 if( cell_ptr
->config
!= k_cell_type_stub
)
2301 if( world
.num_fishes
< vg_list_size(world
.fishes
))
2303 struct cell_description
*desc
= &cell_descriptions
[ cell_ptr
->config
];
2305 v2i_copy( desc
->start
, fish
->dir
);
2306 fish
->flow_reversed
= 1;
2308 world
.num_fishes
++;
2312 vg_warn( "Max marbles exceeded\n" );
2318 if( alive_count
== 0 )
2320 world
.completed
= 1;
2322 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
2324 struct cell_terminal
*term
= &world
.io
[ i
];
2326 if( pcell(term
->pos
)->state
& FLAG_OUTPUT
)
2328 struct terminal_run
*run
= &term
->runs
[ world
.sim_run
];
2330 if( run
->recv_count
== run
->step_count
)
2332 for( int j
= 0; j
< run
->step_count
; j
++ )
2334 if( run
->recieved
[j
] != run
->steps
[j
] )
2336 world
.completed
= 0;
2343 world
.completed
= 0;
2349 if( world
.completed
)
2351 if( world
.sim_run
< world
.max_runs
-1 )
2353 vg_success( "Run passed, starting next\n" );
2355 world
.sim_frame
= 0;
2356 world
.sim_target
= 0;
2357 world
.num_fishes
= 0;
2359 // Reset timing reference points
2360 world
.sim_delta_ref
= vg_time
;
2361 world
.sim_internal_ref
= 0.0f
;
2363 if( world
.st
.buttons
[ k_world_button_pause
].state
)
2364 world
.pause_offset_target
= 0.5f
;
2366 world
.pause_offset_target
= 0.0f
;
2368 world
.sim_internal_time
= 0.0f
;
2370 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
2371 world
.data
[ i
].state
&= ~FLAG_FLIP_FLOP
;
2377 vg_success( "Level passed!\n" );
2380 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
2381 if( world
.data
[ i
].state
& FLAG_CANAL
)
2384 world
.score
= score
;
2385 world
.time
= world
.sim_frame
;
2387 // Copy into career data
2388 if( world
.pCmpLevel
)
2390 career_pass_level( world
.pCmpLevel
, world
.score
, 1 );
2393 sfx_set_play( &audio_tones
, &audio_system_balls_extra
, 9 );
2394 failure_this_frame
= 0;
2395 success_this_frame
= 0;
2401 if( world
.sim_run
> 0 )
2402 sw_set_achievement( "GOOD_ENOUGH" );
2405 vg_error( "Level failed :(\n" );
2416 if( failure_this_frame
)
2418 sfx_set_play( &audio_tones
, &audio_system_balls_extra
, 0 );
2420 else if( success_this_frame
)
2422 static int succes_counter
= 0;
2424 sfx_set_play( &audio_tones
, &audio_system_balls_extra
, 1+(succes_counter
++) );
2426 if( succes_counter
== 7 )
2431 // =====================================================================================================
2433 world
.frame_lerp
= world
.sim_internal_time
- floorf( world
.sim_internal_time
);
2435 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2437 struct fish
*fish
= &world
.fishes
[i
];
2439 if( fish
->state
== k_fish_state_dead
)
2442 if( fish
->state
== k_fish_state_soon_dead
&& (world
.sim_internal_time
> fish
->death_time
) )
2443 continue; // Todo: particle thing?
2445 struct cell
*cell
= pcell(fish
->pos
);
2446 struct cell_description
*desc
= &cell_descriptions
[ cell
->config
];
2450 float t
= world
.frame_lerp
;
2451 if( fish
->flow_reversed
&& !desc
->is_linear
)
2454 v2_copy( fish
->physics_co
, fish
->physics_v
);
2456 switch( cell
->config
)
2458 case k_cell_type_merge
:
2459 if( fish
->dir
[0] == 1 )
2464 case k_cell_type_con_r
: curve
= curve_1
; break;
2465 case k_cell_type_con_l
: curve
= curve_4
; break;
2466 case k_cell_type_con_u
: curve
= curve_2
; break;
2467 case k_cell_type_con_d
: curve
= curve_8
; break;
2468 case 3: curve
= curve_3
; break;
2469 case 6: curve
= curve_6
; break;
2470 case 9: curve
= curve_9
; break;
2471 case 12: curve
= curve_12
; break;
2473 if( t
> curve_7_linear_section
)
2475 t
-= curve_7_linear_section
;
2476 t
*= (1.0f
/(1.0f
-curve_7_linear_section
));
2478 curve
= cell
->state
& FLAG_FLIP_FLOP
? curve_7
: curve_7_1
;
2482 default: curve
= NULL
; break;
2488 float t3
= t
* t
* t
;
2490 float cA
= 3.0f
*t2
- 3.0f
*t3
;
2491 float cB
= 3.0f
*t3
- 6.0f
*t2
+ 3.0f
*t
;
2492 float cC
= 3.0f
*t2
- t3
- 3.0f
*t
+ 1.0f
;
2494 fish
->physics_co
[0] = t3
*curve
[3][0] + cA
*curve
[2][0] + cB
*curve
[1][0] + cC
*curve
[0][0];
2495 fish
->physics_co
[1] = t3
*curve
[3][1] + cA
*curve
[2][1] + cB
*curve
[1][1] + cC
*curve
[0][1];
2496 fish
->physics_co
[0] += (float)fish
->pos
[0];
2497 fish
->physics_co
[1] += (float)fish
->pos
[1];
2502 origin
[0] = (float)fish
->pos
[0] + (float)fish
->dir
[0]*-0.5f
+ 0.5f
;
2503 origin
[1] = (float)fish
->pos
[1] + (float)fish
->dir
[1]*-0.5f
+ 0.5f
;
2505 fish
->physics_co
[0] = origin
[0] + (float)fish
->dir
[0]*t
;
2506 fish
->physics_co
[1] = origin
[1] + (float)fish
->dir
[1]*t
;
2509 v2_sub( fish
->physics_co
, fish
->physics_v
, fish
->physics_v
);
2510 v2_divs( fish
->physics_v
, world
.sim_internal_delta
, fish
->physics_v
);
2515 static void render_tile( v2i pos
, struct cell
*ptr
, v4f
const regular_colour
,
2516 v4f
const selected_colour
, int with_glow
)
2518 int selected
= world
.selected
== pos
[1]*world
.w
+ pos
[0];
2521 uv
[0] = ptr
->config
& 0x3;
2522 uv
[1] = ptr
->config
>> 2;
2524 glUniform4f( SHADER_UNIFORM( shader_tile_main
, "uOffset" ),
2533 glUniform3fv( SHADER_UNIFORM( shader_tile_main
, "uGlowA" ),
2535 glUniform3fv( SHADER_UNIFORM( shader_tile_main
, "uGlowB" ),
2540 glUniform3f( SHADER_UNIFORM( shader_tile_main
, "uGlowA" ),
2544 glUniform3f( SHADER_UNIFORM( shader_tile_main
, "uGlowB" ),
2552 glUniform4fv( SHADER_UNIFORM( shader_tile_main
, "uColour" ), 1, selected_colour
);
2554 glUniform4fv( SHADER_UNIFORM( shader_tile_main
, "uColour" ), 1, regular_colour
);
2560 // Renders specific chunk of tiles
2561 static void render_tile_block( v2i start
, v2i end
, v4f
const regular_colour
, v4f
const selected_colour
)
2563 v2i full_start
= { 0,0 };
2564 v2i full_end
= { world
.w
, world
.h
};
2566 if( !start
|| !end
)
2572 for( int y
= start
[1]; y
< end
[1]; y
++ )
2574 for( int x
= start
[0]; x
< end
[0]; x
++ )
2577 struct cell
*cell
= pcell( pos
);
2579 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
2580 render_tile( pos
, cell
, regular_colour
, selected_colour
, 0 );
2585 // Renders all tiles in the command list
2586 static void render_tiles( v4f
const regular_colour
, v4f
const selected_colour
,
2589 glUniform4fv( SHADER_UNIFORM( shader_tile_main
, "uColour" ), 1, regular_colour
);
2593 struct render_cmd
*arr
;
2597 { world
.cmd_buf_tiles
, world
.tile_count
},
2598 { world
.cmd_buf_specials
, world
.tile_special_count
}
2601 int world_paused
= world
.st
.buttons
[k_world_button_pause
].state
;
2602 if( with_glow
&& !world_paused
)
2604 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2606 struct fish
*fish
= &world
.fishes
[i
];
2608 if( !(fish
->state
== k_fish_state_alive
||
2609 fish
->state
== k_fish_state_soon_alive
) ) continue;
2611 struct cell
*cell_x
= pcell( fish
->pos
);
2613 colour_code_v3( fish
->colour
, glow_colour
);
2616 if( cell_x
->config
== k_cell_type_split
)
2617 c
= cell_x
->state
& FLAG_FLIP_FLOP
? 1:0;
2619 if( cell_x
->config
== k_cell_type_merge
)
2620 c
= fish
->dir
[0]==-1?1:0;
2622 v3_muladds( cell_x
->glow
[c
], glow_colour
,
2623 powf(world
.frame_lerp
,2.0f
)*0.03f
* world
.sim_delta_speed
,
2628 for( int i
= 0; i
< vg_list_size( render_lists
); i
++ )
2630 struct render_list
*list
= &render_lists
[i
];
2631 for( int j
= 0; j
< list
->count
; j
++ )
2633 struct render_cmd
*cmd
= &list
->arr
[j
];
2634 struct cell
*cell
= cmd
->ptr
;
2636 render_tile( cmd
->pos
, cell
, regular_colour
, selected_colour
, with_glow
);
2641 static int world_button_exec( struct world_button
*btn
, v2f texture
, v3f colour
, enum world_button_status
*status
)
2643 static v2i click_grab
= { -9999, -9999 };
2648 click_grab
[0] = -9999;
2649 click_grab
[1] = -9999;
2653 v2i click_tile
= { world
.tile_x
, world
.tile_y
};
2656 int is_hovering
= v2i_eq( click_tile
, btn
->position
) && !gui_want_mouse();
2658 // Set up light targets before logic runs
2660 btn
->light_target
= is_hovering
? 0.7f
: 0.6f
;
2662 btn
->light_target
= is_hovering
? 0.2f
: 0.0f
;
2664 if( vg_get_button( "primary" ) && is_hovering
)
2665 btn
->light_target
= 1.0f
;
2667 // Process click action
2670 if( vg_get_button_down( "primary" ) && is_hovering
)
2671 v2i_copy( click_tile
, click_grab
);
2672 else if( v2i_eq( click_grab
, click_tile
) && vg_get_button_up( "primary" ) )
2675 *status
= btn
->state
? k_world_button_on_disable
: k_world_button_on_enable
;
2677 if( btn
->mode
== k_world_button_mode_toggle
)
2680 sfx_set_play( &audio_clicks
, &audio_system_ui
, btn
->state
? 1:0 );
2688 btn
->light
= vg_lerpf( btn
->light
, btn
->light_target
+ btn
->extra_light
, vg_time_delta
*26.0f
);
2690 v3_copy( colour
, final_colour
);
2691 final_colour
[3] = btn
->light
;
2693 glUniform4f( SHADER_UNIFORM( shader_buttons
, "uOffset" ),
2699 glUniform4fv( SHADER_UNIFORM( shader_buttons
, "uColour" ), 1, final_colour
);
2705 static void level_selection_buttons(void)
2707 v3f tutorial_colour
= { 0.204f
, 0.345f
, 0.553f
};
2708 v3f locked_colour
= { 0.2f
, 0.2f
, 0.2f
};
2710 struct cmp_level
*switch_level_to
= NULL
;
2712 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
2714 struct career_level_pack
*grid
= &career_packs
[i
];
2716 for( int j
= 0; j
< grid
->count
; j
++ )
2718 struct cmp_level
*lvl
= &grid
->pack
[ j
];
2720 if( world
.pCmpLevel
== lvl
)
2721 lvl
->btn
.extra_light
= 0.35f
+ fabsf(sinf( vg_time
* 2.0f
)) * 0.05f
;
2722 else lvl
->btn
.extra_light
= 0.2f
;
2724 if( lvl
->completed_score
)
2725 lvl
->btn
.extra_light
+= 0.6f
;
2727 enum world_button_status status
;
2728 if( world_button_exec(
2731 lvl
->unlocked
? (lvl
->is_tutorial
? tutorial_colour
: grid
->primary_colour
): locked_colour
,
2735 if( status
== k_world_button_on_enable
&& lvl
->unlocked
)
2736 switch_level_to
= lvl
;
2741 if( switch_level_to
)
2743 world
.st
.lvl_to_load
= switch_level_to
;
2744 world
.st
.lvl_load_time
= vg_time
+ 0.25f
;
2745 world
.st
.world_transition
= 1.0f
;
2748 if( console_changelevel( 1, &switch_level_to->map_name ) )
2750 world.pCmpLevel = switch_level_to;
2751 gen_level_text( world.pCmpLevel );
2757 static void render_sprite( enum sprites_auto_combine_index id
, v3f pos
)
2759 struct vg_sprite
*sp
= &sprites_auto_combine
[ id
];
2761 glUniform4fv( SHADER_UNIFORM( shader_sprite
, "uUv" ), 1, sp
->uv_xywh
);
2762 glUniform3f( SHADER_UNIFORM( shader_sprite
, "uPos" ),
2763 pos
[0], pos
[1], pos
[2] * world
.st
.world_transition
);
2768 static void vg_framebuffer_resize(int w
, int h
)
2770 glBindTexture( GL_TEXTURE_2D
, world
.st
.colourbuffer
);
2771 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGB
, w
, h
,
2772 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
2774 for( int i
=0; i
<2; i
++ )
2776 glBindTexture( GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[i
] );
2777 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGB
,
2778 w
/EFFECT_BUFFER_RATIO
, h
/EFFECT_BUFFER_RATIO
,
2779 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
2783 void vg_render(void)
2785 if( enable_bloom
|| enable_vignette
)
2786 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.framebuffer
);
2788 glBindFramebuffer( GL_FRAMEBUFFER
, 0 );
2790 glViewport( 0,0, vg_window_x
, vg_window_y
);
2792 glDisable( GL_DEPTH_TEST
);
2793 glClearColor( 0.14f
, 0.14f
, 0.14f
, 1.0f
);
2794 glClear( GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
2796 v4f
const colour_default
= {1.0f
, 1.0f
, 1.0f
, 1.0f
};
2797 v4f
const colour_selected
= {0.90f
, 0.92f
, 1.0f
, 1.0f
};
2799 int const circle_base
= 6;
2800 int const filled_start
= circle_base
+0;
2801 int const filled_count
= circle_base
+32;
2802 int const empty_start
= circle_base
+32;
2803 int const empty_count
= circle_base
+32*2;
2806 struct world_theme
*theme
= &world_themes
[ world_theme_id
];
2808 struct world_theme
*theme
= &world_themes
[ 0 ];
2811 if( !world
.initialzed
)
2814 // Extract render commands
2815 world
.tile_count
= 0;
2816 world
.tile_special_count
= 0;
2818 for( int y
= 1; y
< world
.h
-1; y
++ )
2820 for( int x
= 1; x
< world
.w
-1; x
++ )
2822 struct cell
*cell
= pcell((v2i
){x
,y
});
2824 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
|FLAG_INPUT_NICE
) )
2826 struct render_cmd
*cmd
;
2829 (cell
->config
== k_cell_type_split
&& (cell
->state
& FLAG_CANAL
))
2830 || (cell
->state
& (FLAG_EMITTER
|FLAG_IS_TRIGGER
))
2832 cmd
= &world
.cmd_buf_tiles
[ world
.max_commands
- (++ world
.tile_special_count
) ];
2834 cmd
= &world
.cmd_buf_tiles
[ world
.tile_count
++ ];
2840 int world_paused
= world
.st
.buttons
[k_world_button_pause
].state
;
2843 float decay
= 1.0f
- world
.sim_delta_speed
*0.005f
;
2844 v3_muls( cell
->glow
[0], decay
, cell
->glow
[0] );
2845 v3_muls( cell
->glow
[1], decay
, cell
->glow
[1] );
2851 world
.cmd_buf_specials
= &world
.cmd_buf_tiles
[ world
.max_commands
- world
.tile_special_count
];
2854 // ========================================================================================================
2855 use_mesh( &world
.shapes
);
2857 SHADER_USE( shader_background
);
2858 glUniformMatrix3fv( SHADER_UNIFORM( shader_background
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2860 glActiveTexture( GL_TEXTURE0
);
2861 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
2862 glUniform1i( SHADER_UNIFORM( shader_background
, "uTexMain" ), 0 );
2864 glUniform3f( SHADER_UNIFORM( shader_background
, "uOffset" ), -16, -16, 64 );
2865 glUniform1f( SHADER_UNIFORM( shader_background
, "uVariance" ), 0.05f
);
2867 glActiveTexture( GL_TEXTURE1
);
2868 glBindTexture( GL_TEXTURE_2D
, world
.random_samples
);
2869 glUniform1i( SHADER_UNIFORM( shader_background
, "uSamplerNoise" ), 1 );
2870 glUniform1f( SHADER_UNIFORM( shader_background
, "uVisibility" ), 1.0f
); //world.st.world_transition );
2874 // TILESET BACKGROUND LAYER
2875 // ========================================================================================================
2876 use_mesh( &world
.shapes
);
2877 SHADER_USE( shader_tile_main
);
2880 m2x2_identity( subtransform
);
2881 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main
, "uSubTransform" ), 1, GL_FALSE
, (float *)subtransform
);
2882 glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_main
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2883 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uGhost" ), 0.0f
);
2884 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uForeground" ), 0.0f
);
2885 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uVisibility" ), world
.st
.world_transition
* 2.0f
);
2888 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
2889 glBlendEquation(GL_FUNC_ADD
);
2892 vg_tex2d_bind( &tex_tile_data
, 0 );
2893 vg_tex2d_bind( theme
->tex_tiles
, 1 );
2894 vg_tex2d_bind( &tex_tile_glow
, 2 );
2896 glUniform1i( SHADER_UNIFORM( shader_tile_main
, "uTexGlyphs" ), 0 );
2897 glUniform1i( SHADER_UNIFORM( shader_tile_main
, "uTexWood" ), 1 );
2898 glUniform1i( SHADER_UNIFORM( shader_tile_main
, "uTexGlow" ), 2 );
2900 glUniform3fv( SHADER_UNIFORM( shader_tile_main
, "uShadowing" ), 1, theme
->col_shadow
);
2902 render_tiles( colour_default
, colour_default
, 1 );
2905 // ========================================================================================================
2906 SHADER_USE( shader_ball
);
2907 glUniformMatrix3fv( SHADER_UNIFORM( shader_ball
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2909 vg_tex2d_bind( &tex_ball_noise
, 0 );
2910 glUniform1i( SHADER_UNIFORM( shader_ball
, "uTexMain" ), 0 );
2912 if( world
.st
.buttons
[ k_world_button_sim
].state
)
2914 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2916 struct fish
*fish
= &world
.fishes
[i
];
2918 render_pos
[2] = 1.0f
;
2920 if( fish
->state
== k_fish_state_dead
|| fish
->state
== k_fish_state_soon_dead
)
2922 float death_anim_time
= world
.sim_internal_time
- fish
->death_time
;
2925 if( death_anim_time
> 0.0f
&& death_anim_time
< 1.0f
)
2927 float amt
= 1.0f
-death_anim_time
*death_anim_time
;
2929 v2_muladds( fish
->physics_co
, fish
->physics_v
, -1.0f
* world
.sim_internal_delta
* amt
, fish
->physics_co
);
2930 render_pos
[2] = amt
;
2932 else if( world
.sim_internal_time
> fish
->death_time
)
2935 else if( fish
->state
== k_fish_state_bg
)
2938 v2_copy( fish
->physics_co
, render_pos
);
2940 v4f dot_colour
= { 0.0f
, 0.0f
, 0.0f
, 1.0f
};
2941 colour_code_v3( fish
->colour
, dot_colour
);
2943 glUniform3fv( SHADER_UNIFORM( shader_ball
, "uColour" ), 1, dot_colour
);
2944 glUniform3fv( SHADER_UNIFORM( shader_ball
, "uOffset" ), 1, render_pos
);
2945 glUniform2f( SHADER_UNIFORM( shader_ball
, "uTexOffset" ), (float)i
* 1.2334, (float)i
* -0.3579f
);
2950 // TILESET FOREGROUND LAYER
2951 // ========================================================================================================
2952 SHADER_USE( shader_tile_main
);
2955 vg_tex2d_bind( &tex_tile_data
, 0 );
2956 vg_tex2d_bind( theme
->tex_tiles
, 1 );
2957 vg_tex2d_bind( &tex_tile_glow
, 2 );
2959 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uForeground" ), 1.0f
);
2960 render_tiles( colour_default
, colour_selected
, 0 );
2963 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
2965 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
2966 struct cell
*cell
= cmd
->ptr
;
2968 if( cell
->config
== k_cell_type_split
)
2970 float rotation
= cell
->state
& FLAG_FLIP_FLOP
? vg_rad( -45.0f
): vg_rad( 45.0f
);
2972 if( cell
->state
& FLAG_FLIP_ROTATING
)
2974 if( (world
.frame_lerp
> curve_7_linear_section
) )
2976 float const rotation_speed
= 0.4f
;
2977 if( (world
.frame_lerp
< 1.0f
-rotation_speed
) )
2979 float t
= world
.frame_lerp
- curve_7_linear_section
;
2980 t
*= -2.0f
* (1.0f
/(1.0f
-(curve_7_linear_section
+rotation_speed
)));
2990 m2x2_create_rotation( subtransform
, rotation
);
2992 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main
,"uSubTransform" ),
2993 1, GL_FALSE
, (float *)subtransform
);
2995 glUniform4f( SHADER_UNIFORM( shader_tile_main
, "uOffset" ),
2997 (float)cmd
->pos
[1] + 0.125f
,
2998 cell
->state
& FLAG_TARGETED
? 3.0f
: 2.0f
,
3006 // ========================================================================================================
3007 if( world
.selected
!= -1 && !(world
.data
[ world
.selected
].state
& FLAG_CANAL
) && !world
.id_drag_from
)
3009 v2i new_begin
= { world
.tile_x
- 2, world
.tile_y
- 2 };
3010 v2i new_end
= { world
.tile_x
+ 2, world
.tile_y
+ 2 };
3012 world
.data
[ world
.selected
].state
^= FLAG_CANAL
;
3013 map_reclassify( new_begin
, new_end
, 0 );
3015 m2x2_identity( subtransform
);
3016 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uGhost" ), 1.0f
);
3017 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main
, "uSubTransform" ), 1, GL_FALSE
, (float *)subtransform
);
3018 glUniform2fv( SHADER_UNIFORM( shader_tile_main
, "uMousePos" ), 1, world
.tile_pos
);
3020 render_tile_block( new_begin
, new_end
, colour_default
, colour_default
);
3022 world
.data
[ world
.selected
].state
^= FLAG_CANAL
;
3023 map_reclassify( new_begin
, new_end
, 0 );
3027 // ========================================================================================================
3028 SHADER_USE( shader_buttons
);
3029 glUniformMatrix3fv( SHADER_UNIFORM( shader_buttons
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
3031 vg_tex2d_bind( &tex_buttons
, 0 );
3032 glUniform1i( SHADER_UNIFORM( shader_buttons
, "uTexMain" ), 0 );
3034 enum world_button_status stat
;
3035 int world_paused
= world
.st
.buttons
[k_world_button_pause
].state
;
3036 int world_running
= world
.st
.buttons
[k_world_button_sim
].state
;
3038 float sim_icon_x
= world_paused
? 3.0f
: (world_running
? 2.0f
: 0.0f
);
3040 v3f btn_dark_blue
= { 0.204f
, 0.345f
, 0.553f
};
3041 v3f btn_orange
= { 0.553f
, 0.345f
, 0.204f
};
3043 if( world_button_exec( &world
.st
.buttons
[k_world_button_sim
], (v2f
){ sim_icon_x
, 3.0f
}, btn_dark_blue
, &stat
))
3045 if( stat
== k_world_button_on_enable
)
3050 world
.pause_offset_target
= 0.5f
;
3056 // Trigger single step
3057 world
.pause_offset_target
+= 1.0f
;
3058 world
.st
.buttons
[k_world_button_sim
].state
= 1;
3067 if( world_button_exec( &world
.st
.buttons
[k_world_button_pause
], (v2f
){ 1.0f
, 3.0f
}, btn_dark_blue
, &stat
))
3069 world
.sim_internal_ref
= world
.sim_internal_time
;
3070 world
.sim_delta_ref
= vg_time
;
3072 if( stat
== k_world_button_on_enable
)
3074 float time_frac
= world
.sim_internal_time
-floorf(world
.sim_internal_time
);
3075 world
.pause_offset_target
= 0.5f
- time_frac
;
3078 world
.pause_offset_target
= 0.0f
;
3081 if( world_button_exec( &world
.st
.buttons
[k_world_button_speedy
], (v2f
){ 0.0f
, 2.0f
}, btn_orange
, &stat
))
3083 world
.sim_delta_speed
= stat
== k_world_button_on_enable
? 10.0f
: 2.5f
;
3087 world
.sim_delta_ref
= vg_time
;
3088 world
.sim_internal_ref
= world
.sim_internal_time
;
3092 if( world_button_exec( &world
.st
.buttons
[k_world_button_settings
], (v2f
){ 1.0f
, 2.0f
}, btn_orange
, &stat
))
3094 world
.st
.state
= stat
== k_world_button_on_enable
?
3095 k_game_state_settings
: k_game_state_main
;
3098 level_selection_buttons();
3100 if( vg_get_button_up( "primary" ) )
3101 world_button_exec( NULL
, NULL
, NULL
, NULL
);
3104 // ========================================================================================================
3106 //glEnable(GL_BLEND);
3107 SHADER_USE( shader_tile_colour
);
3108 glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
3110 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
3112 struct cell_terminal
*term
= &world
.io
[ i
];
3113 struct cell
*cell
= pcell(term
->pos
);
3115 int is_input
= cell
->state
& FLAG_INPUT
;
3116 v4f dot_colour
= { 0.0f
, 0.0f
, 0.0f
, 1.0f
};
3118 if( cell
->state
& FLAG_EMITTER
)
3120 for( int j
= 0; j
< 2; j
++ )
3122 if( cell
->emit
[j
] != -1 )
3124 colour_code_v3( cell
->emit
[j
], dot_colour
);
3126 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
3127 term
->pos
[0] + 0.25f
+ (float)j
* 0.5f
,
3128 term
->pos
[1] + 0.25f
,
3132 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
3133 draw_mesh( filled_start
, filled_count
);
3139 for( int k
= 0; k
< term
->run_count
; k
++ )
3141 float arr_base
= is_input
? 1.2f
: -0.2f
,
3142 run_offset
= (is_input
? 0.2f
: -0.2f
) * (float)k
,
3143 y_position
= is_input
?
3144 (arr_base
+ (float)term
->pos
[1] + (float)(term
->run_count
-1)*0.2f
) - run_offset
:
3145 (float)term
->pos
[1] + arr_base
+ run_offset
;
3150 if( is_simulation_running() )
3152 if( k
== world
.sim_run
)
3154 float a
= fabsf(sinf( vg_time
* 2.0f
)) * 0.075f
+ 0.075f
;
3156 v4_copy( (v4f
){ 1.0f
, 1.0f
, 1.0f
, a
}, bar_colour
);
3159 v4_copy( (v4f
){ 0.0f
, 0.0f
, 0.0f
, 0.13f
}, bar_colour
);
3163 else if( 1 || k
& 0x1 )
3166 v4_copy( (v4f
){ 1.0f
, 1.0f
, 1.0f
, 0.07f
}, bar_colour
);
3168 v4_copy( (v4f
){ 0.0f
, 0.0f
, 0.0f
, 0.13f
}, bar_colour
);
3175 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, bar_colour
);
3176 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
3177 (float)term
->pos
[0], y_position
- 0.1f
, 1.0f
);
3182 for( int j
= 0; j
< term
->runs
[k
].step_count
; j
++ )
3184 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
3185 (float)term
->pos
[0] + 0.2f
+ 0.2f
* (float)j
,
3192 i8 colour
= term
->runs
[k
].steps
[j
];
3195 colour_code_v3( colour
, dot_colour
);
3196 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
3198 // Draw filled if tick not passed, draw empty if empty
3199 if( (world
.sim_frame
> j
&& world
.sim_run
>= k
) || world
.sim_run
> k
)
3200 draw_mesh( empty_start
, empty_count
);
3202 draw_mesh( filled_start
, filled_count
);
3208 if( term
->runs
[k
].recv_count
> j
)
3210 colour_code_v3( term
->runs
[k
].recieved
[j
], dot_colour
);
3211 v3_muls( dot_colour
, 0.8f
, dot_colour
);
3212 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
3214 draw_mesh( filled_start
, filled_count
);
3217 colour_code_v3( term
->runs
[k
].steps
[j
], dot_colour
);
3218 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
3220 draw_mesh( empty_start
, empty_count
);
3227 // ========================================================================================================
3228 SHADER_USE( shader_sprite
);
3229 glUniformMatrix3fv( SHADER_UNIFORM( shader_sprite
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
3231 vg_tex2d_bind( &tex_sprites
, 0 );
3232 glUniform1i( SHADER_UNIFORM( shader_sprite
, "uTexMain" ), 0 );
3234 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3236 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3237 struct cell
*cell
= cmd
->ptr
;
3239 if( (cell
->config
== k_cell_type_split
) || (cell
->state
& FLAG_EMITTER
) )
3241 v2f center
= { cmd
->pos
[0] + 0.5f
, cmd
->pos
[1] + 0.5f
};
3243 v3f p0
= { 0.0f
, 0.0f
, 4.0f
};
3244 v3f p1
= { 0.0f
, 0.0f
, 4.0f
};
3246 v2_add( center
, (v2f
){ -0.25f
, -0.25f
}, p0
);
3247 v2_add( center
, (v2f
){ 0.25f
, -0.25f
}, p1
);
3249 render_sprite( k_sprite_jack_1
, p0
);
3250 render_sprite( k_sprite_jack_2
, p1
);
3252 else if( cell
->state
& FLAG_IS_TRIGGER
)
3254 v3f p0
= { 0.0f
, 0.0f
, 4.0f
};
3256 struct cell_description
*desc
= &cell_descriptions
[ cell
->config
];
3258 v2_add( (v2f
){ cmd
->pos
[0], cmd
->pos
[1] }, desc
->trigger_pos
, p0
);
3259 render_sprite( desc
->trigger_sprite
, p0
);
3264 // ========================================================================================================
3267 m3x3_identity( mvp_text
);
3268 m3x3_scale( mvp_text
, (v3f
){
3269 1.0f
/ ((float)UI_GLYPH_SPACING_X
*4.0f
),
3270 1.0f
/ -((float)UI_GLYPH_SPACING_X
*4.0f
),
3274 m3x3_mul( vg_pv
, mvp_text
, mvp_text
);
3275 ui_draw( &world
.st
.world_text
, mvp_text
);
3278 // ========================================================================================================
3281 SHADER_USE( shader_wire
);
3282 glBindVertexArray( world
.wire
.vao
);
3284 glUniformMatrix3fv( SHADER_UNIFORM( shader_wire
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
3286 v4f
const wire_left_colour
= { 0.9f
, 0.9f
, 0.9f
, 1.0f
};
3287 v4f
const wire_right_colour
= { 0.5f
, 0.5f
, 0.5f
, 1.0f
};
3288 v4f
const wire_drag_colour
= { 0.3f
, 0.3f
, 0.3f
, 0.6f
};
3290 glUniform1f( SHADER_UNIFORM( shader_wire
, "uTime" ), world
.frame_lerp
);
3291 glUniform1f( SHADER_UNIFORM( shader_wire
, "uGlow" ), 0.0f
);
3293 if( world
.id_drag_from
)
3295 glUniform4fv( SHADER_UNIFORM( shader_wire
, "uColour" ), 1, wire_drag_colour
);
3296 glUniform1f( SHADER_UNIFORM( shader_wire
, "uCurve" ), 0.4f
);
3297 glUniform3f( SHADER_UNIFORM( shader_wire
, "uStart" ), world
.drag_from_co
[0], world
.drag_from_co
[1], 0.20f
*world
.st
.world_transition
);
3298 glUniform3f( SHADER_UNIFORM( shader_wire
, "uEnd" ), world
.drag_to_co
[0], world
.drag_to_co
[1], 0.20f
*world
.st
.world_transition
);
3299 glDrawElements( GL_TRIANGLES
, world
.wire
.em
, GL_UNSIGNED_SHORT
, (void*)(0) );
3302 // Pulling animation
3303 float rp_x1
= world
.frame_lerp
*9.0f
;
3304 float rp_xa
= rp_x1
*expf(1.0f
-rp_x1
)* 0.36f
;
3305 float rp_x2
= 1.0f
-rp_xa
;
3307 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3309 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3310 struct cell
*cell
= cmd
->ptr
;
3312 if( cell
->state
& FLAG_TARGETED
)
3314 for( int j
= 0; j
< 2; j
++ )
3316 if( !cell
->links
[j
] )
3319 struct cell
*other_cell
= &world
.data
[ cell
->links
[ j
]];
3320 struct cell_description
*desc
= &cell_descriptions
[ other_cell
->config
];
3322 int x2
= cell
->links
[j
] % world
.w
;
3323 int y2
= (cell
->links
[j
] - x2
) / world
.w
;
3328 endpoint
[0] = (float)cmd
->pos
[0] + (j
? 0.75f
: 0.25f
);
3329 endpoint
[1] = (float)cmd
->pos
[1] + 0.25f
;
3334 v2_add( desc
->trigger_pos
, startpoint
, startpoint
);
3336 if( cmd
->ptr
->state
& FLAG_EMITTER
)
3339 colour_code_v3( cmd
->ptr
->emit
[j
], wire_colour
);
3340 wire_colour
[3] = 1.0f
;
3342 glUniform4fv( SHADER_UNIFORM( shader_wire
, "uColour" ), 1, wire_colour
);
3345 glUniform4fv( SHADER_UNIFORM( shader_wire
, "uColour" ), 1, j
? wire_right_colour
: wire_left_colour
);
3347 glUniform1f( SHADER_UNIFORM( shader_wire
, "uCurve" ), other_cell
->state
& FLAG_TRIGGERED
? rp_x2
* 0.4f
: 0.4f
);
3348 glUniform1f( SHADER_UNIFORM( shader_wire
, "uGlow" ), other_cell
->state
& FLAG_TRIGGERED
? rp_xa
: 0.0f
);
3349 glUniform3f( SHADER_UNIFORM( shader_wire
, "uEnd" ), startpoint
[0], startpoint
[1], 0.18f
*world
.st
.world_transition
);
3350 glUniform3f( SHADER_UNIFORM( shader_wire
, "uStart" ), endpoint
[0], endpoint
[1], 0.18f
*world
.st
.world_transition
);
3351 glDrawElements( GL_TRIANGLES
, world
.wire
.em
, GL_UNSIGNED_SHORT
, (void*)(0) );
3357 // ========================================================================================================
3359 SHADER_USE( shader_tile_colour
);
3360 use_mesh( &world
.shapes
);
3362 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3364 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3365 struct cell
*cell
= cmd
->ptr
;
3367 if( cell
->state
& FLAG_TARGETED
)
3369 for( int j
= 0; j
< 2; j
++ )
3371 if( !cell
->links
[j
] )
3374 struct cell
*other_cell
= &world
.data
[ cell
->links
[ j
]];
3375 struct cell_description
*desc
= &cell_descriptions
[ other_cell
->config
];
3377 int x2
= cell
->links
[j
] % world
.w
;
3378 int y2
= (cell
->links
[j
] - x2
) / world
.w
;
3382 pts
[0][0] = (float)cmd
->pos
[0] + (j
? 0.75f
: 0.25f
);
3383 pts
[0][1] = (float)cmd
->pos
[1] + 0.25f
;
3388 v2_add( desc
->trigger_pos
, pts
[1], pts
[1] );
3390 if( cell
->state
& FLAG_EMITTER
)
3393 colour_code_v3( cell
->emit
[j
], wire_colour
);
3395 v3_muls( wire_colour
, 0.8f
, wire_colour
);
3396 wire_colour
[3] = 1.0f
;
3398 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, wire_colour
);
3401 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1,j
?wire_right_colour
: wire_left_colour
);
3403 for( int i
= 0; i
< 2; i
++ )
3405 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
3408 0.08f
* world
.st
.world_transition
3410 draw_mesh( filled_start
, filled_count
);
3416 // SUB SPLITTER DIRECTION
3417 // ========================================================================================================
3420 glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.9f, 0.35f, 0.1f, 0.75f );
3422 for( int i = 0; i < world.tile_special_count; i ++ )
3424 struct render_cmd *cmd = &world.cmd_buf_specials[i];
3425 struct cell *cell = cmd->ptr;
3427 if( cell->state & FLAG_TARGETED && cell->config == k_cell_type_split )
3429 glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), cmd->pos[0], cmd->pos[1], 1.0f );
3430 draw_mesh( cell->state & FLAG_FLIP_FLOP? 5: 4, 1 );
3436 // ========================================================================================================
3437 glBlendFunc(GL_ONE
, GL_ONE
);
3438 glBlendEquation(GL_FUNC_ADD
);
3440 SHADER_USE( shader_sprite
);
3442 vg_tex2d_bind( &tex_sprites
, 0 );
3443 glUniform1i( SHADER_UNIFORM( shader_sprite
, "uTexMain" ), 0 );
3445 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3447 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3448 struct cell
*cell
= cmd
->ptr
;
3450 if( cell
->config
== k_cell_type_split
)
3452 v2f center
= { cmd
->pos
[0] + 0.5f
, cmd
->pos
[1] + 0.5f
};
3454 v3f p0
= { 0.0f
, 0.0f
, 12.0f
};
3455 v3f p1
= { 0.0f
, 0.0f
, 12.0f
};
3457 v2_add( center
, (v2f
){ -0.25f
, -0.25f
}, p0
);
3458 v2_add( center
, (v2f
){ 0.25f
, -0.25f
}, p1
);
3460 if( cell
->state
& FLAG_TARGETED
)
3462 if( cell
->state
& FLAG_FLIP_FLOP
)
3463 render_sprite( k_sprite_flare_y
, p1
);
3465 render_sprite( k_sprite_flare_b
, p0
);
3468 render_sprite( k_sprite_flare_w
, cell
->state
&FLAG_FLIP_FLOP
? p1
: p0
);
3472 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
3473 glBlendEquation(GL_FUNC_ADD
);
3475 glDisable(GL_BLEND
);
3479 float const score_bright = 1.25f;
3480 glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ),
3481 0.4f*score_bright, 0.39f*score_bright, 0.45f*score_bright, 1.0f );
3483 use_mesh( &world.numbers );
3484 draw_numbers( (v3f){ 2.0f, (float)world.h-1.875f, 0.3333f }, world.score );
3489 if( enable_vignette
)
3490 goto image_composite
;
3495 /* Scale down image and remap colour values */
3497 vg_window_x
/EFFECT_BUFFER_RATIO
, vg_window_y
/EFFECT_BUFFER_RATIO
);
3498 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.bloomframebuffer
[0] );
3500 SHADER_USE( shader_post_darken
);
3501 glActiveTexture( GL_TEXTURE0
);
3502 glBindTexture( GL_TEXTURE_2D
, world
.st
.colourbuffer
);
3503 glUniform1i( SHADER_UNIFORM( shader_post_darken
, "uTexMain" ), 0 );
3508 v2f res_inv
, blur_dir
;
3509 res_inv
[0] = 1.0f
/ (float)( vg_window_x
/EFFECT_BUFFER_RATIO
);
3510 res_inv
[1] = 1.0f
/ (float)( vg_window_y
/EFFECT_BUFFER_RATIO
);
3512 SHADER_USE( shader_post_blur
);
3513 glUniform1i( SHADER_UNIFORM( shader_post_blur
, "uTexMain" ), 0 );
3515 for( int i
=0; i
<1; i
++ )
3517 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.bloomframebuffer
[1] );
3519 v2_mul( (v2f
){ 1.0f
*(float)(i
+1), 0.0f
}, res_inv
, blur_dir
);
3521 glUniform2fv( SHADER_UNIFORM(shader_post_blur
,"uDir"), 1, blur_dir
);
3522 glActiveTexture( GL_TEXTURE0
);
3523 glBindTexture( GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[0] );
3527 v2_mul( (v2f
){ 0.0f
, 1.0f
*(float)(i
+1) }, res_inv
, blur_dir
);
3529 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.bloomframebuffer
[0] );
3530 glUniform2fv( SHADER_UNIFORM(shader_post_blur
,"uDir"), 1, blur_dir
);
3531 glBindTexture( GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[1] );
3535 /* Scene composite */
3536 glViewport( 0,0, vg_window_x
, vg_window_y
);
3539 glBindFramebuffer( GL_FRAMEBUFFER
, 0 );
3541 SHADER_USE( shader_post_comp
);
3543 glActiveTexture( GL_TEXTURE0
);
3544 glBindTexture( GL_TEXTURE_2D
, world
.st
.colourbuffer
);
3545 glUniform1i( SHADER_UNIFORM( shader_post_comp
, "uTexMain" ), 0 );
3547 glActiveTexture( GL_TEXTURE1
);
3548 glBindTexture( GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[0] );
3549 glUniform1i( SHADER_UNIFORM( shader_post_comp
, "uTexBloom" ), 1 );
3551 glUniform2f( SHADER_UNIFORM( shader_post_comp
, "uComp" ),
3552 enable_bloom
? 1.0f
: 0.0f
,
3553 enable_vignette
? 0.0f
: 1.0f
);
3560 // Drawing world name
3561 if( world
.pCmpLevel
)
3563 gui_text( (ui_px
[2]){ vg_window_x
/ 2, 4 }, world
.pCmpLevel
->title
, 2, k_text_align_center
);
3564 gui_text( (ui_px
[2]){ vg_window_x
/ 2, 28 }, world
.pCmpLevel
->description
, 1, k_text_align_center
);
3568 if( world
.st
.state
== k_game_state_update
)
3572 ui_global_ctx
.cursor
[2] = 458;
3573 ui_global_ctx
.cursor
[3] = 316;
3574 ui_global_ctx
.cursor
[0] = vg_window_x
/ 2 - 229;
3575 ui_global_ctx
.cursor
[1] = vg_window_y
/ 2 - 158;
3579 gui_capture_mouse( 200 );
3580 gui_fill_rect( ui_global_ctx
.cursor
, 0xE8303030 );
3583 title_pos
[0] = ui_global_ctx
.cursor
[0] + 229;
3584 title_pos
[1] = ui_global_ctx
.cursor
[1] + 16;
3586 gui_text( title_pos
, "Update 1.5", 2, k_text_align_center
);
3588 gui_text( (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 16, title_pos
[1] + 45 },
3589 "Welcome to the first update to marble computing!"
3591 "New features have been added:\n"
3593 " - Settings menu\n"
3595 " - More levels and a new block type\n"
3596 " - Scores for each level\n"
3597 " - Zooming and panning (mousewheel)\n"
3599 "There is much more in the works, such as a\n"
3600 "soundtrack, and the rest of the levels for the\n"
3603 "Thank you everyone for enjoying my game :)\n",
3604 1, k_text_align_left
3607 ui_global_ctx
.cursor
[2] = 100;
3608 ui_global_ctx
.cursor
[3] = 30;
3609 ui_global_ctx
.cursor
[0] += 229 - 50;
3610 ui_global_ctx
.cursor
[1] += 316 - 30 - 16;
3612 if( gui_button( 1 ) )
3614 world
.st
.state
= k_game_state_main
;
3616 gui_text( (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 50,
3617 ui_global_ctx
.cursor
[1] + 4 }, "OK", 1, k_text_align_center
);
3624 if( world
.st
.state
== k_game_state_settings
)
3628 ui_global_ctx
.cursor
[2] = 225;
3634 gui_capture_mouse( 200 );
3636 gui_fill_rect( ui_global_ctx
.cursor
, 0xC0202020 );
3637 ui_rect_pad( ui_global_ctx
.cursor
, 8 );
3639 ui_global_ctx
.cursor
[3] = 25;
3643 gui_text( ui_global_ctx
.cursor
, "SETTINGS", 2, 0 );
3645 ui_global_ctx
.cursor
[2] = 25;
3648 if( gui_button(4) == k_button_click
)
3650 world
.st
.buttons
[ k_world_button_settings
].state
= 0;
3651 world
.st
.state
= k_game_state_main
;
3652 vg_info( "exit\n" );
3654 ui_global_ctx
.cursor
[0] += 4;
3655 ui_global_ctx
.cursor
[1] -= 4;
3656 gui_text( ui_global_ctx
.cursor
, "x", 2, 0 );
3661 // Colour scheme selection
3662 ui_global_ctx
.cursor
[1] += 30;
3664 gui_text( ui_global_ctx
.cursor
, "Colour Scheme", 1, 0 );
3665 ui_global_ctx
.cursor
[1] += 25;
3669 ui_global_ctx
.cursor
[2] = 50;
3671 for( int i
= 0; i
< 4; i
++ )
3676 u32 rgb
= 0xff000000;
3678 for( int j
= 0; j
< 3; j
++ )
3679 rgb
|= (u32
)(colour_sets
[ colour_set_id
][i
][j
]*255.0f
) << j
* 8;
3681 gui_fill_rect( ui_global_ctx
.cursor
, rgb
);
3690 ui_global_ctx
.cursor
[2] = 25;
3691 if( gui_button( 0 ) == k_button_click
)
3693 if( colour_set_id
> 0 )
3696 gui_text( ui_global_ctx
.cursor
, "<", 2, 0 );
3699 ui_global_ctx
.cursor
[2] = 150;
3702 gui_fill_rect( ui_global_ctx
.cursor
, 0x33ffffff );
3704 (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 75, ui_global_ctx
.cursor
[1] + 6 },
3705 (const char *[]){ "Normal", "Extra1", "Extra2" }[ colour_set_id
],
3706 1, k_text_align_center
3711 ui_global_ctx
.cursor
[2] = 25;
3712 if( gui_button( 1 ) == k_button_click
)
3714 if( colour_set_id
< vg_list_size( colour_sets
)-1 )
3717 gui_text( ui_global_ctx
.cursor
, ">", 2, 0 );
3723 ui_global_ctx
.cursor
[1] += 16;
3726 gui_text( ui_global_ctx
.cursor
, "Tile Theme", 1, 0 );
3727 ui_global_ctx
.cursor
[1] += 20;
3731 ui_global_ctx
.cursor
[2] = 25;
3732 if( gui_button( 2 ) == k_button_click
)
3734 if( world_theme_id
> 0 )
3737 gui_text( ui_global_ctx
.cursor
, "<", 2, 0 );
3740 ui_global_ctx
.cursor
[2] = 150;
3743 gui_fill_rect( ui_global_ctx
.cursor
, 0x33ffffff );
3745 (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 75, ui_global_ctx
.cursor
[1] + 6 },
3746 world_themes
[ world_theme_id
].name
, 1, k_text_align_center
3751 ui_global_ctx
.cursor
[2] = 25;
3752 if( gui_button( 3 ) == k_button_click
)
3754 if( world_theme_id
< vg_list_size( world_themes
)-1 )
3757 gui_text( ui_global_ctx
.cursor
, ">", 2, 0 );
3763 gui_text( ui_global_ctx
.cursor
, "Graphics", 1, 0 );
3764 ui_global_ctx
.cursor
[1] += 20;
3768 ui_global_ctx
.cursor
[2] = 200;
3769 if( gui_button( 5 ) == k_button_click
)
3771 enable_bloom
^= 0x1;
3773 ui_global_ctx
.cursor
[0] += 4;
3774 ui_global_ctx
.cursor
[1] += 4;
3775 gui_text( ui_global_ctx
.cursor
, enable_bloom
?
3777 "Bloom: DISABLED", 1, 0 );
3782 ui_global_ctx
.cursor
[1] += 10;
3785 ui_global_ctx
.cursor
[2] = 200;
3786 if( gui_button( 6 ) == k_button_click
)
3788 enable_vignette
^= 0x1;
3790 ui_global_ctx
.cursor
[0] += 4;
3791 ui_global_ctx
.cursor
[1] += 4;
3792 gui_text( ui_global_ctx
.cursor
, enable_vignette
?
3793 "Vignette: ENABLED":
3794 "Vignette: DISABLED", 1, 0 );
3799 ui_global_ctx
.cursor
[1] += 16;
3800 gui_text( ui_global_ctx
.cursor
, "Music Volume", 1, 0 );
3801 ui_global_ctx
.cursor
[1] += 20;
3805 ui_px slider_start
= ui_global_ctx
.cursor
[0];
3807 float const bar_width
= 45.0f
,
3809 bar_movement
= bar_total
-bar_width
,
3810 bar_start
= bar_width
* 0.5f
;
3812 ui_global_ctx
.cursor
[2] = bar_total
;
3813 ui_fill_rect( &ui_global_ctx
,
3814 ui_global_ctx
.cursor
,
3817 ui_global_ctx
.cursor
[2] = bar_width
;
3818 ui_global_ctx
.cursor
[0] = slider_start
+ music_volume
* bar_movement
;
3820 int status
= gui_button( 7 );
3822 static ui_px drag_start
= 0.0f
;
3824 if( status
== k_button_start_click
)
3825 drag_start
= ui_global_ctx
.mouse
[0];
3826 else if( ui_global_ctx
.capture_lock
&&
3827 (ui_global_ctx
.capture_mouse_id
== ui_group_id(&ui_global_ctx
,7)))
3829 ui_px drag_offset
= ui_global_ctx
.mouse
[0] - drag_start
;
3830 float offset_local
= (drag_start
+ drag_offset
- slider_start
- bar_start
) / bar_movement
;
3832 music_volume
= vg_minf( vg_maxf( offset_local
, 0.0f
), 1.0f
);
3833 music_volume_update();
3836 ui_global_ctx
.cursor
[0] += 4;
3837 ui_global_ctx
.cursor
[1] += 4;
3840 snprintf( volbuf
, 12, "%.2f", music_volume
);
3841 gui_text( ui_global_ctx
.cursor
, volbuf
, 1, 0 );
3850 #if STEAM_LEADERBOARDS
3851 void leaderboard_dispatch_score(void)
3854 sw_upload_leaderboard_score(
3855 ui_data
.upload_request
.level
->steam_leaderboard
,
3856 k_ELeaderboardUploadScoreMethodKeepBest
,
3857 ui_data
.upload_request
.score
,
3862 ui_data
.upload_request
.is_waiting
= 0;
3864 vg_success( "Dispatched leaderboard score\n" );
3867 void leaderboard_found( LeaderboardFindResult_t
*pCallback
)
3869 if( !pCallback
->m_bLeaderboardFound
)
3871 vg_error( "Leaderboard could not be found\n" );
3872 ui_data
.steam_leaderboard
= 0;
3876 const char *recieved_name
= sw_get_leaderboard_name( pCallback
->m_hSteamLeaderboard
);
3878 // Update UI state and request entries if this callback found the current UI level
3879 if( ui_data
.level_selected
)
3881 if( !strcmp( recieved_name
, ui_data
.level_selected
->map_name
) )
3883 sw_download_leaderboard_entries( pCallback
->m_hSteamLeaderboard
, k_ELeaderboardDataRequestFriends
, 0, 8 );
3884 ui_data
.level_selected
->steam_leaderboard
= pCallback
->m_hSteamLeaderboard
;
3888 // Dispatch the waiting request if there was one
3889 if( ui_data
.upload_request
.is_waiting
)
3891 if( !strcmp( recieved_name
, ui_data
.upload_request
.level
->map_name
) )
3893 ui_data
.upload_request
.level
->steam_leaderboard
= pCallback
->m_hSteamLeaderboard
;
3894 leaderboard_dispatch_score();
3900 void leaderboard_downloaded( LeaderboardScoresDownloaded_t
*pCallback
)
3902 // Update UI if this leaderboard matches what we currently have in view
3903 if( ui_data
.level_selected
->steam_leaderboard
== pCallback
->m_hSteamLeaderboard
)
3905 vg_info( "Recieved %d entries\n", pCallback
->m_cEntryCount
);
3906 ui_data
.leaderboard_count
= VG_MIN( pCallback
->m_cEntryCount
, 8 );
3908 u64_steamid local_player
= sw_get_steamid();
3910 for( int i
= 0; i
< ui_data
.leaderboard_count
; i
++ )
3912 LeaderboardEntry_t entry
;
3913 sw_get_downloaded_entry( pCallback
->m_hSteamLeaderboardEntries
, i
, &entry
, NULL
, 0 );
3915 struct leaderboard_player
*player
= &ui_data
.leaderboard_players
[i
];
3917 player
->id
= entry
.m_steamIDUser
.m_unAll64Bits
;
3918 strncpy( player
->player_name
, sw_get_friend_persona_name( player
->id
), vg_list_size( player
->player_name
)-1 );
3919 player
->score
= entry
.m_nScore
;
3921 snprintf( player
->score_text
, vg_list_size(player
->score_text
), "%d", player
->score
);
3922 player
->texture
= sw_get_player_image( player
->id
);
3924 if( player
->texture
== 0 )
3925 player
->texture
= tex_unkown
.name
;
3927 player
->is_local_player
= local_player
== player
->id
? 1: 0;
3930 if( ui_data
.leaderboard_count
)
3931 ui_data
.leaderboard_show
= 1;
3933 ui_data
.leaderboard_show
= 0;
3935 else vg_warn( "Downloaded leaderboard does not match requested!\n" );
3938 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
)
3940 if( ui_data
.upload_request
.is_waiting
)
3941 vg_warn( "You are uploading leaderboard entries too quickly!\n" );
3943 ui_data
.upload_request
.level
= cmp_level
;
3944 ui_data
.upload_request
.score
= score
;
3945 ui_data
.upload_request
.is_waiting
= 1;
3947 // If leaderboard ID has been downloaded already then just immediately dispatch this
3948 if( cmp_level
->steam_leaderboard
)
3949 leaderboard_dispatch_score();
3951 sw_find_leaderboard( cmp_level
->map_name
);
3956 // ===========================================================================================================
3958 static int console_credits( int argc
, char const *argv
[] )
3960 vg_info( "Aknowledgements:\n" );
3961 vg_info( " GLFW zlib/libpng glfw.org\n" );
3962 vg_info( " miniaudio MIT0 miniaud.io\n" );
3963 vg_info( " QOI MIT phoboslab.org\n" );
3964 vg_info( " STB library MIT nothings.org\n" );
3968 static int console_save_map( int argc
, char const *argv
[] )
3970 if( !world
.initialzed
)
3972 vg_error( "Tried to save uninitialized map!\n" );
3976 char map_path
[ 256 ];
3978 strcpy( map_path
, "sav/" );
3979 strcat( map_path
, world
.map_name
);
3980 strcat( map_path
, ".map" );
3982 FILE *test_writer
= fopen( map_path
, "wb" );
3985 vg_info( "Saving map to '%s'\n", map_path
);
3986 map_serialize( test_writer
);
3988 fclose( test_writer
);
3993 vg_error( "Unable to open stream for writing\n" );
3998 static int console_load_map( int argc
, char const *argv
[] )
4000 char map_path
[ 256 ];
4005 strcpy( map_path
, "sav/" );
4006 strcat( map_path
, argv
[0] );
4007 strcat( map_path
, ".map" );
4009 char *text_source
= vg_textasset_read( map_path
);
4013 strcpy( map_path
, "maps/" );
4014 strcat( map_path
, argv
[0] );
4015 strcat( map_path
, ".map" );
4017 text_source
= vg_textasset_read( map_path
);
4022 vg_info( "Loading map: '%s'\n", map_path
);
4023 world
.pCmpLevel
= NULL
;
4025 if( !map_load( text_source
, argv
[0] ) )
4027 free( text_source
);
4031 free( text_source
);
4036 vg_error( "Missing maps '%s'\n", argv
[0] );
4042 vg_error( "Missing argument <map_path>\n" );
4047 static int console_changelevel( int argc
, char const *argv
[] )
4051 // Save current level
4052 console_save_map( 0, NULL
);
4054 if( console_load_map( argc
, argv
) )
4056 world
.st
.zoom
= 0.0f
;
4063 vg_error( "Missing argument <map_path>\n" );
4069 // START UP / SHUTDOWN
4070 // ===========================================================================================================
4072 #define TRANSFORM_TRI_2D( S, OX, OY, X1, Y1, X2, Y2, X3, Y3 ) \
4073 X1*S+OX, Y1*S+OY, X2*S+OX, Y2*S+OY, X3*S+OX, Y3*S+OY
4077 // Steamworks callbacks
4078 #ifdef STEAM_LEADERBOARDS
4079 sw_leaderboard_found
= &leaderboard_found
;
4080 sw_leaderboard_downloaded
= &leaderboard_downloaded
;
4083 vg_function_push( (struct vg_cmd
){
4084 .name
= "_map_write",
4085 .function
= console_save_map
4088 vg_function_push( (struct vg_cmd
){
4089 .name
= "_map_load",
4090 .function
= console_load_map
4093 vg_function_push( (struct vg_cmd
){
4095 .function
= console_changelevel
4098 vg_function_push( (struct vg_cmd
){
4100 .function
= console_credits
4103 vg_convar_push( (struct vg_convar
){
4105 .data
= &colour_set_id
,
4106 .data_type
= k_convar_dtype_i32
,
4107 .opt_i32
= { .min
= 0, .max
= 2, .clamp
= 1 },
4111 vg_convar_push( (struct vg_convar
){
4113 .data
= &world_theme_id
,
4114 .data_type
= k_convar_dtype_i32
,
4115 .opt_i32
= { .min
= 0, .max
= vg_list_size( world_themes
)-1, .clamp
= 1 },
4120 vg_convar_push( (struct vg_convar
){
4121 .name
= "enable_bloom",
4122 .data
= &enable_bloom
,
4123 .data_type
= k_convar_dtype_i32
,
4124 .opt_i32
= { .min
= 0, .max
= 1, .clamp
= 1 },
4129 vg_convar_push( (struct vg_convar
){
4130 .name
= "enable_vignette",
4131 .data
= &enable_vignette
,
4132 .data_type
= k_convar_dtype_i32
,
4133 .opt_i32
= { .min
= 0, .max
= 1, .clamp
= 1 },
4138 vg_convar_push( (struct vg_convar
){
4139 .name
= "music_volume",
4140 .data
= &music_volume
,
4141 .data_type
= k_convar_dtype_f32
,
4142 .opt_f32
= { .min
= 0.0f
, .max
= 1.0f
, .clamp
= 1 },
4144 .update
= music_volume_update
4147 // Combined quad, long quad / empty circle / filled circle mesh
4149 float combined_mesh
[6*6 + 32*6*3] = {
4150 0.0f
, 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
,
4151 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
, 0.0f
,
4153 0.0f
, 0.0f
, 0.0f
, 0.2f
, 1.0f
, 0.2f
,
4154 0.0f
, 0.0f
, 1.0f
, 0.2f
, 1.0f
, 0.0f
,
4156 TRANSFORM_TRI_2D( 0.15f
,0.05f
,0.4f
, 0.0f
, 1.0f
, 1.0f
, 2.0f
, 1.0f
, 0.0f
),
4157 TRANSFORM_TRI_2D( 0.15f
,0.80f
,0.4f
, 0.0f
, 0.0f
, 0.0f
, 2.0f
, 1.0f
, 1.0f
)
4160 float *circle_mesh
= combined_mesh
+ 6*6;
4163 for( int i
= 0; i
< res
; i
++ )
4165 v2f v0
= { sinf( ((float)i
/(float)res
)*VG_TAUf
), cosf( ((float)i
/(float)res
)*VG_TAUf
) };
4166 v2f v1
= { sinf( ((float)(i
+1)/(float)res
)*VG_TAUf
), cosf( ((float)(i
+1)/(float)res
)*VG_TAUf
) };
4168 circle_mesh
[ i
*6+0 ] = 0.0f
;
4169 circle_mesh
[ i
*6+1 ] = 0.0f
;
4171 v2_copy( v0
, circle_mesh
+ 32*6 + i
*12 );
4172 v2_muls( v0
, 0.8f
, circle_mesh
+ 32*6 + i
*12+2 );
4173 v2_copy( v1
, circle_mesh
+ 32*6 + i
*12+4 );
4175 v2_copy( v1
, circle_mesh
+ 32*6 + i
*12+6 );
4176 v2_muls( v1
, 0.8f
, circle_mesh
+ 32*6 + i
*12+8 );
4177 v2_muls( v0
, 0.8f
, circle_mesh
+ 32*6 + i
*12+10 );
4179 v2_copy( v0
, circle_mesh
+ i
*6+4 );
4180 v2_copy( v1
, circle_mesh
+ i
*6+2 );
4181 v2_copy( v0
, circle_mesh
+i
*6+4 );
4182 v2_copy( v1
, circle_mesh
+i
*6+2 );
4185 init_mesh( &world
.shapes
, combined_mesh
, vg_list_size( combined_mesh
) );
4190 int const num_segments
= 64;
4192 struct mesh_wire
*mw
= &world
.wire
;
4194 v2f wire_points
[ num_segments
* 2 ];
4195 u16 wire_indices
[ 6*(num_segments
-1) ];
4197 for( int i
= 0; i
< num_segments
; i
++ )
4199 float l
= (float)i
/ (float)(num_segments
-1);
4201 v2_copy( (v2f
){ l
, -0.5f
}, wire_points
[i
*2+0] );
4202 v2_copy( (v2f
){ l
, 0.5f
}, wire_points
[i
*2+1] );
4204 if( i
< num_segments
-1 )
4206 wire_indices
[ i
*6+0 ] = i
*2 + 0;
4207 wire_indices
[ i
*6+1 ] = i
*2 + 1;
4208 wire_indices
[ i
*6+2 ] = i
*2 + 3;
4209 wire_indices
[ i
*6+3 ] = i
*2 + 0;
4210 wire_indices
[ i
*6+4 ] = i
*2 + 3;
4211 wire_indices
[ i
*6+5 ] = i
*2 + 2;
4215 glGenVertexArrays( 1, &mw
->vao
);
4216 glGenBuffers( 1, &mw
->vbo
);
4217 glGenBuffers( 1, &mw
->ebo
);
4218 glBindVertexArray( mw
->vao
);
4220 glBindBuffer( GL_ARRAY_BUFFER
, mw
->vbo
);
4222 glBufferData( GL_ARRAY_BUFFER
, sizeof( wire_points
), wire_points
, GL_STATIC_DRAW
);
4223 glBindVertexArray( mw
->vao
);
4225 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, mw
->ebo
);
4226 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, sizeof( wire_indices
), wire_indices
, GL_STATIC_DRAW
);
4229 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
, 2*sizeof(float), (void*)0 );
4230 glEnableVertexAttribArray( 0 );
4234 mw
->em
= vg_list_size( wire_indices
);
4237 // Create info data texture
4239 glGenTextures( 1, &world
.background_data
);
4240 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
4241 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGBA
, 64, 64, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, NULL
);
4245 // Create random smaples texture
4247 u8
*data
= malloc(512*512*2);
4248 for( int i
= 0; i
< 512*512*2; i
++ )
4249 data
[ i
] = rand()/(RAND_MAX
/255);
4251 glGenTextures( 1, &world
.random_samples
);
4252 glBindTexture( GL_TEXTURE_2D
, world
.random_samples
);
4253 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RG
, 512, 512, 0, GL_RG
, GL_UNSIGNED_BYTE
, data
);
4260 resource_load_main();
4264 ui_init_context( &world
.st
.world_text
, 15000 );
4267 // Restore gamestate
4268 career_local_data_init();
4271 /* Create framebuffers */
4272 glGenFramebuffers( 1, &world
.st
.framebuffer
);
4273 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.framebuffer
);
4275 glGenTextures( 1, &world
.st
.colourbuffer
);
4276 glBindTexture( GL_TEXTURE_2D
, world
.st
.colourbuffer
);
4277 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGB
, vg_window_x
, vg_window_y
,
4278 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
4280 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
4281 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
4282 glFramebufferTexture2D( GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
, GL_TEXTURE_2D
,
4283 world
.st
.colourbuffer
, 0);
4285 /* Bloom framebuffer (quater res) */
4286 glGenFramebuffers( 2, world
.st
.bloomframebuffer
);
4287 glGenTextures( 2, world
.st
.bloomcolourbuffer
);
4289 for( int i
=0; i
<2; i
++ )
4291 glBindFramebuffer( GL_FRAMEBUFFER
, world
.st
.bloomframebuffer
[i
] );
4293 glBindTexture( GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[i
] );
4294 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGB
,
4295 vg_window_x
/EFFECT_BUFFER_RATIO
, vg_window_y
/EFFECT_BUFFER_RATIO
,
4296 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
4299 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
4300 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
4301 glFramebufferTexture2D( GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
4302 GL_TEXTURE_2D
, world
.st
.bloomcolourbuffer
[i
], 0);
4312 console_save_map( 0, NULL
);
4315 resource_free_main();
4317 glDeleteTextures( 1, &world
.background_data
);
4318 glDeleteTextures( 1, &world
.random_samples
);
4320 glDeleteVertexArrays( 1, &world
.wire
.vao
);
4321 glDeleteBuffers( 1, &world
.wire
.vbo
);
4322 glDeleteBuffers( 1, &world
.wire
.ebo
);
4324 free_mesh( &world
.shapes
);
4326 ui_context_free( &world
.st
.world_text
);
4331 int main( int argc
, char *argv
[] )
4333 vg_init( argc
, argv
, "Marble Computing" );