1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
3 #define MARBLE_COMP_VERSION 4
4 //#define VG_CAPTURE_MODE
5 #define VG_STEAM_APPID 1218140U
10 k_world_button_mode_once
,
11 k_world_button_mode_toggle
18 float light_target
, light
, extra_light
;
21 enum world_button_mode mode
;
24 enum world_button_status
26 k_world_button_on_enable
,
27 k_world_button_on_disable
30 #include "fishladder_resources.h"
32 // #define STEAM_LEADERBOARDS
35 // ===========================================================================================================
40 k_cell_type_ramp_right
= 3,
41 k_cell_type_ramp_left
= 6,
42 k_cell_type_split
= 7,
43 k_cell_type_merge
= 13,
44 k_cell_type_con_r
= 1,
45 k_cell_type_con_u
= 2,
46 k_cell_type_con_l
= 4,
52 k_fish_state_soon_dead
= -1,
53 k_fish_state_dead
= 0,
56 k_fish_state_soon_alive
61 k_world_button_none
= -1,
62 k_world_button_sim
= 0,
63 k_world_button_pause
= 1,
64 k_world_button_speedy
= 2,
65 k_world_button_settings
= 3
71 k_game_state_settings
,
75 #define FLAG_CANAL 0x1
76 #define FLAG_IS_TRIGGER 0x2
77 #define FLAG_RESERVED0 0x4
78 #define FLAG_RESERVED1 0x8
80 #define FLAG_INPUT 0x10
81 #define FLAG_OUTPUT 0x20
82 #define FLAG_WALL 0x40
83 #define FLAG_EMITTER 0x80
85 #define FLAG_FLIP_FLOP 0x100
86 #define FLAG_TRIGGERED 0x200
87 #define FLAG_FLIP_ROTATING 0x400
88 #define FLAG_TARGETED 0x800
90 #define FLAG_INPUT_NICE 0x1000
93 0000 0 | 0001 1 | 0010 2 | 0011 3
97 0100 4 | 0101 5 | 0110 6 | 0111 7
101 1000 8 | 1001 9 | 1010 10 | 1011 11
105 1100 12 | 1101 13 | 1110 14 | 1111 15
111 static struct cell_description
120 enum sprites_auto_combine_index trigger_sprite
;
122 cell_descriptions
[] =
125 { .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
},
126 { .start
= { 1, 0 }, .end
= { -1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
},
127 { .start
= { 0, 1 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
}, .trigger_sprite
= k_sprite_brk_l
},
128 { .start
= { 0, 1 }, .end
= { 1, 0 }, .trigger_pos
= { 0.25f
, 0.5f
}, .trigger_sprite
= k_sprite_brk_l
},
130 { .start
= { -1, 0 }, .end
= { 1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
},
131 { .start
= { -1, 0 }, .end
= { 1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
, .is_linear
= 1 },
132 { .start
= { 0, 1 }, .end
= { -1, 0 }, .trigger_pos
= { 0.5f
, 0.25f
}, .trigger_sprite
= k_sprite_brk_d
},
133 { .start
= { 0, 1 }, .is_special
= 1 },
135 { .start
= { 0, -1 }, .end
= { 0, 1 }, .trigger_pos
= { 0.25f
, 0.5f
}, .trigger_sprite
= k_sprite_brk_l
},
136 { .start
= { 1, 0 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
}, .trigger_sprite
= k_sprite_brk_l
},
137 { .start
= { 0, 1 }, .end
= { 0, -1 }, .trigger_pos
= { 0.25f
, 0.5f
}, .trigger_sprite
= k_sprite_brk_l
, .is_linear
= 1 },
140 { .start
= { -1, 0 }, .end
= { 0, -1 }, .trigger_pos
= { 0.5f
, 0.75f
}, .trigger_sprite
= k_sprite_brk_u
},
141 { .end
= { 0, -1 }, .is_special
= 1, .trigger_pos
= { 0.5f
, 0.75f
}, .trigger_sprite
= k_sprite_brk_u
},
146 v2f
const curve_3
[] = {{0.5f
,1.0f
},{0.5f
,0.625f
},{0.625f
,0.5f
},{1.0f
,0.5f
}};
147 v2f
const curve_6
[] = {{0.5f
,1.0f
},{0.5f
,0.625f
},{0.375f
,0.5f
},{0.0f
,0.5f
}};
148 v2f
const curve_9
[] = {{1.0f
,0.5f
},{0.625f
,0.5f
},{0.5f
,0.375f
},{0.5f
,0.0f
}};
149 v2f
const curve_12
[]= {{0.0f
,0.5f
},{0.375f
,0.5f
},{0.5f
,0.375f
},{0.5f
,0.0f
}};
151 v2f
const curve_1
[] = {{1.0f
,0.5f
},{0.8f
,0.5f
},{0.3f
,0.5f
},{0.2f
,0.5f
}};
152 v2f
const curve_4
[] = {{0.0f
,0.5f
},{0.3f
,0.5f
},{0.5f
,0.5f
},{0.8f
,0.5f
}};
153 v2f
const curve_2
[] = {{0.5f
,1.0f
},{0.5f
,0.8f
},{0.5f
,0.3f
},{0.5f
,0.2f
}};
154 v2f
const curve_8
[] = {{0.5f
,0.0f
},{0.5f
,0.3f
},{0.5f
,0.5f
},{0.5f
,0.8f
}};
156 v2f
const curve_7
[] = {{0.5f
,0.8438f
},{0.875f
,0.8438f
},{0.625f
,0.5f
},{1.0f
,0.5f
}};
157 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
}};
159 float const curve_7_linear_section
= 0.1562f
;
162 // ===========================================================================================================
172 // Things that are 'static', aka, initialized once
175 struct world_button buttons
[4];
177 enum e_game_state state
;
179 struct cmp_level
*lvl_to_load
;
182 float world_transition
;
201 *cmd_buf_tiles
, *cmd_buf_specials
;
203 u32 tile_count
, tile_special_count
, max_commands
;
206 int sim_run
, max_runs
;
208 int sim_frame
, sim_target
;
209 float sim_internal_time
, // current tick-time
210 sim_internal_delta
, // time delta
211 sim_internal_ref
, // Reference point of internal time
212 sim_delta_ref
, // Reference point of vg_time when we started at current sim_speed
213 sim_delta_speed
, // Rate to apply time delta
214 frame_lerp
, // Fractional part of sim_internal_time
215 pause_offset_target
; //
224 int step_count
, recv_count
;
239 GLuint vao
, vbo
, ebo
;
244 GLuint background_data
;
245 GLuint random_samples
;
247 int selected
, tile_x
, tile_y
;
254 enum e_fish_state state
;
266 struct cmp_level
*pCmpLevel
;
280 .buttons
= { { .mode
= k_world_button_mode_toggle
},
281 { .mode
= k_world_button_mode_toggle
},
282 { .mode
= k_world_button_mode_toggle
},
283 { .mode
= k_world_button_mode_toggle
} }
288 // Forward declerations
289 // --------------------
294 static void colour_code_v3( i8 cc
, v3f target
);
295 static int hash21i( v2i p
, u32 umod
);
299 static void init_mesh( struct mesh
*m
, float const *tris
, u32 length
);
300 static void free_mesh( struct mesh
*m
);
301 static void use_mesh( struct mesh
*m
);
302 static void draw_mesh( int const start
, int const count
);
306 static void level_selection_buttons(void);
308 // Map/world interface
309 // -------------------
310 static void map_free(void);
311 static void io_reset(void);
312 static struct cell
*pcell( v2i pos
);
313 static void lcell( int id
, v2i pos
);
314 static void map_reclassify( v2i start
, v2i end
, int update_texbuffer
);
315 static void gen_level_text(void);
316 static int map_load( const char *str
, const char *name
);
317 static void map_serialize( FILE *stream
);
321 static void career_serialize(void);
322 static void career_unlock_level( struct cmp_level
*lvl
);
323 static void career_unlock_level( struct cmp_level
*lvl
);
324 static void career_pass_level( struct cmp_level
*lvl
, int score
, int upload
);
325 static void career_reset_level( struct cmp_level
*lvl
);
326 static void career_load(void);
327 static void clear_animation_flags(void);
331 static void simulation_stop(void);
332 static void simulation_start(void);
333 static int world_check_pos_ok( v2i co
, int dist
);
334 static int cell_interactive( v2i co
);
336 void vg_update(void);
337 static void render_tiles( v4f
const regular_colour
, v4f
const selected_colour
);
338 static void render_tile_block( v2i start
, v2i end
, v4f
const regular_colour
, v4f
const selected_colour
);
340 void vg_render(void);
345 #ifdef STEAM_LEADERBOARDS
346 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
347 void leaderboard_dispatch_score(void);
348 void leaderboard_found( LeaderboardFindResult_t
*pCallback
);
349 void leaderboard_downloaded( LeaderboardScoresDownloaded_t
*pCallback
);
350 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
355 static int console_credits( int argc
, char const *argv
[] );
356 static int console_save_map( int argc
, char const *argv
[] );
357 static int console_load_map( int argc
, char const *argv
[] );
358 static int console_changelevel( int argc
, char const *argv
[] );
363 int main( int argc
, char *argv
[] );
366 // ===========================================================================================================
373 // ===========================================================================================================
375 static int colour_set_id
= 0;
376 static int world_theme_id
= 0;
378 static v3f colour_sets
[][4] =
380 { { 1.0f
, 0.9f
, 0.3f
}, // Yellow
381 { 0.4f
, 0.8f
, 1.00f
}, // Blue
382 { 0.2f
, 0.9f
, 0.14f
}, // Green
383 { 0.882f
, 0.204f
, 0.922f
} // Pink
385 { { 1.0f
, 0.9f
, 0.3f
}, // Yellow
386 { 0.4f
, 0.8f
, 1.00f
}, // Blue
387 { 0.85f
, 0.85f
, 0.85f
}, // Weiss
388 { 0.2f
, 0.2f
, 0.2f
} // Black/Gray
390 { { 1.0f
, 0.9f
, 0.3f
}, // Yellow
391 { 0.827f
, 0.373f
, 0.718f
}, // Pink
392 { 0.0f
, 0.353f
, 0.71f
}, // Blue
393 { 0.863f
, 0.196f
, 0.125f
} // Red
397 static struct world_theme
408 { 0.8f
, 0.8f
, 0.8f
},
413 { 0.89f
, 0.8f
, 0.7f
},
418 { 0.7f
, 0.7f
, 0.7f
},
423 static void colour_code_v3( i8 cc
, v3f target
)
425 if( (cc
>= 0) && (cc
< vg_list_size( colour_sets
[0] )) )
427 v3_copy( colour_sets
[colour_set_id
][ cc
], target
);
431 vg_error( "Invalid colour code used '%d'\n", (int)cc
);
432 v3_copy( (v3f
){0.0f
,0.0f
,0.0f
}, target
);
435 static int hash21i( v2i p
, u32 umod
)
437 static const int random_noise
[] =
439 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,
440 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,
441 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,
442 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,
443 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,
444 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,
445 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,
446 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,
447 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,
448 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,
449 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,
450 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,
451 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,
452 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,
453 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,
454 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,
455 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,
456 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,
457 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,
458 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,
459 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,
460 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,
461 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,
462 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,
463 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,
464 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,
465 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,
466 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,
467 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,
468 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,
469 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,
470 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
473 return random_noise
[ (random_noise
[p
[1] & 1023] + p
[0]) & 1023 ] & umod
;
477 // ===========================================================================================================
479 static void init_mesh( struct mesh
*m
, float const *tris
, u32 length
)
481 m
->elements
= length
/3;
482 glGenVertexArrays( 1, &m
->vao
);
483 glGenBuffers( 1, &m
->vbo
);
485 glBindVertexArray( m
->vao
);
486 glBindBuffer( GL_ARRAY_BUFFER
, m
->vbo
);
487 glBufferData( GL_ARRAY_BUFFER
, length
*sizeof(float), tris
, GL_STATIC_DRAW
);
489 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
, 2 * sizeof(float), (void*)0 );
490 glEnableVertexAttribArray( 0 );
495 static void free_mesh( struct mesh
*m
)
497 glDeleteVertexArrays( 1, &m
->vao
);
498 glDeleteBuffers( 1, &m
->vbo
);
501 static void draw_mesh( int const start
, int const count
)
503 glDrawArrays( GL_TRIANGLES
, start
*3, count
*3 );
506 static void use_mesh( struct mesh
*m
)
508 glBindVertexArray( m
->vao
);
511 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
);
514 // ===========================================================================================================
516 static void map_free(void)
518 arrfree( world
.data
);
521 free( world
.cmd_buf_tiles
);
522 world
.cmd_buf_tiles
= NULL
;
532 world
.initialzed
= 0;
535 static void io_reset(void)
537 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
539 struct cell_terminal
*term
= &world
.io
[i
];
541 for( int j
= 0; j
< term
->run_count
; j
++ )
542 term
->runs
[j
].recv_count
= 0;
546 static struct cell
*pcell( v2i pos
)
548 return &world
.data
[ pos
[1]*world
.w
+ pos
[0] ];
551 static void lcell( int id
, v2i pos
)
553 pos
[0] = id
% world
.w
;
554 pos
[1] = (id
- pos
[0]) / world
.w
;
557 static void map_reclassify( v2i start
, v2i end
, int update_texbuffer
)
559 v2i full_start
= { 1,1 };
560 v2i full_end
= { world
.w
-1, world
.h
-1 };
569 u8 info_buffer
[64*64*4];
572 int px0
= vg_max( start
[0], full_start
[0] ),
573 px1
= vg_min( end
[0], full_end
[0] ),
574 py0
= vg_max( start
[1], full_start
[1] ),
575 py1
= vg_min( end
[1], full_end
[1] );
577 for( int y
= py0
; y
< py1
; y
++ )
579 for( int x
= px0
; x
< px1
; x
++ )
581 struct cell
*cell
= pcell((v2i
){x
,y
});
583 v2i dirs
[] = {{1,0},{0,1},{-1,0},{0,-1}};
588 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
590 for( int i
= 0; i
< vg_list_size( dirs
); i
++ )
592 struct cell
*neighbour
= pcell((v2i
){x
+dirs
[i
][0], y
+dirs
[i
][1]});
593 if( neighbour
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
601 if( cell
->state
& FLAG_WALL
)
602 height
= 0xFF-0x3F + hash21i( (v2i
){x
,y
}, 0x3F );
604 config
= cell
->state
& FLAG_INPUT_NICE
? 0xB: 0xF;
607 pcell((v2i
){x
,y
})->config
= config
;
609 u8
*info_px
= &info_buffer
[ (pixel_id
++)*4 ];
611 info_px
[1] = cell
->state
& FLAG_WALL
? 0: 255;
617 ((cell
->state
& FLAG_IS_TRIGGER
) && (cell
->config
== 0xF || cell
->config
== k_cell_type_split
)) ||
618 ((cell
->state
& FLAG_TARGETED
) && ((cell
->config
!= k_cell_type_split
) && !(cell
->state
& FLAG_EMITTER
)))
619 ) && update_texbuffer
621 cell
->state
&= ~(FLAG_TARGETED
|FLAG_IS_TRIGGER
);
622 for( u32 i
= 0; i
< 2; i
++ )
626 struct cell
*other_ptr
= &world
.data
[ cell
->links
[i
] ];
627 other_ptr
->links
[ i
] = 0;
628 other_ptr
->state
&= ~FLAG_IS_TRIGGER
;
630 if( other_ptr
->links
[ i
^ 0x1 ] == 0 )
631 other_ptr
->state
&= ~FLAG_TARGETED
;
641 if( update_texbuffer
)
643 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
644 glTexSubImage2D( GL_TEXTURE_2D
, 0, px0
+ 16, py0
+ 16, px1
-px0
, py1
-py0
, GL_RGBA
, GL_UNSIGNED_BYTE
, info_buffer
);
648 static void gen_level_text(void)
651 ui_px
const unit_scale_px
= 4*UI_GLYPH_SPACING_X
; // 4 char per unit
652 ui_begin( &world
.st
.world_text
, world
.w
*unit_scale_px
, world
.h
*unit_scale_px
);
654 if( world
.pCmpLevel
)
656 for( int i
= 0; i
< vg_list_size( world
.pCmpLevel
->strings
); i
++ )
658 struct world_string
*wstr
= &world
.pCmpLevel
->strings
[i
];
664 pos
[0] = -UI_GLYPH_SPACING_X
/2;
666 if( wstr
->placement
== k_placement_bottom
)
667 pos
[1] = 2*-unit_scale_px
;
669 pos
[1] = (world
.h
-1)*-unit_scale_px
-6;
671 ui_text( &world
.st
.world_text
, pos
, wstr
->str
, 1, k_text_align_left
);
676 // re-create level scores
677 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
679 struct career_level_pack
*set
= &career_packs
[i
];
681 ui_text( &world
.st
.world_text
,
683 set
->origin
[0]*unit_scale_px
,
684 -(set
->origin
[1]+set
->dims
[1]+1)*unit_scale_px
+ 18
686 set
->title
, 1, k_text_align_left
);
688 for( int j
= 0; j
< set
->count
; j
++ )
690 struct cmp_level
*lvl
= &set
->pack
[j
];
692 if( lvl
->completed_score
&& !lvl
->is_tutorial
)
695 snprintf( num
, 9, "%d", lvl
->completed_score
);
697 ui_text( &world
.st
.world_text
,
699 lvl
->btn
.position
[0]*unit_scale_px
+ unit_scale_px
/2,
700 -lvl
->btn
.position
[1]*unit_scale_px
- unit_scale_px
/2
702 num
, 1, k_text_align_center
);
707 //ui_text( &world.st.world_text, (ui_px [2]){ 0, 0 }, "Preview", 1, k_text_align_left );
709 ui_resolve( &world
.st
.world_text
);
712 static int map_load( const char *str
, const char *name
)
721 if( c
[world
.w
] == ';' )
723 else if( !c
[world
.w
] )
725 vg_error( "Unexpected EOF when parsing level\n" );
730 struct cell
*row
= arraddnptr( world
.data
, world
.w
);
732 int reg_start
= 0, reg_end
= 0;
734 u32
*links_to_make
= NULL
;
735 int links_satisfied
= 0;
737 char link_id_buffer
[32];
745 if( *c
== '\r' ) { c
++; continue; } // fuck off windows
751 if( *c
== '\r' ) c
++;
758 if( *c
== '\r' ) { c
++; continue; }
760 if( reg_start
< reg_end
)
762 struct cell_terminal
*terminal
= &world
.io
[ reg_start
];
763 struct terminal_run
*run
= &terminal
->runs
[ terminal
->run_count
-1 ];
765 if( (*c
>= 'a' && *c
<= 'z') || *c
== ' ' )
771 run
->steps
[ run
->step_count
++ ] = code
;
775 if( *c
== ',' || *c
== '\n' )
784 terminal
->runs
[ terminal
->run_count
].step_count
= 0;
785 terminal
->run_count
++;
786 world
.max_runs
= vg_max( world
.max_runs
, terminal
->run_count
);
790 vg_error( "Unkown attribute '%c' (row: %u)\n", *c
, world
.h
);
797 if( links_satisfied
< arrlen( links_to_make
) )
799 struct cell
*target
= &world
.data
[ links_to_make
[ links_satisfied
] ];
801 if( (((u32
)*c
>= (u32
)'0') && ((u32
)*c
<= (u32
)'9')) || *c
== '-' )
803 if( link_id_n
>= vg_list_size( link_id_buffer
)-1 )
805 vg_error( "Number was way too long to be parsed (row: %u)\n", world
.h
);
809 link_id_buffer
[ link_id_n
++ ] = *c
;
811 else if( *c
== ',' || *c
== '\n' )
813 link_id_buffer
[ link_id_n
] = 0x00;
814 int value
= atoi( link_id_buffer
);
816 target
->links
[value
>= 0? 1:0] = abs(value
);
825 vg_error( "Invalid character '%c' (row: %u)\n", *c
, world
.h
);
831 vg_error( "Too many values to assign (row: %u)\n", world
.h
);
840 // Registry length-error checks
841 if( reg_start
!= reg_end
)
843 vg_error( "Not enough spawn values assigned (row: %u, %u of %u)\n", world
.h
, reg_start
, reg_end
);
847 if( links_satisfied
!= arrlen( links_to_make
) )
849 vg_error( "Not enough link values assigned (row: %u, %u of %u)\n", world
.h
, links_satisfied
, arrlen( links_to_make
) );
855 vg_error( "Not enough cells to match previous row definition (row: %u, %u<%u)\n", world
.h
, cx
, world
.w
);
859 row
= arraddnptr( world
.data
, world
.w
);
862 reg_end
= reg_start
= arrlen( world
.io
);
864 arrsetlen( links_to_make
, 0 );
871 vg_error( "Too many cells to match previous row definition (row: %u, %u>%u)\n", world
.h
, cx
, world
.w
);
875 // Tile initialization
877 struct cell
*cell
= &row
[ cx
];
883 if( *c
== '+' || *c
== '-' || *c
== '*' )
885 struct cell_terminal
*term
= arraddnptr( world
.io
, 1 );
887 term
->pos
[1] = world
.h
;
890 term
->runs
[0].step_count
= 0;
894 case '+': cell
->state
= FLAG_INPUT
; break;
895 case '-': cell
->state
= FLAG_OUTPUT
; break;
896 case '*': cell
->state
= FLAG_EMITTER
; break;
901 else if( *c
== '.' ) cell
->state
= FLAG_INPUT_NICE
;
902 else if( *c
== '#' ) cell
->state
= FLAG_WALL
;
903 else if( ((u32
)*c
>= (u32
)'A') && ((u32
)*c
<= (u32
)'A'+0xf) )
905 // Canal flag bits (4bit/16 value):
911 cell
->state
= ((u32
)*c
- (u32
)'A') & (FLAG_CANAL
|FLAG_IS_TRIGGER
);
913 if( cell
->state
& FLAG_IS_TRIGGER
)
914 arrpush( links_to_make
, cx
+ world
.h
*world
.w
);
918 else cell
->state
= 0x00;
926 // Assign emitter codes
927 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
929 struct cell_terminal
*term
= &world
.io
[i
];
930 struct cell
*cell
= pcell( term
->pos
);
932 if( cell
->state
& FLAG_EMITTER
)
934 if( (term
->run_count
> 0) && (term
->runs
[0].step_count
>= 2) )
936 cell
->emit
[0] = term
->runs
[0].steps
[0];
937 cell
->emit
[1] = term
->runs
[0].steps
[1];
941 vg_error( "Emitter was not assigned emit values\n" );
947 // Update data texture to fill out the background
949 u8 info_buffer
[64*64*4];
950 for( int x
= 0; x
< 64; x
++ )
952 for( int y
= 0; y
< 64; y
++ )
954 u8
*px
= &info_buffer
[((x
*64)+y
)*4];
956 // Fade out edges of world so that there isnt an obvious line
957 int dist_x
= 16 - VG_MIN( VG_MIN( x
, 16 ), 16-VG_MAX( x
-16-world
.w
, 0 ) );
958 int dist_y
= 16 - VG_MIN( VG_MIN( y
, 16 ), 16-VG_MAX( y
-16-world
.h
, 0 ) );
959 int dist
= VG_MAX( dist_x
, dist_y
) * 16;
961 int value
= VG_MAX( 0, 0xFF-0x3F + hash21i( (v2i
){x
,y
}, 0x3F ) - dist
);
970 // Level selection area
972 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
974 struct career_level_pack
*grid
= &career_packs
[ i
];
978 for( int y
= 0; y
< grid
->dims
[1]; y
++ )
980 for( int x
= 0; x
< grid
->dims
[0]; x
++ )
982 u8
*px
= &info_buffer
[((y
+16+grid
->origin
[1])*64+16+x
+grid
->origin
[0])*4];
985 if( j
< grid
->count
)
987 struct cmp_level
*lvl
= &grid
->pack
[ j
++ ];
988 v2i_add( grid
->origin
, (v2i
){x
,y
}, lvl
->btn
.position
);
994 info_buffer
[(((16+world
.h
-3)*64)+world
.w
+16-1)*4] = 0x30;
995 info_buffer
[(((16+world
.h
-2)*64)+world
.w
+16-1)*4] = 0x30;
997 // Random walks.. kinda
998 for( int i
= 0; i
< arrlen(world
.io
); i
++ )
1000 struct cell_terminal
*term
= &world
.io
[ i
];
1006 // Only make breakouts for terminals on the edge
1007 if( !(term
->pos
[1] == 1 || term
->pos
[1] == world
.h
-2) )
1010 turtle
[0] = 16+term
->pos
[0];
1011 turtle
[1] = 16+term
->pos
[1];
1014 turtle_dir
[1] = pcell(term
->pos
)->state
& (FLAG_INPUT
|FLAG_EMITTER
)? 1: -1;
1015 original_y
= turtle_dir
[1];
1017 info_buffer
[((turtle
[1]*64)+turtle
[0])*4] = 0;
1018 v2i_add( turtle_dir
, turtle
, turtle
);
1020 for( int i
= 0; i
< 100; i
++ )
1022 info_buffer
[((turtle
[1]*64)+turtle
[0])*4] = 0;
1024 v2i_add( turtle_dir
, turtle
, turtle
);
1026 if( turtle
[0] == 0 ) break;
1027 if( turtle
[0] == 63 ) break;
1028 if( turtle
[1] == 0 ) break;
1029 if( turtle
[1] == 63 ) break;
1031 int random_value
= hash21i( turtle
, 0xFF );
1032 if( random_value
> 255-40 && !turtle_dir
[0] )
1037 else if( random_value
> 255-80 && !turtle_dir
[0] )
1042 else if( random_value
> 255-100 )
1045 turtle_dir
[1] = original_y
;
1050 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
1051 glTexSubImage2D( GL_TEXTURE_2D
, 0, 0, 0, 64, 64, GL_RGBA
, GL_UNSIGNED_BYTE
, info_buffer
);
1054 arrfree( links_to_make
);
1056 map_reclassify( NULL
, NULL
, 1 );
1059 for( int i
= 0; i
< world
.h
*world
.w
; i
++ )
1061 struct cell
*src
= &world
.data
[i
];
1062 if( src
->state
& FLAG_IS_TRIGGER
)
1064 int link_id
= src
->links
[0]?0:1;
1065 if( src
->links
[link_id
] <= world
.h
*world
.w
)
1067 struct cell
*target
= &world
.data
[ src
->links
[link_id
] ];
1068 if( ((target
->state
& FLAG_CANAL
) && (target
->config
== k_cell_type_split
))
1069 || (target
->state
& FLAG_EMITTER
) )
1071 if( target
->links
[ link_id
] )
1073 vg_error( "Link target was already targeted\n" );
1079 target
->links
[ link_id
] = i
;
1080 target
->state
|= FLAG_TARGETED
;
1085 vg_error( "Link target was invalid\n" );
1091 vg_error( "Link target out of bounds\n" );
1097 // ==========================================================
1100 vg_success( "Map '%s' loaded! (%u:%u)\n", name
, world
.w
, world
.h
);
1104 strncpy( world
.map_name
, name
, vg_list_size( world
.map_name
)-1 );
1105 world
.initialzed
= 1;
1107 // Setup world button locations
1108 for( int i
= 0; i
< vg_list_size( world
.st
.buttons
); i
++ )
1110 struct world_button
*btn
= &world
.st
.buttons
[i
];
1111 btn
->position
[0] = world
.w
-1;
1112 btn
->position
[1] = world
.h
-i
-2;
1115 // Allocate buffers for render commands
1116 world
.cmd_buf_tiles
= malloc( world
.w
*world
.h
* sizeof( struct render_cmd
) );
1117 world
.max_commands
= world
.w
*world
.h
;
1122 arrfree( links_to_make
);
1127 static void map_serialize( FILE *stream
)
1129 for( int y
= 0; y
< world
.h
; y
++ )
1131 for( int x
= 0; x
< world
.w
; x
++ )
1133 struct cell
*cell
= pcell( (v2i
){ x
, y
} );
1135 if( cell
->state
& FLAG_WALL
) fputc( '#', stream
);
1136 else if( cell
->state
& FLAG_INPUT_NICE
) fputc( '.', stream
);
1137 else if( cell
->state
& FLAG_INPUT
) fputc( '+', stream
);
1138 else if( cell
->state
& FLAG_OUTPUT
) fputc( '-', stream
);
1139 else if( cell
->state
& FLAG_EMITTER
) fputc( '*', stream
);
1140 else if( cell
->state
& (FLAG_CANAL
|FLAG_IS_TRIGGER
|FLAG_RESERVED0
|FLAG_RESERVED1
) )
1142 fputc( (cell
->state
& (FLAG_CANAL
|FLAG_IS_TRIGGER
|FLAG_RESERVED0
|FLAG_RESERVED1
)) + (u32
)'A', stream
);
1144 else fputc( ' ', stream
);
1147 fputc( ';', stream
);
1149 int terminal_write_count
= 0;
1151 for( int x
= 0; x
< world
.w
; x
++ )
1153 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
1155 struct cell_terminal
*term
= &world
.io
[i
];
1156 if( v2i_eq( term
->pos
, (v2i
){x
,y
} ) )
1158 if( terminal_write_count
)
1159 fputc( ',', stream
);
1160 terminal_write_count
++;
1162 for( int j
= 0; j
< term
->run_count
; j
++ )
1164 struct terminal_run
*run
= &term
->runs
[j
];
1166 for( int k
= 0; k
< run
->step_count
; k
++ )
1168 i8 step
= run
->steps
[k
];
1169 fputc( step
== -1? ' ': ('a' + run
->steps
[k
]), stream
);
1172 if( j
< term
->run_count
-1 )
1173 fputc( ':', stream
);
1179 for( int x
= 0; x
< world
.w
; x
++ )
1181 struct cell
*cell
= pcell( (v2i
){ x
,y
} );
1182 if( cell
->state
& FLAG_IS_TRIGGER
)
1184 if( terminal_write_count
)
1185 fputc( ',', stream
);
1186 terminal_write_count
++;
1188 fprintf( stream
, "%d", cell
->links
[0]? -cell
->links
[0]: cell
->links
[1] );
1192 fputc( '\n', stream
);
1197 // ===========================================================================================================
1199 #pragma pack(push,1)
1200 struct dcareer_state
1214 levels
[ NUM_CAMPAIGN_LEVELS
];
1218 static int career_load_success
= 0;
1220 static void career_serialize(void)
1222 if( !career_load_success
)
1225 struct dcareer_state encoded
;
1226 encoded
.version
= MARBLE_COMP_VERSION
;
1227 encoded
.in_map
= world
.pCmpLevel
? world
.pCmpLevel
->serial_id
: -1;
1229 memset( encoded
.reserved
, 0, sizeof( encoded
.reserved
) );
1231 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1233 struct career_level_pack
*set
= &career_packs
[i
];
1235 for( int j
= 0; j
< set
->count
; j
++ )
1237 struct cmp_level
*lvl
= &set
->pack
[j
];
1238 struct dlevel_state
*dest
= &encoded
.levels
[lvl
->serial_id
];
1240 dest
->score
= lvl
->completed_score
;
1241 dest
->unlocked
= lvl
->unlocked
;
1242 dest
->reserved
[0] = 0;
1243 dest
->reserved
[1] = 0;
1247 vg_asset_write( "sav/game.sv2", &encoded
, sizeof( struct dcareer_state
) );
1250 static void career_unlock_level( struct cmp_level
*lvl
);
1251 static void career_unlock_level( struct cmp_level
*lvl
)
1256 career_unlock_level( lvl
->linked
);
1259 static void career_pass_level( struct cmp_level
*lvl
, int score
, int upload
)
1263 lvl
->completed_score
= score
;
1267 career_unlock_level( lvl
->unlock
);
1269 if( lvl
->achievement
)
1270 sw_set_achievement( lvl
->achievement
);
1272 // Check ALL maps to trigger master engineer
1273 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1275 struct career_level_pack
*set
= &career_packs
[i
];
1277 for( int j
= 0; j
< set
->count
; j
++ )
1279 if( set
->pack
[j
].completed_score
== 0 )
1284 sw_set_achievement( "MASTER_ENGINEER" );
1288 static void career_reset_level( struct cmp_level
*lvl
)
1291 lvl
->completed_score
= 0;
1294 static void career_load(void)
1297 struct dcareer_state encoded
;
1300 memset( (void*)&encoded
, 0, sizeof( struct dcareer_state
) );
1302 encoded
.levels
[0].unlocked
= 1;
1304 // Load and copy data into encoded
1305 void *cr
= vg_asset_read_s( "sav/game.sv2", &sz
);
1309 if( sz
> sizeof( struct dcareer_state
) )
1310 vg_warn( "This save file is too big! Some levels will be lost\n" );
1312 if( sz
<= offsetof( struct dcareer_state
, levels
) )
1314 vg_warn( "This save file is too small to have a header. Creating a blank one\n" );
1318 memcpy( (void*)&encoded
, cr
, VG_MIN( sizeof( struct dcareer_state
), sz
) );
1322 vg_info( "No save file... Using blank one\n" );
1325 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1327 struct career_level_pack
*set
= &career_packs
[i
];
1329 for( int j
= 0; j
< set
->count
; j
++ )
1330 career_reset_level( &set
->pack
[j
] );
1333 // Header information
1334 // =================================
1336 struct cmp_level
*lvl_to_load
= &career_packs
[0].pack
[0];
1338 // Decode everything from dstate
1339 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
1341 struct career_level_pack
*set
= &career_packs
[i
];
1343 for( int j
= 0; j
< set
->count
; j
++ )
1345 struct cmp_level
*lvl
= &set
->pack
[j
];
1346 struct dlevel_state
*src
= &encoded
.levels
[lvl
->serial_id
];
1348 if( src
->unlocked
) career_unlock_level( lvl
);
1351 lvl
->completed_score
= src
->score
;
1353 // Apply unlocking to next levels in case there was an update
1355 career_unlock_level( lvl
->unlock
);
1358 if( lvl
->serial_id
== encoded
.in_map
)
1363 if( console_changelevel( 1, &lvl_to_load
->map_name
) )
1365 world
.pCmpLevel
= lvl_to_load
;
1369 career_load_success
= 1;
1371 if( encoded
.version
< MARBLE_COMP_VERSION
|| 1 )
1372 world
.st
.state
= k_game_state_update
;
1376 // ===========================================================================================================
1377 static int is_simulation_running(void)
1379 return world
.st
.buttons
[ k_world_button_sim
].state
;
1382 static void clear_animation_flags(void)
1384 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
1385 world
.data
[ i
].state
&= ~(FLAG_FLIP_FLOP
|FLAG_FLIP_ROTATING
);
1388 static void simulation_stop(void)
1390 world
.st
.buttons
[ k_world_button_sim
].state
= 0;
1391 world
.st
.buttons
[ k_world_button_pause
].state
= 0;
1393 world
.num_fishes
= 0;
1394 world
.sim_frame
= 0;
1396 world
.frame_lerp
= 0.0f
;
1400 sfx_system_fadeout( &audio_system_balls_rolling
, 44100 );
1402 clear_animation_flags();
1404 vg_info( "Stopping simulation!\n" );
1407 static void simulation_start(void)
1409 vg_success( "Starting simulation!\n" );
1411 sfx_set_playrnd( &audio_rolls
, &audio_system_balls_rolling
, 0, 1 );
1413 world
.num_fishes
= 0;
1414 world
.sim_frame
= 0;
1417 world
.sim_delta_speed
= world
.st
.buttons
[ k_world_button_speedy
].state
? 10.0f
: 2.5f
;
1418 world
.sim_delta_ref
= vg_time
;
1419 world
.sim_internal_ref
= 0.0f
;
1420 world
.sim_internal_time
= 0.0f
;
1421 world
.pause_offset_target
= 0.0f
;
1423 world
.sim_target
= 0;
1425 clear_animation_flags();
1429 if( world
.pCmpLevel
)
1431 world
.pCmpLevel
->completed_score
= 0;
1436 static int world_check_pos_ok( v2i co
, int dist
)
1438 return (co
[0] < dist
|| co
[0] >= world
.w
-dist
|| co
[1] < dist
|| co
[1] >= world
.h
-dist
)? 0: 1;
1441 static int cell_interactive( v2i co
)
1446 if( world_check_pos_ok( co
, 1 ) )
1449 if( cell
->state
& FLAG_EMITTER
)
1453 if( !world_check_pos_ok( co
, 2 ) )
1457 if( cell
->state
& (FLAG_WALL
|FLAG_INPUT
|FLAG_OUTPUT
) )
1460 // List of 3x3 configurations that we do not allow
1461 static u32 invalid_src
[][9] =
1493 // Statically compile invalid configurations into bitmasks
1494 static u32 invalid
[ vg_list_size(invalid_src
) ];
1496 for( int i
= 0; i
< vg_list_size(invalid_src
); i
++ )
1500 for( int j
= 0; j
< 3; j
++ )
1501 for( int k
= 0; k
< 3; k
++ )
1502 comped
|= invalid_src
[i
][ j
*3+k
] << ((j
*5)+k
);
1504 invalid
[i
] = comped
;
1507 // Extract 5x5 grid surrounding tile
1509 for( int y
= co
[1]-2; y
< co
[1]+3; y
++ )
1510 for( int x
= co
[0]-2; x
< co
[0]+3; x
++ )
1512 struct cell
*cell
= pcell((v2i
){x
,y
});
1514 if( cell
&& (cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
)) )
1515 blob
|= 0x1 << ((y
-(co
[1]-2))*5 + x
-(co
[0]-2));
1518 // Run filter over center 3x3 grid to check for invalid configurations
1519 int kernel
[] = { 0, 1, 2, 5, 6, 7, 10, 11, 12 };
1520 for( int i
= 0; i
< vg_list_size(kernel
); i
++ )
1522 if( blob
& (0x1 << (6+kernel
[i
])) )
1524 u32 window
= blob
>> kernel
[i
];
1526 for( int j
= 0; j
< vg_list_size(invalid
); j
++ )
1527 if((window
& invalid
[j
]) == invalid
[j
])
1535 static void vg_update(void)
1538 if( world
.st
.lvl_to_load
)
1540 world
.st
.world_transition
= (world
.st
.lvl_load_time
-vg_time
) * 4.0f
;
1542 if( vg_time
> world
.st
.lvl_load_time
)
1544 if( console_changelevel( 1, &world
.st
.lvl_to_load
->map_name
) )
1546 world
.pCmpLevel
= world
.st
.lvl_to_load
;
1550 world
.st
.lvl_to_load
= NULL
;
1555 world
.st
.world_transition
= vg_minf( 1.0f
, (vg_time
-world
.st
.lvl_load_time
) * 4.0f
);
1559 // ========================================================================================================
1561 float r1
= (float)vg_window_y
/ (float)vg_window_x
,
1562 r2
= (float)world
.h
/ (float)world
.w
,
1565 static float size_current
= 2.0f
;
1566 static v3f origin_current
= { 0.0f
, 0.0f
, 0.0f
};
1567 static v2f drag_offset
= { 0.0f
, 0.0f
};
1568 static v2f view_point
= { 0.0f
, 0.0f
};
1571 size
= ( r2
< r1
? (float)(world
.w
+5) * 0.5f
: ((float)(world
.h
+5) * 0.5f
) / r1
) + 0.5f
;
1576 origin
[0] = floorf( -0.5f
* ((float)world
.w
-4.5f
) );
1577 origin
[1] = floorf( -0.5f
* world
.h
);
1579 // Create and clamp result view
1580 v2_add( view_point
, drag_offset
, result_view
);
1581 result_view
[0] = vg_clampf( result_view
[0], -world
.st
.zoom
, world
.st
.zoom
);
1582 result_view
[1] = vg_clampf( result_view
[1], -world
.st
.zoom
*r1
, world
.st
.zoom
*r1
);
1584 v2_add( origin
, result_view
, vt_target
);
1586 // Lerp towards target
1587 size_current
= vg_lerpf( size_current
, size
- world
.st
.zoom
, vg_time_delta
* 6.0f
);
1588 v2_lerp( origin_current
, vt_target
, vg_time_delta
* 6.0f
, origin_current
);
1590 m3x3_projection( m_projection
, -size_current
, size_current
, -size_current
*r1
, size_current
*r1
);
1591 m3x3_identity( m_view
);
1592 m3x3_translate( m_view
, origin_current
);
1593 m3x3_mul( m_projection
, m_view
, vg_pv
);
1594 vg_projection_update();
1596 if( world
.st
.state
== k_game_state_update
)
1600 // ========================================================================================================
1601 v2_copy( vg_mouse_ws
, world
.tile_pos
);
1603 world
.tile_x
= floorf( world
.tile_pos
[0] );
1604 world
.tile_y
= floorf( world
.tile_pos
[1] );
1608 static v2f drag_origin
; // x/y pixel
1610 if( vg_get_button_down( "tertiary" ) )
1611 v2_copy( vg_mouse
, drag_origin
);
1612 else if( vg_get_button( "tertiary" ) )
1615 v2_sub( vg_mouse
, drag_origin
, drag_offset
);
1616 v2_div( drag_offset
, (v2f
){ vg_window_x
, vg_window_y
}, drag_offset
);
1617 v2_mul( drag_offset
, (v2f
){ size_current
*2.0f
, -size_current
*r1
*2.0f
}, drag_offset
);
1621 v2_copy( result_view
, view_point
);
1622 v2_copy( (v2f
){0.0f
,0.0f
}, drag_offset
);
1634 rsize
= size
-world
.st
.zoom
;
1636 v2_div( vg_mouse
, (v2f
){ vg_window_x
*0.5f
, vg_window_y
*0.5f
}, mview_local
);
1637 v2_add( (v2f
){ -rsize
, -rsize
*r1
}, (v2f
){ mview_local
[0]*rsize
, (2.0f
-mview_local
[1])*rsize
*r1
}, mview_cur
);
1639 world
.st
.zoom
= vg_clampf( world
.st
.zoom
+ vg_mouse_wheel
[1], 0.0f
, size
- 4.0f
);
1641 // Recalculate new position
1642 rsize
= size
-world
.st
.zoom
;
1643 v2_add( (v2f
){ -rsize
, -rsize
*r1
}, (v2f
){ mview_local
[0]*rsize
, (2.0f
-mview_local
[1])*rsize
*r1
}, mview_new
);
1646 v2_sub( mview_new
, mview_cur
, mview_delta
);
1647 v2_add( mview_delta
, view_point
, view_point
);
1651 // ========================================================================================================
1652 if( !is_simulation_running() && !gui_want_mouse() )
1654 v2_copy( vg_mouse_ws
, world
.drag_to_co
);
1656 if( cell_interactive( (v2i
){ world
.tile_x
, world
.tile_y
} ))
1658 world
.selected
= world
.tile_y
* world
.w
+ world
.tile_x
;
1660 static u32 modify_state
= 0;
1661 struct cell
*cell_ptr
= &world
.data
[world
.selected
];
1663 if( !(cell_ptr
->state
& FLAG_EMITTER
) )
1665 if( vg_get_button_down("primary") )
1666 modify_state
= (cell_ptr
->state
& FLAG_CANAL
) ^ FLAG_CANAL
;
1668 if( vg_get_button("primary") && ((cell_ptr
->state
& FLAG_CANAL
) != modify_state
) )
1670 cell_ptr
->state
&= ~FLAG_CANAL
;
1671 cell_ptr
->state
|= modify_state
;
1673 if( cell_ptr
->state
& FLAG_CANAL
)
1675 cell_ptr
->links
[0] = 0;
1676 cell_ptr
->links
[1] = 0;
1678 sfx_set_playrnd( &audio_tile_mod
, &audio_system_sfx
, 3, 6 );
1683 sfx_set_playrnd( &audio_tile_mod
, &audio_system_sfx
, 0, 3 );
1687 map_reclassify((v2i
){ world
.tile_x
-2, world
.tile_y
-2 },
1688 (v2i
){ world
.tile_x
+2, world
.tile_y
+2 }, 1 );
1691 if( vg_get_button_down("secondary") && (cell_ptr
->state
& FLAG_CANAL
) && !(cell_ptr
->config
== k_cell_type_split
) )
1693 world
.id_drag_from
= world
.selected
;
1695 struct cell_description
*desc
= &cell_descriptions
[ world
.data
[world
.id_drag_from
].config
];
1696 v2_add( desc
->trigger_pos
, (v2f
){ world
.tile_x
, world
.tile_y
}, world
.drag_from_co
);
1700 float local_x
= vg_mouse_ws
[0] - (float)world
.tile_x
;
1702 if( vg_get_button_up("secondary") && world
.id_drag_from
== world
.selected
)
1704 u32 link_id
= cell_ptr
->links
[ 0 ]? 0: 1;
1706 // break existing connection off
1707 if( cell_ptr
->links
[ link_id
] )
1709 struct cell
*current_connection
= &world
.data
[ cell_ptr
->links
[ link_id
]];
1711 if( !current_connection
->links
[ link_id
^ 0x1 ] )
1712 current_connection
->state
&= ~FLAG_TARGETED
;
1714 current_connection
->links
[ link_id
] = 0;
1715 cell_ptr
->links
[ link_id
] = 0;
1718 cell_ptr
->state
&= ~FLAG_IS_TRIGGER
;
1719 world
.id_drag_from
= 0;
1722 else if( world
.id_drag_from
&& (cell_ptr
->state
& (FLAG_CANAL
|FLAG_EMITTER
)) &&
1723 ((cell_ptr
->config
== k_cell_type_split
) || (cell_ptr
->state
& FLAG_EMITTER
)) )
1725 world
.drag_to_co
[0] = (float)world
.tile_x
+ (local_x
> 0.5f
? 0.75f
: 0.25f
);
1726 world
.drag_to_co
[1] = (float)world
.tile_y
+ 0.25f
;
1728 if( vg_get_button_up("secondary") )
1730 struct cell
*drag_ptr
= &world
.data
[world
.id_drag_from
];
1731 u32 link_id
= local_x
> 0.5f
? 1: 0;
1733 // Cleanup existing connections
1734 if( cell_ptr
->links
[ link_id
] )
1736 vg_warn( "Destroying existing connection on link %u (%hu)\n", link_id
, cell_ptr
->links
[ link_id
] );
1738 struct cell
*current_connection
= &world
.data
[ cell_ptr
->links
[ link_id
]];
1739 current_connection
->state
&= ~FLAG_IS_TRIGGER
;
1740 current_connection
->links
[ link_id
] = 0;
1743 for( u32 i
= 0; i
< 2; i
++ )
1745 if( drag_ptr
->links
[ i
] )
1747 vg_warn( "Destroying link %u (%hu)\n", i
, drag_ptr
->links
[ i
] );
1749 struct cell
*current_connection
= &world
.data
[ drag_ptr
->links
[ i
]];
1750 if( current_connection
->links
[ i
^ 0x1 ] == 0 )
1751 current_connection
->state
&= ~FLAG_TARGETED
;
1753 current_connection
->links
[ i
] = 0;
1754 drag_ptr
->links
[ i
] = 0;
1758 // Create the new connection
1759 vg_success( "Creating connection on link %u (%hu)\n", link_id
, world
.id_drag_from
);
1761 cell_ptr
->links
[ link_id
] = world
.id_drag_from
;
1762 drag_ptr
->links
[ link_id
] = world
.selected
;
1764 cell_ptr
->state
|= FLAG_TARGETED
;
1765 drag_ptr
->state
|= FLAG_IS_TRIGGER
;
1766 world
.id_drag_from
= 0;
1772 world
.selected
= -1;
1775 if( !(vg_get_button("secondary") && world
.id_drag_from
) )
1776 world
.id_drag_from
= 0;
1780 world
.selected
= -1;
1781 world
.id_drag_from
= 0;
1784 // Marble state updates
1785 // ========================================================================================================
1786 if( is_simulation_running() )
1788 float old_time
= world
.sim_internal_time
;
1790 if( !world
.st
.buttons
[ k_world_button_pause
].state
)
1791 world
.sim_internal_time
= world
.sim_internal_ref
+ (vg_time
-world
.sim_delta_ref
) * world
.sim_delta_speed
;
1793 world
.sim_internal_time
= vg_lerpf( world
.sim_internal_time
, world
.sim_internal_ref
+ world
.pause_offset_target
, vg_time_delta
*15.0f
);
1794 world
.sim_internal_delta
= world
.sim_internal_time
-old_time
;
1796 world
.sim_target
= (int)floorf(world
.sim_internal_time
);
1798 int success_this_frame
= 0;
1799 int failure_this_frame
= 0;
1801 while( world
.sim_frame
< world
.sim_target
)
1803 sfx_set_playrnd( &audio_random
, &audio_system_balls_switching
, 0, 8 );
1805 // Update splitter deltas
1806 for( int i
= 0; i
< world
.h
*world
.w
; i
++ )
1808 struct cell
*cell
= &world
.data
[i
];
1809 if( cell
->config
== k_cell_type_split
)
1811 cell
->state
&= ~FLAG_FLIP_ROTATING
;
1813 if( cell
->state
& (FLAG_IS_TRIGGER
|FLAG_EMITTER
) )
1814 cell
->state
&= ~FLAG_TRIGGERED
;
1817 int alive_count
= 0;
1819 // Update fish positions
1820 for( int i
= 0; i
< world
.num_fishes
; i
++ )
1822 struct fish
*fish
= &world
.fishes
[i
];
1824 if( fish
->state
== k_fish_state_soon_dead
)
1825 fish
->state
= k_fish_state_dead
;
1827 if( fish
->state
== k_fish_state_soon_alive
)
1828 fish
->state
= k_fish_state_alive
;
1830 if( fish
->state
< k_fish_state_alive
)
1833 struct cell
*cell_current
= pcell( fish
->pos
);
1835 if( fish
->state
== k_fish_state_alive
)
1838 if( cell_current
->state
& FLAG_OUTPUT
)
1840 for( int j
= 0; j
< arrlen( world
.io
); j
++ )
1842 struct cell_terminal
*term
= &world
.io
[j
];
1844 if( v2i_eq( term
->pos
, fish
->pos
) )
1846 struct terminal_run
*run
= &term
->runs
[ world
.sim_run
];
1847 if( run
->recv_count
< vg_list_size( run
->recieved
) )
1849 if( fish
->colour
== run
->steps
[ run
->recv_count
] )
1850 success_this_frame
= 1;
1852 failure_this_frame
= 1;
1854 run
->recieved
[ run
->recv_count
++ ] = fish
->colour
;
1857 failure_this_frame
= 1;
1863 fish
->state
= k_fish_state_dead
;
1864 fish
->death_time
= -1000.0f
;
1869 if( cell_current
->config
== k_cell_type_merge
)
1874 fish
->flow_reversed
= 0;
1878 if( cell_current
->config
== k_cell_type_split
)
1881 fish
->dir
[0] = cell_current
->state
&FLAG_FLIP_FLOP
?1:-1;
1884 if( !(cell_current
->state
& FLAG_TARGETED
) )
1885 cell_current
->state
^= FLAG_FLIP_FLOP
;
1889 // Apply cell out-flow
1890 struct cell_description
*desc
= &cell_descriptions
[ cell_current
->config
];
1892 v2i_copy( fish
->flow_reversed
? desc
->start
: desc
->end
, fish
->dir
);
1896 v2i_add( fish
->pos
, fish
->dir
, pos_next
);
1898 struct cell
*cell_next
= pcell( pos_next
);
1900 if( cell_next
->state
& (FLAG_CANAL
|FLAG_OUTPUT
) )
1902 struct cell_description
*desc
= &cell_descriptions
[ cell_next
->config
];
1904 if( cell_next
->config
== k_cell_type_merge
)
1906 if( fish
->dir
[0] == 0 )
1908 fish
->state
= k_fish_state_dead
;
1909 fish
->death_time
= world
.sim_internal_time
;
1912 fish
->flow_reversed
= 0;
1916 if( cell_next
->config
== k_cell_type_split
)
1918 if( fish
->dir
[0] == 0 )
1920 sfx_set_playrnd( &audio_splitter
, &audio_system_balls_important
, 0, 1 );
1921 cell_next
->state
|= FLAG_FLIP_ROTATING
;
1923 fish
->flow_reversed
= 0;
1927 fish
->state
= k_fish_state_dead
;
1928 fish
->death_time
= world
.sim_internal_time
;
1932 fish
->flow_reversed
= ( fish
->dir
[0] != -desc
->start
[0] ||
1933 fish
->dir
[1] != -desc
->start
[1] )? 1: 0;
1938 if( world_check_pos_ok( fish
->pos
, 2 ) )
1939 fish
->state
= k_fish_state_bg
;
1942 fish
->state
= k_fish_state_dead
;
1943 fish
->death_time
= world
.sim_internal_time
;
1948 //v2i_add( fish->pos, fish->dir, fish->pos );
1950 else if( fish
->state
== k_fish_state_bg
)
1952 v2i_add( fish
->pos
, fish
->dir
, fish
->pos
);
1954 if( !world_check_pos_ok( fish
->pos
, 2 ) )
1956 fish
->state
= k_fish_state_dead
;
1957 fish
->death_time
= -1000.0f
;
1961 struct cell
*cell_entry
= pcell( fish
->pos
);
1963 if( cell_entry
->state
& FLAG_CANAL
)
1965 if( cell_entry
->config
== k_cell_type_con_r
|| cell_entry
->config
== k_cell_type_con_u
1966 || cell_entry
->config
== k_cell_type_con_l
|| cell_entry
->config
== k_cell_type_con_d
)
1968 sw_set_achievement( "CAN_DO_THAT" );
1970 fish
->state
= k_fish_state_soon_alive
;
1974 fish
->flow_reversed
= 1;
1976 switch( cell_entry
->config
)
1978 case k_cell_type_con_r
: fish
->dir
[0] = 1; break;
1979 case k_cell_type_con_l
: fish
->dir
[0] = -1; break;
1980 case k_cell_type_con_u
: fish
->dir
[1] = 1; break;
1981 case k_cell_type_con_d
: fish
->dir
[1] = -1; break;
1987 else { vg_error( "fish behaviour unimplemented for behaviour type (%d)\n" ); }
1989 if( fish
->state
>= k_fish_state_alive
)
1993 // Second pass (triggers)
1994 for( int i
= 0; i
< world
.num_fishes
; i
++ )
1996 struct fish
*fish
= &world
.fishes
[i
];
1999 if( fish
->state
== k_fish_state_alive
)
2000 v2i_add( fish
->pos
, fish
->dir
, fish
->pos
);
2002 if( fish
->state
== k_fish_state_alive
|| fish
->state
== k_fish_state_soon_alive
)
2004 struct cell
*cell_current
= pcell( fish
->pos
);
2006 if( cell_current
->state
& FLAG_IS_TRIGGER
)
2008 int trigger_id
= cell_current
->links
[0]?0:1;
2010 struct cell
*target_peice
= &world
.data
[ cell_current
->links
[trigger_id
] ];
2013 if( (target_peice
->state
& FLAG_EMITTER
) && !(target_peice
->state
& FLAG_TRIGGERED
))
2015 if( world
.num_fishes
< vg_list_size( world
.fishes
) )
2017 struct fish
*fish
= &world
.fishes
[ world
.num_fishes
];
2018 lcell( cell_current
->links
[trigger_id
], fish
->pos
);
2020 fish
->state
= k_fish_state_soon_alive
;
2021 fish
->colour
= target_peice
->emit
[ trigger_id
];
2023 if( target_peice
->config
!= k_cell_type_stub
)
2025 struct cell_description
*desc
= &cell_descriptions
[ target_peice
->config
];
2026 v2i_copy( desc
->start
, fish
->dir
);
2027 fish
->flow_reversed
= 1;
2029 world
.num_fishes
++;
2034 vg_warn( "Max marbles exceeded\n" );
2039 target_peice
->state
|= FLAG_FLIP_FLOP
;
2041 target_peice
->state
&= ~FLAG_FLIP_FLOP
;
2044 cell_current
->state
|= FLAG_TRIGGERED
;
2049 // Third pass (collisions)
2050 struct fish
*fi
, *fj
;
2052 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2054 fi
= &world
.fishes
[i
];
2056 if( fi
->state
== k_fish_state_alive
)
2058 int continue_again
= 0;
2060 for( int j
= i
+1; j
< world
.num_fishes
; j
++ )
2062 fj
= &world
.fishes
[j
];
2064 if( fj
->state
== k_fish_state_alive
)
2069 v2i_sub( fi
->pos
, fi
->dir
, fi_prev
);
2070 v2i_sub( fj
->pos
, fj
->dir
, fj_prev
);
2073 collide_next_frame
= (
2074 (fi
->pos
[0] == fj
->pos
[0]) &&
2075 (fi
->pos
[1] == fj
->pos
[1]))? 1: 0,
2076 collide_this_frame
= (
2077 (fi_prev
[0] == fj
->pos
[0]) &&
2078 (fi_prev
[1] == fj
->pos
[1]) &&
2079 (fj_prev
[0] == fi
->pos
[0]) &&
2080 (fj_prev
[1] == fi
->pos
[1])
2083 if( collide_next_frame
|| collide_this_frame
)
2085 sw_set_achievement( "BANG" );
2087 // Shatter death (+0.5s)
2088 float death_time
= world
.sim_internal_time
+ ( collide_this_frame
? 0.0f
: 0.5f
);
2090 fi
->state
= k_fish_state_soon_dead
;
2091 fj
->state
= k_fish_state_soon_dead
;
2092 fi
->death_time
= death_time
;
2093 fj
->death_time
= death_time
;
2100 if( continue_again
)
2106 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
2108 struct cell_terminal
*term
= &world
.io
[ i
];
2109 int is_input
= pcell(term
->pos
)->state
& FLAG_INPUT
;
2113 if( world
.sim_frame
< term
->runs
[ world
.sim_run
].step_count
)
2115 i8 emit
= term
->runs
[ world
.sim_run
].steps
[ world
.sim_frame
];
2119 struct fish
*fish
= &world
.fishes
[ world
.num_fishes
];
2120 v2i_copy( term
->pos
, fish
->pos
);
2122 fish
->state
= k_fish_state_alive
;
2123 fish
->colour
= emit
;
2125 struct cell
*cell_ptr
= pcell( fish
->pos
);
2127 if( cell_ptr
->config
!= k_cell_type_stub
)
2129 if( world
.num_fishes
< vg_list_size(world
.fishes
))
2131 struct cell_description
*desc
= &cell_descriptions
[ cell_ptr
->config
];
2133 v2i_copy( desc
->start
, fish
->dir
);
2134 fish
->flow_reversed
= 1;
2136 world
.num_fishes
++;
2140 vg_warn( "Max marbles exceeded\n" );
2146 if( alive_count
== 0 )
2148 world
.completed
= 1;
2150 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
2152 struct cell_terminal
*term
= &world
.io
[ i
];
2154 if( pcell(term
->pos
)->state
& FLAG_OUTPUT
)
2156 struct terminal_run
*run
= &term
->runs
[ world
.sim_run
];
2158 if( run
->recv_count
== run
->step_count
)
2160 for( int j
= 0; j
< run
->step_count
; j
++ )
2162 if( run
->recieved
[j
] != run
->steps
[j
] )
2164 world
.completed
= 0;
2171 world
.completed
= 0;
2177 if( world
.completed
)
2179 if( world
.sim_run
< world
.max_runs
-1 )
2181 vg_success( "Run passed, starting next\n" );
2183 world
.sim_frame
= 0;
2184 world
.sim_target
= 0;
2185 world
.num_fishes
= 0;
2187 // Reset timing reference points
2188 world
.sim_delta_ref
= vg_time
;
2189 world
.sim_internal_ref
= 0.0f
;
2191 if( world
.st
.buttons
[ k_world_button_pause
].state
)
2192 world
.pause_offset_target
= 0.5f
;
2194 world
.pause_offset_target
= 0.0f
;
2196 world
.sim_internal_time
= 0.0f
;
2198 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
2199 world
.data
[ i
].state
&= ~FLAG_FLIP_FLOP
;
2205 vg_success( "Level passed!\n" );
2208 for( int i
= 0; i
< world
.w
*world
.h
; i
++ )
2209 if( world
.data
[ i
].state
& FLAG_CANAL
)
2212 world
.score
= score
;
2213 world
.time
= world
.sim_frame
;
2215 // Copy into career data
2216 if( world
.pCmpLevel
)
2218 career_pass_level( world
.pCmpLevel
, world
.score
, 1 );
2221 sfx_set_play( &audio_tones
, &audio_system_balls_extra
, 9 );
2222 failure_this_frame
= 0;
2223 success_this_frame
= 0;
2228 if( world
.sim_run
> 0 )
2229 sw_set_achievement( "GOOD_ENOUGH" );
2231 vg_error( "Level failed :(\n" );
2242 if( failure_this_frame
)
2244 sfx_set_play( &audio_tones
, &audio_system_balls_extra
, 0 );
2246 else if( success_this_frame
)
2248 static int succes_counter
= 0;
2250 sfx_set_play( &audio_tones
, &audio_system_balls_extra
, 1+(succes_counter
++) );
2252 if( succes_counter
== 7 )
2257 // =====================================================================================================
2259 world
.frame_lerp
= world
.sim_internal_time
- floorf( world
.sim_internal_time
);
2261 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2263 struct fish
*fish
= &world
.fishes
[i
];
2265 if( fish
->state
== k_fish_state_dead
)
2268 if( fish
->state
== k_fish_state_soon_dead
&& (world
.sim_internal_time
> fish
->death_time
) )
2269 continue; // Todo: particle thing?
2271 struct cell
*cell
= pcell(fish
->pos
);
2272 struct cell_description
*desc
= &cell_descriptions
[ cell
->config
];
2276 float t
= world
.frame_lerp
;
2277 if( fish
->flow_reversed
&& !desc
->is_linear
)
2280 v2_copy( fish
->physics_co
, fish
->physics_v
);
2282 switch( cell
->config
)
2284 case k_cell_type_merge
:
2285 if( fish
->dir
[0] == 1 )
2290 case k_cell_type_con_r
: curve
= curve_1
; break;
2291 case k_cell_type_con_l
: curve
= curve_4
; break;
2292 case k_cell_type_con_u
: curve
= curve_2
; break;
2293 case k_cell_type_con_d
: curve
= curve_8
; break;
2294 case 3: curve
= curve_3
; break;
2295 case 6: curve
= curve_6
; break;
2296 case 9: curve
= curve_9
; break;
2297 case 12: curve
= curve_12
; break;
2299 if( t
> curve_7_linear_section
)
2301 t
-= curve_7_linear_section
;
2302 t
*= (1.0f
/(1.0f
-curve_7_linear_section
));
2304 curve
= cell
->state
& FLAG_FLIP_FLOP
? curve_7
: curve_7_1
;
2308 default: curve
= NULL
; break;
2314 float t3
= t
* t
* t
;
2316 float cA
= 3.0f
*t2
- 3.0f
*t3
;
2317 float cB
= 3.0f
*t3
- 6.0f
*t2
+ 3.0f
*t
;
2318 float cC
= 3.0f
*t2
- t3
- 3.0f
*t
+ 1.0f
;
2320 fish
->physics_co
[0] = t3
*curve
[3][0] + cA
*curve
[2][0] + cB
*curve
[1][0] + cC
*curve
[0][0];
2321 fish
->physics_co
[1] = t3
*curve
[3][1] + cA
*curve
[2][1] + cB
*curve
[1][1] + cC
*curve
[0][1];
2322 fish
->physics_co
[0] += (float)fish
->pos
[0];
2323 fish
->physics_co
[1] += (float)fish
->pos
[1];
2328 origin
[0] = (float)fish
->pos
[0] + (float)fish
->dir
[0]*-0.5f
+ 0.5f
;
2329 origin
[1] = (float)fish
->pos
[1] + (float)fish
->dir
[1]*-0.5f
+ 0.5f
;
2331 fish
->physics_co
[0] = origin
[0] + (float)fish
->dir
[0]*t
;
2332 fish
->physics_co
[1] = origin
[1] + (float)fish
->dir
[1]*t
;
2335 v2_sub( fish
->physics_co
, fish
->physics_v
, fish
->physics_v
);
2336 v2_divs( fish
->physics_v
, world
.sim_internal_delta
, fish
->physics_v
);
2341 static void render_tile( v2i pos
, struct cell
*ptr
, v4f
const regular_colour
, v4f
const selected_colour
)
2343 int selected
= world
.selected
== pos
[1]*world
.w
+ pos
[0];
2346 uv
[0] = ptr
->config
& 0x3;
2347 uv
[1] = ptr
->config
>> 2;
2349 glUniform4f( SHADER_UNIFORM( shader_tile_main
, "uOffset" ),
2358 glUniform4fv( SHADER_UNIFORM( shader_tile_main
, "uColour" ), 1, selected_colour
);
2360 glUniform4fv( SHADER_UNIFORM( shader_tile_main
, "uColour" ), 1, regular_colour
);
2366 // Renders specific chunk of tiles
2367 static void render_tile_block( v2i start
, v2i end
, v4f
const regular_colour
, v4f
const selected_colour
)
2369 v2i full_start
= { 0,0 };
2370 v2i full_end
= { world
.w
, world
.h
};
2372 if( !start
|| !end
)
2378 for( int y
= start
[1]; y
< end
[1]; y
++ )
2380 for( int x
= start
[0]; x
< end
[0]; x
++ )
2383 struct cell
*cell
= pcell( pos
);
2385 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
) )
2386 render_tile( pos
, cell
, regular_colour
, selected_colour
);
2391 // Renders all tiles in the command list
2392 static void render_tiles( v4f
const regular_colour
, v4f
const selected_colour
)
2394 glUniform4fv( SHADER_UNIFORM( shader_tile_main
, "uColour" ), 1, regular_colour
);
2398 struct render_cmd
*arr
;
2402 { world
.cmd_buf_tiles
, world
.tile_count
},
2403 { world
.cmd_buf_specials
, world
.tile_special_count
}
2406 for( int i
= 0; i
< vg_list_size( render_lists
); i
++ )
2408 struct render_list
*list
= &render_lists
[i
];
2409 for( int j
= 0; j
< list
->count
; j
++ )
2411 struct render_cmd
*cmd
= &list
->arr
[j
];
2412 struct cell
*cell
= cmd
->ptr
;
2414 render_tile( cmd
->pos
, cell
, regular_colour
, selected_colour
);
2419 static int world_button_exec( struct world_button
*btn
, v2f texture
, v3f colour
, enum world_button_status
*status
)
2421 static v2i click_grab
= { -9999, -9999 };
2426 click_grab
[0] = -9999;
2427 click_grab
[1] = -9999;
2431 v2i click_tile
= { world
.tile_x
, world
.tile_y
};
2434 int is_hovering
= v2i_eq( click_tile
, btn
->position
);
2436 // Set up light targets before logic runs
2438 btn
->light_target
= is_hovering
? 0.9f
: 0.8f
;
2440 btn
->light_target
= is_hovering
? 0.2f
: 0.0f
;
2442 if( vg_get_button( "primary" ) && is_hovering
)
2443 btn
->light_target
= 1.0f
;
2445 // Process click action
2448 if( vg_get_button_down( "primary" ) && is_hovering
)
2449 v2i_copy( click_tile
, click_grab
);
2450 else if( v2i_eq( click_grab
, click_tile
) && vg_get_button_up( "primary" ) )
2453 *status
= btn
->state
? k_world_button_on_disable
: k_world_button_on_enable
;
2455 if( btn
->mode
== k_world_button_mode_toggle
)
2458 sfx_set_play( &audio_clicks
, &audio_system_ui
, btn
->state
? 1:0 );
2466 btn
->light
= vg_lerpf( btn
->light
, btn
->light_target
+ btn
->extra_light
, vg_time_delta
*26.0f
);
2468 v3_copy( colour
, final_colour
);
2469 final_colour
[3] = btn
->light
;
2471 glUniform4f( SHADER_UNIFORM( shader_buttons
, "uOffset" ),
2477 glUniform4fv( SHADER_UNIFORM( shader_buttons
, "uColour" ), 1, final_colour
);
2483 static void level_selection_buttons(void)
2485 v3f tutorial_colour
= { 0.204f
, 0.345f
, 0.553f
};
2486 v3f locked_colour
= { 0.2f
, 0.2f
, 0.2f
};
2488 struct cmp_level
*switch_level_to
= NULL
;
2490 for( int i
= 0; i
< vg_list_size( career_packs
); i
++ )
2492 struct career_level_pack
*grid
= &career_packs
[i
];
2494 for( int j
= 0; j
< grid
->count
; j
++ )
2496 struct cmp_level
*lvl
= &grid
->pack
[ j
];
2498 if( world
.pCmpLevel
== lvl
)
2499 lvl
->btn
.extra_light
= 0.35f
+ fabsf(sinf( vg_time
* 2.0f
)) * 0.05f
;
2500 else lvl
->btn
.extra_light
= 0.2f
;
2502 if( lvl
->completed_score
)
2503 lvl
->btn
.extra_light
+= 0.8f
;
2505 enum world_button_status status
;
2506 if( world_button_exec(
2509 lvl
->unlocked
? (lvl
->is_tutorial
? tutorial_colour
: grid
->primary_colour
): locked_colour
,
2513 if( status
== k_world_button_on_enable
&& lvl
->unlocked
)
2514 switch_level_to
= lvl
;
2519 if( switch_level_to
)
2521 world
.st
.lvl_to_load
= switch_level_to
;
2522 world
.st
.lvl_load_time
= vg_time
+ 0.25f
;
2523 world
.st
.world_transition
= 1.0f
;
2526 if( console_changelevel( 1, &switch_level_to->map_name ) )
2528 world.pCmpLevel = switch_level_to;
2529 gen_level_text( world.pCmpLevel );
2535 static void render_sprite( enum sprites_auto_combine_index id
, v3f pos
)
2537 struct vg_sprite
*sp
= &sprites_auto_combine
[ id
];
2539 glUniform4fv( SHADER_UNIFORM( shader_sprite
, "uUv" ), 1, sp
->uv_xywh
);
2540 glUniform3f( SHADER_UNIFORM( shader_sprite
, "uPos" ), pos
[0], pos
[1], pos
[2] * world
.st
.world_transition
);
2545 void vg_render(void)
2547 glViewport( 0,0, vg_window_x
, vg_window_y
);
2549 glDisable( GL_DEPTH_TEST
);
2550 glClearColor( 0.369768f
, 0.3654f
, 0.42f
, 1.0f
);
2551 glClear( GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
2553 v4f
const colour_default
= {1.0f
, 1.0f
, 1.0f
, 1.0f
};
2554 v4f
const colour_selected
= {0.90f
, 0.92f
, 1.0f
, 1.0f
};
2556 int const circle_base
= 6;
2557 int const filled_start
= circle_base
+0;
2558 int const filled_count
= circle_base
+32;
2559 int const empty_start
= circle_base
+32;
2560 int const empty_count
= circle_base
+32*2;
2562 struct world_theme
*theme
= &world_themes
[ world_theme_id
];
2564 if( !world
.initialzed
)
2567 // Extract render commands
2568 world
.tile_count
= 0;
2569 world
.tile_special_count
= 0;
2571 for( int y
= 1; y
< world
.h
-1; y
++ )
2573 for( int x
= 1; x
< world
.w
-1; x
++ )
2575 struct cell
*cell
= pcell((v2i
){x
,y
});
2577 if( cell
->state
& (FLAG_CANAL
|FLAG_INPUT
|FLAG_OUTPUT
|FLAG_EMITTER
|FLAG_INPUT_NICE
) )
2579 struct render_cmd
*cmd
;
2582 (cell
->config
== k_cell_type_split
&& (cell
->state
& FLAG_CANAL
))
2583 || (cell
->state
& (FLAG_EMITTER
|FLAG_IS_TRIGGER
))
2585 cmd
= &world
.cmd_buf_tiles
[ world
.max_commands
- (++ world
.tile_special_count
) ];
2587 cmd
= &world
.cmd_buf_tiles
[ world
.tile_count
++ ];
2596 world
.cmd_buf_specials
= &world
.cmd_buf_tiles
[ world
.max_commands
- world
.tile_special_count
];
2599 // ========================================================================================================
2600 use_mesh( &world
.shapes
);
2602 SHADER_USE( shader_background
);
2603 glUniformMatrix3fv( SHADER_UNIFORM( shader_background
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2605 glActiveTexture( GL_TEXTURE0
);
2606 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
2607 glUniform1i( SHADER_UNIFORM( shader_background
, "uTexMain" ), 0 );
2609 glUniform3f( SHADER_UNIFORM( shader_background
, "uOffset" ), -16, -16, 64 );
2610 glUniform1f( SHADER_UNIFORM( shader_background
, "uVariance" ), 0.02f
);
2612 glActiveTexture( GL_TEXTURE1
);
2613 glBindTexture( GL_TEXTURE_2D
, world
.random_samples
);
2614 glUniform1i( SHADER_UNIFORM( shader_background
, "uSamplerNoise" ), 1 );
2615 glUniform1f( SHADER_UNIFORM( shader_background
, "uVisibility" ), 1.0f
); //world.st.world_transition );
2619 // TILESET BACKGROUND LAYER
2620 // ========================================================================================================
2621 use_mesh( &world
.shapes
);
2622 SHADER_USE( shader_tile_main
);
2625 m2x2_identity( subtransform
);
2626 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main
, "uSubTransform" ), 1, GL_FALSE
, (float *)subtransform
);
2627 glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_main
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2628 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uGhost" ), 0.0f
);
2629 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uForeground" ), 0.0f
);
2630 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uVisibility" ), world
.st
.world_transition
* 2.0f
);
2633 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
2634 glBlendEquation(GL_FUNC_ADD
);
2637 vg_tex2d_bind( &tex_tile_data
, 0 );
2638 vg_tex2d_bind( theme
->tex_tiles
, 1 );
2640 glUniform3fv( SHADER_UNIFORM( shader_tile_main
, "uShadowing" ), 1, theme
->col_shadow
);
2642 render_tiles( colour_default
, colour_default
);
2645 // ========================================================================================================
2646 SHADER_USE( shader_ball
);
2647 glUniformMatrix3fv( SHADER_UNIFORM( shader_ball
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2649 vg_tex2d_bind( &tex_ball_noise
, 0 );
2650 glUniform1i( SHADER_UNIFORM( shader_ball
, "uTexMain" ), 0 );
2652 if( world
.st
.buttons
[ k_world_button_sim
].state
)
2654 for( int i
= 0; i
< world
.num_fishes
; i
++ )
2656 struct fish
*fish
= &world
.fishes
[i
];
2658 render_pos
[2] = 1.0f
;
2660 if( fish
->state
== k_fish_state_dead
|| fish
->state
== k_fish_state_soon_dead
)
2662 float death_anim_time
= world
.sim_internal_time
- fish
->death_time
;
2665 if( death_anim_time
> 0.0f
&& death_anim_time
< 1.0f
)
2667 float amt
= 1.0f
-death_anim_time
*death_anim_time
;
2669 v2_muladds( fish
->physics_co
, fish
->physics_v
, -1.0f
* world
.sim_internal_delta
* amt
, fish
->physics_co
);
2670 render_pos
[2] = amt
;
2672 else if( world
.sim_internal_time
> fish
->death_time
)
2675 else if( fish
->state
== k_fish_state_bg
)
2678 v2_copy( fish
->physics_co
, render_pos
);
2680 v4f dot_colour
= { 0.0f
, 0.0f
, 0.0f
, 1.0f
};
2681 colour_code_v3( fish
->colour
, dot_colour
);
2683 glUniform3fv( SHADER_UNIFORM( shader_ball
, "uColour" ), 1, dot_colour
);
2684 glUniform3fv( SHADER_UNIFORM( shader_ball
, "uOffset" ), 1, render_pos
);
2685 glUniform2f( SHADER_UNIFORM( shader_ball
, "uTexOffset" ), (float)i
* 1.2334, (float)i
* -0.3579f
);
2690 // TILESET FOREGROUND LAYER
2691 // ========================================================================================================
2692 SHADER_USE( shader_tile_main
);
2695 vg_tex2d_bind( &tex_tile_data
, 0 );
2696 glUniform1i( SHADER_UNIFORM( shader_tile_main
, "uTexGlyphs" ), 0 );
2698 // TODO: is this needed to be rebinded?
2699 vg_tex2d_bind( theme
->tex_tiles
, 1 );
2700 glUniform1i( SHADER_UNIFORM( shader_tile_main
, "uTexWood" ), 1 );
2702 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uForeground" ), 1.0f
);
2703 render_tiles( colour_default
, colour_selected
);
2706 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
2708 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
2709 struct cell
*cell
= cmd
->ptr
;
2711 if( cell
->config
== k_cell_type_split
)
2713 float rotation
= cell
->state
& FLAG_FLIP_FLOP
? vg_rad( -45.0f
): vg_rad( 45.0f
);
2715 if( cell
->state
& FLAG_FLIP_ROTATING
)
2717 if( (world
.frame_lerp
> curve_7_linear_section
) )
2719 float const rotation_speed
= 0.4f
;
2720 if( (world
.frame_lerp
< 1.0f
-rotation_speed
) )
2722 float t
= world
.frame_lerp
- curve_7_linear_section
;
2723 t
*= -2.0f
* (1.0f
/(1.0f
-(curve_7_linear_section
+rotation_speed
)));
2733 m2x2_create_rotation( subtransform
, rotation
);
2735 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main
, "uSubTransform" ), 1, GL_FALSE
, (float *)subtransform
);
2736 glUniform4f( SHADER_UNIFORM( shader_tile_main
, "uOffset" ),
2738 (float)cmd
->pos
[1] + 0.125f
,
2739 cell
->state
& FLAG_TARGETED
? 3.0f
: 2.0f
,
2747 // ========================================================================================================
2748 if( world
.selected
!= -1 && !(world
.data
[ world
.selected
].state
& FLAG_CANAL
) && !world
.id_drag_from
)
2750 v2i new_begin
= { world
.tile_x
- 2, world
.tile_y
- 2 };
2751 v2i new_end
= { world
.tile_x
+ 2, world
.tile_y
+ 2 };
2753 world
.data
[ world
.selected
].state
^= FLAG_CANAL
;
2754 map_reclassify( new_begin
, new_end
, 0 );
2756 m2x2_identity( subtransform
);
2757 glUniform1f( SHADER_UNIFORM( shader_tile_main
, "uGhost" ), 1.0f
);
2758 glUniformMatrix2fv( SHADER_UNIFORM( shader_tile_main
, "uSubTransform" ), 1, GL_FALSE
, (float *)subtransform
);
2759 glUniform2fv( SHADER_UNIFORM( shader_tile_main
, "uMousePos" ), 1, world
.tile_pos
);
2761 render_tile_block( new_begin
, new_end
, colour_default
, colour_default
);
2763 world
.data
[ world
.selected
].state
^= FLAG_CANAL
;
2764 map_reclassify( new_begin
, new_end
, 0 );
2768 // ========================================================================================================
2769 SHADER_USE( shader_buttons
);
2770 glUniformMatrix3fv( SHADER_UNIFORM( shader_buttons
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2772 vg_tex2d_bind( &tex_buttons
, 0 );
2773 glUniform1i( SHADER_UNIFORM( shader_buttons
, "uTexMain" ), 0 );
2775 enum world_button_status stat
;
2776 int world_paused
= world
.st
.buttons
[k_world_button_pause
].state
;
2777 int world_running
= world
.st
.buttons
[k_world_button_sim
].state
;
2779 float sim_icon_x
= world_paused
? 3.0f
: (world_running
? 2.0f
: 0.0f
);
2781 v3f btn_dark_blue
= { 0.204f
, 0.345f
, 0.553f
};
2782 v3f btn_orange
= { 0.553f
, 0.345f
, 0.204f
};
2784 if( world_button_exec( &world
.st
.buttons
[k_world_button_sim
], (v2f
){ sim_icon_x
, 3.0f
}, btn_dark_blue
, &stat
))
2786 if( stat
== k_world_button_on_enable
)
2791 world
.pause_offset_target
= 0.5f
;
2797 // Trigger single step
2798 world
.pause_offset_target
+= 1.0f
;
2799 world
.st
.buttons
[k_world_button_sim
].state
= 1;
2808 if( world_button_exec( &world
.st
.buttons
[k_world_button_pause
], (v2f
){ 1.0f
, 3.0f
}, btn_dark_blue
, &stat
))
2810 world
.sim_internal_ref
= world
.sim_internal_time
;
2811 world
.sim_delta_ref
= vg_time
;
2813 if( stat
== k_world_button_on_enable
)
2815 float time_frac
= world
.sim_internal_time
-floorf(world
.sim_internal_time
);
2816 world
.pause_offset_target
= 0.5f
- time_frac
;
2819 world
.pause_offset_target
= 0.0f
;
2822 if( world_button_exec( &world
.st
.buttons
[k_world_button_speedy
], (v2f
){ 0.0f
, 2.0f
}, btn_orange
, &stat
))
2824 world
.sim_delta_speed
= stat
== k_world_button_on_enable
? 10.0f
: 2.5f
;
2828 world
.sim_delta_ref
= vg_time
;
2829 world
.sim_internal_ref
= world
.sim_internal_time
;
2833 if( world_button_exec( &world
.st
.buttons
[k_world_button_settings
], (v2f
){ 1.0f
, 2.0f
}, btn_orange
, &stat
))
2835 world
.st
.state
= stat
== k_world_button_on_enable
? k_game_state_settings
: k_game_state_main
;
2838 level_selection_buttons();
2840 if( vg_get_button_up( "primary" ) )
2841 world_button_exec( NULL
, NULL
, NULL
, NULL
);
2844 // ========================================================================================================
2846 //glEnable(GL_BLEND);
2847 SHADER_USE( shader_tile_colour
);
2848 glUniformMatrix3fv( SHADER_UNIFORM( shader_tile_colour
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2850 for( int i
= 0; i
< arrlen( world
.io
); i
++ )
2852 struct cell_terminal
*term
= &world
.io
[ i
];
2853 struct cell
*cell
= pcell(term
->pos
);
2855 int is_input
= cell
->state
& FLAG_INPUT
;
2856 v4f dot_colour
= { 0.0f
, 0.0f
, 0.0f
, 1.0f
};
2858 if( cell
->state
& FLAG_EMITTER
)
2860 for( int j
= 0; j
< 2; j
++ )
2862 if( cell
->emit
[j
] != -1 )
2864 colour_code_v3( cell
->emit
[j
], dot_colour
);
2866 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
2867 term
->pos
[0] + 0.25f
+ (float)j
* 0.5f
,
2868 term
->pos
[1] + 0.25f
,
2872 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
2873 draw_mesh( filled_start
, filled_count
);
2879 for( int k
= 0; k
< term
->run_count
; k
++ )
2881 float arr_base
= is_input
? 1.2f
: -0.2f
,
2882 run_offset
= (is_input
? 0.2f
: -0.2f
) * (float)k
,
2883 y_position
= is_input
?
2884 (arr_base
+ (float)term
->pos
[1] + (float)(term
->run_count
-1)*0.2f
) - run_offset
:
2885 (float)term
->pos
[1] + arr_base
+ run_offset
;
2890 if( is_simulation_running() )
2892 if( k
== world
.sim_run
)
2894 float a
= fabsf(sinf( vg_time
* 2.0f
)) * 0.075f
+ 0.075f
;
2896 v4_copy( (v4f
){ 1.0f
, 1.0f
, 1.0f
, a
}, bar_colour
);
2899 v4_copy( (v4f
){ 0.0f
, 0.0f
, 0.0f
, 0.13f
}, bar_colour
);
2903 else if( 1 || k
& 0x1 )
2906 v4_copy( (v4f
){ 1.0f
, 1.0f
, 1.0f
, 0.07f
}, bar_colour
);
2908 v4_copy( (v4f
){ 0.0f
, 0.0f
, 0.0f
, 0.13f
}, bar_colour
);
2915 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, bar_colour
);
2916 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
2917 (float)term
->pos
[0], y_position
- 0.1f
, 1.0f
);
2922 for( int j
= 0; j
< term
->runs
[k
].step_count
; j
++ )
2924 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
2925 (float)term
->pos
[0] + 0.2f
+ 0.2f
* (float)j
,
2932 i8 colour
= term
->runs
[k
].steps
[j
];
2935 colour_code_v3( colour
, dot_colour
);
2936 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
2938 // Draw filled if tick not passed, draw empty if empty
2939 if( (world
.sim_frame
> j
&& world
.sim_run
>= k
) || world
.sim_run
> k
)
2940 draw_mesh( empty_start
, empty_count
);
2942 draw_mesh( filled_start
, filled_count
);
2948 if( term
->runs
[k
].recv_count
> j
)
2950 colour_code_v3( term
->runs
[k
].recieved
[j
], dot_colour
);
2951 v3_muls( dot_colour
, 0.8f
, dot_colour
);
2952 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
2954 draw_mesh( filled_start
, filled_count
);
2957 colour_code_v3( term
->runs
[k
].steps
[j
], dot_colour
);
2958 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, dot_colour
);
2960 draw_mesh( empty_start
, empty_count
);
2967 // ========================================================================================================
2968 SHADER_USE( shader_sprite
);
2969 glUniformMatrix3fv( SHADER_UNIFORM( shader_sprite
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
2971 vg_tex2d_bind( &tex_sprites
, 0 );
2972 glUniform1i( SHADER_UNIFORM( shader_sprite
, "uTexMain" ), 0 );
2974 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
2976 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
2977 struct cell
*cell
= cmd
->ptr
;
2979 if( (cell
->config
== k_cell_type_split
) || (cell
->state
& FLAG_EMITTER
) )
2981 v2f center
= { cmd
->pos
[0] + 0.5f
, cmd
->pos
[1] + 0.5f
};
2983 v3f p0
= { 0.0f
, 0.0f
, 4.0f
};
2984 v3f p1
= { 0.0f
, 0.0f
, 4.0f
};
2986 v2_add( center
, (v2f
){ -0.25f
, -0.25f
}, p0
);
2987 v2_add( center
, (v2f
){ 0.25f
, -0.25f
}, p1
);
2989 render_sprite( k_sprite_jack_1
, p0
);
2990 render_sprite( k_sprite_jack_2
, p1
);
2992 else if( cell
->state
& FLAG_IS_TRIGGER
)
2994 v3f p0
= { 0.0f
, 0.0f
, 4.0f
};
2996 struct cell_description
*desc
= &cell_descriptions
[ cell
->config
];
2998 v2_add( (v2f
){ cmd
->pos
[0], cmd
->pos
[1] }, desc
->trigger_pos
, p0
);
2999 render_sprite( desc
->trigger_sprite
, p0
);
3004 // ========================================================================================================
3007 m3x3_identity( mvp_text
);
3008 m3x3_scale( mvp_text
, (v3f
){
3009 1.0f
/ ((float)UI_GLYPH_SPACING_X
*4.0f
),
3010 1.0f
/ -((float)UI_GLYPH_SPACING_X
*4.0f
),
3014 m3x3_mul( vg_pv
, mvp_text
, mvp_text
);
3015 ui_draw( &world
.st
.world_text
, mvp_text
);
3018 // ========================================================================================================
3021 SHADER_USE( shader_wire
);
3022 glBindVertexArray( world
.wire
.vao
);
3024 glUniformMatrix3fv( SHADER_UNIFORM( shader_wire
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
3026 v4f
const wire_left_colour
= { 0.5f
, 0.5f
, 0.5f
, 1.0f
};
3027 v4f
const wire_right_colour
= { 0.2f
, 0.2f
, 0.2f
, 1.0f
};
3028 v4f
const wire_drag_colour
= { 0.2f
, 0.2f
, 0.2f
, 0.6f
};
3030 glUniform1f( SHADER_UNIFORM( shader_wire
, "uTime" ), world
.frame_lerp
);
3031 glUniform1f( SHADER_UNIFORM( shader_wire
, "uGlow" ), 0.0f
);
3033 if( world
.id_drag_from
)
3035 glUniform4fv( SHADER_UNIFORM( shader_wire
, "uColour" ), 1, wire_drag_colour
);
3036 glUniform1f( SHADER_UNIFORM( shader_wire
, "uCurve" ), 0.4f
);
3037 glUniform3f( SHADER_UNIFORM( shader_wire
, "uStart" ), world
.drag_from_co
[0], world
.drag_from_co
[1], 0.20f
*world
.st
.world_transition
);
3038 glUniform3f( SHADER_UNIFORM( shader_wire
, "uEnd" ), world
.drag_to_co
[0], world
.drag_to_co
[1], 0.20f
*world
.st
.world_transition
);
3039 glDrawElements( GL_TRIANGLES
, world
.wire
.em
, GL_UNSIGNED_SHORT
, (void*)(0) );
3042 // Pulling animation
3043 float rp_x1
= world
.frame_lerp
*9.0f
;
3044 float rp_xa
= rp_x1
*expf(1.0f
-rp_x1
)* 0.36f
;
3045 float rp_x2
= 1.0f
-rp_xa
;
3047 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3049 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3050 struct cell
*cell
= cmd
->ptr
;
3052 if( cell
->state
& FLAG_TARGETED
)
3054 for( int j
= 0; j
< 2; j
++ )
3056 if( !cell
->links
[j
] )
3059 struct cell
*other_cell
= &world
.data
[ cell
->links
[ j
]];
3060 struct cell_description
*desc
= &cell_descriptions
[ other_cell
->config
];
3062 int x2
= cell
->links
[j
] % world
.w
;
3063 int y2
= (cell
->links
[j
] - x2
) / world
.w
;
3068 endpoint
[0] = (float)cmd
->pos
[0] + (j
? 0.75f
: 0.25f
);
3069 endpoint
[1] = (float)cmd
->pos
[1] + 0.25f
;
3074 v2_add( desc
->trigger_pos
, startpoint
, startpoint
);
3076 if( cmd
->ptr
->state
& FLAG_EMITTER
)
3079 colour_code_v3( cmd
->ptr
->emit
[j
], wire_colour
);
3080 wire_colour
[3] = 1.0f
;
3082 glUniform4fv( SHADER_UNIFORM( shader_wire
, "uColour" ), 1, wire_colour
);
3085 glUniform4fv( SHADER_UNIFORM( shader_wire
, "uColour" ), 1, j
? wire_right_colour
: wire_left_colour
);
3087 glUniform1f( SHADER_UNIFORM( shader_wire
, "uCurve" ), other_cell
->state
& FLAG_TRIGGERED
? rp_x2
* 0.4f
: 0.4f
);
3088 glUniform1f( SHADER_UNIFORM( shader_wire
, "uGlow" ), other_cell
->state
& FLAG_TRIGGERED
? rp_xa
: 0.0f
);
3089 glUniform3f( SHADER_UNIFORM( shader_wire
, "uEnd" ), startpoint
[0], startpoint
[1], 0.18f
*world
.st
.world_transition
);
3090 glUniform3f( SHADER_UNIFORM( shader_wire
, "uStart" ), endpoint
[0], endpoint
[1], 0.18f
*world
.st
.world_transition
);
3091 glDrawElements( GL_TRIANGLES
, world
.wire
.em
, GL_UNSIGNED_SHORT
, (void*)(0) );
3097 // ========================================================================================================
3099 SHADER_USE( shader_tile_colour
);
3100 use_mesh( &world
.shapes
);
3102 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3104 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3105 struct cell
*cell
= cmd
->ptr
;
3107 if( cell
->state
& FLAG_TARGETED
)
3109 for( int j
= 0; j
< 2; j
++ )
3111 if( !cell
->links
[j
] )
3114 struct cell
*other_cell
= &world
.data
[ cell
->links
[ j
]];
3115 struct cell_description
*desc
= &cell_descriptions
[ other_cell
->config
];
3117 int x2
= cell
->links
[j
] % world
.w
;
3118 int y2
= (cell
->links
[j
] - x2
) / world
.w
;
3122 pts
[0][0] = (float)cmd
->pos
[0] + (j
? 0.75f
: 0.25f
);
3123 pts
[0][1] = (float)cmd
->pos
[1] + 0.25f
;
3128 v2_add( desc
->trigger_pos
, pts
[1], pts
[1] );
3130 if( cell
->state
& FLAG_EMITTER
)
3133 colour_code_v3( cell
->emit
[j
], wire_colour
);
3135 v3_muls( wire_colour
, 0.8f
, wire_colour
);
3136 wire_colour
[3] = 1.0f
;
3138 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1, wire_colour
);
3141 glUniform4fv( SHADER_UNIFORM( shader_tile_colour
, "uColour" ), 1,j
?wire_right_colour
: wire_left_colour
);
3143 for( int i
= 0; i
< 2; i
++ )
3145 glUniform3f( SHADER_UNIFORM( shader_tile_colour
, "uOffset" ),
3148 0.08f
* world
.st
.world_transition
3150 draw_mesh( filled_start
, filled_count
);
3156 // SUB SPLITTER DIRECTION
3157 // ========================================================================================================
3160 glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ), 0.9f, 0.35f, 0.1f, 0.75f );
3162 for( int i = 0; i < world.tile_special_count; i ++ )
3164 struct render_cmd *cmd = &world.cmd_buf_specials[i];
3165 struct cell *cell = cmd->ptr;
3167 if( cell->state & FLAG_TARGETED && cell->config == k_cell_type_split )
3169 glUniform3f( SHADER_UNIFORM( shader_tile_colour, "uOffset" ), cmd->pos[0], cmd->pos[1], 1.0f );
3170 draw_mesh( cell->state & FLAG_FLIP_FLOP? 5: 4, 1 );
3176 // ========================================================================================================
3177 glBlendFunc(GL_ONE
, GL_ONE
);
3178 glBlendEquation(GL_FUNC_ADD
);
3180 SHADER_USE( shader_sprite
);
3182 vg_tex2d_bind( &tex_sprites
, 0 );
3183 glUniform1i( SHADER_UNIFORM( shader_sprite
, "uTexMain" ), 0 );
3185 for( int i
= 0; i
< world
.tile_special_count
; i
++ )
3187 struct render_cmd
*cmd
= &world
.cmd_buf_specials
[i
];
3188 struct cell
*cell
= cmd
->ptr
;
3190 if( cell
->config
== k_cell_type_split
)
3192 v2f center
= { cmd
->pos
[0] + 0.5f
, cmd
->pos
[1] + 0.5f
};
3194 v3f p0
= { 0.0f
, 0.0f
, 12.0f
};
3195 v3f p1
= { 0.0f
, 0.0f
, 12.0f
};
3197 v2_add( center
, (v2f
){ -0.25f
, -0.25f
}, p0
);
3198 v2_add( center
, (v2f
){ 0.25f
, -0.25f
}, p1
);
3200 if( cell
->state
& FLAG_TARGETED
)
3202 if( cell
->state
& FLAG_FLIP_FLOP
)
3203 render_sprite( k_sprite_flare_y
, p1
);
3205 render_sprite( k_sprite_flare_b
, p0
);
3208 render_sprite( k_sprite_flare_w
, cell
->state
&FLAG_FLIP_FLOP
? p1
: p0
);
3212 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
3213 glBlendEquation(GL_FUNC_ADD
);
3216 glDisable(GL_BLEND
);
3220 float const score_bright = 1.25f;
3221 glUniform4f( SHADER_UNIFORM( shader_tile_colour, "uColour" ),
3222 0.4f*score_bright, 0.39f*score_bright, 0.45f*score_bright, 1.0f );
3224 use_mesh( &world.numbers );
3225 draw_numbers( (v3f){ 2.0f, (float)world.h-1.875f, 0.3333f }, world.score );
3231 // Drawing world name
3232 if( world
.pCmpLevel
)
3234 gui_text( (ui_px
[2]){ vg_window_x
/ 2, 4 }, world
.pCmpLevel
->title
, 2, k_text_align_center
);
3235 gui_text( (ui_px
[2]){ vg_window_x
/ 2, 28 }, world
.pCmpLevel
->description
, 1, k_text_align_center
);
3238 if( world
.st
.state
== k_game_state_update
)
3242 ui_global_ctx
.cursor
[2] = 458;
3243 ui_global_ctx
.cursor
[3] = 316;
3244 ui_global_ctx
.cursor
[0] = vg_window_x
/ 2 - 229;
3245 ui_global_ctx
.cursor
[1] = vg_window_y
/ 2 - 158;
3249 gui_capture_mouse( 200 );
3250 gui_fill_rect( ui_global_ctx
.cursor
, 0xE8303030 );
3253 title_pos
[0] = ui_global_ctx
.cursor
[0] + 229;
3254 title_pos
[1] = ui_global_ctx
.cursor
[1] + 16;
3256 gui_text( title_pos
, "Update 1.5", 2, k_text_align_center
);
3258 gui_text( (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 16, title_pos
[1] + 45 },
3259 "Welcome to the first update to marble computing!"
3261 "New features have been added:\n"
3263 " - Settings menu\n"
3265 " - More levels and a new block type\n"
3266 " - Scores for each level\n"
3267 " - Zooming and panning (mousewheel)\n"
3269 "There is much more in the works, such as a\n"
3270 "soundtrack, and the rest of the levels for the\n"
3273 "Thank you everyone for enjoying my game :)\n",
3274 1, k_text_align_left
3277 ui_global_ctx
.cursor
[2] = 100;
3278 ui_global_ctx
.cursor
[3] = 30;
3279 ui_global_ctx
.cursor
[0] += 229 - 50;
3280 ui_global_ctx
.cursor
[1] += 316 - 30 - 16;
3282 if( gui_button( 1 ) )
3284 world
.st
.state
= k_game_state_main
;
3286 gui_text( (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 50,
3287 ui_global_ctx
.cursor
[1] + 4 }, "OK", 1, k_text_align_center
);
3292 else if( world
.st
.state
== k_game_state_settings
)
3296 ui_global_ctx
.cursor
[2] = 225;
3302 gui_capture_mouse( 200 );
3304 gui_fill_rect( ui_global_ctx
.cursor
, 0xC0202020 );
3305 ui_rect_pad( ui_global_ctx
.cursor
, 8 );
3307 ui_global_ctx
.cursor
[3] = 25;
3311 gui_text( ui_global_ctx
.cursor
, "SETTINGS", 2, 0 );
3315 // Colour scheme selection
3316 ui_global_ctx
.cursor
[1] += 30;
3318 gui_text( ui_global_ctx
.cursor
, "Colour Scheme", 1, 0 );
3319 ui_global_ctx
.cursor
[1] += 25;
3323 ui_global_ctx
.cursor
[2] = 50;
3325 for( int i
= 0; i
< 4; i
++ )
3330 u32 rgb
= 0xff000000;
3332 for( int j
= 0; j
< 3; j
++ )
3333 rgb
|= (u32
)(colour_sets
[ colour_set_id
][i
][j
]*255.0f
) << j
* 8;
3335 gui_fill_rect( ui_global_ctx
.cursor
, rgb
);
3344 ui_global_ctx
.cursor
[2] = 25;
3345 if( gui_button( 0 ) == k_button_click
)
3347 if( colour_set_id
> 0 )
3350 gui_text( ui_global_ctx
.cursor
, "<", 2, 0 );
3353 ui_global_ctx
.cursor
[2] = 150;
3356 gui_fill_rect( ui_global_ctx
.cursor
, 0x33ffffff );
3358 (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 75, ui_global_ctx
.cursor
[1] + 6 },
3359 (const char *[]){ "Normal", "Extra1", "Extra2" }[ colour_set_id
],
3360 1, k_text_align_center
3365 ui_global_ctx
.cursor
[2] = 25;
3366 if( gui_button( 1 ) == k_button_click
)
3368 if( colour_set_id
< vg_list_size( colour_sets
)-1 )
3371 gui_text( ui_global_ctx
.cursor
, ">", 2, 0 );
3377 // TODO: remove code dupe
3378 ui_global_ctx
.cursor
[1] += 16;
3380 gui_text( ui_global_ctx
.cursor
, "Tile Theme", 1, 0 );
3381 ui_global_ctx
.cursor
[1] += 20;
3385 ui_global_ctx
.cursor
[2] = 25;
3386 if( gui_button( 0 ) == k_button_click
)
3388 if( world_theme_id
> 0 )
3391 gui_text( ui_global_ctx
.cursor
, "<", 2, 0 );
3394 ui_global_ctx
.cursor
[2] = 150;
3397 gui_fill_rect( ui_global_ctx
.cursor
, 0x33ffffff );
3399 (ui_px
[2]){ ui_global_ctx
.cursor
[0] + 75, ui_global_ctx
.cursor
[1] + 6 },
3400 world_themes
[ world_theme_id
].name
, 1, k_text_align_center
3405 ui_global_ctx
.cursor
[2] = 25;
3406 if( gui_button( 1 ) == k_button_click
)
3408 if( world_theme_id
< vg_list_size( world_themes
)-1 )
3411 gui_text( ui_global_ctx
.cursor
, ">", 2, 0 );
3420 #if STEAM_LEADERBOARDS
3421 void leaderboard_dispatch_score(void)
3424 sw_upload_leaderboard_score(
3425 ui_data
.upload_request
.level
->steam_leaderboard
,
3426 k_ELeaderboardUploadScoreMethodKeepBest
,
3427 ui_data
.upload_request
.score
,
3432 ui_data
.upload_request
.is_waiting
= 0;
3434 vg_success( "Dispatched leaderboard score\n" );
3437 void leaderboard_found( LeaderboardFindResult_t
*pCallback
)
3439 if( !pCallback
->m_bLeaderboardFound
)
3441 vg_error( "Leaderboard could not be found\n" );
3442 ui_data
.steam_leaderboard
= 0;
3446 const char *recieved_name
= sw_get_leaderboard_name( pCallback
->m_hSteamLeaderboard
);
3448 // Update UI state and request entries if this callback found the current UI level
3449 if( ui_data
.level_selected
)
3451 if( !strcmp( recieved_name
, ui_data
.level_selected
->map_name
) )
3453 sw_download_leaderboard_entries( pCallback
->m_hSteamLeaderboard
, k_ELeaderboardDataRequestFriends
, 0, 8 );
3454 ui_data
.level_selected
->steam_leaderboard
= pCallback
->m_hSteamLeaderboard
;
3458 // Dispatch the waiting request if there was one
3459 if( ui_data
.upload_request
.is_waiting
)
3461 if( !strcmp( recieved_name
, ui_data
.upload_request
.level
->map_name
) )
3463 ui_data
.upload_request
.level
->steam_leaderboard
= pCallback
->m_hSteamLeaderboard
;
3464 leaderboard_dispatch_score();
3470 void leaderboard_downloaded( LeaderboardScoresDownloaded_t
*pCallback
)
3472 // Update UI if this leaderboard matches what we currently have in view
3473 if( ui_data
.level_selected
->steam_leaderboard
== pCallback
->m_hSteamLeaderboard
)
3475 vg_info( "Recieved %d entries\n", pCallback
->m_cEntryCount
);
3476 ui_data
.leaderboard_count
= VG_MIN( pCallback
->m_cEntryCount
, 8 );
3478 u64_steamid local_player
= sw_get_steamid();
3480 for( int i
= 0; i
< ui_data
.leaderboard_count
; i
++ )
3482 LeaderboardEntry_t entry
;
3483 sw_get_downloaded_entry( pCallback
->m_hSteamLeaderboardEntries
, i
, &entry
, NULL
, 0 );
3485 struct leaderboard_player
*player
= &ui_data
.leaderboard_players
[i
];
3487 player
->id
= entry
.m_steamIDUser
.m_unAll64Bits
;
3488 strncpy( player
->player_name
, sw_get_friend_persona_name( player
->id
), vg_list_size( player
->player_name
)-1 );
3489 player
->score
= entry
.m_nScore
;
3491 snprintf( player
->score_text
, vg_list_size(player
->score_text
), "%d", player
->score
);
3492 player
->texture
= sw_get_player_image( player
->id
);
3494 if( player
->texture
== 0 )
3495 player
->texture
= tex_unkown
.name
;
3497 player
->is_local_player
= local_player
== player
->id
? 1: 0;
3500 if( ui_data
.leaderboard_count
)
3501 ui_data
.leaderboard_show
= 1;
3503 ui_data
.leaderboard_show
= 0;
3505 else vg_warn( "Downloaded leaderboard does not match requested!\n" );
3508 void leaderboard_set_score( struct cmp_level
*cmp_level
, u32 score
)
3510 if( ui_data
.upload_request
.is_waiting
)
3511 vg_warn( "You are uploading leaderboard entries too quickly!\n" );
3513 ui_data
.upload_request
.level
= cmp_level
;
3514 ui_data
.upload_request
.score
= score
;
3515 ui_data
.upload_request
.is_waiting
= 1;
3517 // If leaderboard ID has been downloaded already then just immediately dispatch this
3518 if( cmp_level
->steam_leaderboard
)
3519 leaderboard_dispatch_score();
3521 sw_find_leaderboard( cmp_level
->map_name
);
3526 // ===========================================================================================================
3528 static int console_credits( int argc
, char const *argv
[] )
3530 vg_info( "Aknowledgements:\n" );
3531 vg_info( " GLFW zlib/libpng glfw.org\n" );
3532 vg_info( " miniaudio MIT0 miniaud.io\n" );
3533 vg_info( " QOI MIT phoboslab.org\n" );
3534 vg_info( " STB library MIT nothings.org\n" );
3538 static int console_save_map( int argc
, char const *argv
[] )
3540 if( !world
.initialzed
)
3542 vg_error( "Tried to save uninitialized map!\n" );
3546 char map_path
[ 256 ];
3548 strcpy( map_path
, "sav/" );
3549 strcat( map_path
, world
.map_name
);
3550 strcat( map_path
, ".map" );
3552 FILE *test_writer
= fopen( map_path
, "wb" );
3555 vg_info( "Saving map to '%s'\n", map_path
);
3556 map_serialize( test_writer
);
3558 fclose( test_writer
);
3563 vg_error( "Unable to open stream for writing\n" );
3568 static int console_load_map( int argc
, char const *argv
[] )
3570 char map_path
[ 256 ];
3575 strcpy( map_path
, "sav/" );
3576 strcat( map_path
, argv
[0] );
3577 strcat( map_path
, ".map" );
3579 char *text_source
= vg_textasset_read( map_path
);
3583 strcpy( map_path
, "maps/" );
3584 strcat( map_path
, argv
[0] );
3585 strcat( map_path
, ".map" );
3587 text_source
= vg_textasset_read( map_path
);
3592 vg_info( "Loading map: '%s'\n", map_path
);
3593 world
.pCmpLevel
= NULL
;
3595 if( !map_load( text_source
, argv
[0] ) )
3597 free( text_source
);
3601 free( text_source
);
3606 vg_error( "Missing maps '%s'\n", argv
[0] );
3612 vg_error( "Missing argument <map_path>\n" );
3617 static int console_changelevel( int argc
, char const *argv
[] )
3621 // Save current level
3622 console_save_map( 0, NULL
);
3624 if( console_load_map( argc
, argv
) )
3626 world
.st
.zoom
= 0.0f
;
3633 vg_error( "Missing argument <map_path>\n" );
3639 // START UP / SHUTDOWN
3640 // ===========================================================================================================
3642 #define TRANSFORM_TRI_2D( S, OX, OY, X1, Y1, X2, Y2, X3, Y3 ) \
3643 X1*S+OX, Y1*S+OY, X2*S+OX, Y2*S+OY, X3*S+OX, Y3*S+OY
3647 // Steamworks callbacks
3648 #ifdef STEAM_LEADERBOARDS
3649 sw_leaderboard_found
= &leaderboard_found
;
3650 sw_leaderboard_downloaded
= &leaderboard_downloaded
;
3653 vg_function_push( (struct vg_cmd
){
3654 .name
= "_map_write",
3655 .function
= console_save_map
3658 vg_function_push( (struct vg_cmd
){
3659 .name
= "_map_load",
3660 .function
= console_load_map
3663 vg_function_push( (struct vg_cmd
){
3665 .function
= console_changelevel
3668 vg_function_push( (struct vg_cmd
){
3670 .function
= console_credits
3673 vg_convar_push( (struct vg_convar
){
3675 .data
= &colour_set_id
,
3676 .data_type
= k_convar_dtype_i32
,
3677 .opt_i32
= { .min
= 0, .max
= 2, .clamp
= 1 },
3681 vg_convar_push( (struct vg_convar
){
3683 .data
= &world_theme_id
,
3684 .data_type
= k_convar_dtype_i32
,
3685 .opt_i32
= { .min
= 0, .max
= vg_list_size( world_themes
)-1, .clamp
= 1 },
3689 // Combined quad, long quad / empty circle / filled circle mesh
3691 float combined_mesh
[6*6 + 32*6*3] = {
3692 0.0f
, 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
,
3693 0.0f
, 0.0f
, 1.0f
, 1.0f
, 1.0f
, 0.0f
,
3695 0.0f
, 0.0f
, 0.0f
, 0.2f
, 1.0f
, 0.2f
,
3696 0.0f
, 0.0f
, 1.0f
, 0.2f
, 1.0f
, 0.0f
,
3698 TRANSFORM_TRI_2D( 0.15f
,0.05f
,0.4f
, 0.0f
, 1.0f
, 1.0f
, 2.0f
, 1.0f
, 0.0f
),
3699 TRANSFORM_TRI_2D( 0.15f
,0.80f
,0.4f
, 0.0f
, 0.0f
, 0.0f
, 2.0f
, 1.0f
, 1.0f
)
3702 float *circle_mesh
= combined_mesh
+ 6*6;
3705 for( int i
= 0; i
< res
; i
++ )
3707 v2f v0
= { sinf( ((float)i
/(float)res
)*VG_TAUf
), cosf( ((float)i
/(float)res
)*VG_TAUf
) };
3708 v2f v1
= { sinf( ((float)(i
+1)/(float)res
)*VG_TAUf
), cosf( ((float)(i
+1)/(float)res
)*VG_TAUf
) };
3710 circle_mesh
[ i
*6+0 ] = 0.0f
;
3711 circle_mesh
[ i
*6+1 ] = 0.0f
;
3713 v2_copy( v0
, circle_mesh
+ 32*6 + i
*12 );
3714 v2_muls( v0
, 0.8f
, circle_mesh
+ 32*6 + i
*12+2 );
3715 v2_copy( v1
, circle_mesh
+ 32*6 + i
*12+4 );
3717 v2_copy( v1
, circle_mesh
+ 32*6 + i
*12+6 );
3718 v2_muls( v1
, 0.8f
, circle_mesh
+ 32*6 + i
*12+8 );
3719 v2_muls( v0
, 0.8f
, circle_mesh
+ 32*6 + i
*12+10 );
3721 v2_copy( v0
, circle_mesh
+ i
*6+4 );
3722 v2_copy( v1
, circle_mesh
+ i
*6+2 );
3723 v2_copy( v0
, circle_mesh
+i
*6+4 );
3724 v2_copy( v1
, circle_mesh
+i
*6+2 );
3727 init_mesh( &world
.shapes
, combined_mesh
, vg_list_size( combined_mesh
) );
3732 int const num_segments
= 64;
3734 struct mesh_wire
*mw
= &world
.wire
;
3736 v2f wire_points
[ num_segments
* 2 ];
3737 u16 wire_indices
[ 6*(num_segments
-1) ];
3739 for( int i
= 0; i
< num_segments
; i
++ )
3741 float l
= (float)i
/ (float)(num_segments
-1);
3743 v2_copy( (v2f
){ l
, -0.5f
}, wire_points
[i
*2+0] );
3744 v2_copy( (v2f
){ l
, 0.5f
}, wire_points
[i
*2+1] );
3746 if( i
< num_segments
-1 )
3748 wire_indices
[ i
*6+0 ] = i
*2 + 0;
3749 wire_indices
[ i
*6+1 ] = i
*2 + 1;
3750 wire_indices
[ i
*6+2 ] = i
*2 + 3;
3751 wire_indices
[ i
*6+3 ] = i
*2 + 0;
3752 wire_indices
[ i
*6+4 ] = i
*2 + 3;
3753 wire_indices
[ i
*6+5 ] = i
*2 + 2;
3757 glGenVertexArrays( 1, &mw
->vao
);
3758 glGenBuffers( 1, &mw
->vbo
);
3759 glGenBuffers( 1, &mw
->ebo
);
3760 glBindVertexArray( mw
->vao
);
3762 glBindBuffer( GL_ARRAY_BUFFER
, mw
->vbo
);
3764 glBufferData( GL_ARRAY_BUFFER
, sizeof( wire_points
), wire_points
, GL_STATIC_DRAW
);
3765 glBindVertexArray( mw
->vao
);
3767 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER
, mw
->ebo
);
3768 glBufferData( GL_ELEMENT_ARRAY_BUFFER
, sizeof( wire_indices
), wire_indices
, GL_STATIC_DRAW
);
3771 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
, 2*sizeof(float), (void*)0 );
3772 glEnableVertexAttribArray( 0 );
3776 mw
->em
= vg_list_size( wire_indices
);
3779 // Create info data texture
3781 glGenTextures( 1, &world
.background_data
);
3782 glBindTexture( GL_TEXTURE_2D
, world
.background_data
);
3783 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGBA
, 64, 64, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, NULL
);
3787 // Create random smaples texture
3789 u8
*data
= malloc(512*512*2);
3790 for( int i
= 0; i
< 512*512*2; i
++ )
3791 data
[ i
] = rand()/(RAND_MAX
/255);
3793 glGenTextures( 1, &world
.random_samples
);
3794 glBindTexture( GL_TEXTURE_2D
, world
.random_samples
);
3795 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RG
, 512, 512, 0, GL_RG
, GL_UNSIGNED_BYTE
, data
);
3802 resource_load_main();
3806 ui_init_context( &world
.st
.world_text
, 15000 );
3809 // Restore gamestate
3810 career_local_data_init();
3816 console_save_map( 0, NULL
);
3819 resource_free_main();
3821 glDeleteTextures( 1, &world
.background_data
);
3822 glDeleteTextures( 1, &world
.random_samples
);
3824 glDeleteVertexArrays( 1, &world
.wire
.vao
);
3825 glDeleteBuffers( 1, &world
.wire
.vbo
);
3826 glDeleteBuffers( 1, &world
.wire
.ebo
);
3828 free_mesh( &world
.shapes
);
3830 ui_context_free( &world
.st
.world_text
);
3835 int main( int argc
, char *argv
[] )
3837 vg_init( argc
, argv
, "Marble Computing" );