1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
3 #define MARBLE_COMP_VERSION 4
4 //#define VG_CAPTURE_MODE
6 #define VG_STEAM_APPID 1218140U
11 k_world_button_mode_once
,
12 k_world_button_mode_toggle
19 float light_target
, light
, extra_light
;
22 enum world_button_mode mode
;
25 enum world_button_status
27 k_world_button_on_enable
,
28 k_world_button_on_disable
31 #include "fishladder_resources.h"
33 // #define STEAM_LEADERBOARDS
36 // ===========================================================================================================
41 k_cell_type_ramp_right
= 3,
42 k_cell_type_ramp_left
= 6,
43 k_cell_type_split
= 7,
44 k_cell_type_merge
= 13,
45 k_cell_type_con_r
= 1,
46 k_cell_type_con_u
= 2,
47 k_cell_type_con_l
= 4,
53 k_fish_state_soon_dead
= -1,
54 k_fish_state_dead
= 0,
57 k_fish_state_soon_alive
62 k_world_button_none
= -1,
63 k_world_button_sim
= 0,
64 k_world_button_pause
= 1,
65 k_world_button_speedy
= 2,
66 k_world_button_settings
= 3
72 k_game_state_settings
,
76 #define FLAG_CANAL 0x1
77 #define FLAG_IS_TRIGGER 0x2
78 #define FLAG_RESERVED0 0x4
79 #define FLAG_RESERVED1 0x8
81 #define FLAG_INPUT 0x10
82 #define FLAG_OUTPUT 0x20
83 #define FLAG_WALL 0x40
84 #define FLAG_EMITTER 0x80
86 #define FLAG_FLIP_FLOP 0x100
87 #define FLAG_TRIGGERED 0x200
88 #define FLAG_FLIP_ROTATING 0x400
89 #define FLAG_TARGETED 0x800
91 #define FLAG_INPUT_NICE 0x1000
94 0000 0 | 0001 1 | 0010 2 | 0011 3
98 0100 4 | 0101 5 | 0110 6 | 0111 7
102 1000 8 | 1001 9 | 1010 10 | 1011 11
106 1100 12 | 1101 13 | 1110 14 | 1111 15
112 static struct cell_description
121 enum sprites_auto_combine_index trigger_sprite
;
123 cell_descriptions
[] =
126 { .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
},
127 { .start
= { 1, 0 }, .end
= { -1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
},
128 { .start
= { 0, 1 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
}, .trigger_sprite
= k_sprite_brk_l
},
129 { .start
= { 0, 1 }, .end
= { 1, 0 }, .trigger_pos
= { 0.25f
, 0.5f
}, .trigger_sprite
= k_sprite_brk_l
},
131 { .start
= { -1, 0 }, .end
= { 1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
},
132 { .start
= { -1, 0 }, .end
= { 1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
, .is_linear
= 1 },
133 { .start
= { 0, 1 }, .end
= { -1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
},
134 { .start
= { 0, 1 }, .is_special
= 1 },
136 { .start
= { 0, -1 }, .end
= { 0, 1 }, .trigger_pos
= { 0.25f
, 0.5f
}, .trigger_sprite
= k_sprite_brk_l
},
137 { .start
= { 1, 0 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
}, .trigger_sprite
= k_sprite_brk_l
},
138 { .start
= { 0, 1 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
}, .trigger_sprite
= k_sprite_brk_l
, .is_linear
= 1 },
141 { .start
= { -1, 0 }, .end
= { 0, -1 }, .trigger_pos
= { 0.5f
, 0.75f
}, .trigger_sprite
= k_sprite_brk_u
},
142 { .end
= { 0, -1 }, .is_special
= 1, .trigger_pos
= { 0.5f
, 0.75f
}, .trigger_sprite
= k_sprite_brk_u
},
147 v2f
const curve_3
[] = {{0.5f
,1.0f
},{0.5f
,0.625f
},{0.625f
,0.5f
},{1.0f
,0.5f
}};
148 v2f
const curve_6
[] = {{0.5f
,1.0f
},{0.5f
,0.625f
},{0.375f
,0.5f
},{0.0f
,0.5f
}};
149 v2f
const curve_9
[] = {{1.0f
,0.5f
},{0.625f
,0.5f
},{0.5f
,0.375f
},{0.5f
,0.0f
}};
150 v2f
const curve_12
[]= {{0.0f
,0.5f
},{0.375f
,0.5f
},{0.5f
,0.375f
},{0.5f
,0.0f
}};
152 v2f
const curve_1
[] = {{1.0f
,0.5f
},{0.8f
,0.5f
},{0.3f
,0.5f
},{0.2f
,0.5f
}};
153 v2f
const curve_4
[] = {{0.0f
,0.5f
},{0.3f
,0.5f
},{0.5f
,0.5f
},{0.8f
,0.5f
}};
154 v2f
const curve_2
[] = {{0.5f
,1.0f
},{0.5f
,0.8f
},{0.5f
,0.3f
},{0.5f
,0.2f
}};
155 v2f
const curve_8
[] = {{0.5f
,0.0f
},{0.5f
,0.3f
},{0.5f
,0.5f
},{0.5f
,0.8f
}};
157 v2f
const curve_7
[] = {{0.5f
,0.8438f
},{0.875f
,0.8438f
},{0.625f
,0.5f
},{1.0f
,0.5f
}};
158 v2f
const curve_7_1
[] = {{0.5f
,0.8438f
},{1.0f
-0.875f
,0.8438f
},{1.0-0.625f
,0.5f
},{0.0f
,0.5f
}};
160 float const curve_7_linear_section
= 0.1562f
;
163 // ===========================================================================================================
173 // Things that are 'static', aka, initialized once
176 struct world_button buttons
[4];
178 enum e_game_state state
;
180 struct cmp_level
*lvl_to_load
;
183 float world_transition
;
202 *cmd_buf_tiles
, *cmd_buf_specials
;
204 u32 tile_count
, tile_special_count
, max_commands
;
207 int sim_run
, max_runs
;
209 int sim_frame
, sim_target
;
210 float sim_internal_time
, // current tick-time
211 sim_internal_delta
, // time delta
212 sim_internal_ref
, // Reference point of internal time
213 sim_delta_ref
, // Reference point of vg_time when we started at current sim_speed
214 sim_delta_speed
, // Rate to apply time delta
215 frame_lerp
, // Fractional part of sim_internal_time
216 pause_offset_target
; //
225 int step_count
, recv_count
;
240 GLuint vao
, vbo
, ebo
;
245 GLuint background_data
;
246 GLuint random_samples
;
248 int selected
, tile_x
, tile_y
;
255 enum e_fish_state state
;
267 struct cmp_level
*pCmpLevel
;
281 .buttons
= { { .mode
= k_world_button_mode_toggle
},
282 { .mode
= k_world_button_mode_toggle
},
283 { .mode
= k_world_button_mode_toggle
},
284 { .mode
= k_world_button_mode_toggle
} }
289 // Forward declerations
290 // --------------------
295 static void colour_code_v3( i8 cc
, v3f target
);
296 static int hash21i( v2i p
, u32 umod
);
300 static void init_mesh( struct mesh
*m
, float const *tris
, u32 length
);
301 static void free_mesh( struct mesh
*m
);
302 static void use_mesh( struct mesh
*m
);
303 static void draw_mesh( int const start
, int const count
);
307 static void level_selection_buttons(void);
309 // Map/world interface
310 // -------------------
311 static void map_free(void);
312 static void io_reset(void);
313 static struct cell
*pcell( v2i pos
);
314 static void lcell( int id
, v2i pos
);
315 static void map_reclassify( v2i start
, v2i end
, int update_texbuffer
);
316 static void gen_level_text(void);
317 static int map_load( const char *str
, const char *name
);
318 static void map_serialize( FILE *stream
);
322 static void career_serialize(void);
323 static void career_unlock_level( struct cmp_level
*lvl
);
324 static void career_unlock_level( struct cmp_level
*lvl
);
325 static void career_pass_level( struct cmp_level
*lvl
, int score
, int upload
);
326 static void career_reset_level( struct cmp_level
*lvl
);
327 static void career_load(void);
328 static void clear_animation_flags(void);
332 static void simulation_stop(void);
333 static void simulation_start(void);
334 static int world_check_pos_ok( v2i co
, int dist
);
335 static int cell_interactive( v2i co
);
337 void vg_update(void);
338 static void render_tiles( v4f
const regular_colour
, v4f
const selected_colour
);
339 static void render_tile_block( v2i start
, v2i end
, v4f
const regular_colour
, v4f
const selected_colour
);
341 void vg_render(void);
346 #ifdef STEAM_LEADERBOARDS
347 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
348 void leaderboard_dispatch_score(void);
349 void leaderboard_found( LeaderboardFindResult_t
*pCallback
);
350 void leaderboard_downloaded( LeaderboardScoresDownloaded_t
*pCallback
);
351 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
356 static int console_credits( int argc
, char const *argv
[] );
357 static int console_save_map( int argc
, char const *argv
[] );
358 static int console_load_map( int argc
, char const *argv
[] );
359 static int console_changelevel( int argc
, char const *argv
[] );
364 int main( int argc
, char *argv
[] );
367 // ===========================================================================================================
374 // ===========================================================================================================
376 static int colour_set_id
= 0;
377 static int world_theme_id
= 0;
379 static v3f colour_sets
[][4] =
381 { { 1.0f
, 0.9f
, 0.3f
}, // Yellow
382 { 0.4f
, 0.8f
, 1.00f
}, // Blue
383 { 0.2f
, 0.9f
, 0.14f
}, // Green
384 { 0.882f
, 0.204f
, 0.922f
} // Pink
386 { { 1.0f
, 0.9f
, 0.3f
}, // Yellow
387 { 0.4f
, 0.8f
, 1.00f
}, // Blue
388 { 0.85f
, 0.85f
, 0.85f
}, // Weiss
389 { 0.2f
, 0.2f
, 0.2f
} // Black/Gray
391 { { 1.0f
, 0.9f
, 0.3f
}, // Yellow
392 { 0.827f
, 0.373f
, 0.718f
}, // Pink
393 { 0.0f
, 0.353f
, 0.71f
}, // Blue
394 { 0.863f
, 0.196f
, 0.125f
} // Red
398 static struct world_theme
409 { 0.8f
, 0.8f
, 0.8f
},
414 { 0.89f
, 0.8f
, 0.7f
},
419 { 0.7f
, 0.7f
, 0.7f
},
424 static void colour_code_v3( i8 cc
, v3f target
)
426 if( (cc
>= 0) && (cc
< vg_list_size( colour_sets
[0] )) )
428 v3_copy( colour_sets
[colour_set_id
][ cc
], target
);
432 vg_error( "Invalid colour code used '%d'\n", (int)cc
);
433 v3_copy( (v3f
){0.0f
,0.0f
,0.0f
}, target
);
436 static int hash21i( v2i p
, u32 umod
)
438 static const int random_noise
[] =
440 0x46,0xD5,0xB8,0xD3,0xF2,0xE5,0xCC,0x07,0xD0,0xB3,0x7A,0xA2,0xC3,0xDA,0xDC,0x7F,0xE0,0xB7,0x42,0xA0,0xBF,0x41,0x92,0x32,0x6F,0x0D,0x45,0xC7,0x54,0xDB,0x30,0xC2,
441 0xD5,0xDA,0x55,0x09,0xDE,0x74,0x48,0x20,0xE1,0x24,0x5C,0x4D,0x6F,0x36,0xD8,0xE9,0x8D,0x8F,0x54,0x99,0x98,0x51,0xFE,0xDB,0x26,0x04,0x65,0x57,0x56,0xF3,0x53,0x30,
442 0x3D,0x16,0xC0,0xB6,0xF2,0x47,0xCF,0x62,0xB0,0x6C,0x8F,0x4F,0x8C,0x4C,0x17,0xF0,0x19,0x7E,0x2D,0x81,0x8D,0xFB,0x10,0xD3,0x49,0x50,0x60,0xFD,0x38,0x15,0x3B,0xEE,
443 0x05,0xC1,0xCF,0x62,0x97,0x75,0xDF,0x4E,0x4D,0x89,0x5E,0x88,0x5C,0x30,0x8C,0x54,0x1D,0x39,0x41,0xEA,0xA2,0x63,0x12,0x1B,0x8E,0x35,0x22,0x9B,0x98,0xA3,0x7F,0x80,
444 0xD6,0x27,0x94,0x66,0xB5,0x1D,0x7E,0xDF,0x96,0x28,0x38,0x3A,0xA0,0xE8,0x71,0x09,0x62,0x5E,0x9D,0x53,0x58,0x1B,0x7D,0x0D,0x2D,0x99,0x77,0x83,0xC3,0x89,0xC2,0xA2,
445 0xA7,0x1D,0x78,0x80,0x37,0xC1,0x87,0xFF,0x65,0xBF,0x2C,0xF1,0xE5,0xB3,0x09,0xE0,0x25,0x92,0x83,0x0F,0x8A,0x57,0x3C,0x0B,0xC6,0xBC,0x44,0x16,0xE3,0xCE,0xC3,0x0D,
446 0x69,0xD3,0xC6,0x99,0xB8,0x46,0x44,0xC4,0xF3,0x1E,0xBF,0xF5,0xB4,0xDB,0xFB,0x93,0xA1,0x7B,0xC9,0x08,0x77,0x22,0xE5,0x02,0xEF,0x9E,0x90,0x94,0x8A,0xA6,0x3D,0x7E,
447 0xA2,0xA0,0x10,0x82,0x47,0x5C,0xAA,0xF8,0x2F,0x0D,0x9F,0x76,0xDA,0x99,0x0F,0xCB,0xE2,0x02,0x0C,0x75,0xCA,0x35,0x29,0xA6,0x49,0x83,0x6D,0x91,0xB4,0xEC,0x31,0x69,
448 0xBA,0x13,0xF3,0xC7,0x21,0x06,0xC8,0x79,0xEF,0xB1,0x9C,0x6A,0xEE,0x64,0x9A,0xDC,0x1E,0xC6,0x18,0x93,0xA9,0x7E,0x89,0x7D,0x96,0xE5,0x44,0xB8,0x00,0x15,0xAF,0x8C,
449 0x78,0x8F,0xA8,0x05,0xA7,0x07,0x25,0x9A,0xC8,0x5D,0x90,0x1A,0x41,0x53,0x30,0xD3,0x24,0x33,0x71,0xB4,0x50,0x6E,0xE4,0xEA,0x0D,0x2B,0x6D,0xF5,0x17,0x08,0x74,0x49,
450 0x71,0xC2,0xAC,0xF7,0xDC,0xB2,0x7E,0xCC,0xB6,0x1B,0xB8,0xA9,0x52,0xCF,0x6B,0x51,0xD2,0x4E,0xC9,0x43,0xEE,0x2E,0x92,0x24,0xBB,0x47,0x4D,0x0C,0x3E,0x21,0x53,0x19,
451 0xD4,0x82,0xE2,0xC6,0x93,0x85,0x0A,0xF8,0xFA,0x04,0x07,0xD3,0x1D,0xEC,0x03,0x66,0xFD,0xB1,0xFB,0x8F,0xC5,0xDE,0xE8,0x29,0xDF,0x23,0x09,0x9D,0x7C,0x43,0x3D,0x4D,
452 0x89,0xB9,0x6F,0xB4,0x6B,0x4A,0x51,0xC3,0x94,0xF4,0x7C,0x5E,0x19,0x87,0x79,0xC1,0x80,0x0C,0x45,0x12,0xEC,0x95,0xF3,0x31,0x68,0x42,0xE1,0x06,0x57,0x0E,0xA7,0xFB,
453 0x78,0x96,0x87,0x23,0xA5,0x20,0x7A,0x09,0x3A,0x45,0xE6,0xD9,0x5E,0x6A,0xD6,0xAA,0x29,0x50,0x92,0x4E,0xD0,0xB5,0x91,0xC2,0x9A,0xCF,0x07,0xFE,0xB2,0x15,0xEB,0xE4,
454 0x84,0x40,0x14,0x47,0xFA,0x93,0xB9,0x06,0x69,0xDB,0xBD,0x4E,0xEA,0x52,0x9B,0xDE,0x5B,0x50,0x36,0xAB,0xB3,0x1F,0xD2,0xCD,0x9C,0x13,0x07,0x7E,0x8B,0xED,0x72,0x62,
455 0x74,0x77,0x3B,0x88,0xAC,0x5B,0x6A,0xBC,0xDA,0x99,0xE8,0x24,0x90,0x5A,0xCA,0x8D,0x5C,0x2B,0xF8,0xF1,0xE1,0x1D,0x94,0x11,0xEA,0xCC,0x02,0x09,0x1E,0xA2,0x48,0x67,
456 0x87,0x5A,0x7E,0xC6,0xCC,0xA3,0xFB,0xC5,0x36,0xEB,0x5C,0xE1,0xAF,0x1E,0xBE,0xE7,0xD8,0x8F,0x70,0xAE,0x42,0x05,0xF5,0xCD,0x2D,0xA2,0xB0,0xFD,0xEF,0x65,0x2C,0x22,
457 0xCB,0x8C,0x8B,0xAA,0x3D,0x86,0xE2,0xCD,0xBE,0xC3,0x42,0x38,0xE3,0x9C,0x08,0xB5,0xAE,0xBD,0x54,0x73,0x83,0x70,0x24,0x47,0xCA,0x4C,0x04,0xC4,0xE0,0x1D,0x40,0xED,
458 0xF4,0x2B,0x50,0x8E,0x97,0xB3,0xF0,0xA6,0x76,0xDB,0x49,0x30,0xE5,0xD9,0x71,0x07,0xB2,0xF1,0x0F,0xD6,0x77,0xAA,0x72,0xC0,0xAF,0x66,0xD8,0x40,0xC6,0x08,0x19,0x8C,
459 0xD9,0x8F,0x5A,0x75,0xAC,0xBE,0xC2,0x40,0x5B,0xBD,0x0D,0x1D,0x00,0xAF,0x26,0x5E,0x78,0x43,0xAA,0xC6,0x4F,0xF3,0xD8,0xE2,0x7F,0x0C,0x1E,0x77,0x4D,0x35,0x96,0x23,
460 0x32,0x44,0x03,0x8D,0x92,0xE7,0xFD,0x48,0x07,0xD0,0x58,0xFC,0x6D,0xC9,0x91,0x33,0xF0,0x23,0x45,0xA4,0x29,0xB9,0xF5,0xB0,0x68,0x8F,0x7B,0x59,0x15,0x8E,0xA6,0x66,
461 0x15,0xA0,0x76,0x9B,0x69,0xCB,0x38,0xA5,0xF4,0xB4,0x6B,0xDC,0x1F,0xAB,0xAE,0x12,0x77,0xC0,0x8C,0x4A,0x03,0xB9,0x73,0xD3,0x6D,0x52,0xC5,0xF5,0x6E,0x4E,0x4B,0xA3,
462 0x24,0x02,0x58,0xEE,0x5F,0xF9,0xD6,0xD0,0x1D,0xBC,0xF4,0xB8,0x4F,0xFD,0x4B,0x2D,0x34,0x77,0x46,0xE5,0xD4,0x33,0x7B,0x9C,0x35,0xCD,0xB0,0x5D,0x06,0x39,0x99,0xEB,
463 0x0C,0xD0,0x0F,0xF7,0x92,0xB5,0x58,0x5B,0x5E,0x79,0x12,0xF4,0x05,0xF6,0x21,0x07,0x0B,0x49,0x1A,0xFB,0xD4,0x98,0xC4,0xEF,0x7A,0xD6,0xCA,0xA1,0xDA,0xB3,0x51,0x00,
464 0x76,0xEC,0x08,0x48,0x40,0x35,0xD7,0x94,0xBE,0xF5,0x7B,0xA4,0x20,0x81,0x5F,0x82,0xF3,0x6F,0x96,0x24,0x98,0xB6,0x49,0x18,0xC8,0xC5,0x8C,0xD2,0x38,0x7F,0xC4,0xF6,
465 0xAA,0x87,0xDC,0x73,0x5B,0xA1,0xAF,0xE5,0x3D,0x37,0x6B,0x85,0xED,0x38,0x62,0x7D,0x57,0xBD,0xCF,0xB5,0x1B,0xA8,0xBB,0x32,0x33,0xD3,0x34,0x5A,0xC1,0x5D,0xFB,0x28,
466 0x6E,0xE1,0x67,0x51,0xBB,0x31,0x92,0x83,0xAC,0xAA,0x72,0x52,0xFD,0x13,0x4F,0x73,0xD3,0xF0,0x5E,0xFC,0xBA,0xB1,0x3C,0x7B,0x08,0x76,0x03,0x38,0x1E,0xD1,0xCC,0x33,
467 0xA3,0x1E,0xFC,0xE0,0x82,0x30,0x27,0x93,0x71,0x35,0x75,0x77,0xBA,0x78,0x10,0x33,0xCD,0xAB,0xCF,0x8E,0xAD,0xF9,0x32,0xC9,0x15,0x9F,0xD6,0x6D,0xA8,0xAE,0xB1,0x3F,
468 0x90,0xEB,0xD4,0xF9,0x31,0x81,0xA3,0x53,0x99,0x4B,0x3C,0x93,0x3B,0xFE,0x55,0xFF,0x25,0x9F,0xCC,0x07,0xC5,0x2C,0x14,0xA7,0xA4,0x1E,0x6C,0xB6,0x91,0x2A,0xE0,0x3E,
469 0x7F,0x39,0x0A,0xD9,0x24,0x3C,0x01,0xA0,0x30,0x99,0x8E,0xB8,0x1D,0xF9,0xA7,0x78,0x86,0x95,0x35,0x0E,0x21,0xDA,0x7A,0x7B,0xAD,0x9F,0x4E,0xF6,0x63,0x5B,0x96,0xBB,
470 0x87,0x36,0x3F,0xA7,0x1A,0x66,0x91,0xCD,0xB0,0x3B,0xC0,0x4F,0x54,0xD2,0x5F,0xBB,0x38,0x89,0x1C,0x79,0x7E,0xA2,0x02,0xE4,0x80,0x84,0x1E,0x33,0xAB,0x74,0xFA,0xBE,
471 0x31,0x46,0x2E,0xC5,0x15,0xB9,0x12,0xE9,0xD3,0x73,0x43,0xEA,0x74,0x11,0xA7,0xC0,0xD5,0xD8,0x39,0x08,0x9F,0x4F,0xC7,0x71,0x25,0x09,0x51,0x65,0xD6,0xA8,0x02,0x1F
474 return random_noise
[ (random_noise
[p
[1] & 1023] + p
[0]) & 1023 ] & umod
;
478 // ===========================================================================================================
480 static void init_mesh( struct mesh
*m
, float const *tris
, u32 length
)
482 m
->elements
= length
/3;
483 glGenVertexArrays( 1, &m
->vao
);
484 glGenBuffers( 1, &m
->vbo
);
486 glBindVertexArray( m
->vao
);
487 glBindBuffer( GL_ARRAY_BUFFER
, m
->vbo
);
488 glBufferData( GL_ARRAY_BUFFER
, length
*sizeof(float), tris
, GL_STATIC_DRAW
);
490 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
, 2 * sizeof(float), (void*)0 );
491 glEnableVertexAttribArray( 0 );
496 static void free_mesh( struct mesh
*m
)
498 glDeleteVertexArrays( 1, &m
->vao
);
499 glDeleteBuffers( 1, &m
->vbo
);
502 static void draw_mesh( int const start
, int const count
)
504 glDrawArrays( GL_TRIANGLES
, start
*3, count
*3 );
507 static void use_mesh( struct mesh
*m
)
509 glBindVertexArray( m
->vao
);
512 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
515 // ===========================================================================================================
517 static void map_free(void)
519 arrfree( world
.data
);
522 free( world
.cmd_buf_tiles
);
523 world
.cmd_buf_tiles
= NULL
;
533 world
.initialzed
= 0;
536 static void io_reset(void)
538 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
540 struct cell_terminal
*term
= &world
.io
[i
];
542 for( int j
= 0; j
< term
->run_count
; j
++ )
543 term
->runs
[j
].recv_count
= 0;
547 static struct cell
*pcell( v2i pos
)
549 return &world
.data
[ pos
[1]*world
.w
+ pos
[0] ];
552 static void lcell( int id
, v2i pos
)
554 pos
[0] = id
% world
.w
;
555 pos
[1] = (id
- pos
[0]) / world
.w
;
558 static void map_reclassify( v2i start
, v2i end
, int update_texbuffer
)
560 v2i full_start
= { 1,1 };
561 v2i full_end
= { world
.w
-1, world
.h
-1 };
570 u8 info_buffer
[64*64*4];
573 int px0
= vg_max( start
[0], full_start
[0] ),
574 px1
= vg_min( end
[0], full_end
[0] ),
575 py0
= vg_max( start
[1], full_start
[1] ),
576 py1
= vg_min( end
[1], full_end
[1] );
578 for( int y
= py0
; y
< py1
; y
++ )
580 for( int x
= px0
; x
< px1
; x
++ )
582 struct cell
*cell
= pcell((v2i
){x
,y
});
584 v2i dirs
[] = {{1,0},{0,1},{-1,0},{0,-1}};
589 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
591 for( int i
= 0; i
< vg_list_size( dirs
); i
++ )
593 struct cell
*neighbour
= pcell((v2i
){x
+dirs
[i
][0], y
+dirs
[i
][1]});
594 if( neighbour
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
602 if( cell
->state
& FLAG_WALL
)
603 height
= 0xFF-0x3F + hash21i( (v2i
){x
,y
}, 0x3F );
605 config
= cell
->state
& FLAG_INPUT_NICE
? 0xB: 0xF;
608 pcell((v2i
){x
,y
})->config
= config
;
610 u8
*info_px
= &info_buffer
[ (pixel_id
++)*4 ];
612 info_px
[1] = cell
->state
& FLAG_WALL
? 0: 255;
618 ((cell
->state
& FLAG_IS_TRIGGER
) && (cell
->config
== 0xF || cell
->config
== k_cell_type_split
)) ||
619 ((cell
->state
& FLAG_TARGETED
) && ((cell
->config
!= k_cell_type_split
) && !(cell
->state
& FLAG_EMITTER
)))
620 ) && update_texbuffer
622 cell
->state
&= ~(FLAG_TARGETED
|FLAG_IS_TRIGGER
);
623 for( u32 i
= 0; i
< 2; i
++ )
627 struct cell
*other_ptr
= &world
.data
[ cell
->links
[i
] ];
628 other_ptr
->links
[ i
] = 0;
629 other_ptr
->state
&= ~FLAG_IS_TRIGGER
;
631 if( other_ptr
->links
[ i
^ 0x1 ] == 0 )
632 other_ptr
->state
&= ~FLAG_TARGETED
;
642 if( update_texbuffer
)
644 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
645 glTexSubImage2D( GL_TEXTURE_2D
, 0, px0
+ 16, py0
+ 16, px1
-px0
, py1
-py0
, GL_RGBA
, GL_UNSIGNED_BYTE
, info_buffer
);
649 static void gen_level_text(void)
652 ui_px
const unit_scale_px
= 4*UI_GLYPH_SPACING_X
; // 4 char per unit
653 ui_begin( &world
.st
.world_text
, world
.w
*unit_scale_px
, world
.h
*unit_scale_px
);
655 if( world
.pCmpLevel
)
657 for( int i
= 0; i
< vg_list_size( world
.pCmpLevel
->strings
); i
++ )
659 struct world_string
*wstr
= &world
.pCmpLevel
->strings
[i
];
665 pos
[0] = -UI_GLYPH_SPACING_X
/2;
667 if( wstr
->placement
== k_placement_bottom
)
668 pos
[1] = 2*-unit_scale_px
;
670 pos
[1] = (world
.h
-1)*-unit_scale_px
-6;
672 ui_text( &world
.st
.world_text
, pos
, wstr
->str
, 1, k_text_align_left
);
677 // re-create level scores
678 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
680 struct career_level_pack
*set
= &career_packs
[i
];
682 ui_text( &world
.st
.world_text
,
684 set
->origin
[0]*unit_scale_px
,
685 -(set
->origin
[1]+set
->dims
[1]+1)*unit_scale_px
+ 18
687 set
->title
, 1, k_text_align_left
);
689 for( int j
= 0; j
< set
->count
; j
++ )
691 struct cmp_level
*lvl
= &set
->pack
[j
];
693 if( lvl
->completed_score
&& !lvl
->is_tutorial
)
696 snprintf( num
, 9, "%d", lvl
->completed_score
);
698 ui_text( &world
.st
.world_text
,
700 lvl
->btn
.position
[0]*unit_scale_px
+ unit_scale_px
/2,
701 -lvl
->btn
.position
[1]*unit_scale_px
- unit_scale_px
/2
703 num
, 1, k_text_align_center
);
708 //ui_text( &world.st.world_text, (ui_px [2]){ 0, 0 }, "Preview", 1, k_text_align_left );
710 ui_resolve( &world
.st
.world_text
);
713 static int map_load( const char *str
, const char *name
)
722 if( c
[world
.w
] == ';' )
724 else if( !c
[world
.w
] )
726 vg_error( "Unexpected EOF when parsing level\n" );
731 struct cell
*row
= arraddnptr( world
.data
, world
.w
);
733 int reg_start
= 0, reg_end
= 0;
735 u32
*links_to_make
= NULL
;
736 int links_satisfied
= 0;
738 char link_id_buffer
[32];
746 if( *c
== '\r' ) { c
++; continue; } // fuck off windows
752 if( *c
== '\r' ) c
++;
759 if( *c
== '\r' ) { c
++; continue; }
761 if( reg_start
< reg_end
)
763 struct cell_terminal
*terminal
= &world
.io
[ reg_start
];
764 struct terminal_run
*run
= &terminal
->runs
[ terminal
->run_count
-1 ];
766 if( (*c
>= 'a' && *c
<= 'z') || *c
== ' ' )
772 run
->steps
[ run
->step_count
++ ] = code
;
776 if( *c
== ',' || *c
== '\n' )
785 terminal
->runs
[ terminal
->run_count
].step_count
= 0;
786 terminal
->run_count
++;
787 world
.max_runs
= vg_max( world
.max_runs
, terminal
->run_count
);
791 vg_error( "Unkown attribute '%c' (row: %u)\n", *c
, world
.h
);
798 if( links_satisfied
< arrlen( links_to_make
) )
800 struct cell
*target
= &world
.data
[ links_to_make
[ links_satisfied
] ];
802 if( (((u32
)*c
>= (u32
)'0') && ((u32
)*c
<= (u32
)'9')) || *c
== '-' )
804 if( link_id_n
>= vg_list_size( link_id_buffer
)-1 )
806 vg_error( "Number was way too long to be parsed (row: %u)\n", world
.h
);
810 link_id_buffer
[ link_id_n
++ ] = *c
;
812 else if( *c
== ',' || *c
== '\n' )
814 link_id_buffer
[ link_id_n
] = 0x00;
815 int value
= atoi( link_id_buffer
);
817 target
->links
[value
>= 0? 1:0] = abs(value
);
826 vg_error( "Invalid character '%c' (row: %u)\n", *c
, world
.h
);
832 vg_error( "Too many values to assign (row: %u)\n", world
.h
);
841 // Registry length-error checks
842 if( reg_start
!= reg_end
)
844 vg_error( "Not enough spawn values assigned (row: %u, %u of %u)\n", world
.h
, reg_start
, reg_end
);
848 if( links_satisfied
!= arrlen( links_to_make
) )
850 vg_error( "Not enough link values assigned (row: %u, %u of %u)\n", world
.h
, links_satisfied
, arrlen( links_to_make
) );
856 vg_error( "Not enough cells to match previous row definition (row: %u, %u<%u)\n", world
.h
, cx
, world
.w
);
860 row
= arraddnptr( world
.data
, world
.w
);
863 reg_end
= reg_start
= arrlen( world
.io
);
865 arrsetlen( links_to_make
, 0 );
872 vg_error( "Too many cells to match previous row definition (row: %u, %u>%u)\n", world
.h
, cx
, world
.w
);
876 // Tile initialization
878 struct cell
*cell
= &row
[ cx
];
884 if( *c
== '+' || *c
== '-' || *c
== '*' )
886 struct cell_terminal
*term
= arraddnptr( world
.io
, 1 );
888 term
->pos
[1] = world
.h
;
891 term
->runs
[0].step_count
= 0;
895 case '+': cell
->state
= FLAG_INPUT
; break;
896 case '-': cell
->state
= FLAG_OUTPUT
; break;
897 case '*': cell
->state
= FLAG_EMITTER
; break;
902 else if( *c
== '.' ) cell
->state
= FLAG_INPUT_NICE
;
903 else if( *c
== '#' ) cell
->state
= FLAG_WALL
;
904 else if( ((u32
)*c
>= (u32
)'A') && ((u32
)*c
<= (u32
)'A'+0xf) )
906 // Canal flag bits (4bit/16 value):
912 cell
->state
= ((u32
)*c
- (u32
)'A') & (FLAG_CANAL
|FLAG_IS_TRIGGER
);
914 if( cell
->state
& FLAG_IS_TRIGGER
)
915 arrpush( links_to_make
, cx
+ world
.h
*world
.w
);
919 else cell
->state
= 0x00;
927 // Assign emitter codes
928 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
930 struct cell_terminal
*term
= &world
.io
[i
];
931 struct cell
*cell
= pcell( term
->pos
);
933 if( cell
->state
& FLAG_EMITTER
)
935 if( (term
->run_count
> 0) && (term
->runs
[0].step_count
>= 2) )
937 cell
->emit
[0] = term
->runs
[0].steps
[0];
938 cell
->emit
[1] = term
->runs
[0].steps
[1];
942 vg_error( "Emitter was not assigned emit values\n" );
948 // Update data texture to fill out the background
950 u8 info_buffer
[64*64*4];
951 for( int x
= 0; x
< 64; x
++ )
953 for( int y
= 0; y
< 64; y
++ )
955 u8
*px
= &info_buffer
[((x
*64)+y
)*4];
957 // Fade out edges of world so that there isnt an obvious line
958 int dist_x
= 16 - VG_MIN( VG_MIN( x
, 16 ), 16-VG_MAX( x
-16-world
.w
, 0 ) );
959 int dist_y
= 16 - VG_MIN( VG_MIN( y
, 16 ), 16-VG_MAX( y
-16-world
.h
, 0 ) );
960 int dist
= VG_MAX( dist_x
, dist_y
) * 16;
962 int value
= VG_MAX( 0, 0xFF-0x3F + hash21i( (v2i
){x
,y
}, 0x3F ) - dist
);
971 // Level selection area
973 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
975 struct career_level_pack
*grid
= &career_packs
[ i
];
979 for( int y
= 0; y
< grid
->dims
[1]; y
++ )
981 for( int x
= 0; x
< grid
->dims
[0]; x
++ )
983 u8
*px
= &info_buffer
[((y
+16+grid
->origin
[1])*64+16+x
+grid
->origin
[0])*4];
986 if( j
< grid
->count
)
988 struct cmp_level
*lvl
= &grid
->pack
[ j
++ ];
989 v2i_add( grid
->origin
, (v2i
){x
,y
}, lvl
->btn
.position
);
995 info_buffer
[(((16+world
.h
-3)*64)+world
.w
+16-1)*4] = 0x30;
996 info_buffer
[(((16+world
.h
-2)*64)+world
.w
+16-1)*4] = 0x30;
998 // Random walks.. kinda
999 for( int i
= 0; i
< arrlen(world
.io
); i
++ )
1001 struct cell_terminal
*term
= &world
.io
[ i
];
1007 // Only make breakouts for terminals on the edge
1008 if( !(term
->pos
[1] == 1 || term
->pos
[1] == world
.h
-2) )
1011 turtle
[0] = 16+term
->pos
[0];
1012 turtle
[1] = 16+term
->pos
[1];
1015 turtle_dir
[1] = pcell(term
->pos
)->state
& (FLAG_INPUT
|FLAG_EMITTER
)? 1: -1;
1016 original_y
= turtle_dir
[1];
1018 info_buffer
[((turtle
[1]*64)+turtle
[0])*4] = 0;
1019 v2i_add( turtle_dir
, turtle
, turtle
);
1021 for( int i
= 0; i
< 100; i
++ )
1023 info_buffer
[((turtle
[1]*64)+turtle
[0])*4] = 0;
1025 v2i_add( turtle_dir
, turtle
, turtle
);
1027 if( turtle
[0] == 0 ) break;
1028 if( turtle
[0] == 63 ) break;
1029 if( turtle
[1] == 0 ) break;
1030 if( turtle
[1] == 63 ) break;
1032 int random_value
= hash21i( turtle
, 0xFF );
1033 if( random_value
> 255-40 && !turtle_dir
[0] )
1038 else if( random_value
> 255-80 && !turtle_dir
[0] )
1043 else if( random_value
> 255-100 )
1046 turtle_dir
[1] = original_y
;
1051 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
1052 glTexSubImage2D( GL_TEXTURE_2D
, 0, 0, 0, 64, 64, GL_RGBA
, GL_UNSIGNED_BYTE
, info_buffer
);
1055 arrfree( links_to_make
);
1057 map_reclassify( NULL
, NULL
, 1 );
1060 for( int i
= 0; i
< world
.h
*world
.w
; i
++ )
1062 struct cell
*src
= &world
.data
[i
];
1063 if( src
->state
& FLAG_IS_TRIGGER
)
1065 int link_id
= src
->links
[0]?0:1;
1066 if( src
->links
[link_id
] <= world
.h
*world
.w
)
1068 struct cell
*target
= &world
.data
[ src
->links
[link_id
] ];
1069 if( ((target
->state
& FLAG_CANAL
) && (target
->config
== k_cell_type_split
))
1070 || (target
->state
& FLAG_EMITTER
) )
1072 if( target
->links
[ link_id
] )
1074 vg_error( "Link target was already targeted\n" );
1080 target
->links
[ link_id
] = i
;
1081 target
->state
|= FLAG_TARGETED
;
1086 vg_error( "Link target was invalid\n" );
1092 vg_error( "Link target out of bounds\n" );
1098 // ==========================================================
1101 vg_success( "Map '%s' loaded! (%u:%u)\n", name
, world
.w
, world
.h
);
1105 strncpy( world
.map_name
, name
, vg_list_size( world
.map_name
)-1 );
1106 world
.initialzed
= 1;
1108 // Setup world button locations
1109 for( int i
= 0; i
< vg_list_size( world
.st
.buttons
); i
++ )
1111 struct world_button
*btn
= &world
.st
.buttons
[i
];
1112 btn
->position
[0] = world
.w
-1;
1113 btn
->position
[1] = world
.h
-i
-2;
1116 // Allocate buffers for render commands
1117 world
.cmd_buf_tiles
= malloc( world
.w
*world
.h
* sizeof( struct render_cmd
) );
1118 world
.max_commands
= world
.w
*world
.h
;
1123 arrfree( links_to_make
);
1128 static void map_serialize( FILE *stream
)
1130 for( int y
= 0; y
< world
.h
; y
++ )
1132 for( int x
= 0; x
< world
.w
; x
++ )
1134 struct cell
*cell
= pcell( (v2i
){ x
, y
} );
1136 if( cell
->state
& FLAG_WALL
) fputc( '#', stream
);
1137 else if( cell
->state
& FLAG_INPUT_NICE
) fputc( '.', stream
);
1138 else if( cell
->state
& FLAG_INPUT
) fputc( '+', stream
);
1139 else if( cell
->state
& FLAG_OUTPUT
) fputc( '-', stream
);
1140 else if( cell
->state
& FLAG_EMITTER
) fputc( '*', stream
);
1141 else if( cell
->state
& (FLAG_CANAL
|FLAG_IS_TRIGGER
|FLAG_RESERVED0
|FLAG_RESERVED1
) )
1143 fputc( (cell
->state
& (FLAG_CANAL
|FLAG_IS_TRIGGER
|FLAG_RESERVED0
|FLAG_RESERVED1
)) + (u32
)'A', stream
);
1145 else fputc( ' ', stream
);
1148 fputc( ';', stream
);
1150 int terminal_write_count
= 0;
1152 for( int x
= 0; x
< world
.w
; x
++ )
1154 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
1156 struct cell_terminal
*term
= &world
.io
[i
];
1157 if( v2i_eq( term
->pos
, (v2i
){x
,y
} ) )
1159 if( terminal_write_count
)
1160 fputc( ',', stream
);
1161 terminal_write_count
++;
1163 for( int j
= 0; j
< term
->run_count
; j
++ )
1165 struct terminal_run
*run
= &term
->runs
[j
];
1167 for( int k
= 0; k
< run
->step_count
; k
++ )
1169 i8 step
= run
->steps
[k
];
1170 fputc( step
== -1? ' ': ('a' + run
->steps
[k
]), stream
);
1173 if( j
< term
->run_count
-1 )
1174 fputc( ':', stream
);
1180 for( int x
= 0; x
< world
.w
; x
++ )
1182 struct cell
*cell
= pcell( (v2i
){ x
,y
} );
1183 if( cell
->state
& FLAG_IS_TRIGGER
)
1185 if( terminal_write_count
)
1186 fputc( ',', stream
);
1187 terminal_write_count
++;
1189 fprintf( stream
, "%d", cell
->links
[0]? -cell
->links
[0]: cell
->links
[1] );
1193 fputc( '\n', stream
);
1198 // ===========================================================================================================
1200 #pragma pack(push,1)
1201 struct dcareer_state
1215 levels
[ NUM_CAMPAIGN_LEVELS
];
1219 static int career_load_success
= 0;
1221 static void career_serialize(void)
1223 if( !career_load_success
)
1226 struct dcareer_state encoded
;
1227 encoded
.version
= MARBLE_COMP_VERSION
;
1228 encoded
.in_map
= world
.pCmpLevel
? world
.pCmpLevel
->serial_id
: -1;
1230 memset( encoded
.reserved
, 0, sizeof( encoded
.reserved
) );
1232 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1234 struct career_level_pack
*set
= &career_packs
[i
];
1236 for( int j
= 0; j
< set
->count
; j
++ )
1238 struct cmp_level
*lvl
= &set
->pack
[j
];
1239 struct dlevel_state
*dest
= &encoded
.levels
[lvl
->serial_id
];
1241 dest
->score
= lvl
->completed_score
;
1242 dest
->unlocked
= lvl
->unlocked
;
1243 dest
->reserved
[0] = 0;
1244 dest
->reserved
[1] = 0;
1248 vg_asset_write( "sav/game.sv2", &encoded
, sizeof( struct dcareer_state
) );
1251 static void career_unlock_level( struct cmp_level
*lvl
);
1252 static void career_unlock_level( struct cmp_level
*lvl
)
1257 career_unlock_level( lvl
->linked
);
1260 static void career_pass_level( struct cmp_level
*lvl
, int score
, int upload
)
1264 lvl
->completed_score
= score
;
1268 career_unlock_level( lvl
->unlock
);
1271 if( lvl
->achievement
)
1272 sw_set_achievement( lvl
->achievement
);
1274 // Check ALL maps to trigger master engineer
1275 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1277 struct career_level_pack
*set
= &career_packs
[i
];
1279 for( int j
= 0; j
< set
->count
; j
++ )
1281 if( set
->pack
[j
].completed_score
== 0 )
1286 sw_set_achievement( "MASTER_ENGINEER" );
1291 static void career_reset_level( struct cmp_level
*lvl
)
1294 lvl
->completed_score
= 0;
1297 static void career_load(void)
1300 struct dcareer_state encoded
;
1303 memset( (void*)&encoded
, 0, sizeof( struct dcareer_state
) );
1305 encoded
.levels
[0].unlocked
= 1;
1307 // Load and copy data into encoded
1308 void *cr
= vg_asset_read_s( "sav/game.sv2", &sz
);
1312 if( sz
> sizeof( struct dcareer_state
) )
1313 vg_warn( "This save file is too big! Some levels will be lost\n" );
1315 if( sz
<= offsetof( struct dcareer_state
, levels
) )
1317 vg_warn( "This save file is too small to have a header. Creating a blank one\n" );
1321 memcpy( (void*)&encoded
, cr
, VG_MIN( sizeof( struct dcareer_state
), sz
) );
1325 vg_info( "No save file... Using blank one\n" );
1328 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1330 struct career_level_pack
*set
= &career_packs
[i
];
1332 for( int j
= 0; j
< set
->count
; j
++ )
1333 career_reset_level( &set
->pack
[j
] );
1336 // Header information
1337 // =================================
1339 struct cmp_level
*lvl_to_load
= &career_packs
[0].pack
[0];
1341 // Decode everything from dstate
1342 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1344 struct career_level_pack
*set
= &career_packs
[i
];
1346 for( int j
= 0; j
< set
->count
; j
++ )
1348 struct cmp_level
*lvl
= &set
->pack
[j
];
1349 struct dlevel_state
*src
= &encoded
.levels
[lvl
->serial_id
];
1351 if( src
->unlocked
) career_unlock_level( lvl
);
1354 lvl
->completed_score
= src
->score
;
1356 // Apply unlocking to next levels in case there was an update
1358 career_unlock_level( lvl
->unlock
);
1361 if( lvl
->serial_id
== encoded
.in_map
)
1366 if( console_changelevel( 1, &lvl_to_load
->map_name
) )
1368 world
.pCmpLevel
= lvl_to_load
;
1372 career_load_success
= 1;
1374 if( encoded
.version
< MARBLE_COMP_VERSION
|| 1 )
1375 world
.st
.state
= k_game_state_update
;
1379 // ===========================================================================================================
1380 static int is_simulation_running(void)
1382 return world
.st
.buttons
[ k_world_button_sim
].state
;
1385 static void clear_animation_flags(void)
1387 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
1388 world
.data
[ i
].state
&= ~(FLAG_FLIP_FLOP
|FLAG_FLIP_ROTATING
);
1391 static void simulation_stop(void)
1393 world
.st
.buttons
[ k_world_button_sim
].state
= 0;
1394 world
.st
.buttons
[ k_world_button_pause
].state
= 0;
1396 world
.num_fishes
= 0;
1397 world
.sim_frame
= 0;
1399 world
.frame_lerp
= 0.0f
;
1403 sfx_system_fadeout( &audio_system_balls_rolling
, 44100 );
1405 clear_animation_flags();
1407 vg_info( "Stopping simulation!\n" );
1410 static void simulation_start(void)
1412 vg_success( "Starting simulation!\n" );
1414 sfx_set_playrnd( &audio_rolls
, &audio_system_balls_rolling
, 0, 1 );
1416 world
.num_fishes
= 0;
1417 world
.sim_frame
= 0;
1420 world
.sim_delta_speed
= world
.st
.buttons
[ k_world_button_speedy
].state
? 10.0f
: 2.5f
;
1421 world
.sim_delta_ref
= vg_time
;
1422 world
.sim_internal_ref
= 0.0f
;
1423 world
.sim_internal_time
= 0.0f
;
1424 world
.pause_offset_target
= 0.0f
;
1426 world
.sim_target
= 0;
1428 clear_animation_flags();
1432 if( world
.pCmpLevel
)
1434 world
.pCmpLevel
->completed_score
= 0;
1439 static int world_check_pos_ok( v2i co
, int dist
)
1441 return (co
[0] < dist
|| co
[0] >= world
.w
-dist
|| co
[1] < dist
|| co
[1] >= world
.h
-dist
)? 0: 1;
1444 static int cell_interactive( v2i co
)
1449 if( world_check_pos_ok( co
, 1 ) )
1452 if( cell
->state
& FLAG_EMITTER
)
1456 if( !world_check_pos_ok( co
, 2 ) )
1460 if( cell
->state
& (FLAG_WALL
|FLAG_INPUT
|FLAG_OUTPUT
) )
1463 // List of 3x3 configurations that we do not allow
1464 static u32 invalid_src
[][9] =
1496 // Statically compile invalid configurations into bitmasks
1497 static u32 invalid
[ vg_list_size(invalid_src
) ];
1499 for( int i
= 0; i
< vg_list_size(invalid_src
); i
++ )
1503 for( int j
= 0; j
< 3; j
++ )
1504 for( int k
= 0; k
< 3; k
++ )
1505 comped
|= invalid_src
[i
][ j
*3+k
] << ((j
*5)+k
);
1507 invalid
[i
] = comped
;
1510 // Extract 5x5 grid surrounding tile
1512 for( int y
= co
[1]-2; y
< co
[1]+3; y
++ )
1513 for( int x
= co
[0]-2; x
< co
[0]+3; x
++ )
1515 struct cell
*cell
= pcell((v2i
){x
,y
});
1517 if( cell
&& (cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
)) )
1518 blob
|= 0x1 << ((y
-(co
[1]-2))*5 + x
-(co
[0]-2));
1521 // Run filter over center 3x3 grid to check for invalid configurations
1522 int kernel
[] = { 0, 1, 2, 5, 6, 7, 10, 11, 12 };
1523 for( int i
= 0; i
< vg_list_size(kernel
); i
++ )
1525 if( blob
& (0x1 << (6+kernel
[i
])) )
1527 u32 window
= blob
>> kernel
[i
];
1529 for( int j
= 0; j
< vg_list_size(invalid
); j
++ )
1530 if((window
& invalid
[j
]) == invalid
[j
])
1538 static void vg_update(void)
1541 if( world
.st
.lvl_to_load
)
1543 world
.st
.world_transition
= (world
.st
.lvl_load_time
-vg_time
) * 4.0f
;
1545 if( vg_time
> world
.st
.lvl_load_time
)
1547 if( console_changelevel( 1, &world
.st
.lvl_to_load
->map_name
) )
1549 world
.pCmpLevel
= world
.st
.lvl_to_load
;
1553 world
.st
.lvl_to_load
= NULL
;
1558 world
.st
.world_transition
= vg_minf( 1.0f
, (vg_time
-world
.st
.lvl_load_time
) * 4.0f
);
1562 // ========================================================================================================
1564 float r1
= (float)vg_window_y
/ (float)vg_window_x
,
1565 r2
= (float)world
.h
/ (float)world
.w
,
1568 static float size_current
= 2.0f
;
1569 static v3f origin_current
= { 0.0f
, 0.0f
, 0.0f
};
1570 static v2f drag_offset
= { 0.0f
, 0.0f
};
1571 static v2f view_point
= { 0.0f
, 0.0f
};
1574 size
= ( r2
< r1
? (float)(world
.w
+5) * 0.5f
: ((float)(world
.h
+5) * 0.5f
) / r1
) + 0.5f
;
1579 origin
[0] = floorf( -0.5f
* ((float)world
.w
-4.5f
) );
1580 origin
[1] = floorf( -0.5f
* world
.h
);
1582 // Create and clamp result view
1583 v2_add( view_point
, drag_offset
, result_view
);
1584 result_view
[0] = vg_clampf( result_view
[0], -world
.st
.zoom
, world
.st
.zoom
);
1585 result_view
[1] = vg_clampf( result_view
[1], -world
.st
.zoom
*r1
, world
.st
.zoom
*r1
);
1587 v2_add( origin
, result_view
, vt_target
);
1589 // Lerp towards target
1590 size_current
= vg_lerpf( size_current
, size
- world
.st
.zoom
, vg_time_delta
* 6.0f
);
1591 v2_lerp( origin_current
, vt_target
, vg_time_delta
* 6.0f
, origin_current
);
1593 m3x3_projection( m_projection
, -size_current
, size_current
, -size_current
*r1
, size_current
*r1
);
1594 m3x3_identity( m_view
);
1595 m3x3_translate( m_view
, origin_current
);
1596 m3x3_mul( m_projection
, m_view
, vg_pv
);
1597 vg_projection_update();
1599 if( world
.st
.state
== k_game_state_update
)
1603 // ========================================================================================================
1604 v2_copy( vg_mouse_ws
, world
.tile_pos
);
1606 world
.tile_x
= floorf( world
.tile_pos
[0] );
1607 world
.tile_y
= floorf( world
.tile_pos
[1] );
1611 static v2f drag_origin
; // x/y pixel
1613 if( vg_get_button_down( "tertiary" ) )
1614 v2_copy( vg_mouse
, drag_origin
);
1615 else if( vg_get_button( "tertiary" ) )
1618 v2_sub( vg_mouse
, drag_origin
, drag_offset
);
1619 v2_div( drag_offset
, (v2f
){ vg_window_x
, vg_window_y
}, drag_offset
);
1620 v2_mul( drag_offset
, (v2f
){ size_current
*2.0f
, -size_current
*r1
*2.0f
}, drag_offset
);
1624 v2_copy( result_view
, view_point
);
1625 v2_copy( (v2f
){0.0f
,0.0f
}, drag_offset
);
1637 rsize
= size
-world
.st
.zoom
;
1639 v2_div( vg_mouse
, (v2f
){ vg_window_x
*0.5f
, vg_window_y
*0.5f
}, mview_local
);
1640 v2_add( (v2f
){ -rsize
, -rsize
*r1
}, (v2f
){ mview_local
[0]*rsize
, (2.0f
-mview_local
[1])*rsize
*r1
}, mview_cur
);
1642 world
.st
.zoom
= vg_clampf( world
.st
.zoom
+ vg_mouse_wheel
[1], 0.0f
, size
- 4.0f
);
1644 // Recalculate new position
1645 rsize
= size
-world
.st
.zoom
;
1646 v2_add( (v2f
){ -rsize
, -rsize
*r1
}, (v2f
){ mview_local
[0]*rsize
, (2.0f
-mview_local
[1])*rsize
*r1
}, mview_new
);
1649 v2_sub( mview_new
, mview_cur
, mview_delta
);
1650 v2_add( mview_delta
, view_point
, view_point
);
1654 // ========================================================================================================
1655 if( !is_simulation_running() && !gui_want_mouse() )
1657 v2_copy( vg_mouse_ws
, world
.drag_to_co
);
1659 if( cell_interactive( (v2i
){ world
.tile_x
, world
.tile_y
} ))
1661 world
.selected
= world
.tile_y
* world
.w
+ world
.tile_x
;
1663 static u32 modify_state
= 0;
1664 struct cell
*cell_ptr
= &world
.data
[world
.selected
];
1666 if( !(cell_ptr
->state
& FLAG_EMITTER
) )
1668 if( vg_get_button_down("primary") )
1669 modify_state
= (cell_ptr
->state
& FLAG_CANAL
) ^ FLAG_CANAL
;
1671 if( vg_get_button("primary") && ((cell_ptr
->state
& FLAG_CANAL
) != modify_state
) )
1673 cell_ptr
->state
&= ~FLAG_CANAL
;
1674 cell_ptr
->state
|= modify_state
;
1676 if( cell_ptr
->state
& FLAG_CANAL
)
1678 cell_ptr
->links
[0] = 0;
1679 cell_ptr
->links
[1] = 0;
1681 sfx_set_playrnd( &audio_tile_mod
, &audio_system_sfx
, 3, 6 );
1686 sfx_set_playrnd( &audio_tile_mod
, &audio_system_sfx
, 0, 3 );
1690 map_reclassify((v2i
){ world
.tile_x
-2, world
.tile_y
-2 },
1691 (v2i
){ world
.tile_x
+2, world
.tile_y
+2 }, 1 );
1694 if( vg_get_button_down("secondary") && (cell_ptr
->state
& FLAG_CANAL
) && !(cell_ptr
->config
== k_cell_type_split
) )
1696 world
.id_drag_from
= world
.selected
;
1698 struct cell_description
*desc
= &cell_descriptions
[ world
.data
[world
.id_drag_from
].config
];
1699 v2_add( desc
->trigger_pos
, (v2f
){ world
.tile_x
, world
.tile_y
}, world
.drag_from_co
);
1703 float local_x
= vg_mouse_ws
[0] - (float)world
.tile_x
;
1705 if( vg_get_button_up("secondary") && world
.id_drag_from
== world
.selected
)
1707 u32 link_id
= cell_ptr
->links
[ 0 ]? 0: 1;
1709 // break existing connection off
1710 if( cell_ptr
->links
[ link_id
] )
1712 struct cell
*current_connection
= &world
.data
[ cell_ptr
->links
[ link_id
]];
1714 if( !current_connection
->links
[ link_id
^ 0x1 ] )
1715 current_connection
->state
&= ~FLAG_TARGETED
;
1717 current_connection
->links
[ link_id
] = 0;
1718 cell_ptr
->links
[ link_id
] = 0;
1721 cell_ptr
->state
&= ~FLAG_IS_TRIGGER
;
1722 world
.id_drag_from
= 0;
1725 else if( world
.id_drag_from
&& (cell_ptr
->state
& (FLAG_CANAL
|FLAG_EMITTER
)) &&
1726 ((cell_ptr
->config
== k_cell_type_split
) || (cell_ptr
->state
& FLAG_EMITTER
)) )
1728 world
.drag_to_co
[0] = (float)world
.tile_x
+ (local_x
> 0.5f
? 0.75f
: 0.25f
);
1729 world
.drag_to_co
[1] = (float)world
.tile_y
+ 0.25f
;
1731 if( vg_get_button_up("secondary") )
1733 struct cell
*drag_ptr
= &world
.data
[world
.id_drag_from
];
1734 u32 link_id
= local_x
> 0.5f
? 1: 0;
1736 // Cleanup existing connections
1737 if( cell_ptr
->links
[ link_id
] )
1739 vg_warn( "Destroying existing connection on link %u (%hu)\n", link_id
, cell_ptr
->links
[ link_id
] );
1741 struct cell
*current_connection
= &world
.data
[ cell_ptr
->links
[ link_id
]];
1742 current_connection
->state
&= ~FLAG_IS_TRIGGER
;
1743 current_connection
->links
[ link_id
] = 0;
1746 for( u32 i
= 0; i
< 2; i
++ )
1748 if( drag_ptr
->links
[ i
] )
1750 vg_warn( "Destroying link %u (%hu)\n", i
, drag_ptr
->links
[ i
] );
1752 struct cell
*current_connection
= &world
.data
[ drag_ptr
->links
[ i
]];
1753 if( current_connection
->links
[ i
^ 0x1 ] == 0 )
1754 current_connection
->state
&= ~FLAG_TARGETED
;
1756 current_connection
->links
[ i
] = 0;
1757 drag_ptr
->links
[ i
] = 0;
1761 // Create the new connection
1762 vg_success( "Creating connection on link %u (%hu)\n", link_id
, world
.id_drag_from
);
1764 cell_ptr
->links
[ link_id
] = world
.id_drag_from
;
1765 drag_ptr
->links
[ link_id
] = world
.selected
;
1767 cell_ptr
->state
|= FLAG_TARGETED
;
1768 drag_ptr
->state
|= FLAG_IS_TRIGGER
;
1769 world
.id_drag_from
= 0;
1775 world
.selected
= -1;
1778 if( !(vg_get_button("secondary") && world
.id_drag_from
) )
1779 world
.id_drag_from
= 0;
1783 world
.selected
= -1;
1784 world
.id_drag_from
= 0;
1787 // Marble state updates
1788 // ========================================================================================================
1789 if( is_simulation_running() )
1791 float old_time
= world
.sim_internal_time
;
1793 if( !world
.st
.buttons
[ k_world_button_pause
].state
)
1794 world
.sim_internal_time
= world
.sim_internal_ref
+ (vg_time
-world
.sim_delta_ref
) * world
.sim_delta_speed
;
1796 world
.sim_internal_time
= vg_lerpf( world
.sim_internal_time
, world
.sim_internal_ref
+ world
.pause_offset_target
, vg_time_delta
*15.0f
);
1797 world
.sim_internal_delta
= world
.sim_internal_time
-old_time
;
1799 world
.sim_target
= (int)floorf(world
.sim_internal_time
);
1801 int success_this_frame
= 0;
1802 int failure_this_frame
= 0;
1804 while( world
.sim_frame
< world
.sim_target
)
1806 sfx_set_playrnd( &audio_random
, &audio_system_balls_switching
, 0, 8 );
1808 // Update splitter deltas
1809 for( int i
= 0; i
< world
.h
*world
.w
; i
++ )
1811 struct cell
*cell
= &world
.data
[i
];
1812 if( cell
->config
== k_cell_type_split
)
1814 cell
->state
&= ~FLAG_FLIP_ROTATING
;
1816 if( cell
->state
& (FLAG_IS_TRIGGER
|FLAG_EMITTER
) )
1817 cell
->state
&= ~FLAG_TRIGGERED
;
1820 int alive_count
= 0;
1822 // Update fish positions
1823 for( int i
= 0; i
< world
.num_fishes
; i
++ )
1825 struct fish
*fish
= &world
.fishes
[i
];
1827 if( fish
->state
== k_fish_state_soon_dead
)
1828 fish
->state
= k_fish_state_dead
;
1830 if( fish
->state
== k_fish_state_soon_alive
)
1831 fish
->state
= k_fish_state_alive
;
1833 if( fish
->state
< k_fish_state_alive
)
1836 struct cell
*cell_current
= pcell( fish
->pos
);
1838 if( fish
->state
== k_fish_state_alive
)
1841 if( cell_current
->state
& FLAG_OUTPUT
)
1843 for( int j
= 0; j
< arrlen( world
.io
); j
++ )
1845 struct cell_terminal
*term
= &world
.io
[j
];
1847 if( v2i_eq( term
->pos
, fish
->pos
) )
1849 struct terminal_run
*run
= &term
->runs
[ world
.sim_run
];
1850 if( run
->recv_count
< vg_list_size( run
->recieved
) )
1852 if( fish
->colour
== run
->steps
[ run
->recv_count
] )
1853 success_this_frame
= 1;
1855 failure_this_frame
= 1;
1857 run
->recieved
[ run
->recv_count
++ ] = fish
->colour
;
1860 failure_this_frame
= 1;
1866 fish
->state
= k_fish_state_dead
;
1867 fish
->death_time
= -1000.0f
;
1872 if( cell_current
->config
== k_cell_type_merge
)
1877 fish
->flow_reversed
= 0;
1881 if( cell_current
->config
== k_cell_type_split
)
1884 fish
->dir
[0] = cell_current
->state
&FLAG_FLIP_FLOP
?1:-1;
1887 if( !(cell_current
->state
& FLAG_TARGETED
) )
1888 cell_current
->state
^= FLAG_FLIP_FLOP
;
1892 // Apply cell out-flow
1893 struct cell_description
*desc
= &cell_descriptions
[ cell_current
->config
];
1895 v2i_copy( fish
->flow_reversed
? desc
->start
: desc
->end
, fish
->dir
);
1899 v2i_add( fish
->pos
, fish
->dir
, pos_next
);
1901 struct cell
*cell_next
= pcell( pos_next
);
1903 if( cell_next
->state
& (FLAG_CANAL
|FLAG_OUTPUT
) )
1905 struct cell_description
*desc
= &cell_descriptions
[ cell_next
->config
];
1907 if( cell_next
->config
== k_cell_type_merge
)
1909 if( fish
->dir
[0] == 0 )
1911 fish
->state
= k_fish_state_dead
;
1912 fish
->death_time
= world
.sim_internal_time
;
1915 fish
->flow_reversed
= 0;
1919 if( cell_next
->config
== k_cell_type_split
)
1921 if( fish
->dir
[0] == 0 )
1923 sfx_set_playrnd( &audio_splitter
, &audio_system_balls_important
, 0, 1 );
1924 cell_next
->state
|= FLAG_FLIP_ROTATING
;
1926 fish
->flow_reversed
= 0;
1930 fish
->state
= k_fish_state_dead
;
1931 fish
->death_time
= world
.sim_internal_time
;
1935 fish
->flow_reversed
= ( fish
->dir
[0] != -desc
->start
[0] ||
1936 fish
->dir
[1] != -desc
->start
[1] )? 1: 0;
1941 if( world_check_pos_ok( fish
->pos
, 2 ) )
1942 fish
->state
= k_fish_state_bg
;
1945 fish
->state
= k_fish_state_dead
;
1946 fish
->death_time
= world
.sim_internal_time
;
1951 //v2i_add( fish->pos, fish->dir, fish->pos );
1953 else if( fish
->state
== k_fish_state_bg
)
1955 v2i_add( fish
->pos
, fish
->dir
, fish
->pos
);
1957 if( !world_check_pos_ok( fish
->pos
, 2 ) )
1959 fish
->state
= k_fish_state_dead
;
1960 fish
->death_time
= -1000.0f
;
1964 struct cell
*cell_entry
= pcell( fish
->pos
);
1966 if( cell_entry
->state
& FLAG_CANAL
)
1968 if( cell_entry
->config
== k_cell_type_con_r
|| cell_entry
->config
== k_cell_type_con_u
1969 || cell_entry
->config
== k_cell_type_con_l
|| cell_entry
->config
== k_cell_type_con_d
)
1972 sw_set_achievement( "CAN_DO_THAT" );
1975 fish
->state
= k_fish_state_soon_alive
;
1979 fish
->flow_reversed
= 1;
1981 switch( cell_entry
->config
)
1983 case k_cell_type_con_r
: fish
->dir
[0] = 1; break;
1984 case k_cell_type_con_l
: fish
->dir
[0] = -1; break;
1985 case k_cell_type_con_u
: fish
->dir
[1] = 1; break;
1986 case k_cell_type_con_d
: fish
->dir
[1] = -1; break;
1992 else { vg_error( "fish behaviour unimplemented for behaviour type (%d)\n" ); }
1994 if( fish
->state
>= k_fish_state_alive
)
1998 // Second pass (triggers)
1999 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2001 struct fish
*fish
= &world
.fishes
[i
];
2004 if( fish
->state
== k_fish_state_alive
)
2005 v2i_add( fish
->pos
, fish
->dir
, fish
->pos
);
2007 if( fish
->state
== k_fish_state_alive
|| fish
->state
== k_fish_state_soon_alive
)
2009 struct cell
*cell_current
= pcell( fish
->pos
);
2011 if( cell_current
->state
& FLAG_IS_TRIGGER
)
2013 int trigger_id
= cell_current
->links
[0]?0:1;
2015 struct cell
*target_peice
= &world
.data
[ cell_current
->links
[trigger_id
] ];
2018 if( (target_peice
->state
& FLAG_EMITTER
) && !(target_peice
->state
& FLAG_TRIGGERED
))
2020 if( world
.num_fishes
< vg_list_size( world
.fishes
) )
2022 struct fish
*fish
= &world
.fishes
[ world
.num_fishes
];
2023 lcell( cell_current
->links
[trigger_id
], fish
->pos
);
2025 fish
->state
= k_fish_state_soon_alive
;
2026 fish
->colour
= target_peice
->emit
[ trigger_id
];
2028 if( target_peice
->config
!= k_cell_type_stub
)
2030 struct cell_description
*desc
= &cell_descriptions
[ target_peice
->config
];
2031 v2i_copy( desc
->start
, fish
->dir
);
2032 fish
->flow_reversed
= 1;
2034 world
.num_fishes
++;
2039 vg_warn( "Max marbles exceeded\n" );
2044 target_peice
->state
|= FLAG_FLIP_FLOP
;
2046 target_peice
->state
&= ~FLAG_FLIP_FLOP
;
2049 cell_current
->state
|= FLAG_TRIGGERED
;
2054 // Third pass (collisions)
2055 struct fish
*fi
, *fj
;
2057 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2059 fi
= &world
.fishes
[i
];
2061 if( fi
->state
== k_fish_state_alive
)
2063 int continue_again
= 0;
2065 for( int j
= i
+1; j
< world
.num_fishes
; j
++ )
2067 fj
= &world
.fishes
[j
];
2069 if( fj
->state
== k_fish_state_alive
)
2074 v2i_sub( fi
->pos
, fi
->dir
, fi_prev
);
2075 v2i_sub( fj
->pos
, fj
->dir
, fj_prev
);
2078 collide_next_frame
= (
2079 (fi
->pos
[0] == fj
->pos
[0]) &&
2080 (fi
->pos
[1] == fj
->pos
[1]))? 1: 0,
2081 collide_this_frame
= (
2082 (fi_prev
[0] == fj
->pos
[0]) &&
2083 (fi_prev
[1] == fj
->pos
[1]) &&
2084 (fj_prev
[0] == fi
->pos
[0]) &&
2085 (fj_prev
[1] == fi
->pos
[1])
2088 if( collide_next_frame
|| collide_this_frame
)
2091 sw_set_achievement( "BANG" );
2094 // Shatter death (+0.5s)
2095 float death_time
= world
.sim_internal_time
+ ( collide_this_frame
? 0.0f
: 0.5f
);
2097 fi
->state
= k_fish_state_soon_dead
;
2098 fj
->state
= k_fish_state_soon_dead
;
2099 fi
->death_time
= death_time
;
2100 fj
->death_time
= death_time
;
2107 if( continue_again
)
2113 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
2115 struct cell_terminal
*term
= &world
.io
[ i
];
2116 int is_input
= pcell(term
->pos
)->state
& FLAG_INPUT
;
2120 if( world
.sim_frame
< term
->runs
[ world
.sim_run
].step_count
)
2122 i8 emit
= term
->runs
[ world
.sim_run
].steps
[ world
.sim_frame
];
2126 struct fish
*fish
= &world
.fishes
[ world
.num_fishes
];
2127 v2i_copy( term
->pos
, fish
->pos
);
2129 fish
->state
= k_fish_state_alive
;
2130 fish
->colour
= emit
;
2132 struct cell
*cell_ptr
= pcell( fish
->pos
);
2134 if( cell_ptr
->config
!= k_cell_type_stub
)
2136 if( world
.num_fishes
< vg_list_size(world
.fishes
))
2138 struct cell_description
*desc
= &cell_descriptions
[ cell_ptr
->config
];
2140 v2i_copy( desc
->start
, fish
->dir
);
2141 fish
->flow_reversed
= 1;
2143 world
.num_fishes
++;
2147 vg_warn( "Max marbles exceeded\n" );
2153 if( alive_count
== 0 )
2155 world
.completed
= 1;
2157 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
2159 struct cell_terminal
*term
= &world
.io
[ i
];
2161 if( pcell(term
->pos
)->state
& FLAG_OUTPUT
)
2163 struct terminal_run
*run
= &term
->runs
[ world
.sim_run
];
2165 if( run
->recv_count
== run
->step_count
)
2167 for( int j
= 0; j
< run
->step_count
; j
++ )
2169 if( run
->recieved
[j
] != run
->steps
[j
] )
2171 world
.completed
= 0;
2178 world
.completed
= 0;
2184 if( world
.completed
)
2186 if( world
.sim_run
< world
.max_runs
-1 )
2188 vg_success( "Run passed, starting next\n" );
2190 world
.sim_frame
= 0;
2191 world
.sim_target
= 0;
2192 world
.num_fishes
= 0;
2194 // Reset timing reference points
2195 world
.sim_delta_ref
= vg_time
;
2196 world
.sim_internal_ref
= 0.0f
;
2198 if( world
.st
.buttons
[ k_world_button_pause
].state
)
2199 world
.pause_offset_target
= 0.5f
;
2201 world
.pause_offset_target
= 0.0f
;
2203 world
.sim_internal_time
= 0.0f
;
2205 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
2206 world
.data
[ i
].state
&= ~FLAG_FLIP_FLOP
;
2212 vg_success( "Level passed!\n" );
2215 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
2216 if( world
.data
[ i
].state
& FLAG_CANAL
)
2219 world
.score
= score
;
2220 world
.time
= world
.sim_frame
;
2222 // Copy into career data
2223 if( world
.pCmpLevel
)
2225 career_pass_level( world
.pCmpLevel
, world
.score
, 1 );
2228 sfx_set_play( &audio_tones
, &audio_system_balls_extra
, 9 );
2229 failure_this_frame
= 0;
2230 success_this_frame
= 0;
2236 if( world
.sim_run
> 0 )
2237 sw_set_achievement( "GOOD_ENOUGH" );
2240 vg_error( "Level failed :(\n" );
2251 if( failure_this_frame
)
2253 sfx_set_play( &audio_tones
, &audio_system_balls_extra
, 0 );
2255 else if( success_this_frame
)
2257 static int succes_counter
= 0;
2259 sfx_set_play( &audio_tones
, &audio_system_balls_extra
, 1+(succes_counter
++) );
2261 if( succes_counter
== 7 )
2266 // =====================================================================================================
2268 world
.frame_lerp
= world
.sim_internal_time
- floorf( world
.sim_internal_time
);
2270 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2272 struct fish
*fish
= &world
.fishes
[i
];
2274 if( fish
->state
== k_fish_state_dead
)
2277 if( fish
->state
== k_fish_state_soon_dead
&& (world
.sim_internal_time
> fish
->death_time
) )
2278 continue; // Todo: particle thing?
2280 struct cell
*cell
= pcell(fish
->pos
);
2281 struct cell_description
*desc
= &cell_descriptions
[ cell
->config
];
2285 float t
= world
.frame_lerp
;
2286 if( fish
->flow_reversed
&& !desc
->is_linear
)
2289 v2_copy( fish
->physics_co
, fish
->physics_v
);
2291 switch( cell
->config
)
2293 case k_cell_type_merge
:
2294 if( fish
->dir
[0] == 1 )
2299 case k_cell_type_con_r
: curve
= curve_1
; break;
2300 case k_cell_type_con_l
: curve
= curve_4
; break;
2301 case k_cell_type_con_u
: curve
= curve_2
; break;
2302 case k_cell_type_con_d
: curve
= curve_8
; break;
2303 case 3: curve
= curve_3
; break;
2304 case 6: curve
= curve_6
; break;
2305 case 9: curve
= curve_9
; break;
2306 case 12: curve
= curve_12
; break;
2308 if( t
> curve_7_linear_section
)
2310 t
-= curve_7_linear_section
;
2311 t
*= (1.0f
/(1.0f
-curve_7_linear_section
));
2313 curve
= cell
->state
& FLAG_FLIP_FLOP
? curve_7
: curve_7_1
;
2317 default: curve
= NULL
; break;
2323 float t3
= t
* t
* t
;
2325 float cA
= 3.0f
*t2
- 3.0f
*t3
;
2326 float cB
= 3.0f
*t3
- 6.0f
*t2
+ 3.0f
*t
;
2327 float cC
= 3.0f
*t2
- t3
- 3.0f
*t
+ 1.0f
;
2329 fish
->physics_co
[0] = t3
*curve
[3][0] + cA
*curve
[2][0] + cB
*curve
[1][0] + cC
*curve
[0][0];
2330 fish
->physics_co
[1] = t3
*curve
[3][1] + cA
*curve
[2][1] + cB
*curve
[1][1] + cC
*curve
[0][1];
2331 fish
->physics_co
[0] += (float)fish
->pos
[0];
2332 fish
->physics_co
[1] += (float)fish
->pos
[1];
2337 origin
[0] = (float)fish
->pos
[0] + (float)fish
->dir
[0]*-0.5f
+ 0.5f
;
2338 origin
[1] = (float)fish
->pos
[1] + (float)fish
->dir
[1]*-0.5f
+ 0.5f
;
2340 fish
->physics_co
[0] = origin
[0] + (float)fish
->dir
[0]*t
;
2341 fish
->physics_co
[1] = origin
[1] + (float)fish
->dir
[1]*t
;
2344 v2_sub( fish
->physics_co
, fish
->physics_v
, fish
->physics_v
);
2345 v2_divs( fish
->physics_v
, world
.sim_internal_delta
, fish
->physics_v
);
2350 static void render_tile( v2i pos
, struct cell
*ptr
, v4f
const regular_colour
, v4f
const selected_colour
)
2352 int selected
= world
.selected
== pos
[1]*world
.w
+ pos
[0];
2355 uv
[0] = ptr
->config
& 0x3;
2356 uv
[1] = ptr
->config
>> 2;
2358 glUniform4f( SHADER_UNIFORM( shader_tile_main
, "uOffset" ),
2367 glUniform4fv( SHADER_UNIFORM( shader_tile_main
, "uColour" ), 1, selected_colour
);
2369 glUniform4fv( SHADER_UNIFORM( shader_tile_main
, "uColour" ), 1, regular_colour
);
2375 // Renders specific chunk of tiles
2376 static void render_tile_block( v2i start
, v2i end
, v4f
const regular_colour
, v4f
const selected_colour
)
2378 v2i full_start
= { 0,0 };
2379 v2i full_end
= { world
.w
, world
.h
};
2381 if( !start
|| !end
)
2387 for( int y
= start
[1]; y
< end
[1]; y
++ )
2389 for( int x
= start
[0]; x
< end
[0]; x
++ )
2392 struct cell
*cell
= pcell( pos
);
2394 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
2395 render_tile( pos
, cell
, regular_colour
, selected_colour
);
2400 // Renders all tiles in the command list
2401 static void render_tiles( v4f
const regular_colour
, v4f
const selected_colour
)
2403 glUniform4fv( SHADER_UNIFORM( shader_tile_main
, "uColour" ), 1, regular_colour
);
2407 struct render_cmd
*arr
;
2411 { world
.cmd_buf_tiles
, world
.tile_count
},
2412 { world
.cmd_buf_specials
, world
.tile_special_count
}
2415 for( int i
= 0; i
< vg_list_size( render_lists
); i
++ )
2417 struct render_list
*list
= &render_lists
[i
];
2418 for( int j
= 0; j
< list
->count
; j
++ )
2420 struct render_cmd
*cmd
= &list
->arr
[j
];
2421 struct cell
*cell
= cmd
->ptr
;
2423 render_tile( cmd
->pos
, cell
, regular_colour
, selected_colour
);
2428 static int world_button_exec( struct world_button
*btn
, v2f texture
, v3f colour
, enum world_button_status
*status
)
2430 static v2i click_grab
= { -9999, -9999 };
2435 click_grab
[0] = -9999;
2436 click_grab
[1] = -9999;
2440 v2i click_tile
= { world
.tile_x
, world
.tile_y
};
2443 int is_hovering
= v2i_eq( click_tile
, btn
->position
);
2445 // Set up light targets before logic runs
2447 btn
->light_target
= is_hovering
? 0.9f
: 0.8f
;
2449 btn
->light_target
= is_hovering
? 0.2f
: 0.0f
;
2451 if( vg_get_button( "primary" ) && is_hovering
)
2452 btn
->light_target
= 1.0f
;
2454 // Process click action
2457 if( vg_get_button_down( "primary" ) && is_hovering
)
2458 v2i_copy( click_tile
, click_grab
);
2459 else if( v2i_eq( click_grab
, click_tile
) && vg_get_button_up( "primary" ) )
2462 *status
= btn
->state
? k_world_button_on_disable
: k_world_button_on_enable
;
2464 if( btn
->mode
== k_world_button_mode_toggle
)
2467 sfx_set_play( &audio_clicks
, &audio_system_ui
, btn
->state
? 1:0 );
2475 btn
->light
= vg_lerpf( btn
->light
, btn
->light_target
+ btn
->extra_light
, vg_time_delta
*26.0f
);
2477 v3_copy( colour
, final_colour
);
2478 final_colour
[3] = btn
->light
;
2480 glUniform4f( SHADER_UNIFORM( shader_buttons
, "uOffset" ),
2486 glUniform4fv( SHADER_UNIFORM( shader_buttons
, "uColour" ), 1, final_colour
);
2492 static void level_selection_buttons(void)
2494 v3f tutorial_colour
= { 0.204f
, 0.345f
, 0.553f
};
2495 v3f locked_colour
= { 0.2f
, 0.2f
, 0.2f
};
2497 struct cmp_level
*switch_level_to
= NULL
;
2499 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
2501 struct career_level_pack
*grid
= &career_packs
[i
];
2503 for( int j
= 0; j
< grid
->count
; j
++ )
2505 struct cmp_level
*lvl
= &grid
->pack
[ j
];
2507 if( world
.pCmpLevel
== lvl
)
2508 lvl
->btn
.extra_light
= 0.35f
+ fabsf(sinf( vg_time
* 2.0f
)) * 0.05f
;
2509 else lvl
->btn
.extra_light
= 0.2f
;
2511 if( lvl
->completed_score
)
2512 lvl
->btn
.extra_light
+= 0.8f
;
2514 enum world_button_status status
;
2515 if( world_button_exec(
2518 lvl
->unlocked
? (lvl
->is_tutorial
? tutorial_colour
: grid
->primary_colour
): locked_colour
,
2522 if( status
== k_world_button_on_enable
&& lvl
->unlocked
)
2523 switch_level_to
= lvl
;
2528 if( switch_level_to
)
2530 world
.st
.lvl_to_load
= switch_level_to
;
2531 world
.st
.lvl_load_time
= vg_time
+ 0.25f
;
2532 world
.st
.world_transition
= 1.0f
;
2535 if( console_changelevel( 1, &switch_level_to->map_name ) )
2537 world.pCmpLevel = switch_level_to;
2538 gen_level_text( world.pCmpLevel );
2544 static void render_sprite( enum sprites_auto_combine_index id
, v3f pos
)
2546 struct vg_sprite
*sp
= &sprites_auto_combine
[ id
];
2548 glUniform4fv( SHADER_UNIFORM( shader_sprite
, "uUv" ), 1, sp
->uv_xywh
);
2549 glUniform3f( SHADER_UNIFORM( shader_sprite
, "uPos" ), pos
[0], pos
[1], pos
[2] * world
.st
.world_transition
);
2554 void vg_render(void)
2556 glViewport( 0,0, vg_window_x
, vg_window_y
);
2558 glDisable( GL_DEPTH_TEST
);
2559 glClearColor( 0.369768f
, 0.3654f
, 0.42f
, 1.0f
);
2560 glClear( GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
2562 v4f
const colour_default
= {1.0f
, 1.0f
, 1.0f
, 1.0f
};
2563 v4f
const colour_selected
= {0.90f
, 0.92f
, 1.0f
, 1.0f
};
2565 int const circle_base
= 6;
2566 int const filled_start
= circle_base
+0;
2567 int const filled_count
= circle_base
+32;
2568 int const empty_start
= circle_base
+32;
2569 int const empty_count
= circle_base
+32*2;
2571 struct world_theme
*theme
= &world_themes
[ world_theme_id
];
2573 if( !world
.initialzed
)
2576 // Extract render commands
2577 world
.tile_count
= 0;
2578 world
.tile_special_count
= 0;
2580 for( int y
= 1; y
< world
.h
-1; y
++ )
2582 for( int x
= 1; x
< world
.w
-1; x
++ )
2584 struct cell
*cell
= pcell((v2i
){x
,y
});
2586 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
|FLAG_INPUT_NICE
) )
2588 struct render_cmd
*cmd
;
2591 (cell
->config
== k_cell_type_split
&& (cell
->state
& FLAG_CANAL
))
2592 || (cell
->state
& (FLAG_EMITTER
|FLAG_IS_TRIGGER
))
2594 cmd
= &world
.cmd_buf_tiles
[ world
.max_commands
- (++ world
.tile_special_count
) ];
2596 cmd
= &world
.cmd_buf_tiles
[ world
.tile_count
++ ];
2605 world
.cmd_buf_specials
= &world
.cmd_buf_tiles
[ world
.max_commands
- world
.tile_special_count
];
2608 // ========================================================================================================
2609 use_mesh( &world
.shapes
);
2611 SHADER_USE( shader_background
);
2612 glUniformMatrix3fv( SHADER_UNIFORM( shader_background
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2614 glActiveTexture( GL_TEXTURE0
);
2615 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
2616 glUniform1i( SHADER_UNIFORM( shader_background
, "uTexMain" ), 0 );
2618 glUniform3f( SHADER_UNIFORM( shader_background
, "uOffset" ), -16, -16, 64 );
2619 glUniform1f( SHADER_UNIFORM( shader_background
, "uVariance" ), 0.02f
);
2621 glActiveTexture( GL_TEXTURE1
);
2622 glBindTexture( GL_TEXTURE_2D
, world
.random_samples
);
2623 glUniform1i( SHADER_UNIFORM( shader_background
, "uSamplerNoise" ), 1 );
2624 glUniform1f( SHADER_UNIFORM( shader_background
, "uVisibility" ), 1.0f
); //world.st.world_transition );
2628 // TILESET BACKGROUND LAYER
2629 // ========================================================================================================
2630 use_mesh( &world
.shapes
);
2631 SHADER_USE( shader_tile_main
);
2634 m2x2_identity( subtransform
);
2635 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main
, "uSubTransform" ), 1, GL_FALSE
, (float *)subtransform
);
2636 glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_main
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2637 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uGhost" ), 0.0f
);
2638 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uForeground" ), 0.0f
);
2639 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uVisibility" ), world
.st
.world_transition
* 2.0f
);
2642 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
2643 glBlendEquation(GL_FUNC_ADD
);
2646 vg_tex2d_bind( &tex_tile_data
, 0 );
2647 vg_tex2d_bind( theme
->tex_tiles
, 1 );
2649 glUniform3fv( SHADER_UNIFORM( shader_tile_main
, "uShadowing" ), 1, theme
->col_shadow
);
2651 render_tiles( colour_default
, colour_default
);
2654 // ========================================================================================================
2655 SHADER_USE( shader_ball
);
2656 glUniformMatrix3fv( SHADER_UNIFORM( shader_ball
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2658 vg_tex2d_bind( &tex_ball_noise
, 0 );
2659 glUniform1i( SHADER_UNIFORM( shader_ball
, "uTexMain" ), 0 );
2661 if( world
.st
.buttons
[ k_world_button_sim
].state
)
2663 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2665 struct fish
*fish
= &world
.fishes
[i
];
2667 render_pos
[2] = 1.0f
;
2669 if( fish
->state
== k_fish_state_dead
|| fish
->state
== k_fish_state_soon_dead
)
2671 float death_anim_time
= world
.sim_internal_time
- fish
->death_time
;
2674 if( death_anim_time
> 0.0f
&& death_anim_time
< 1.0f
)
2676 float amt
= 1.0f
-death_anim_time
*death_anim_time
;
2678 v2_muladds( fish
->physics_co
, fish
->physics_v
, -1.0f
* world
.sim_internal_delta
* amt
, fish
->physics_co
);
2679 render_pos
[2] = amt
;
2681 else if( world
.sim_internal_time
> fish
->death_time
)
2684 else if( fish
->state
== k_fish_state_bg
)
2687 v2_copy( fish
->physics_co
, render_pos
);
2689 v4f dot_colour
= { 0.0f
, 0.0f
, 0.0f
, 1.0f
};
2690 colour_code_v3( fish
->colour
, dot_colour
);
2692 glUniform3fv( SHADER_UNIFORM( shader_ball
, "uColour" ), 1, dot_colour
);
2693 glUniform3fv( SHADER_UNIFORM( shader_ball
, "uOffset" ), 1, render_pos
);
2694 glUniform2f( SHADER_UNIFORM( shader_ball
, "uTexOffset" ), (float)i
* 1.2334, (float)i
* -0.3579f
);
2699 // TILESET FOREGROUND LAYER
2700 // ========================================================================================================
2701 SHADER_USE( shader_tile_main
);
2704 vg_tex2d_bind( &tex_tile_data
, 0 );
2705 glUniform1i( SHADER_UNIFORM( shader_tile_main
, "uTexGlyphs" ), 0 );
2707 // TODO: is this needed to be rebinded?
2708 vg_tex2d_bind( theme
->tex_tiles
, 1 );
2709 glUniform1i( SHADER_UNIFORM( shader_tile_main
, "uTexWood" ), 1 );
2711 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uForeground" ), 1.0f
);
2712 render_tiles( colour_default
, colour_selected
);
2715 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
2717 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
2718 struct cell
*cell
= cmd
->ptr
;
2720 if( cell
->config
== k_cell_type_split
)
2722 float rotation
= cell
->state
& FLAG_FLIP_FLOP
? vg_rad( -45.0f
): vg_rad( 45.0f
);
2724 if( cell
->state
& FLAG_FLIP_ROTATING
)
2726 if( (world
.frame_lerp
> curve_7_linear_section
) )
2728 float const rotation_speed
= 0.4f
;
2729 if( (world
.frame_lerp
< 1.0f
-rotation_speed
) )
2731 float t
= world
.frame_lerp
- curve_7_linear_section
;
2732 t
*= -2.0f
* (1.0f
/(1.0f
-(curve_7_linear_section
+rotation_speed
)));
2742 m2x2_create_rotation( subtransform
, rotation
);
2744 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main
, "uSubTransform" ), 1, GL_FALSE
, (float *)subtransform
);
2745 glUniform4f( SHADER_UNIFORM( shader_tile_main
, "uOffset" ),
2747 (float)cmd
->pos
[1] + 0.125f
,
2748 cell
->state
& FLAG_TARGETED
? 3.0f
: 2.0f
,
2756 // ========================================================================================================
2757 if( world
.selected
!= -1 && !(world
.data
[ world
.selected
].state
& FLAG_CANAL
) && !world
.id_drag_from
)
2759 v2i new_begin
= { world
.tile_x
- 2, world
.tile_y
- 2 };
2760 v2i new_end
= { world
.tile_x
+ 2, world
.tile_y
+ 2 };
2762 world
.data
[ world
.selected
].state
^= FLAG_CANAL
;
2763 map_reclassify( new_begin
, new_end
, 0 );
2765 m2x2_identity( subtransform
);
2766 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uGhost" ), 1.0f
);
2767 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main
, "uSubTransform" ), 1, GL_FALSE
, (float *)subtransform
);
2768 glUniform2fv( SHADER_UNIFORM( shader_tile_main
, "uMousePos" ), 1, world
.tile_pos
);
2770 render_tile_block( new_begin
, new_end
, colour_default
, colour_default
);
2772 world
.data
[ world
.selected
].state
^= FLAG_CANAL
;
2773 map_reclassify( new_begin
, new_end
, 0 );
2777 // ========================================================================================================
2778 SHADER_USE( shader_buttons
);
2779 glUniformMatrix3fv( SHADER_UNIFORM( shader_buttons
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2781 vg_tex2d_bind( &tex_buttons
, 0 );
2782 glUniform1i( SHADER_UNIFORM( shader_buttons
, "uTexMain" ), 0 );
2784 enum world_button_status stat
;
2785 int world_paused
= world
.st
.buttons
[k_world_button_pause
].state
;
2786 int world_running
= world
.st
.buttons
[k_world_button_sim
].state
;
2788 float sim_icon_x
= world_paused
? 3.0f
: (world_running
? 2.0f
: 0.0f
);
2790 v3f btn_dark_blue
= { 0.204f
, 0.345f
, 0.553f
};
2791 v3f btn_orange
= { 0.553f
, 0.345f
, 0.204f
};
2793 if( world_button_exec( &world
.st
.buttons
[k_world_button_sim
], (v2f
){ sim_icon_x
, 3.0f
}, btn_dark_blue
, &stat
))
2795 if( stat
== k_world_button_on_enable
)
2800 world
.pause_offset_target
= 0.5f
;
2806 // Trigger single step
2807 world
.pause_offset_target
+= 1.0f
;
2808 world
.st
.buttons
[k_world_button_sim
].state
= 1;
2817 if( world_button_exec( &world
.st
.buttons
[k_world_button_pause
], (v2f
){ 1.0f
, 3.0f
}, btn_dark_blue
, &stat
))
2819 world
.sim_internal_ref
= world
.sim_internal_time
;
2820 world
.sim_delta_ref
= vg_time
;
2822 if( stat
== k_world_button_on_enable
)
2824 float time_frac
= world
.sim_internal_time
-floorf(world
.sim_internal_time
);
2825 world
.pause_offset_target
= 0.5f
- time_frac
;
2828 world
.pause_offset_target
= 0.0f
;
2831 if( world_button_exec( &world
.st
.buttons
[k_world_button_speedy
], (v2f
){ 0.0f
, 2.0f
}, btn_orange
, &stat
))
2833 world
.sim_delta_speed
= stat
== k_world_button_on_enable
? 10.0f
: 2.5f
;
2837 world
.sim_delta_ref
= vg_time
;
2838 world
.sim_internal_ref
= world
.sim_internal_time
;
2842 if( world_button_exec( &world
.st
.buttons
[k_world_button_settings
], (v2f
){ 1.0f
, 2.0f
}, btn_orange
, &stat
))
2844 world
.st
.state
= stat
== k_world_button_on_enable
? k_game_state_settings
: k_game_state_main
;
2847 level_selection_buttons();
2849 if( vg_get_button_up( "primary" ) )
2850 world_button_exec( NULL
, NULL
, NULL
, NULL
);
2853 // ========================================================================================================
2855 //glEnable(GL_BLEND);
2856 SHADER_USE( shader_tile_colour
);
2857 glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2859 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
2861 struct cell_terminal
*term
= &world
.io
[ i
];
2862 struct cell
*cell
= pcell(term
->pos
);
2864 int is_input
= cell
->state
& FLAG_INPUT
;
2865 v4f dot_colour
= { 0.0f
, 0.0f
, 0.0f
, 1.0f
};
2867 if( cell
->state
& FLAG_EMITTER
)
2869 for( int j
= 0; j
< 2; j
++ )
2871 if( cell
->emit
[j
] != -1 )
2873 colour_code_v3( cell
->emit
[j
], dot_colour
);
2875 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
2876 term
->pos
[0] + 0.25f
+ (float)j
* 0.5f
,
2877 term
->pos
[1] + 0.25f
,
2881 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
2882 draw_mesh( filled_start
, filled_count
);
2888 for( int k
= 0; k
< term
->run_count
; k
++ )
2890 float arr_base
= is_input
? 1.2f
: -0.2f
,
2891 run_offset
= (is_input
? 0.2f
: -0.2f
) * (float)k
,
2892 y_position
= is_input
?
2893 (arr_base
+ (float)term
->pos
[1] + (float)(term
->run_count
-1)*0.2f
) - run_offset
:
2894 (float)term
->pos
[1] + arr_base
+ run_offset
;
2899 if( is_simulation_running() )
2901 if( k
== world
.sim_run
)
2903 float a
= fabsf(sinf( vg_time
* 2.0f
)) * 0.075f
+ 0.075f
;
2905 v4_copy( (v4f
){ 1.0f
, 1.0f
, 1.0f
, a
}, bar_colour
);
2908 v4_copy( (v4f
){ 0.0f
, 0.0f
, 0.0f
, 0.13f
}, bar_colour
);
2912 else if( 1 || k
& 0x1 )
2915 v4_copy( (v4f
){ 1.0f
, 1.0f
, 1.0f
, 0.07f
}, bar_colour
);
2917 v4_copy( (v4f
){ 0.0f
, 0.0f
, 0.0f
, 0.13f
}, bar_colour
);
2924 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, bar_colour
);
2925 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
2926 (float)term
->pos
[0], y_position
- 0.1f
, 1.0f
);
2931 for( int j
= 0; j
< term
->runs
[k
].step_count
; j
++ )
2933 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
2934 (float)term
->pos
[0] + 0.2f
+ 0.2f
* (float)j
,
2941 i8 colour
= term
->runs
[k
].steps
[j
];
2944 colour_code_v3( colour
, dot_colour
);
2945 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
2947 // Draw filled if tick not passed, draw empty if empty
2948 if( (world
.sim_frame
> j
&& world
.sim_run
>= k
) || world
.sim_run
> k
)
2949 draw_mesh( empty_start
, empty_count
);
2951 draw_mesh( filled_start
, filled_count
);
2957 if( term
->runs
[k
].recv_count
> j
)
2959 colour_code_v3( term
->runs
[k
].recieved
[j
], dot_colour
);
2960 v3_muls( dot_colour
, 0.8f
, dot_colour
);
2961 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
2963 draw_mesh( filled_start
, filled_count
);
2966 colour_code_v3( term
->runs
[k
].steps
[j
], dot_colour
);
2967 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
2969 draw_mesh( empty_start
, empty_count
);
2976 // ========================================================================================================
2977 SHADER_USE( shader_sprite
);
2978 glUniformMatrix3fv( SHADER_UNIFORM( shader_sprite
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2980 vg_tex2d_bind( &tex_sprites
, 0 );
2981 glUniform1i( SHADER_UNIFORM( shader_sprite
, "uTexMain" ), 0 );
2983 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
2985 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
2986 struct cell
*cell
= cmd
->ptr
;
2988 if( (cell
->config
== k_cell_type_split
) || (cell
->state
& FLAG_EMITTER
) )
2990 v2f center
= { cmd
->pos
[0] + 0.5f
, cmd
->pos
[1] + 0.5f
};
2992 v3f p0
= { 0.0f
, 0.0f
, 4.0f
};
2993 v3f p1
= { 0.0f
, 0.0f
, 4.0f
};
2995 v2_add( center
, (v2f
){ -0.25f
, -0.25f
}, p0
);
2996 v2_add( center
, (v2f
){ 0.25f
, -0.25f
}, p1
);
2998 render_sprite( k_sprite_jack_1
, p0
);
2999 render_sprite( k_sprite_jack_2
, p1
);
3001 else if( cell
->state
& FLAG_IS_TRIGGER
)
3003 v3f p0
= { 0.0f
, 0.0f
, 4.0f
};
3005 struct cell_description
*desc
= &cell_descriptions
[ cell
->config
];
3007 v2_add( (v2f
){ cmd
->pos
[0], cmd
->pos
[1] }, desc
->trigger_pos
, p0
);
3008 render_sprite( desc
->trigger_sprite
, p0
);
3013 // ========================================================================================================
3016 m3x3_identity( mvp_text
);
3017 m3x3_scale( mvp_text
, (v3f
){
3018 1.0f
/ ((float)UI_GLYPH_SPACING_X
*4.0f
),
3019 1.0f
/ -((float)UI_GLYPH_SPACING_X
*4.0f
),
3023 m3x3_mul( vg_pv
, mvp_text
, mvp_text
);
3024 ui_draw( &world
.st
.world_text
, mvp_text
);
3027 // ========================================================================================================
3030 SHADER_USE( shader_wire
);
3031 glBindVertexArray( world
.wire
.vao
);
3033 glUniformMatrix3fv( SHADER_UNIFORM( shader_wire
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
3035 v4f
const wire_left_colour
= { 0.5f
, 0.5f
, 0.5f
, 1.0f
};
3036 v4f
const wire_right_colour
= { 0.2f
, 0.2f
, 0.2f
, 1.0f
};
3037 v4f
const wire_drag_colour
= { 0.2f
, 0.2f
, 0.2f
, 0.6f
};
3039 glUniform1f( SHADER_UNIFORM( shader_wire
, "uTime" ), world
.frame_lerp
);
3040 glUniform1f( SHADER_UNIFORM( shader_wire
, "uGlow" ), 0.0f
);
3042 if( world
.id_drag_from
)
3044 glUniform4fv( SHADER_UNIFORM( shader_wire
, "uColour" ), 1, wire_drag_colour
);
3045 glUniform1f( SHADER_UNIFORM( shader_wire
, "uCurve" ), 0.4f
);
3046 glUniform3f( SHADER_UNIFORM( shader_wire
, "uStart" ), world
.drag_from_co
[0], world
.drag_from_co
[1], 0.20f
*world
.st
.world_transition
);
3047 glUniform3f( SHADER_UNIFORM( shader_wire
, "uEnd" ), world
.drag_to_co
[0], world
.drag_to_co
[1], 0.20f
*world
.st
.world_transition
);
3048 glDrawElements( GL_TRIANGLES
, world
.wire
.em
, GL_UNSIGNED_SHORT
, (void*)(0) );
3051 // Pulling animation
3052 float rp_x1
= world
.frame_lerp
*9.0f
;
3053 float rp_xa
= rp_x1
*expf(1.0f
-rp_x1
)* 0.36f
;
3054 float rp_x2
= 1.0f
-rp_xa
;
3056 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3058 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3059 struct cell
*cell
= cmd
->ptr
;
3061 if( cell
->state
& FLAG_TARGETED
)
3063 for( int j
= 0; j
< 2; j
++ )
3065 if( !cell
->links
[j
] )
3068 struct cell
*other_cell
= &world
.data
[ cell
->links
[ j
]];
3069 struct cell_description
*desc
= &cell_descriptions
[ other_cell
->config
];
3071 int x2
= cell
->links
[j
] % world
.w
;
3072 int y2
= (cell
->links
[j
] - x2
) / world
.w
;
3077 endpoint
[0] = (float)cmd
->pos
[0] + (j
? 0.75f
: 0.25f
);
3078 endpoint
[1] = (float)cmd
->pos
[1] + 0.25f
;
3083 v2_add( desc
->trigger_pos
, startpoint
, startpoint
);
3085 if( cmd
->ptr
->state
& FLAG_EMITTER
)
3088 colour_code_v3( cmd
->ptr
->emit
[j
], wire_colour
);
3089 wire_colour
[3] = 1.0f
;
3091 glUniform4fv( SHADER_UNIFORM( shader_wire
, "uColour" ), 1, wire_colour
);
3094 glUniform4fv( SHADER_UNIFORM( shader_wire
, "uColour" ), 1, j
? wire_right_colour
: wire_left_colour
);
3096 glUniform1f( SHADER_UNIFORM( shader_wire
, "uCurve" ), other_cell
->state
& FLAG_TRIGGERED
? rp_x2
* 0.4f
: 0.4f
);
3097 glUniform1f( SHADER_UNIFORM( shader_wire
, "uGlow" ), other_cell
->state
& FLAG_TRIGGERED
? rp_xa
: 0.0f
);
3098 glUniform3f( SHADER_UNIFORM( shader_wire
, "uEnd" ), startpoint
[0], startpoint
[1], 0.18f
*world
.st
.world_transition
);
3099 glUniform3f( SHADER_UNIFORM( shader_wire
, "uStart" ), endpoint
[0], endpoint
[1], 0.18f
*world
.st
.world_transition
);
3100 glDrawElements( GL_TRIANGLES
, world
.wire
.em
, GL_UNSIGNED_SHORT
, (void*)(0) );
3106 // ========================================================================================================
3108 SHADER_USE( shader_tile_colour
);
3109 use_mesh( &world
.shapes
);
3111 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3113 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3114 struct cell
*cell
= cmd
->ptr
;
3116 if( cell
->state
& FLAG_TARGETED
)
3118 for( int j
= 0; j
< 2; j
++ )
3120 if( !cell
->links
[j
] )
3123 struct cell
*other_cell
= &world
.data
[ cell
->links
[ j
]];
3124 struct cell_description
*desc
= &cell_descriptions
[ other_cell
->config
];
3126 int x2
= cell
->links
[j
] % world
.w
;
3127 int y2
= (cell
->links
[j
] - x2
) / world
.w
;
3131 pts
[0][0] = (float)cmd
->pos
[0] + (j
? 0.75f
: 0.25f
);
3132 pts
[0][1] = (float)cmd
->pos
[1] + 0.25f
;
3137 v2_add( desc
->trigger_pos
, pts
[1], pts
[1] );
3139 if( cell
->state
& FLAG_EMITTER
)
3142 colour_code_v3( cell
->emit
[j
], wire_colour
);
3144 v3_muls( wire_colour
, 0.8f
, wire_colour
);
3145 wire_colour
[3] = 1.0f
;
3147 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, wire_colour
);
3150 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1,j
?wire_right_colour
: wire_left_colour
);
3152 for( int i
= 0; i
< 2; i
++ )
3154 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
3157 0.08f
* world
.st
.world_transition
3159 draw_mesh( filled_start
, filled_count
);
3165 // SUB SPLITTER DIRECTION
3166 // ========================================================================================================
3169 glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.9f, 0.35f, 0.1f, 0.75f );
3171 for( int i = 0; i < world.tile_special_count; i ++ )
3173 struct render_cmd *cmd = &world.cmd_buf_specials[i];
3174 struct cell *cell = cmd->ptr;
3176 if( cell->state & FLAG_TARGETED && cell->config == k_cell_type_split )
3178 glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), cmd->pos[0], cmd->pos[1], 1.0f );
3179 draw_mesh( cell->state & FLAG_FLIP_FLOP? 5: 4, 1 );
3185 // ========================================================================================================
3186 glBlendFunc(GL_ONE
, GL_ONE
);
3187 glBlendEquation(GL_FUNC_ADD
);
3189 SHADER_USE( shader_sprite
);
3191 vg_tex2d_bind( &tex_sprites
, 0 );
3192 glUniform1i( SHADER_UNIFORM( shader_sprite
, "uTexMain" ), 0 );
3194 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3196 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3197 struct cell
*cell
= cmd
->ptr
;
3199 if( cell
->config
== k_cell_type_split
)
3201 v2f center
= { cmd
->pos
[0] + 0.5f
, cmd
->pos
[1] + 0.5f
};
3203 v3f p0
= { 0.0f
, 0.0f
, 12.0f
};
3204 v3f p1
= { 0.0f
, 0.0f
, 12.0f
};
3206 v2_add( center
, (v2f
){ -0.25f
, -0.25f
}, p0
);
3207 v2_add( center
, (v2f
){ 0.25f
, -0.25f
}, p1
);
3209 if( cell
->state
& FLAG_TARGETED
)
3211 if( cell
->state
& FLAG_FLIP_FLOP
)
3212 render_sprite( k_sprite_flare_y
, p1
);
3214 render_sprite( k_sprite_flare_b
, p0
);
3217 render_sprite( k_sprite_flare_w
, cell
->state
&FLAG_FLIP_FLOP
? p1
: p0
);
3221 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
3222 glBlendEquation(GL_FUNC_ADD
);
3225 glDisable(GL_BLEND
);
3229 float const score_bright = 1.25f;
3230 glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ),
3231 0.4f*score_bright, 0.39f*score_bright, 0.45f*score_bright, 1.0f );
3233 use_mesh( &world.numbers );
3234 draw_numbers( (v3f){ 2.0f, (float)world.h-1.875f, 0.3333f }, world.score );
3240 // Drawing world name
3241 if( world
.pCmpLevel
)
3243 gui_text( (ui_px
[2]){ vg_window_x
/ 2, 4 }, world
.pCmpLevel
->title
, 2, k_text_align_center
);
3244 gui_text( (ui_px
[2]){ vg_window_x
/ 2, 28 }, world
.pCmpLevel
->description
, 1, k_text_align_center
);
3247 if( world
.st
.state
== k_game_state_update
)
3251 ui_global_ctx
.cursor
[2] = 458;
3252 ui_global_ctx
.cursor
[3] = 316;
3253 ui_global_ctx
.cursor
[0] = vg_window_x
/ 2 - 229;
3254 ui_global_ctx
.cursor
[1] = vg_window_y
/ 2 - 158;
3258 gui_capture_mouse( 200 );
3259 gui_fill_rect( ui_global_ctx
.cursor
, 0xE8303030 );
3262 title_pos
[0] = ui_global_ctx
.cursor
[0] + 229;
3263 title_pos
[1] = ui_global_ctx
.cursor
[1] + 16;
3265 gui_text( title_pos
, "Update 1.5", 2, k_text_align_center
);
3267 gui_text( (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 16, title_pos
[1] + 45 },
3268 "Welcome to the first update to marble computing!"
3270 "New features have been added:\n"
3272 " - Settings menu\n"
3274 " - More levels and a new block type\n"
3275 " - Scores for each level\n"
3276 " - Zooming and panning (mousewheel)\n"
3278 "There is much more in the works, such as a\n"
3279 "soundtrack, and the rest of the levels for the\n"
3282 "Thank you everyone for enjoying my game :)\n",
3283 1, k_text_align_left
3286 ui_global_ctx
.cursor
[2] = 100;
3287 ui_global_ctx
.cursor
[3] = 30;
3288 ui_global_ctx
.cursor
[0] += 229 - 50;
3289 ui_global_ctx
.cursor
[1] += 316 - 30 - 16;
3291 if( gui_button( 1 ) )
3293 world
.st
.state
= k_game_state_main
;
3295 gui_text( (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 50,
3296 ui_global_ctx
.cursor
[1] + 4 }, "OK", 1, k_text_align_center
);
3301 else if( world
.st
.state
== k_game_state_settings
)
3305 ui_global_ctx
.cursor
[2] = 225;
3311 gui_capture_mouse( 200 );
3313 gui_fill_rect( ui_global_ctx
.cursor
, 0xC0202020 );
3314 ui_rect_pad( ui_global_ctx
.cursor
, 8 );
3316 ui_global_ctx
.cursor
[3] = 25;
3320 gui_text( ui_global_ctx
.cursor
, "SETTINGS", 2, 0 );
3324 // Colour scheme selection
3325 ui_global_ctx
.cursor
[1] += 30;
3327 gui_text( ui_global_ctx
.cursor
, "Colour Scheme", 1, 0 );
3328 ui_global_ctx
.cursor
[1] += 25;
3332 ui_global_ctx
.cursor
[2] = 50;
3334 for( int i
= 0; i
< 4; i
++ )
3339 u32 rgb
= 0xff000000;
3341 for( int j
= 0; j
< 3; j
++ )
3342 rgb
|= (u32
)(colour_sets
[ colour_set_id
][i
][j
]*255.0f
) << j
* 8;
3344 gui_fill_rect( ui_global_ctx
.cursor
, rgb
);
3353 ui_global_ctx
.cursor
[2] = 25;
3354 if( gui_button( 0 ) == k_button_click
)
3356 if( colour_set_id
> 0 )
3359 gui_text( ui_global_ctx
.cursor
, "<", 2, 0 );
3362 ui_global_ctx
.cursor
[2] = 150;
3365 gui_fill_rect( ui_global_ctx
.cursor
, 0x33ffffff );
3367 (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 75, ui_global_ctx
.cursor
[1] + 6 },
3368 (const char *[]){ "Normal", "Extra1", "Extra2" }[ colour_set_id
],
3369 1, k_text_align_center
3374 ui_global_ctx
.cursor
[2] = 25;
3375 if( gui_button( 1 ) == k_button_click
)
3377 if( colour_set_id
< vg_list_size( colour_sets
)-1 )
3380 gui_text( ui_global_ctx
.cursor
, ">", 2, 0 );
3386 // TODO: remove code dupe
3387 ui_global_ctx
.cursor
[1] += 16;
3389 gui_text( ui_global_ctx
.cursor
, "Tile Theme", 1, 0 );
3390 ui_global_ctx
.cursor
[1] += 20;
3394 ui_global_ctx
.cursor
[2] = 25;
3395 if( gui_button( 0 ) == k_button_click
)
3397 if( world_theme_id
> 0 )
3400 gui_text( ui_global_ctx
.cursor
, "<", 2, 0 );
3403 ui_global_ctx
.cursor
[2] = 150;
3406 gui_fill_rect( ui_global_ctx
.cursor
, 0x33ffffff );
3408 (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 75, ui_global_ctx
.cursor
[1] + 6 },
3409 world_themes
[ world_theme_id
].name
, 1, k_text_align_center
3414 ui_global_ctx
.cursor
[2] = 25;
3415 if( gui_button( 1 ) == k_button_click
)
3417 if( world_theme_id
< vg_list_size( world_themes
)-1 )
3420 gui_text( ui_global_ctx
.cursor
, ">", 2, 0 );
3429 #if STEAM_LEADERBOARDS
3430 void leaderboard_dispatch_score(void)
3433 sw_upload_leaderboard_score(
3434 ui_data
.upload_request
.level
->steam_leaderboard
,
3435 k_ELeaderboardUploadScoreMethodKeepBest
,
3436 ui_data
.upload_request
.score
,
3441 ui_data
.upload_request
.is_waiting
= 0;
3443 vg_success( "Dispatched leaderboard score\n" );
3446 void leaderboard_found( LeaderboardFindResult_t
*pCallback
)
3448 if( !pCallback
->m_bLeaderboardFound
)
3450 vg_error( "Leaderboard could not be found\n" );
3451 ui_data
.steam_leaderboard
= 0;
3455 const char *recieved_name
= sw_get_leaderboard_name( pCallback
->m_hSteamLeaderboard
);
3457 // Update UI state and request entries if this callback found the current UI level
3458 if( ui_data
.level_selected
)
3460 if( !strcmp( recieved_name
, ui_data
.level_selected
->map_name
) )
3462 sw_download_leaderboard_entries( pCallback
->m_hSteamLeaderboard
, k_ELeaderboardDataRequestFriends
, 0, 8 );
3463 ui_data
.level_selected
->steam_leaderboard
= pCallback
->m_hSteamLeaderboard
;
3467 // Dispatch the waiting request if there was one
3468 if( ui_data
.upload_request
.is_waiting
)
3470 if( !strcmp( recieved_name
, ui_data
.upload_request
.level
->map_name
) )
3472 ui_data
.upload_request
.level
->steam_leaderboard
= pCallback
->m_hSteamLeaderboard
;
3473 leaderboard_dispatch_score();
3479 void leaderboard_downloaded( LeaderboardScoresDownloaded_t
*pCallback
)
3481 // Update UI if this leaderboard matches what we currently have in view
3482 if( ui_data
.level_selected
->steam_leaderboard
== pCallback
->m_hSteamLeaderboard
)
3484 vg_info( "Recieved %d entries\n", pCallback
->m_cEntryCount
);
3485 ui_data
.leaderboard_count
= VG_MIN( pCallback
->m_cEntryCount
, 8 );
3487 u64_steamid local_player
= sw_get_steamid();
3489 for( int i
= 0; i
< ui_data
.leaderboard_count
; i
++ )
3491 LeaderboardEntry_t entry
;
3492 sw_get_downloaded_entry( pCallback
->m_hSteamLeaderboardEntries
, i
, &entry
, NULL
, 0 );
3494 struct leaderboard_player
*player
= &ui_data
.leaderboard_players
[i
];
3496 player
->id
= entry
.m_steamIDUser
.m_unAll64Bits
;
3497 strncpy( player
->player_name
, sw_get_friend_persona_name( player
->id
), vg_list_size( player
->player_name
)-1 );
3498 player
->score
= entry
.m_nScore
;
3500 snprintf( player
->score_text
, vg_list_size(player
->score_text
), "%d", player
->score
);
3501 player
->texture
= sw_get_player_image( player
->id
);
3503 if( player
->texture
== 0 )
3504 player
->texture
= tex_unkown
.name
;
3506 player
->is_local_player
= local_player
== player
->id
? 1: 0;
3509 if( ui_data
.leaderboard_count
)
3510 ui_data
.leaderboard_show
= 1;
3512 ui_data
.leaderboard_show
= 0;
3514 else vg_warn( "Downloaded leaderboard does not match requested!\n" );
3517 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
)
3519 if( ui_data
.upload_request
.is_waiting
)
3520 vg_warn( "You are uploading leaderboard entries too quickly!\n" );
3522 ui_data
.upload_request
.level
= cmp_level
;
3523 ui_data
.upload_request
.score
= score
;
3524 ui_data
.upload_request
.is_waiting
= 1;
3526 // If leaderboard ID has been downloaded already then just immediately dispatch this
3527 if( cmp_level
->steam_leaderboard
)
3528 leaderboard_dispatch_score();
3530 sw_find_leaderboard( cmp_level
->map_name
);
3535 // ===========================================================================================================
3537 static int console_credits( int argc
, char const *argv
[] )
3539 vg_info( "Aknowledgements:\n" );
3540 vg_info( " GLFW zlib/libpng glfw.org\n" );
3541 vg_info( " miniaudio MIT0 miniaud.io\n" );
3542 vg_info( " QOI MIT phoboslab.org\n" );
3543 vg_info( " STB library MIT nothings.org\n" );
3547 static int console_save_map( int argc
, char const *argv
[] )
3549 if( !world
.initialzed
)
3551 vg_error( "Tried to save uninitialized map!\n" );
3555 char map_path
[ 256 ];
3557 strcpy( map_path
, "sav/" );
3558 strcat( map_path
, world
.map_name
);
3559 strcat( map_path
, ".map" );
3561 FILE *test_writer
= fopen( map_path
, "wb" );
3564 vg_info( "Saving map to '%s'\n", map_path
);
3565 map_serialize( test_writer
);
3567 fclose( test_writer
);
3572 vg_error( "Unable to open stream for writing\n" );
3577 static int console_load_map( int argc
, char const *argv
[] )
3579 char map_path
[ 256 ];
3584 strcpy( map_path
, "sav/" );
3585 strcat( map_path
, argv
[0] );
3586 strcat( map_path
, ".map" );
3588 char *text_source
= vg_textasset_read( map_path
);
3592 strcpy( map_path
, "maps/" );
3593 strcat( map_path
, argv
[0] );
3594 strcat( map_path
, ".map" );
3596 text_source
= vg_textasset_read( map_path
);
3601 vg_info( "Loading map: '%s'\n", map_path
);
3602 world
.pCmpLevel
= NULL
;
3604 if( !map_load( text_source
, argv
[0] ) )
3606 free( text_source
);
3610 free( text_source
);
3615 vg_error( "Missing maps '%s'\n", argv
[0] );
3621 vg_error( "Missing argument <map_path>\n" );
3626 static int console_changelevel( int argc
, char const *argv
[] )
3630 // Save current level
3631 console_save_map( 0, NULL
);
3633 if( console_load_map( argc
, argv
) )
3635 world
.st
.zoom
= 0.0f
;
3642 vg_error( "Missing argument <map_path>\n" );
3648 // START UP / SHUTDOWN
3649 // ===========================================================================================================
3651 #define TRANSFORM_TRI_2D( S, OX, OY, X1, Y1, X2, Y2, X3, Y3 ) \
3652 X1*S+OX, Y1*S+OY, X2*S+OX, Y2*S+OY, X3*S+OX, Y3*S+OY
3656 // Steamworks callbacks
3657 #ifdef STEAM_LEADERBOARDS
3658 sw_leaderboard_found
= &leaderboard_found
;
3659 sw_leaderboard_downloaded
= &leaderboard_downloaded
;
3662 vg_function_push( (struct vg_cmd
){
3663 .name
= "_map_write",
3664 .function
= console_save_map
3667 vg_function_push( (struct vg_cmd
){
3668 .name
= "_map_load",
3669 .function
= console_load_map
3672 vg_function_push( (struct vg_cmd
){
3674 .function
= console_changelevel
3677 vg_function_push( (struct vg_cmd
){
3679 .function
= console_credits
3682 vg_convar_push( (struct vg_convar
){
3684 .data
= &colour_set_id
,
3685 .data_type
= k_convar_dtype_i32
,
3686 .opt_i32
= { .min
= 0, .max
= 2, .clamp
= 1 },
3690 vg_convar_push( (struct vg_convar
){
3692 .data
= &world_theme_id
,
3693 .data_type
= k_convar_dtype_i32
,
3694 .opt_i32
= { .min
= 0, .max
= vg_list_size( world_themes
)-1, .clamp
= 1 },
3698 // Combined quad, long quad / empty circle / filled circle mesh
3700 float combined_mesh
[6*6 + 32*6*3] = {
3701 0.0f
, 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
,
3702 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
, 0.0f
,
3704 0.0f
, 0.0f
, 0.0f
, 0.2f
, 1.0f
, 0.2f
,
3705 0.0f
, 0.0f
, 1.0f
, 0.2f
, 1.0f
, 0.0f
,
3707 TRANSFORM_TRI_2D( 0.15f
,0.05f
,0.4f
, 0.0f
, 1.0f
, 1.0f
, 2.0f
, 1.0f
, 0.0f
),
3708 TRANSFORM_TRI_2D( 0.15f
,0.80f
,0.4f
, 0.0f
, 0.0f
, 0.0f
, 2.0f
, 1.0f
, 1.0f
)
3711 float *circle_mesh
= combined_mesh
+ 6*6;
3714 for( int i
= 0; i
< res
; i
++ )
3716 v2f v0
= { sinf( ((float)i
/(float)res
)*VG_TAUf
), cosf( ((float)i
/(float)res
)*VG_TAUf
) };
3717 v2f v1
= { sinf( ((float)(i
+1)/(float)res
)*VG_TAUf
), cosf( ((float)(i
+1)/(float)res
)*VG_TAUf
) };
3719 circle_mesh
[ i
*6+0 ] = 0.0f
;
3720 circle_mesh
[ i
*6+1 ] = 0.0f
;
3722 v2_copy( v0
, circle_mesh
+ 32*6 + i
*12 );
3723 v2_muls( v0
, 0.8f
, circle_mesh
+ 32*6 + i
*12+2 );
3724 v2_copy( v1
, circle_mesh
+ 32*6 + i
*12+4 );
3726 v2_copy( v1
, circle_mesh
+ 32*6 + i
*12+6 );
3727 v2_muls( v1
, 0.8f
, circle_mesh
+ 32*6 + i
*12+8 );
3728 v2_muls( v0
, 0.8f
, circle_mesh
+ 32*6 + i
*12+10 );
3730 v2_copy( v0
, circle_mesh
+ i
*6+4 );
3731 v2_copy( v1
, circle_mesh
+ i
*6+2 );
3732 v2_copy( v0
, circle_mesh
+i
*6+4 );
3733 v2_copy( v1
, circle_mesh
+i
*6+2 );
3736 init_mesh( &world
.shapes
, combined_mesh
, vg_list_size( combined_mesh
) );
3741 int const num_segments
= 64;
3743 struct mesh_wire
*mw
= &world
.wire
;
3745 v2f wire_points
[ num_segments
* 2 ];
3746 u16 wire_indices
[ 6*(num_segments
-1) ];
3748 for( int i
= 0; i
< num_segments
; i
++ )
3750 float l
= (float)i
/ (float)(num_segments
-1);
3752 v2_copy( (v2f
){ l
, -0.5f
}, wire_points
[i
*2+0] );
3753 v2_copy( (v2f
){ l
, 0.5f
}, wire_points
[i
*2+1] );
3755 if( i
< num_segments
-1 )
3757 wire_indices
[ i
*6+0 ] = i
*2 + 0;
3758 wire_indices
[ i
*6+1 ] = i
*2 + 1;
3759 wire_indices
[ i
*6+2 ] = i
*2 + 3;
3760 wire_indices
[ i
*6+3 ] = i
*2 + 0;
3761 wire_indices
[ i
*6+4 ] = i
*2 + 3;
3762 wire_indices
[ i
*6+5 ] = i
*2 + 2;
3766 glGenVertexArrays( 1, &mw
->vao
);
3767 glGenBuffers( 1, &mw
->vbo
);
3768 glGenBuffers( 1, &mw
->ebo
);
3769 glBindVertexArray( mw
->vao
);
3771 glBindBuffer( GL_ARRAY_BUFFER
, mw
->vbo
);
3773 glBufferData( GL_ARRAY_BUFFER
, sizeof( wire_points
), wire_points
, GL_STATIC_DRAW
);
3774 glBindVertexArray( mw
->vao
);
3776 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, mw
->ebo
);
3777 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, sizeof( wire_indices
), wire_indices
, GL_STATIC_DRAW
);
3780 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
, 2*sizeof(float), (void*)0 );
3781 glEnableVertexAttribArray( 0 );
3785 mw
->em
= vg_list_size( wire_indices
);
3788 // Create info data texture
3790 glGenTextures( 1, &world
.background_data
);
3791 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
3792 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGBA
, 64, 64, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, NULL
);
3796 // Create random smaples texture
3798 u8
*data
= malloc(512*512*2);
3799 for( int i
= 0; i
< 512*512*2; i
++ )
3800 data
[ i
] = rand()/(RAND_MAX
/255);
3802 glGenTextures( 1, &world
.random_samples
);
3803 glBindTexture( GL_TEXTURE_2D
, world
.random_samples
);
3804 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RG
, 512, 512, 0, GL_RG
, GL_UNSIGNED_BYTE
, data
);
3811 resource_load_main();
3815 ui_init_context( &world
.st
.world_text
, 15000 );
3818 // Restore gamestate
3819 career_local_data_init();
3829 console_save_map( 0, NULL
);
3832 resource_free_main();
3834 glDeleteTextures( 1, &world
.background_data
);
3835 glDeleteTextures( 1, &world
.random_samples
);
3837 glDeleteVertexArrays( 1, &world
.wire
.vao
);
3838 glDeleteBuffers( 1, &world
.wire
.vbo
);
3839 glDeleteBuffers( 1, &world
.wire
.ebo
);
3841 free_mesh( &world
.shapes
);
3843 ui_context_free( &world
.st
.world_text
);
3848 int main( int argc
, char *argv
[] )
3850 vg_init( argc
, argv
, "Marble Computing" );