1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
6 SHADER_DEFINE( colour_shader
,
9 "layout (location=0) in vec3 a_co;"
15 " vec4 vert_pos = uPv * uMdl * vec4( a_co, 1.0 );"
16 " gl_Position = vert_pos;"
21 "uniform vec4 uColour;"
25 " FragColor = uColour;"
28 UNIFORMS({ "uPv", "uMdl", "uColour" })
35 int main( int argc
, char *argv
[] )
37 vg_init( argc
, argv
, "FishLadder" );
40 #define CELL_FLAG_INPUT 0x1
41 #define CELL_FLAG_OUTPUT 0x2
42 #define CELL_FLAG_IO (CELL_FLAG_INPUT|CELL_FLAG_OUTPUT)
43 #define CELL_FLAG_WALL 0x4
44 #define CELL_FLAG_HOVER 0x8
45 #define CELL_FLAG_ITER 0x10
46 #define CELL_FLAG_CANAL 0x20
47 #define CELL_FLAG_SPLIT 0x40 /* Does this cell split and have an incoming vertical connection? */
48 #define CELL_FLAG_WALKABLE (CELL_FLAG_IO|CELL_FLAG_CANAL)
49 #define CELL_FLAG_VISITED 0x80
50 #define CELL_FLAG_UPLVL 0x100
51 #define CELL_FLAG_MERGE 0x200
80 struct cell
*selected
;
103 static void map_free(void)
105 for( int i
= 0; i
< arrlen( map
.io
); i
++ )
107 arrfree( map
.cells
[ map
.io
[i
] ].conditions
);
110 arrfree( map
.cells
);
118 static struct cell
*map_tile_at( int pos
[2] )
120 if( pos
[0] >= 0 && pos
[0] < map
.x
&& pos
[1] >= 0 && pos
[1] < map
.y
)
121 return map
.cells
+ pos
[1]*map
.x
+ pos
[0];
125 static struct cell
*map_tile_at_cond( int pos
[2], u32 flags
)
127 struct cell
*cell
= map_tile_at( pos
);
128 if( cell
&& (cell
->flags
& flags
) )
134 static void map_tile_coords_from_index( int i
, int coords
[2] )
136 coords
[0] = i
% map
.x
;
137 coords
[1] = (i
- coords
[0])/map
.x
;
140 static void map_stack_refresh(void)
142 for( int i
= 0; i
< map
.x
*map
.y
; i
++ )
143 map
.cells
[i
].flags
&= ~CELL_FLAG_VISITED
;
146 static void map_stack_init( int coords
[2] )
149 map
.stack
.frames
[0].i
= 0;
150 map
.stack
.frames
[0].x
= coords
[0];
151 map
.stack
.frames
[0].y
= coords
[1];
154 static struct cell
*map_stack_next(void)
156 struct cell
*tile
= NULL
;
160 struct vframe
*frame
= &map
.stack
.frames
[ map
.stack
.level
];
162 int output_dirs
[][2] = { {0,-1}, {-1,0}, {1,0} };
166 int *dir
= output_dirs
[ frame
->i
];
167 tile
= map_tile_at( (int[2]){frame
->x
+dir
[0], frame
->y
+dir
[1]} );
170 if( tile
&& !(tile
->flags
& CELL_FLAG_VISITED
) )
174 frame
[1].x
= frame
[0].x
+dir
[0];
175 frame
[1].y
= frame
[0].y
+dir
[1];
185 if( map
.stack
.level
< 0 )
193 static int map_load( const char *str
)
202 if( str
[map
.x
] == ';' )
204 else if( !str
[map
.x
] )
206 vg_error( "Unexpected EOF when parsing level!\n" );
211 struct cell
*row
= arraddnptr( map
.cells
, map
.x
);
213 int reg_start
= 0, reg_end
= 0;
229 if( reg_start
< reg_end
)
231 if( *c
>= 'a' && *c
<= 'z' )
233 arrpush( map
.cells
[ map
.io
[ reg_start
] ].conditions
, *c
);
237 if( *c
== ',' || *c
== '\n' )
246 vg_error( "Unkown attrib '%c' (row: %u)\n", *c
, map
.y
);
253 vg_error( "Over-assigned values (row: %u)\n", map
.y
);
261 if( reg_start
!= reg_end
)
263 vg_error( "Not enough values assigned (row: %u, %u of %u)\n", map
.y
, reg_start
, reg_end
);
269 vg_error( "Map row underflow (row: %u, %u<%u)\n", map
.y
, cx
, map
.x
);
273 row
= arraddnptr( map
.cells
, map
.x
);
276 reg_end
= reg_start
= arrlen( map
.io
);
282 vg_error( "Map row overflow (row: %u, %u>%u)\n", map
.y
, cx
, map
.x
);
286 row
[ cx
].conditions
= NULL
;
288 // Parse the various cell types
289 if( *c
== '+' || *c
== '-' )
291 arrpush( map
.io
, cx
+ map
.y
*map
.x
);
292 row
[ cx
++ ].flags
= *c
== '+'? CELL_FLAG_INPUT
: CELL_FLAG_OUTPUT
;
297 row
[ cx
++ ].flags
= CELL_FLAG_WALL
;
301 row
[ cx
++ ].flags
= 0x00;
308 // Origin top left corner
309 map
.origin
[0] = -((float)map
.x
) * 0.5f
;
310 map
.origin
[2] = -((float)map
.y
) * 0.5f
;
312 vg_success( "Map loaded! (%u:%u)\n", map
.x
, map
.y
);
316 static int map_tile_availible( int co
[2] )
318 // Extract 5x5 grid surrounding tile
320 for( int y
= vg_max( co
[1]-2, 0 ); y
< vg_min( map
.y
, co
[1]+3 ); y
++ )
321 for( int x
= vg_max( co
[0]-2, 0 ); x
< vg_min( map
.x
, co
[0]+3 ); x
++ )
323 struct cell
*cell
= map_tile_at( (int[2]){ x
, y
} );
325 if( cell
&& (cell
->flags
& CELL_FLAG_WALKABLE
) )
326 blob
|= 0x1 << ((y
-(co
[1]-2))*5 + x
-(co
[0]-2));
329 // Run filter over center 3x3 grid to check for invalid configurations
330 int kernel
[] = { 0, 1, 2, 5, 6, 7, 10, 11, 12 };
331 for( int i
= 0; i
< vg_list_size(kernel
); i
++ )
333 if( blob
& (0x1 << (6+kernel
[i
])) )
335 // (reference window: 0x1CE7) Illegal moves
344 u32 invalid
[] = { 0x8E2, 0x63, 0xC6, 0xC60, 0x18C0, 0x862, 0x8C2 };
345 u32 window
= blob
>> kernel
[i
];
347 for( int j
= 0; j
< vg_list_size(invalid
); j
++ )
348 if((window
& invalid
[j
]) == invalid
[j
])
359 float ratio
= (float)vg_window_y
/ (float)vg_window_x
;
360 float const size
= 7.5f
;
361 glm_ortho( -size
, size
, -size
*ratio
, size
*ratio
, 0.1f
, 100.f
, m_projection
);
363 glm_mat4_identity( m_view
);
364 glm_translate_z( m_view
, -10.f
);
365 glm_rotate_x( m_view
, 1.0f
, m_view
);
367 glm_mat4_mul( m_projection
, m_view
, vg_pv
);
369 // Compute map update
370 for( int y
= 0; y
< map
.y
; y
++ )
372 for( int x
= 0; x
< map
.x
; x
++ )
374 struct cell
*tile
, *upper
, *lower
, *l
, *r
;
375 tile
= map_tile_at( (int [2]){ x
, y
} );
376 tile
->flags
&= ~(CELL_FLAG_SPLIT
|CELL_FLAG_MERGE
|CELL_FLAG_UPLVL
);
378 if( tile
->flags
& CELL_FLAG_WALKABLE
)
380 r
= map_tile_at_cond( (int[2]){ x
+1, y
}, CELL_FLAG_WALKABLE
);
381 l
= map_tile_at_cond( (int[2]){ x
-1, y
}, CELL_FLAG_WALKABLE
);
385 upper
= map_tile_at_cond( (int[2]){ x
, y
-1 }, CELL_FLAG_WALKABLE
);
386 lower
= map_tile_at_cond( (int[2]){ x
, y
+1 }, CELL_FLAG_WALKABLE
);
390 tile
->flags
|= CELL_FLAG_MERGE
| CELL_FLAG_UPLVL
;
395 tile
->flags
|= CELL_FLAG_SPLIT
;
396 l
->flags
|= CELL_FLAG_UPLVL
;
397 r
->flags
|= CELL_FLAG_UPLVL
;
404 // Compute classification
408 for( int i = 0; i < arrlen( map.io ); i ++ )
410 struct *cell cell = &map.cells[ map.io ];
413 if( cell->flags & CELL_FLAG_INPUT )
415 map_tile_coords_from_index( map.io, coords );
416 map_stack_init( coords );
420 if( cell->flags & CELL_FLAG_CONNECTOR )
425 while( (cell = map_stack_next()) );
434 vec4 vp
= { 0.f
, 0.f
, vg_window_x
, vg_window_y
};
435 glm_mat4_inv( vg_pv
, pv_inverse
);
436 glm_unprojecti( (vec3
){ vg_mouse_x
, vg_window_y
-vg_mouse_y
, -1.f
}, pv_inverse
, vp
, ray_dir
);
437 glm_unprojecti( (vec3
){ vg_mouse_x
, vg_window_y
-vg_mouse_y
, 0.f
}, pv_inverse
, vp
, ray_origin
);
438 glm_vec3_sub( ray_dir
, ray_origin
, ray_dir
);
440 // Get floor tile intersection
441 float ray_t
= -ray_origin
[1] / ray_dir
[1];
444 glm_vec3_copy( ray_origin
, tile_pos
);
445 glm_vec3_muladds( ray_dir
, ray_t
, tile_pos
);
446 glm_vec3_sub( tile_pos
, map
.origin
, tile_pos
);
448 int tile_x
= floorf( tile_pos
[0] );
449 int tile_y
= floorf( tile_pos
[2] );
451 map
.selected
= map_tile_at( (int [2]){tile_x
, tile_y
} );
455 static int fish_counter
= 0;
458 if( fish_counter
> 20 )
462 // Advance characters
463 for( int i
= 0; i
< map
.num_fishes
; i
++ )
465 struct fish
*fish
= map
.fishes
+ i
;
470 struct cell
*tile
, *next
;
471 tile
= map_tile_at( fish
->co
);
473 if( tile
->flags
& CELL_FLAG_OUTPUT
)
475 vg_info( "Fish got zucced (%d)\n", i
);
481 if( tile
->flags
& CELL_FLAG_SPLIT
)
484 int new_dir
[][2] = { {0,-1},{1,0},{-1,0} };
487 for( int j
= 0; j
< 3; j
++ )
489 test_dir
= new_dir
[ tile
->state
];
490 tile
->state
= (tile
->state
+1)%3;
492 next
= map_tile_at( (int[2]){ fish
->co
[0]+test_dir
[0], fish
->co
[1]+test_dir
[1] } );
493 if( next
&& (next
->flags
& (CELL_FLAG_WALKABLE
)) )
495 fish
->dir
[0] = test_dir
[0];
496 fish
->dir
[1] = test_dir
[1];
503 next
= map_tile_at( (int[2]){ fish
->co
[0]+fish
->dir
[0], fish
->co
[1]+fish
->dir
[1] } );
504 if( !next
|| (next
&& !(next
->flags
& CELL_FLAG_WALKABLE
)) )
512 vg_info( "Fish died! (%d)\n", i
);
518 fish
->co
[0] += fish
->dir
[0];
519 fish
->co
[1] += fish
->dir
[1];
523 for( int i
= 0; i
< arrlen( map
.io
); i
++ )
525 struct cell
*input
= &map
.cells
[ map
.io
[i
] ];
527 if( input
->flags
& CELL_FLAG_INPUT
)
529 if( input
->state
< arrlen( input
->conditions
) )
531 struct fish
*fish
= &map
.fishes
[ map
.num_fishes
];
532 map_tile_coords_from_index( map
.io
[i
], fish
->co
);
534 int output_dirs
[][2] = { {0,-1}, {-1,0}, {1,0} };
537 for( int i
= 0; i
< vg_list_size( output_dirs
); i
++ )
539 int *dir
= output_dirs
[i
];
540 struct cell
*next
= map_tile_at( (int[2]){ fish
->co
[0]+dir
[0], fish
->co
[1]+dir
[1] } );
541 if( next
&& next
->flags
& CELL_FLAG_CANAL
)
543 fish
->dir
[0] = dir
[0];
544 fish
->dir
[1] = dir
[1];
559 vg_info( "There are now %u active fish\n", map
.num_fishes
);
562 if( vg_get_button_down( "go" ) )
567 vg_info( "Ending!\n" );
572 if( vg_get_button_down( "go" ) )
577 for( int i
= 0; i
< map
.x
*map
.y
; i
++ )
578 map
.cells
[ i
].state
= 0;
580 vg_info( "Starting!\n" );
585 map
.select_valid
= map_tile_availible( (int[2]){ tile_x
, tile_y
} );
587 if( map
.select_valid
)
589 if( vg_get_button_down("primary") )
591 if( map
.selected
->flags
& CELL_FLAG_CANAL
)
593 map
.selected
->flags
&= ~(CELL_FLAG_CANAL
);
597 map
.selected
->flags
|= CELL_FLAG_CANAL
;
610 glViewport( 0,0, vg_window_x
, vg_window_y
);
612 glEnable( GL_DEPTH_TEST
);
613 glClearColor( 0.94f
, 0.94f
, 0.94f
, 1.0f
);
614 glClear( GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
616 glBindVertexArray( tile_vao
);
618 SHADER_USE( colour_shader
);
619 glUniformMatrix4fv( SHADER_UNIFORM( colour_shader
, "uPv" ), 1, GL_FALSE
, (float *)vg_pv
);
621 for( int y
= 0; y
< map
.y
; y
++ )
623 for( int x
= 0; x
< map
.x
; x
++ )
625 glm_mat4_identity( m_mdl
);
626 glm_translate( m_mdl
,
628 map
.origin
[0] + (float)x
+ 0.5f
,
630 map
.origin
[2] + (float)y
+ 0.5f
633 glUniformMatrix4fv( SHADER_UNIFORM( colour_shader
, "uMdl" ), 1, GL_FALSE
, (float *)m_mdl
);
635 struct cell
*cell
= &map
.cells
[ y
*map
.x
+x
];
637 vec4 colour
= { 0.7f
, 0.7f
, 0.7f
, 1.f
};
639 if( cell
->flags
& CELL_FLAG_INPUT
) glm_vec3_copy( (vec3
){ 0.9f
,0.5f
,0.5f
}, colour
);
640 else if( cell
->flags
& CELL_FLAG_OUTPUT
) glm_vec3_copy( (vec3
){ 0.5f
,0.9f
,0.5f
}, colour
);
641 else if( cell
->flags
& CELL_FLAG_WALL
) glm_vec3_copy( (vec3
){ 0.1f
,0.1f
,0.1f
}, colour
);
642 else if( cell
->flags
& CELL_FLAG_CANAL
) glm_vec3_copy( (vec3
){ 0.5f
,0.5f
,0.8f
}, colour
);
644 if( cell
->flags
& CELL_FLAG_SPLIT
)
645 glm_vec3_copy( (vec3
){ 0.6f
, 0.f
, 0.9f
}, colour
);
646 else if( cell
->flags
& CELL_FLAG_MERGE
)
647 glm_vec3_copy( (vec3
){ 0.f
, 0.6f
, 0.8f
}, colour
);
649 if( map
.selected
== cell
)
651 if( !map
.select_valid
)
652 glm_vec3_copy( (vec3
){ 1.f
, 0.f
, 0.f
}, colour
);
654 float flash
= sinf( vg_time
*2.5f
) * 0.25f
+ 0.75f
;
655 glm_vec3_scale( colour
, flash
, colour
);
658 glUniform4fv( SHADER_UNIFORM( colour_shader
, "uColour" ), 1, colour
);
659 glDrawArrays( GL_TRIANGLES
, 0, 6 );
663 glUniform4f( SHADER_UNIFORM( colour_shader
, "uColour" ), 1.f
, 0.f
, 1.f
, 1.f
);
665 for( int i
= 0; i
< map
.num_fishes
; i
++ )
667 struct fish
*fish
= map
.fishes
+ i
;
671 glm_mat4_identity( m_mdl
);
672 glm_translate( m_mdl
,
674 map
.origin
[0] + (float)fish
->co
[0] + 0.5f
,
676 map
.origin
[2] + (float)fish
->co
[1] + 0.5f
679 glm_scale_uni( m_mdl
, 0.2f
);
680 glUniformMatrix4fv( SHADER_UNIFORM( colour_shader
, "uMdl" ), 1, GL_FALSE
, (float *)m_mdl
);
681 glDrawArrays( GL_TRIANGLES
, 0, 6 );
688 SHADER_INIT( colour_shader
);
690 glGenVertexArrays( 1, &tile_vao
);
691 glGenBuffers( 1, &tile_vbo
);
703 glBindVertexArray( tile_vao
);
704 glBindBuffer( GL_ARRAY_BUFFER
, tile_vbo
);
713 glVertexAttribPointer( 0, 3, GL_FLOAT
, GL_FALSE
, 3 * sizeof(float), (void*)0 );
714 glEnableVertexAttribArray( 0 );