demo/snake/snake.c

changeset 274
ba7f043f9fdf
parent 273
966bfca56b9d
equal deleted inserted replaced
273:966bfca56b9d 274:ba7f043f9fdf
70 */ 70 */
71 CxList *trace; 71 CxList *trace;
72 /** 72 /**
73 * The new position of the player when @c reset_position is @c true. 73 * The new position of the player when @c reset_position is @c true.
74 */ 74 */
75 asc_vec2i new_position; 75 asc_vec2u new_position;
76 unsigned health; 76 unsigned health;
77 bool alive;
77 bool reset_position; 78 bool reset_position;
78 uint8_t number; 79 uint8_t number;
79 } Player; 80 } Player;
80 81
81 #define GAME_FIELD_SIZE 32 82 #define GAME_FIELD_SIZE 32
270 asc_texture_bind(TEXTURE_PLAYER_COLOR_MAP, shader->map_color, 1); 271 asc_texture_bind(TEXTURE_PLAYER_COLOR_MAP, shader->map_color, 1);
271 asc_shader_upload_color(shader->color, player->color); 272 asc_shader_upload_color(shader->color, player->color);
272 asc_mesh_draw_triangle_strip(&sprite->mesh); 273 asc_mesh_draw_triangle_strip(&sprite->mesh);
273 } 274 }
274 275
275 static void player_set_health(Player *player, unsigned health) { 276 static void player_position(Player *pl, unsigned x, unsigned y) {
276 player->health = health; 277 pl->new_position.x = x;
277 // TODO: probably we want to add more effects when the health changes 278 pl->new_position.y = y;
278 } 279 pl->reset_position = true;
279 280 }
280 static unsigned player_get_health(Player *player) { 281
281 return player->health; 282 static void player_position_random(Player *pl) {
283 // TODO: check if the spawn location is viable when there is more action on the board
284 player_position(
285 pl,
286 4u + asc_util_rand(GAME_FIELD_SIZE - 8u),
287 4u + asc_util_rand(GAME_FIELD_SIZE - 8u)
288 );
289 }
290
291 static void player_main_behavior(AscBehavior *behavior) {
292 AscSceneNode *node = behavior->node;
293 Player *player = node->user_data;
294
295 if (player->alive && player->health == 0) {
296 player->alive = false;
297 asc_scene_node_hide(node);
298 // TODO: probably we don't want the entire trace to disappear instantly
299 cxListClear(player->trace);
300 // TODO: this should be controlled by another behavior that watches all players
301 game.state = GAME_STATE_GAME_OVER;
302 }
303
304 // TODO: replace with respawn event
305 if (!player->alive && player->health > 0) {
306 player_position_random(player);
307 player->alive = true;
308 asc_scene_node_show(node);
309 }
282 } 310 }
283 311
284 static void player_move(AscBehavior *behavior) { 312 static void player_move(AscBehavior *behavior) {
285 AscSceneNode *node = behavior->node; 313 AscSceneNode *node = behavior->node;
286 Player *player = node->user_data; 314 Player *player = node->user_data;
287
288 // TODO: instead of skipping this behavior, it should be disabled when health is zero
289 if (player_get_health(player) == 0) return;
290
291 // TODO: move this to a different behavior
292 asc_scene_node_show(node);
293 315
294 const float ts = (float) GAME_FIELD_TILE_SIZE; 316 const float ts = (float) GAME_FIELD_TILE_SIZE;
295 317
296 // check if the position is set programmatically 318 // check if the position is set programmatically
297 if (player->reset_position) { 319 if (player->reset_position) {
352 374
353 // die when leaving the game field 375 // die when leaving the game field
354 if (node->position.x < 0 || node->position.y < 0 || 376 if (node->position.x < 0 || node->position.y < 0 ||
355 node->position.x > GAME_FIELD_SIZE*GAME_FIELD_TILE_SIZE || 377 node->position.x > GAME_FIELD_SIZE*GAME_FIELD_TILE_SIZE ||
356 node->position.y > GAME_FIELD_SIZE*GAME_FIELD_TILE_SIZE) { 378 node->position.y > GAME_FIELD_SIZE*GAME_FIELD_TILE_SIZE) {
357 // TODO: add fancy death animation 379 // TODO: replace setting health to zero with a "kill" event
358 asc_scene_node_hide(node); 380 player->health = 0;
359 player_set_health(player, 0);
360 // TODO: remove the trace gradually (dequeuing the trace should be a different behavior)
361 cxListClear(player->trace);
362 game.state = GAME_STATE_GAME_OVER;
363 return; 381 return;
364 } 382 }
365 383
366 // TODO: collision detection 384 // TODO: collision detection
367 385
381 } 399 }
382 } 400 }
383 } 401 }
384 402
385 static void player_controls(AscBehavior *behavior) { 403 static void player_controls(AscBehavior *behavior) {
386 // TODO: instead of checking the game state, disable the behavior when the player is dead
387 if (game.state != GAME_STATE_PLAYING) return;
388
389 Player *player = behavior->node->user_data; 404 Player *player = behavior->node->user_data;
390 // TODO: different key bindings for different player number in hot seat mode 405 // TODO: different key bindings for different player number in hot seat mode
391 if (asc_key_pressed(ASC_KEY(LEFT))) { 406 if (asc_key_pressed(ASC_KEY(LEFT))) {
392 if (player->direction != MOVE_RIGHT) { 407 if (player->direction != MOVE_RIGHT) {
393 player->target_direction = MOVE_LEFT; 408 player->target_direction = MOVE_LEFT;
406 if (asc_key_pressed(ASC_KEY(DOWN))) { 421 if (asc_key_pressed(ASC_KEY(DOWN))) {
407 if (player->direction != MOVE_UP) { 422 if (player->direction != MOVE_UP) {
408 player->target_direction = MOVE_DOWN; 423 player->target_direction = MOVE_DOWN;
409 } 424 }
410 } 425 }
411 }
412
413 static void player_position(Player *pl, int x, int y) {
414 pl->new_position.x = x;
415 pl->new_position.y = y;
416 pl->reset_position = true;
417 }
418
419 static void player_position_random(Player *pl) {
420 // TODO: check if the spawn location is viable when there is more action on the board
421 player_position(
422 pl,
423 4 + asc_util_rand(GAME_FIELD_SIZE - 8),
424 4 + asc_util_rand(GAME_FIELD_SIZE - 8)
425 );
426 } 426 }
427 427
428 static void player_destroy(CxAllocator *allocator, Player *player) { 428 static void player_destroy(CxAllocator *allocator, Player *player) {
429 cxListFree(player->trace); 429 cxListFree(player->trace);
430 cxFree(allocator, player); 430 cxFree(allocator, player);
442 .origin_x = GAME_FIELD_TILE_SIZE / 2, 442 .origin_x = GAME_FIELD_TILE_SIZE / 2,
443 .origin_y = GAME_FIELD_TILE_SIZE / 2, 443 .origin_y = GAME_FIELD_TILE_SIZE / 2,
444 ); 444 );
445 asc_scene_add_node(MAIN_SCENE, node); 445 asc_scene_add_node(MAIN_SCENE, node);
446 Player *player = asc_scene_node_allocate_data(node, sizeof(Player)); 446 Player *player = asc_scene_node_allocate_data(node, sizeof(Player));
447 player_position_random(player);
448 player->speed = 3.f; // start with 3 tiles/sec 447 player->speed = 3.f; // start with 3 tiles/sec
449 player->number = 1; 448 player->number = 1;
450 player->health = 100; 449 player->health = 100;
451 player->color = ASC_RGB(1, 0, 0); 450 player->color = ASC_RGB(1, 0, 0);
452 player->trace = cxLinkedListCreateSimple(sizeof(asc_vec2u)); 451 player->trace = cxLinkedListCreateSimple(sizeof(asc_vec2u));
453 cxDefineDestructor(player->trace, player_trace_release_tile); 452 cxDefineDestructor(player->trace, player_trace_release_tile);
454 node->draw_func = player_draw; 453 node->draw_func = player_draw;
455 node->user_data_free_func = (cx_destructor_func2)player_destroy; 454 node->user_data_free_func = (cx_destructor_func2)player_destroy;
456 // add behaviors (the order is important!) 455
456 // add behaviors
457 asc_behavior_add(node, player_main_behavior, .always_enabled = true);
457 asc_behavior_add(node, player_controls); 458 asc_behavior_add(node, player_controls);
458 asc_behavior_add(node, player_move); 459 asc_behavior_add(node, player_move);
460 asc_behavior_pause_all_while_hidden(node);
461
459 return player; 462 return player;
460 } 463 }
461 464
462 static asc_rect main_scene_viewport_update(asc_vec2u window_size) { 465 static asc_rect main_scene_viewport_update(asc_vec2u window_size) {
463 466
578 // game states 581 // game states
579 // TODO: move this into a behavior - probably add a state machine that enables/disables behaviors 582 // TODO: move this into a behavior - probably add a state machine that enables/disables behaviors
580 if (game.state == GAME_STATE_GAME_OVER) { 583 if (game.state == GAME_STATE_GAME_OVER) {
581 asc_scene_node_show(text_game_over); 584 asc_scene_node_show(text_game_over);
582 if (asc_key_pressed(ASC_KEY(R))) { 585 if (asc_key_pressed(ASC_KEY(R))) {
586 // TODO: instead of setting the health, send a "respawn" event to the behavior
587 game.players[0]->health = 100;
583 // TODO: re-load the "level" 588 // TODO: re-load the "level"
584 player_position_random(game.players[0]);
585 player_set_health(game.players[0], 100);
586 game.state = GAME_STATE_PLAYING; 589 game.state = GAME_STATE_PLAYING;
587 asc_scene_node_hide(text_game_over); 590 asc_scene_node_hide(text_game_over);
588 } 591 }
589 } 592 }
590 593

mercurial