diff --git a/Makefile b/Makefile index 051f860..0a72467 100644 --- a/Makefile +++ b/Makefile @@ -35,8 +35,8 @@ clean: rm -f $(OBJ_GAME) $(OBJ_SERVER) fclean: clean - rm -f $(NAME) $(NAME_SERVER) + rm -f $(NAME) $(NAME_SERVER) $(NAME_CALIBRATOR) re: fclean all -.PHONY: all clean fclean re \ No newline at end of file +.PHONY: all clean fclean re diff --git a/include/constants.h b/include/constants.h index 906b66c..9434e9e 100644 --- a/include/constants.h +++ b/include/constants.h @@ -50,10 +50,15 @@ #define BSP_MIN_SIZE 5 #define BSP_MAX_DEPTH 4 #define BSP_ROOM_MARGIN 1 + #define BSP_ROOM_MIN_SIZE 6 + #define BSP_ROOM_RECT_RATE 30 + #define BSP_ROOM_DIAMOND_RATE 25 + #define BSP_ROOM_CROSS_RATE 20 + #define BSP_ROOM_BLOB_RATE 25 #define MAP_AREA 576 #define BFS_QUEUE_SIZE 576 #define MONSTER_SPEED 2.2f - #define MONSTER_AGGRO_RANGE 14.0f + #define MONSTER_AGGRO_RANGE 5.0f #define MONSTER_WAYPOINT_REACH 0.20f #define MONSTER_DT_MAX 0.1f #define MONSTER_INTERCEPT 0.6f @@ -91,15 +96,17 @@ #define JOYSTICK_ID 0 #define JOYSTICK_DEADZONE 18.0f #define JOYSTICK_LOOK_SPEED 2.6f - #define JOY_BTN_A 0 - #define JOY_BTN_B 1 - #define JOY_BTN_X 2 - #define JOY_BTN_Y 3 - #define JOY_BTN_LB 4 - #define JOY_BTN_RB 5 - #define JOY_BTN_BACK 6 - #define JOY_BTN_START 7 - #define JOY_BTN_LSTICK 8 + #define JOY_BTN_EQUIP 0 + #define JOY_BTN_HEAL 1 + #define JOY_BTN_INVENTORY 2 + #define JOY_BTN_RELOAD 3 + #define JOY_BTN_SLOT_PREV 4 + #define JOY_BTN_SLOT_NEXT 5 + #define JOY_BTN_FLASHLIGHT 6 + #define JOY_BTN_SHOOT 7 + #define JOY_BTN_OPTIONS 9 + #define JOY_BTN_LSTICK 11 + #define JOY_TRIGGER_THRESHOLD 0.45f #define WHITE (sfColor){180, 180, 180, 255} #define BLUE (sfColor){80, 170, 255, 255} diff --git a/include/init.h b/include/init.h index 681653b..fee453a 100644 --- a/include/init.h +++ b/include/init.h @@ -10,6 +10,13 @@ #include "types.h" +typedef struct weapon_config_s { + wp_type_t type; + int damage; + int max_ammo; + entity_t *entity; +} weapon_config_t; + wolf_t *init_wolf(void); void init_menu_text(wolf_t *wolf, window_t *window); game_t *init_game(window_t *window_data); @@ -18,8 +25,17 @@ void destroy_player(player_t *player); void init_menu_entities(wolf_t *wolf, window_t *window); void free_wolf(wolf_t *wolf); void free_wolf_resources(wolf_t *wolf); +void free_game(game_t *game); +void free_player_list(list_t **list); +void free_draw_list(wolf_t *wolf, int state, int index); +void free_menu_lists(wolf_t *wolf); +void free_pause_lists(wolf_t *wolf); +void free_settings_lists(wolf_t *wolf); +void free_game_entities(wolf_t *wolf); void init_hud(hud_t *hud, sfFont *font); +void free_hud(hud_t *hud); window_t *create_inv(data_t *data, window_t *win, inv_t *inv); +void free_inv(inv_t *inv); weapon_t *init_weapon(wp_type_t type, int damage, int max_ammo, entity_t *entity); sfRectangleShape *create_rect(sfVector2f *size, diff --git a/include/player.h b/include/player.h index 8851c22..6df824b 100644 --- a/include/player.h +++ b/include/player.h @@ -18,13 +18,24 @@ void move_player(wolf_t *wolf, player_t *player, sfEvent event, game_t *game); int check_collision(wall_t *wall, float x, float y); float joystick_axis(sfJoystickAxis axis); -sfBool joystick_action_down(control_action_t action); -sfBool joystick_action_event(control_action_t action, sfEvent event); +sfBool joystick_action_down(settings_game_t *settings, + control_action_t action); +sfBool joystick_action_event(settings_game_t *settings, + control_action_t action, sfEvent event); sfBool joystick_shoot_down(void); sfBool joystick_menu_up(sfEvent event); sfBool joystick_menu_down(sfEvent event); -sfBool joystick_menu_confirm(sfEvent event); +sfBool joystick_menu_left(sfEvent event); +sfBool joystick_menu_right(sfEvent event); +sfBool joystick_menu_confirm(settings_game_t *settings, sfEvent event); +int joystick_bound_button(settings_game_t *settings, control_action_t action, + int fallback); +sfBool joystick_slot_previous(settings_game_t *settings, sfEvent event); +sfBool joystick_slot_next(settings_game_t *settings, sfEvent event); void open_inventory(wolf_t *wolf, sfEvent event, inv_t *inv); +void use_inventory_item(player_t *player, slot_t *slot); +void move_inventory_slot(wolf_t *wolf, sfEvent event, inv_t *inv); +void use_moved_inventory_slot(wolf_t *wolf, sfEvent event, inv_t *inv); sfBool iskeypressed(int key, sfEvent event); sfBool isactiondown(settings_game_t *settings, control_action_t action); sfBool isactionpressed(settings_game_t *settings, control_action_t action, diff --git a/include/settings.h b/include/settings.h index 012b2e1..c9b0199 100644 --- a/include/settings.h +++ b/include/settings.h @@ -12,6 +12,9 @@ void settings(wolf_t *wolf); void manage_settings(wolf_t *wolf, sfEvent event); +sfBool click_controls_value(wolf_t *wolf, sfEvent event); +sfBool handle_rebind_key(wolf_t *wolf, sfEvent event); +sfBool handle_rebind_joystick(wolf_t *wolf, sfEvent event); sfBool click_settings_triangle(wolf_t *wolf, sfEvent event); void hover_settings_triangle(wolf_t *wolf, sfEvent event); void apply_triangle_action(wolf_t *wolf, text_t *text, int direction); @@ -20,9 +23,12 @@ void init_settings_buttons(wolf_t *wolf, window_t *window); void init_audio(wolf_t *wolf, window_t *window); void init_gameplay(wolf_t *wolf, window_t *window); void init_controls(wolf_t *wolf, window_t *window); +void init_controller_controls(wolf_t *wolf, window_t *window); void sync_controls_values(wolf_t *wolf, settings_game_t *settings); +void sync_controller_values(wolf_t *wolf, settings_game_t *settings); const char *key_to_string(sfKeyCode key); int get_control_action_by_name(const char *name); +int get_controller_action_by_name(const char *name); sfBool is_pause_key(settings_game_t *settings, sfEvent event); settings_game_t *init_settings_params(void); void init_graphics_params(settings_game_t *settings, wolf_t *wolf); diff --git a/include/stage.h b/include/stage.h index 3091904..1813621 100644 --- a/include/stage.h +++ b/include/stage.h @@ -36,11 +36,17 @@ void notify_player_dead(wolf_t *wolf); void stage(wolf_t *wolf, player_t *player, sfEvent event); void card_action(wolf_t *wolf, player_t *player, sfEvent event); void handle_card_input(wolf_t *wolf, intermission_t *inter, sfEvent event); +int card_under_cursor(wolf_t *wolf, intermission_t *inter); +int hovered_card(intermission_t *inter); +void move_card_cursor(wolf_t *wolf); bsp_t *bsp_new(int x, int y, int w, int h); void bsp_free(bsp_t *node); void bsp_split(bsp_t *node, int depth); void carve_h(int map[MAP_HEIGHT][MAP_WIDTH], int x1, int x2, int y); void carve_v(int map[MAP_HEIGHT][MAP_WIDTH], int y1, int y2, int x); +int bsp_room_shape_from_roll(int roll); +void bsp_carve_room_shape(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH], + int shape); void bsp_place_rooms(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH]); sfVector2i bsp_center(bsp_t *node); diff --git a/include/stage_card.h b/include/stage_card.h new file mode 100644 index 0000000..f294bac --- /dev/null +++ b/include/stage_card.h @@ -0,0 +1,16 @@ +/* +** EPITECH PROJECT, 2026 +** stage +** File description: +** card helpers +*/ + +#ifndef STAGE_CARD_H + #define STAGE_CARD_H + + #include "wolf3d.h" + +void card_setup_text(card_t *card, const sfFloatRect *txt); +void card_rise_single(window_t *win, card_t *card, float step); + +#endif diff --git a/include/types.h b/include/types.h index 927ced8..9590f86 100644 --- a/include/types.h +++ b/include/types.h @@ -181,6 +181,7 @@ typedef struct intermission_s { sfClock *clock; sfBool done; sfBool selected; + sfBool confirm_down; int round; /* 0 = first set, 1 = second set */ card_t card[3]; } intermission_t; @@ -236,6 +237,7 @@ typedef enum { AUDIO, GAMEPLAY, CONTROLS, + CONTROLLER, ACCESSIBILITY, } settings_t; @@ -282,6 +284,7 @@ typedef struct settings_game_s { sfBool show_minimap; sfBool crosshair; sfKeyCode key_bindings[CTRL_COUNT]; + int joystick_bindings[CTRL_COUNT]; } settings_game_t; typedef enum { diff --git a/include/wolf3d.h b/include/wolf3d.h index 0d57927..14ca51f 100644 --- a/include/wolf3d.h +++ b/include/wolf3d.h @@ -21,6 +21,7 @@ #include "utils.h" #include "stage.h" #include "save.h" + #include "stage_card.h" int program(sfRenderWindow *window, sfEvent event, wolf_t *wolf); void network_update(wolf_t *wolf, sfClock *send_clock); int network_is_host(wolf_t *wolf); diff --git a/src/check_event.c b/src/check_event.c index 77ca5a2..6d72b7b 100644 --- a/src/check_event.c +++ b/src/check_event.c @@ -10,7 +10,7 @@ static void check_game_event(sfEvent event, wolf_t *wolf) { if (is_pause_key(wolf->settings, event) || - joystick_action_event(CTRL_PAUSE, event)) { + joystick_action_event(wolf->settings, CTRL_PAUSE, event)) { wolf->menu_state = 0; wolf->state = PAUSE; } @@ -31,7 +31,7 @@ static void close_inventory(wolf_t *wolf, inv_t *inv) static void handle_escape(sfEvent event, wolf_t *wolf) { if (!is_pause_key(wolf->settings, event) && - !joystick_action_event(CTRL_PAUSE, event)) + !joystick_action_event(wolf->settings, CTRL_PAUSE, event)) return; if (wolf->state == NEWGAME_MENU) { wolf->state = MENU; @@ -57,7 +57,7 @@ static void handle_menu_state_event(wolf_t *wolf, sfEvent event) static void handle_game_state_event(wolf_t *wolf, sfEvent event) { sfBool pause = is_pause_key(wolf->settings, event) || - joystick_action_event(CTRL_PAUSE, event); + joystick_action_event(wolf->settings, CTRL_PAUSE, event); if (wolf->state == GAME) { if (wolf->game->inv.open && pause) diff --git a/src/init/create_text.c b/src/init/create_text.c index aa2d2ba..7a65e6d 100644 --- a/src/init/create_text.c +++ b/src/init/create_text.c @@ -29,12 +29,16 @@ static void setup_text_data(text_t *text, text_t *data) text_t *create_text(text_t *data, sfFont *font, sfVector2f *pos, sfVector2f *scale) { - text_t *text = malloc(sizeof(text_t)); + text_t *text = calloc(1, sizeof(text_t)); sfFloatRect txt; if (!text) return NULL; text->text = sfText_create(); + if (!text->text) { + free(text); + return NULL; + } setup_text_data(text, data); sfText_setFont(text->text, font); sfText_setString(text->text, data->content); diff --git a/src/init/free_alloc.c b/src/init/free_alloc.c index 4c74122..0896a95 100644 --- a/src/init/free_alloc.c +++ b/src/init/free_alloc.c @@ -1,130 +1,31 @@ /* ** EPITECH PROJECT, 2026 -** menu +** wolf3d ** File description: -** menu +** cleanup entry point */ -#include "../include/wolf3d.h" - -static void free_game(game_t *game) -{ - for (int i = 0; i < 2; i++) - sfClock_destroy(game->clock[i]); - free(game->wall->pixel); - sfSprite_destroy(game->wall->sprite); - sfTexture_destroy(game->wall->texture); - free(game->wall); - free(game->zbuffer); - free(game); -} - -static void free_rectangles(wolf_t *wolf, int state) -{ - rect_t *rect = NULL; - list_t *next = NULL; - - for (list_t *curr = wolf->list[state][RECT]; curr; curr = next) { - next = curr->next; - rect = (rect_t *)curr->data; - sfRectangleShape_destroy(rect->rect); - if (rect->texture) - sfTexture_destroy(rect->texture); - if (rect->click_texture) - sfTexture_destroy(rect->click_texture); - free(rect); - free(curr); - } -} - -static void free_triangle(wolf_t *wolf, int state) -{ - triangle_t *triangle = NULL; - list_t *next = NULL; - - for (list_t *curr = wolf->list[state][TRIANGLE]; curr; curr = next) { - next = curr->next; - triangle = (triangle_t *)curr->data; - sfConvexShape_destroy(triangle->shape); - free(triangle); - free(curr); - } -} - -static void free_texts(wolf_t *wolf, int state) -{ - text_t *text = NULL; - list_t *next = NULL; - - for (list_t *curr = wolf->list[state][TEXT]; curr; curr = next) { - next = curr->next; - text = (text_t *)curr->data; - sfText_destroy(text->text); - free(text); - free(curr); - } -} - -static void free_entities(wolf_t *wolf, int state) -{ - entity_t *entity = NULL; - list_t *next = NULL; - - for (list_t *curr = wolf->list[state][SPRITE]; curr; curr = next) { - next = curr->next; - entity = (entity_t *)curr->data; - sfSprite_destroy(entity->sprite); - sfTexture_destroy(entity->texture); - free(entity); - free(curr); - } -} - -static void free_line(wolf_t *wolf, int state) -{ - rect_t *line = NULL; - list_t *next = NULL; - - for (list_t *curr = wolf->list[state][LINE]; curr; curr = next) { - next = curr->next; - line = (rect_t *)curr->data; - sfRectangleShape_destroy(line->rect); - free(line); - free(curr); - } -} - -static void free_settings(wolf_t *wolf) -{ - free_rectangles(wolf, SETTINGS); - free_texts(wolf, SETTINGS); - free_entities(wolf, SETTINGS); - free_line(wolf, SETTINGS); - free_triangle(wolf, SETTINGS); -} - -static void free_list(wolf_t *wolf) -{ - free_texts(wolf, MENU); - free_entities(wolf, MENU); - free_texts(wolf, GAME_OVER); - free_rectangles(wolf, GAME_OVER); -} +#include "../../include/wolf3d.h" void free_wolf(wolf_t *wolf) { + if (!wolf) + return; if (wolf->connected) client_close(&wolf->net); free_wolf_resources(wolf); - destroy_player(wolf->player); + if (wolf->player) + destroy_player(wolf->player); free_game(wolf->game); - free_list(wolf); - free_rectangles(wolf, PAUSE); - free_texts(wolf, PAUSE); - free_entities(wolf, PAUSE); - free_settings(wolf); - free(wolf->data); - free(wolf->settings); - free(wolf->tmp_settings); + free_menu_lists(wolf); + free_game_entities(wolf); + free_pause_lists(wolf); + free_settings_lists(wolf); + if (wolf->data) + free(wolf->data); + if (wolf->settings) + free(wolf->settings); + if (wolf->tmp_settings) + free(wolf->tmp_settings); free(wolf); } diff --git a/src/init/free_cleanup.c b/src/init/free_cleanup.c new file mode 100644 index 0000000..ddda9b3 --- /dev/null +++ b/src/init/free_cleanup.c @@ -0,0 +1,171 @@ +/* +** EPITECH PROJECT, 2026 +** wolf3d +** File description: +** cleanup +*/ + +#include "../../include/wolf3d.h" + +static void free_rect_drawable(void *data) +{ + rect_t *rect = (rect_t *)data; + + if (!rect) + return; + if (rect->rect) + sfRectangleShape_destroy(rect->rect); + if (rect->texture) + sfTexture_destroy(rect->texture); + if (rect->click_texture) + sfTexture_destroy(rect->click_texture); + free(rect); +} + +static void free_triangle_drawable(void *data) +{ + triangle_t *triangle = (triangle_t *)data; + + if (!triangle) + return; + if (triangle->shape) + sfConvexShape_destroy(triangle->shape); + free(triangle); +} + +static void free_text_drawable(void *data) +{ + text_t *text = (text_t *)data; + + if (!text) + return; + if (text->text) + sfText_destroy(text->text); + free(text); +} + +static void free_entity_drawable(void *data) +{ + entity_t *entity = (entity_t *)data; + + if (!entity) + return; + if (entity->sprite) + sfSprite_destroy(entity->sprite); + if (entity->texture) + sfTexture_destroy(entity->texture); + free(entity); +} + +static void free_line_drawable(void *data) +{ + rect_t *line = (rect_t *)data; + + if (!line) + return; + if (line->rect) + sfRectangleShape_destroy(line->rect); + free(line); +} + +void free_player_list(list_t **list) +{ + list_t *curr = NULL; + list_t *next = NULL; + + if (!list) + return; + curr = *list; + while (curr) { + next = curr->next; + destroy_player((player_t *)curr->data); + free(curr); + curr = next; + } + *list = NULL; +} + +void free_draw_list(wolf_t *wolf, int state, int index) +{ + list_t *curr = NULL; + list_t *next = NULL; + + if (!wolf) + return; + for (curr = wolf->list[state][index]; curr; curr = next) { + next = curr->next; + if (index == RECT) + free_rect_drawable(curr->data); + if (index == TRIANGLE) + free_triangle_drawable(curr->data); + if (index == TEXT) + free_text_drawable(curr->data); + if (index == SPRITE) + free_entity_drawable(curr->data); + if (index == LINE) + free_line_drawable(curr->data); + free(curr); + } + wolf->list[state][index] = NULL; +} + +static void free_game_wall(game_t *game) +{ + int i = 0; + + if (!game->wall) + return; + for (i = 0; i < 2; i++) { + if (game->wall->text_arr[i]) + free(game->wall->text_arr[i]); + } + for (i = 0; i < 6; i++) { + if (game->wall->decor_arr[i]) + free(game->wall->decor_arr[i]); + } + if (game->wall->pixel) + free(game->wall->pixel); + if (game->wall->texture) + sfTexture_destroy(game->wall->texture); + if (game->wall->sprite) + sfSprite_destroy(game->wall->sprite); + free(game->wall); +} + +static void free_game_ui(game_t *game) +{ + int i = 0; + + if (game->zbuffer) + free(game->zbuffer); + if (game->crosshair.cursor) { + if (game->crosshair.cursor->sprite) + sfSprite_destroy(game->crosshair.cursor->sprite); + if (game->crosshair.cursor->texture) + sfTexture_destroy(game->crosshair.cursor->texture); + free(game->crosshair.cursor); + } + if (game->inter.clock) + sfClock_destroy(game->inter.clock); + for (i = 0; i < 3; i++) { + if (game->inter.card[i].text) + sfText_destroy(game->inter.card[i].text); + if (game->inter.card[i].rect) + sfRectangleShape_destroy(game->inter.card[i].rect); + } +} + +void free_game(game_t *game) +{ + if (!game) + return; + for (int i = 0; i < 2; i++) { + if (game->clock[i]) + sfClock_destroy(game->clock[i]); + } + free_game_wall(game); + free_game_ui(game); + free_hud(&game->hud); + free_inv(&game->inv); + free(game); +} diff --git a/src/init/free_lists.c b/src/init/free_lists.c new file mode 100644 index 0000000..3e061e1 --- /dev/null +++ b/src/init/free_lists.c @@ -0,0 +1,39 @@ +/* +** EPITECH PROJECT, 2026 +** wolf3d +** File description: +** list cleanup +*/ + +#include "../../include/wolf3d.h" + +void free_game_entities(wolf_t *wolf) +{ + free_player_list(&wolf->list[GAME][MONSTER]); + free_player_list(&wolf->list[GAME][GARBAGE]); + free_player_list(&wolf->list[GAME][HEAL]); +} + +void free_menu_lists(wolf_t *wolf) +{ + free_draw_list(wolf, MENU, TEXT); + free_draw_list(wolf, MENU, SPRITE); + free_draw_list(wolf, GAME_OVER, TEXT); + free_draw_list(wolf, GAME_OVER, RECT); +} + +void free_pause_lists(wolf_t *wolf) +{ + free_draw_list(wolf, PAUSE, RECT); + free_draw_list(wolf, PAUSE, TEXT); + free_draw_list(wolf, PAUSE, SPRITE); +} + +void free_settings_lists(wolf_t *wolf) +{ + free_draw_list(wolf, SETTINGS, RECT); + free_draw_list(wolf, SETTINGS, TEXT); + free_draw_list(wolf, SETTINGS, SPRITE); + free_draw_list(wolf, SETTINGS, LINE); + free_draw_list(wolf, SETTINGS, TRIANGLE); +} diff --git a/src/init/free_wolf_resources.c b/src/init/free_wolf_resources.c index 1359988..cb9a9b6 100644 --- a/src/init/free_wolf_resources.c +++ b/src/init/free_wolf_resources.c @@ -10,13 +10,16 @@ static void free_window_data(window_t *window_data) { if (window_data) { - sfRenderWindow_destroy(window_data->window); + if (window_data->window) + sfRenderWindow_destroy(window_data->window); free(window_data); } } static void free_music(music_t *music) { + if (!music) + return; for (int i = 0; i < 3; i++) { if (music->sound[i]) sfSound_destroy(music->sound[i]); @@ -31,6 +34,8 @@ static void free_music(music_t *music) void free_wolf_resources(wolf_t *wolf) { + if (!wolf) + return; if (wolf->brightness_shader) sfShader_destroy(wolf->brightness_shader); free_window_data(wolf->window_data); diff --git a/src/init/init_game.c b/src/init/init_game.c index 0809cc9..d0c430b 100644 --- a/src/init/init_game.c +++ b/src/init/init_game.c @@ -7,14 +7,56 @@ #include "../../include/wolf3d.h" +static void free_wall(wall_t *wall) +{ + if (!wall) + return; + for (int i = 0; i < 2; i++) { + if (wall->text_arr[i]) + free(wall->text_arr[i]); + } + for (int i = 0; i < 6; i++) { + if (wall->decor_arr[i]) + free(wall->decor_arr[i]); + } + if (wall->pixel) + free(wall->pixel); + if (wall->texture) + sfTexture_destroy(wall->texture); + if (wall->sprite) + sfSprite_destroy(wall->sprite); + free(wall); +} + +static void free_game_partial(game_t *game) +{ + if (!game) + return; + for (int i = 0; i < 2; i++) { + if (game->clock[i]) + sfClock_destroy(game->clock[i]); + } + free_wall(game->wall); + free(game->zbuffer); + free(game); +} + wall_t *init_texture(wall_t *wall, int size, char *path, sfUint8 **pixel) { sfImage *image = sfImage_createFromFile(path); - const sfUint8 *tmp = sfImage_getPixelsPtr(image); + const sfUint8 *tmp = NULL; + if (!image) + return NULL; + tmp = sfImage_getPixelsPtr(image); *pixel = malloc(size); if (!*pixel) + return sfImage_destroy(image), NULL; + if (!tmp) { + free(*pixel); + sfImage_destroy(image); return NULL; + } memcpy(*pixel, tmp, size); sfImage_destroy(image); return wall; @@ -39,32 +81,33 @@ static wall_t *init_pixels(wall_t *wall, int size) wall_t *init_wall(window_t *window_data) { - wall_t *wall = malloc(sizeof(wall_t)); + wall_t *wall = calloc(1, sizeof(wall_t)); int size = TEX_SIZE * TEX_SIZE * 4; if (!wall) return NULL; - wall->wall = malloc(size); - if (!wall->wall) - return NULL; wall->texture = sfTexture_create(window_data->width, window_data->height); wall->sprite = sfSprite_create(); - if (!init_pixels(wall, size)) + if (!wall->texture || !wall->sprite || !init_pixels(wall, size)) { + free_wall(wall); return NULL; + } sfSprite_setTexture(wall->sprite, wall->texture, sfTrue); return wall; } game_t *init_game(window_t *window_data) { - game_t *game = calloc(sizeof(game_t), 1); + game_t *game = calloc(1, sizeof(game_t)); if (!game) return NULL; for (int i = 0; i < 2; i++) game->clock[i] = sfClock_create(); game->wall = init_wall(window_data); - if (!game->wall) + if (!game->clock[0] || !game->clock[1] || !game->wall) { + free_game_partial(game); return NULL; + } return game; } diff --git a/src/init/init_hud.c b/src/init/init_hud.c index d253045..2bbb41b 100644 --- a/src/init/init_hud.c +++ b/src/init/init_hud.c @@ -78,3 +78,27 @@ void init_hud(hud_t *hud, sfFont *font) init_hud_texts(hud, font); init_minimap(hud); } + +void free_hud(hud_t *hud) +{ + if (!hud) + return; + if (hud->panel) + sfRectangleShape_destroy(hud->panel); + for (int i = 0; i < 4; i++) { + if (hud->bar[i]) + sfRectangleShape_destroy(hud->bar[i]); + } + for (int i = 0; i < 5; i++) { + if (hud->text[i]) + sfText_destroy(hud->text[i]); + } + if (hud->mm_bg) + sfRectangleShape_destroy(hud->mm_bg); + if (hud->mm_tile) + sfRectangleShape_destroy(hud->mm_tile); + if (hud->mm_player) + sfRectangleShape_destroy(hud->mm_player); + if (hud->mm_enemy) + sfRectangleShape_destroy(hud->mm_enemy); +} diff --git a/src/init/init_inv.c b/src/init/init_inv.c index 08b79ca..8501fe2 100644 --- a/src/init/init_inv.c +++ b/src/init/init_inv.c @@ -12,6 +12,8 @@ sfRectangleShape *create_rect(sfVector2f *size, { sfRectangleShape *rect = sfRectangleShape_create(); + if (!rect) + return NULL; sfRectangleShape_setSize(rect, *size); sfRectangleShape_setOrigin(rect, (sfVector2f){size->x / 2, size->y / 2}); @@ -76,7 +78,11 @@ static void init_item(window_t *wn, inv_t *inv) window_t *create_inv(data_t *data, window_t *win, inv_t *inv) { + if (!data || !win || !inv) + return NULL; inv->text = sfText_create(); + if (!inv->text) + return NULL; sfText_setFont(inv->text, data->font); sfText_setCharacterSize(inv->text, 3); create_slot_rect(win, inv); @@ -88,3 +94,45 @@ window_t *create_inv(data_t *data, window_t *win, inv_t *inv) init_item(win, inv); return win; } + +static void destroy_entity(entity_t *entity) +{ + if (!entity) + return; + if (entity->sprite) + sfSprite_destroy(entity->sprite); + if (entity->texture) + sfTexture_destroy(entity->texture); + free(entity); +} + +static void destroy_weapon(weapon_t *weapon) +{ + if (!weapon) + return; + if (weapon->cd) + sfClock_destroy(weapon->cd); + destroy_entity(weapon->entity); + free(weapon); +} + +void free_inv(inv_t *inv) +{ + if (!inv) + return; + if (inv->text) + sfText_destroy(inv->text); + for (int i = 0; i < 10; i++) { + if (inv->rect[i]) + sfRectangleShape_destroy(inv->rect[i]); + } + for (int i = 0; i < 8; i++) { + if (inv->item[i].name) + free(inv->item[i].name); + destroy_weapon((weapon_t *)inv->item[i].data); + destroy_entity(inv->item[i].entity); + inv->item[i].data = NULL; + inv->item[i].entity = NULL; + inv->item[i].name = NULL; + } +} diff --git a/src/init/init_player.c b/src/init/init_player.c index ff65471..d685a33 100644 --- a/src/init/init_player.c +++ b/src/init/init_player.c @@ -8,21 +8,59 @@ #include "../../include/wolf3d.h" #include -weapon_t *init_weapon(wp_type_t type, int damage, +static void destroy_entity(entity_t *entity) +{ + if (!entity) + return; + if (entity->sprite) + sfSprite_destroy(entity->sprite); + if (entity->texture) + sfTexture_destroy(entity->texture); + free(entity); +} + +static void init_weapon_fields(weapon_t *weapon, const weapon_config_t *cfg) +{ + *weapon = (weapon_t){ + .type = cfg->type, + .attack_speed = cfg->type == GUN ? 0.7 : 1.1, + .current_ammo = cfg->max_ammo, + .max_ammo = cfg->max_ammo, + .damage = cfg->damage, + .entity = cfg->entity, + .rect = (sfIntRect){0, 0, 400, 600} + }; +} + +static weapon_t *create_weapon(wp_type_t type, int damage, int max_ammo, entity_t *entity) { weapon_t *weapon = calloc(1, sizeof(weapon_t)); + weapon_config_t cfg; - if (!weapon) + if (!weapon || !entity) { + destroy_entity(entity); + free(weapon); return NULL; - weapon->type = type; - weapon->attack_speed = type == GUN ? 0.7 : 1.1; - weapon->current_ammo = max_ammo; - weapon->max_ammo = max_ammo; - weapon->damage = damage; + } + cfg = (weapon_config_t){type, damage, max_ammo, entity}; + init_weapon_fields(weapon, &cfg); weapon->cd = sfClock_create(); - weapon->entity = entity; - weapon->rect = (sfIntRect){0, 0, 400, 600}; + if (!weapon->cd) { + destroy_entity(weapon->entity); + free(weapon); + return NULL; + } + return weapon; +} + +weapon_t *init_weapon(wp_type_t type, int damage, + int max_ammo, entity_t *entity) +{ + weapon_t *weapon = create_weapon(type, damage, max_ammo, entity); + + if (!weapon) + return NULL; sfSprite_setTextureRect(entity->sprite, weapon->rect); sfSprite_setOrigin(entity->sprite, (sfVector2f){200, 600}); return weapon; diff --git a/src/init/init_rect.c b/src/init/init_rect.c index a0933d3..38f423a 100644 --- a/src/init/init_rect.c +++ b/src/init/init_rect.c @@ -19,10 +19,24 @@ static void set_param(rect_t *rect, sfVector2f *scale, sfVector2f *pos) sfRectangleShape_setTexture(rect->rect, rect->texture, sfTrue); } +static int init_rect_assets(rect_t *rect, const char *texture_path) +{ + rect->texture = sfTexture_createFromFile("assets/settings_button.png", + NULL); + rect->click_texture = sfTexture_createFromFile(texture_path, NULL); + return rect->texture && rect->click_texture; +} + +static void set_line_style(rect_t *line, sfVector2f *pos, sfVector2f *scale) +{ + sfRectangleShape_setSize(line->rect, (sfVector2f){1.0f, 1.0f}); + sfRectangleShape_setPosition(line->rect, *pos); + sfRectangleShape_setFillColor(line->rect, sfColor_fromRGB(90, 75, 60)); + sfRectangleShape_setSize(line->rect, *scale); +} + static void free_rect(rect_t *rect) { - if (rect->name) - free(rect->name); if (rect->texture) sfTexture_destroy(rect->texture); if (rect->click_texture) @@ -35,20 +49,20 @@ static void free_rect(rect_t *rect) rect_t *create_rectangles(rect_t *data, const char *texture_path, sfVector2f *pos, sfVector2f *scale) { - rect_t *rect = malloc(sizeof(rect_t)); + rect_t *rect = calloc(1, sizeof(rect_t)); if (!rect) return NULL; rect->rect = sfRectangleShape_create(); + if (!rect->rect) { + free(rect); + return NULL; + } rect->name = data->name; rect->state = data->state; rect->type = data->type; rect->always_display = data->always_display; - rect->texture = sfTexture_createFromFile("assets/settings_button.png", - NULL); - rect->click_texture - = sfTexture_createFromFile(texture_path, NULL); - if (!rect->name || !rect->texture || !rect->click_texture) { + if (!rect->name || !init_rect_assets(rect, texture_path)) { free_rect(rect); return NULL; } @@ -58,22 +72,23 @@ rect_t *create_rectangles(rect_t *data, const char *texture_path, rect_t *create_line(rect_t *data, sfVector2f *pos, sfVector2f *scale) { - rect_t *line = malloc(sizeof(rect_t)); + rect_t *line = calloc(1, sizeof(rect_t)); if (!line) return NULL; line->rect = sfRectangleShape_create(); + if (!line->rect) { + free(line); + return NULL; + } line->name = data->name; line->state = data->state; line->type = data->type; line->always_display = data->always_display; - if (!line->name || !line->rect) { + if (!line->name) { free_rect(line); return NULL; } - sfRectangleShape_setSize(line->rect, (sfVector2f){1.0f, 1.0f}); - sfRectangleShape_setPosition(line->rect, *pos); - sfRectangleShape_setFillColor(line->rect, sfColor_fromRGB(90, 75, 60)); - sfRectangleShape_setSize(line->rect, *scale); + set_line_style(line, pos, scale); return line; } diff --git a/src/init/init_settings.c b/src/init/init_settings.c index 88c9e12..91b5656 100644 --- a/src/init/init_settings.c +++ b/src/init/init_settings.c @@ -55,6 +55,21 @@ static void init_control_settings(settings_game_t *settings) init_slot_settings(settings); } +static void init_controller_settings(settings_game_t *settings) +{ + for (int i = 0; i < CTRL_COUNT; i++) + settings->joystick_bindings[i] = -1; + settings->joystick_bindings[CTRL_SPRINT] = JOY_BTN_LSTICK; + settings->joystick_bindings[CTRL_INVENTORY] = JOY_BTN_INVENTORY; + settings->joystick_bindings[CTRL_EQUIP] = JOY_BTN_EQUIP; + settings->joystick_bindings[CTRL_RELOAD] = JOY_BTN_RELOAD; + settings->joystick_bindings[CTRL_FLASHLIGHT] = JOY_BTN_FLASHLIGHT; + settings->joystick_bindings[CTRL_HEAL] = JOY_BTN_HEAL; + settings->joystick_bindings[CTRL_PAUSE] = JOY_BTN_OPTIONS; + settings->joystick_bindings[CTRL_SLOT_1] = JOY_BTN_SLOT_PREV; + settings->joystick_bindings[CTRL_SLOT_2] = JOY_BTN_SLOT_NEXT; +} + static void init_gameplay_settings(settings_game_t *settings) { settings->mouse_sensitivity = 50; @@ -75,5 +90,6 @@ settings_game_t *init_settings_params(void) init_audio_settings(settings); init_gameplay_settings(settings); init_control_settings(settings); + init_controller_settings(settings); return settings; } diff --git a/src/init/init_sound_effect.c b/src/init/init_sound_effect.c index 239a61e..ba1409a 100644 --- a/src/init/init_sound_effect.c +++ b/src/init/init_sound_effect.c @@ -15,12 +15,19 @@ sfSound *create_sound(sfSoundBuffer **buffer, char *filepath) if (!*buffer) return NULL; sound = sfSound_create(); + if (!sound) { + sfSoundBuffer_destroy(*buffer); + *buffer = NULL; + return NULL; + } sfSound_setBuffer(sound, *buffer); return sound; } void play_sound(sfSound *sound) { + if (!sound) + return; if (sfSound_getStatus(sound) == sfPlaying) sfSound_stop(sound); sfSound_play(sound); @@ -37,6 +44,8 @@ wolf_t *init_music(wolf_t *wolf, int index, const char *filepath) void play_music(sfMusic *music) { + if (!music) + return; sfMusic_setLoop(music, sfTrue); sfMusic_play(music); } diff --git a/src/init/init_sprite.c b/src/init/init_sprite.c index f610e83..db54f3f 100644 --- a/src/init/init_sprite.c +++ b/src/init/init_sprite.c @@ -22,24 +22,36 @@ entity_t *get_entity(list_t *list, char *name) return NULL; } -entity_t *create_entity(char *name, - const char *texture_path, sfVector2f *pos, sfVector2f *scale) +static entity_t *create_entity_base(char *name, const char *texture_path) { - entity_t *entity = malloc(sizeof(entity_t)); - sfVector2u size; + entity_t *entity = calloc(1, sizeof(entity_t)); if (!entity) return NULL; entity->name = name; entity->texture = sfTexture_createFromFile(texture_path, NULL); - if (!entity->name || !entity->texture) { - if (entity->name) - free(entity->name); + if (!entity->texture) { free(entity); return NULL; } entity->sprite = sfSprite_create(); + if (!entity->sprite) { + sfTexture_destroy(entity->texture); + free(entity); + return NULL; + } sfSprite_setTexture(entity->sprite, entity->texture, sfTrue); + return entity; +} + +entity_t *create_entity(char *name, + const char *texture_path, sfVector2f *pos, sfVector2f *scale) +{ + entity_t *entity = create_entity_base(name, texture_path); + sfVector2u size; + + if (!entity) + return NULL; size = sfTexture_getSize(entity->texture); sfSprite_setOrigin(entity->sprite, (sfVector2f){size.x / 2, size.y / 2}); sfSprite_setPosition(entity->sprite, *pos); diff --git a/src/init/init_triangle.c b/src/init/init_triangle.c index 1e72f08..df87dfc 100644 --- a/src/init/init_triangle.c +++ b/src/init/init_triangle.c @@ -20,7 +20,7 @@ static void set_triangle_param(triangle_t *triangle, sfVector2f *pos, triangle_t *create_triangle(triangle_t *data, sfVector2f *pos, sfVector2f points[3]) { - triangle_t *triangle = malloc(sizeof(triangle_t)); + triangle_t *triangle = calloc(1, sizeof(triangle_t)); if (!triangle) return NULL; diff --git a/src/init/init_wolf.c b/src/init/init_wolf.c index dd67f9a..27cd4d6 100644 --- a/src/init/init_wolf.c +++ b/src/init/init_wolf.c @@ -9,9 +9,12 @@ void push_front(list_t **list, void *data) { - list_t *new_node = malloc(sizeof(list_t)); + list_t *new_node = NULL; - if (!new_node || !data) + if (!list || !data) + return; + new_node = malloc(sizeof(list_t)); + if (!new_node) return; new_node->data = data; new_node->next = *list; @@ -30,41 +33,52 @@ void create_cursor(window_t *win, crosshair_t *ch) window_t *init_window_data(wolf_t *wolf) { window_t *window = malloc(sizeof(window_t)); - sfVideoMode mode = {WINDOW_WIDTH, WINDOW_HEIGHT, 32}; + sfVideoMode resolution = {WINDOW_WIDTH, WINDOW_HEIGHT, 32}; + uint32_t style = sfDefaultStyle; + int has_settings = wolf && wolf->settings; if (!window) return NULL; - if (wolf && wolf->settings) { - window->window = sfRenderWindow_create(wolf->settings->resolution, - "Wolf3D", sfClose, NULL); - window->width = wolf->settings->resolution.width; - window->height = wolf->settings->resolution.height; - } else { - window->window = sfRenderWindow_create(mode, - "Wolf3D", sfDefaultStyle, NULL); - window->width = WINDOW_WIDTH; - window->height = WINDOW_HEIGHT; + if (has_settings) + resolution = wolf->settings->resolution; + style = has_settings ? sfClose : sfDefaultStyle; + window->width = has_settings ? wolf->settings->resolution.width + : WINDOW_WIDTH; + window->height = has_settings ? wolf->settings->resolution.height + : WINDOW_HEIGHT; + window->window = sfRenderWindow_create(resolution, "Wolf3D", style, NULL); + if (!window->window) { + free(window); + return NULL; } return window; } data_t *init_wolf_data(void) { - data_t *data = malloc(sizeof(data_t)); + data_t *data = calloc(1, sizeof(data_t)); if (!data) return NULL; data->font = sfFont_createFromFile("assets/font.ttf"); + if (!data->font) { + free(data); + return NULL; + } return data; } static int init_wolf_player_data(wolf_t *wolf) { + if (!wolf) + return -1; wolf->window_data = init_window_data(wolf); wolf->data = init_wolf_data(); + if (!wolf->window_data || !wolf->data) + return -1; wolf->player = init_player(wolf->window_data, PLAYER, &(sfVector2f){1.5, 1.5}); - if (!wolf->player || !wolf->window_data || !wolf->data) + if (!wolf->player) return -1; return 0; } @@ -77,6 +91,7 @@ static void init_settings(wolf_t *wolf) init_audio(wolf, wolf->window_data); init_gameplay(wolf, wolf->window_data); init_controls(wolf, wolf->window_data); + init_controller_controls(wolf, wolf->window_data); init_graphics_params(wolf->settings, wolf); } @@ -136,17 +151,17 @@ wolf_t *init_wolf(void) if (!wolf) return NULL; if (init_wolf_settings(wolf) < 0) { - free(wolf); + free_wolf(wolf); return NULL; } wolf->net.player_id = MAX_PLAYERS; if (init_wolf_player_data(wolf) < 0) - return NULL; + return free_wolf(wolf), NULL; wolf->state = MENU; wolf->stage = 0; if (init_wolf_game_data(wolf) < 0 || !create_inv(wolf->data, wolf->window_data, &wolf->game->inv) || !init_sound_effect(wolf)) - return NULL; + return free_wolf(wolf), NULL; return wolf; } diff --git a/src/menu/draw_lobby.c b/src/menu/draw_lobby.c index 62748c9..e3d3c4f 100644 --- a/src/menu/draw_lobby.c +++ b/src/menu/draw_lobby.c @@ -13,6 +13,8 @@ static sfText *new_centered_text(wolf_t *wolf, sfText *t = sfText_create(); sfFloatRect bounds; + if (!t) + return NULL; sfText_setFont(t, wolf->data->font); sfText_setString(t, str); sfText_setCharacterSize(t, size); @@ -20,37 +22,47 @@ static sfText *new_centered_text(wolf_t *wolf, sfText_setOutlineColor(t, sfBlack); sfText_setOutlineThickness(t, 1); bounds = sfText_getGlobalBounds(t); - sfText_setPosition(t, (sfVector2f){ - (wolf->window_data->width - bounds.width) / 2.f, y}); + sfText_setPosition(t, + (sfVector2f){(wolf->window_data->width - bounds.width) / 2.f, y}); return t; } static void draw_and_free(wolf_t *wolf, sfText *t) { + if (!t) + return; sfRenderWindow_drawText(wolf->window_data->window, t, NULL); sfText_destroy(t); } +static void draw_lobby_ip_error(wolf_t *wolf, float cy) +{ + sfText *t = new_centered_text(wolf, + "Connexion echouee - verifiez l'IP", cy, 30); + + if (t) + sfText_setColor(t, sfRed); + draw_and_free(wolf, t); +} + static float draw_ip_input(wolf_t *wolf, float cy) { sfText *t; char ip_display[18]; - draw_and_free(wolf, new_centered_text(wolf, - "Entrez l'IP du serveur :", cy, 36)); + draw_and_free(wolf, + new_centered_text(wolf, "Entrez l'IP du serveur :", cy, 36)); cy += 80; snprintf(ip_display, sizeof(ip_display), "%.*s_", wolf->lobby.ip_len, wolf->lobby.ip); t = new_centered_text(wolf, ip_display, cy, 48); - sfText_setColor(t, sfYellow); + if (t) + sfText_setColor(t, sfYellow); draw_and_free(wolf, t); - if (wolf->lobby.connect_error) { - cy += 90; - t = new_centered_text(wolf, - "Connexion echouee - verifiez l'IP", cy, 30); - sfText_setColor(t, sfRed); - draw_and_free(wolf, t); - } + if (!wolf->lobby.connect_error) + return cy; + cy += 90; + draw_lobby_ip_error(wolf, cy); return cy; } @@ -80,27 +92,31 @@ static float draw_player_list(wolf_t *wolf, float cy) return cy; } -static void draw_lobby_actions(wolf_t *wolf, float cy) +static void draw_lobby_action_line(wolf_t *wolf, const char *msg, + float cy, const sfColor *color) { sfText *t; - if (wolf->lobby.is_host) { - t = new_centered_text(wolf, - "ENTREE pour lancer la partie", cy, 34); - sfText_setColor(t, sfGreen); - draw_and_free(wolf, t); - } else { - t = new_centered_text(wolf, - "En attente que l'hote lance la partie...", cy, 30); - sfText_setColor(t, (sfColor){160, 160, 160, 255}); - draw_and_free(wolf, t); - } - cy += 70; - t = new_centered_text(wolf, "ECHAP pour retour", cy, 26); - sfText_setColor(t, (sfColor){160, 160, 160, 255}); + t = new_centered_text(wolf, msg, cy, 34); + if (t) + sfText_setColor(t, *color); draw_and_free(wolf, t); } +static void draw_lobby_actions(wolf_t *wolf, float cy) +{ + if (wolf->lobby.is_host) + draw_lobby_action_line(wolf, "ENTREE pour lancer la partie", + cy, &sfGreen); + else + draw_lobby_action_line(wolf, + "En attente que l'hote lance la partie...", + cy, &(sfColor){160, 160, 160, 255}); + cy += 70; + draw_lobby_action_line(wolf, "ECHAP pour retour", + cy, &(sfColor){160, 160, 160, 255}); +} + static void draw_lobby_wait_phase(wolf_t *wolf) { char buf[64]; diff --git a/src/menu/manage_menu.c b/src/menu/manage_menu.c index f6eab4d..1952cd4 100644 --- a/src/menu/manage_menu.c +++ b/src/menu/manage_menu.c @@ -25,9 +25,10 @@ static void start_game(wolf_t *wolf) wolf->state = GAME; } -static sfBool menu_confirm(sfEvent event) +static sfBool menu_confirm(settings_game_t *settings, sfEvent event) { - return iskeypressed(sfKeyEnter, event) || joystick_menu_confirm(event); + return iskeypressed(sfKeyEnter, event) || joystick_menu_confirm(settings, + event); } static void move_menu_cursor(wolf_t *wolf, sfEvent event, int min, int max) @@ -42,9 +43,9 @@ static void move_menu_cursor(wolf_t *wolf, sfEvent event, int min, int max) void manage_newgame_menu(wolf_t *wolf, sfEvent event) { - if (menu_confirm(event) && wolf->menu_state == SOLO) + if (menu_confirm(wolf->settings, event) && wolf->menu_state == SOLO) start_game(wolf); - if (menu_confirm(event) && wolf->menu_state == MULTI) { + if (menu_confirm(wolf->settings, event) && wolf->menu_state == MULTI) { memset(&wolf->lobby, 0, sizeof(wolf->lobby)); wolf->net.player_id = MAX_PLAYERS; wolf->state = LOBBY; @@ -72,7 +73,7 @@ void manage_menu(wolf_t *wolf, sfEvent event) { if (handle_save_input(wolf, event)) return; - if (menu_confirm(event)) + if (menu_confirm(wolf->settings, event)) apply_menu_action(wolf); move_menu_cursor(wolf, event, 0, LEAVE); } @@ -82,7 +83,7 @@ void manage_game_over(wolf_t *wolf, sfEvent event) if (network_is_host(wolf) && wolf->player->alive == sfFalse && wolf->game && wolf->game->state == STAGE) return; - if (!menu_confirm(event)) + if (!menu_confirm(wolf->settings, event)) return; wolf->state = MENU; wolf->menu_state = NEWGAME; diff --git a/src/menu/pause_menu.c b/src/menu/pause_menu.c index 03a4ff4..025cae4 100644 --- a/src/menu/pause_menu.c +++ b/src/menu/pause_menu.c @@ -34,7 +34,7 @@ void manage_pause(wolf_t *wolf, sfEvent event) if (handle_save_input(wolf, event)) return; if (is_pause_key(wolf->settings, event) || - joystick_action_event(CTRL_PAUSE, event)) + joystick_action_event(wolf->settings, CTRL_PAUSE, event)) wolf->state = GAME; if ((iskeypressed(sfKeyUp, event) || joystick_menu_up(event)) && wolf->menu_state > 0) @@ -42,6 +42,7 @@ void manage_pause(wolf_t *wolf, sfEvent event) if ((iskeypressed(sfKeyDown, event) || joystick_menu_down(event)) && wolf->menu_state < 5) wolf->menu_state += 1; - if (iskeypressed(sfKeyEnter, event) || joystick_menu_confirm(event)) + if (iskeypressed(sfKeyEnter, event) || + joystick_menu_confirm(wolf->settings, event)) pause_apply_action(wolf, actions[wolf->menu_state]); } diff --git a/src/network/client/client.c b/src/network/client/client.c index 8aa3d4c..d4478e7 100644 --- a/src/network/client/client.c +++ b/src/network/client/client.c @@ -45,22 +45,32 @@ int client_send_packet(client_t *client, network_packet_t *packet) return -1; } -int client_recv_packet(client_t *client, network_packet_t *packet) +static int client_recv_full_packet(client_t *client, network_packet_t *packet) { size_t received; + size_t total = 0; sfSocketStatus status; + uint8_t *buf = (uint8_t *)packet; + while (total < sizeof(network_packet_t)) { + status = sfTcpSocket_receive(client->socket, buf + total, + sizeof(network_packet_t) - total, &received); + if (status == sfSocketNotReady) + return total == 0 ? 0 : (int)total; + if (status == sfSocketDisconnected) + return -1; + if (status != sfSocketDone) + return 0; + total += received; + } + return (int)total; +} + +int client_recv_packet(client_t *client, network_packet_t *packet) +{ if (!client || !packet || !client->socket) return -1; - status = sfTcpSocket_receive(client->socket, packet, - sizeof(network_packet_t), &received); - if (status == sfSocketNotReady) - return 0; - if (status == sfSocketDisconnected) - return -1; - if (status != sfSocketDone) - return 0; - return (int)received; + return client_recv_full_packet(client, packet); } void client_close(client_t *client) diff --git a/src/network/server/server.c b/src/network/server/server.c index 30b962c..6f38e98 100644 --- a/src/network/server/server.c +++ b/src/network/server/server.c @@ -47,40 +47,73 @@ int server_accept_client(server_t *server) return slot; } -int server_recv_from_client(server_t *server, int idx, network_packet_t *pkt) +int server_send_to_client(server_t *server, int idx, network_packet_t *pkt) +{ + if (!server || !pkt || !server->client_sockets[idx]) + return -1; + sfTcpSocket_send(server->client_sockets[idx], pkt, sizeof(*pkt)); + return 0; +} + +static void server_drop_client(server_t *server, int idx) +{ + sfSocketSelector_removeTcpSocket(server->selector, + server->client_sockets[idx]); + sfTcpSocket_disconnect(server->client_sockets[idx]); + sfTcpSocket_destroy(server->client_sockets[idx]); + server->client_sockets[idx] = NULL; +} + +static int server_recv_packet_step(server_t *server, int idx, uint8_t *buf, + size_t *total) { size_t received; sfSocketStatus status; - if (!server || !pkt || !server->client_sockets[idx]) - return -1; - status = sfTcpSocket_receive(server->client_sockets[idx], pkt, - sizeof(*pkt), &received); + status = sfTcpSocket_receive(server->client_sockets[idx], + buf + *total, sizeof(network_packet_t) - *total, &received); if (status == sfSocketDisconnected || status == sfSocketError) { - sfSocketSelector_removeTcpSocket(server->selector, - server->client_sockets[idx]); - sfTcpSocket_disconnect(server->client_sockets[idx]); - sfTcpSocket_destroy(server->client_sockets[idx]); - server->client_sockets[idx] = NULL; + server_drop_client(server, idx); return -1; } - return (int)received; + if (status == sfSocketNotReady) + return 0; + if (status != sfSocketDone) + return 0; + *total += received; + return 1; } -int server_send_to_client(server_t *server, int idx, network_packet_t *pkt) +static int server_recv_full_packet(server_t *server, int idx, + network_packet_t *pkt) +{ + size_t total; + uint8_t *buf; + + total = 0; + buf = (uint8_t *)pkt; + while (total < sizeof(network_packet_t)) { + if (server_recv_packet_step(server, idx, buf, &total) <= 0) + return total == 0 ? 0 : (int)total; + } + return (int)total; +} + +int server_recv_from_client(server_t *server, int idx, network_packet_t *pkt) { if (!server || !pkt || !server->client_sockets[idx]) return -1; - sfTcpSocket_send(server->client_sockets[idx], pkt, sizeof(*pkt)); - return 0; + return server_recv_full_packet(server, idx, pkt); } int server_broadcast_to_others(server_t *server, network_packet_t *pkt, int sender) { + int i; + if (!server || !pkt) return -1; - for (int i = 0; i < MAX_PLAYERS; i++) { + for (i = 0; i < MAX_PLAYERS; i++) { if (i == sender || !server->client_sockets[i]) continue; sfTcpSocket_send(server->client_sockets[i], pkt, sizeof(*pkt)); diff --git a/src/player/actions.c b/src/player/actions.c new file mode 100644 index 0000000..b660e61 --- /dev/null +++ b/src/player/actions.c @@ -0,0 +1,21 @@ +/* +** EPITECH PROJECT, 2026 +** wolf3d +** File description: +** Player input action helpers +*/ + +#include "../../include/wolf3d.h" + +sfBool isactiondown(settings_game_t *settings, control_action_t action) +{ + return (settings && sfKeyboard_isKeyPressed(settings->key_bindings[action])) + || joystick_action_down(settings, action); +} + +sfBool isactionpressed(settings_game_t *settings, control_action_t action, + sfEvent event) +{ + return (settings && iskeypressed(settings->key_bindings[action], event)) || + joystick_action_event(settings, action, event); +} diff --git a/src/player/inventory.c b/src/player/inventory.c index 615c989..3840418 100644 --- a/src/player/inventory.c +++ b/src/player/inventory.c @@ -31,7 +31,7 @@ static void use_bandage(player_t *player, slot_t *slot) slot->selected = sfFalse; } -static void use_item(player_t *player, slot_t *slot) +void use_inventory_item(player_t *player, slot_t *slot) { if (!slot->item.data) return; @@ -50,7 +50,7 @@ static void use_item_key(wolf_t *wolf, sfEvent event, player_t *player, for (int i = 0; i < 8; i++) { if (!isactionpressed(wolf->settings, CTRL_SLOT_1 + i, event)) continue; - return use_item(player, &inv->slot[i]); + return use_inventory_item(player, &inv->slot[i]); } } @@ -61,7 +61,7 @@ static void equip_item(wolf_t *wolf, player_t *player, inv_t *inv) for (int i = 0; i < 8; i++) { if (!inv->slot[i].selected) continue; - return use_item(player, &inv->slot[i]); + return use_inventory_item(player, &inv->slot[i]); } } @@ -136,6 +136,10 @@ void open_inventory(wolf_t *wolf, sfEvent event, inv_t *inv) inv->slot[i].selected = sfFalse; } use_item_key(wolf, event, wolf->player, inv); + if (!inv->open) + use_moved_inventory_slot(wolf, event, inv); + if (inv->open) + move_inventory_slot(wolf, event, inv); if (inv->open) check_inventory(wolf, inv); } diff --git a/src/player/inventory_select.c b/src/player/inventory_select.c new file mode 100644 index 0000000..729b7f1 --- /dev/null +++ b/src/player/inventory_select.c @@ -0,0 +1,66 @@ +/* +** EPITECH PROJECT, 2026 +** wolf3d +** File description: +** Inventory controller selection helpers +*/ + +#include "../../include/wolf3d.h" + +static int selected_slot(player_t *player, inv_t *inv) +{ + for (int i = 0; i < 8; i++) + if (inv->slot[i].selected) + return i; + for (int i = 0; i < 8; i++) + if (inv->slot[i].item.data == player->weapon) + return i; + return 0; +} + +static void select_inventory_slot(inv_t *inv, int index) +{ + if (index < 0) + index = 7; + if (index > 7) + index = 0; + for (int i = 0; i < 8; i++) + inv->slot[i].selected = sfFalse; + inv->slot[index].selected = sfTrue; +} + +void move_inventory_slot(wolf_t *wolf, sfEvent event, inv_t *inv) +{ + int index = selected_slot(wolf->player, inv); + + if (joystick_menu_left(event) || joystick_slot_previous(wolf->settings, + event)) + select_inventory_slot(inv, index - 1); + if (joystick_menu_right(event) || joystick_slot_next(wolf->settings, + event)) + select_inventory_slot(inv, index + 1); + if (joystick_menu_up(event)) + select_inventory_slot(inv, index - 4); + if (joystick_menu_down(event)) + select_inventory_slot(inv, index + 4); +} + +void use_moved_inventory_slot(wolf_t *wolf, sfEvent event, inv_t *inv) +{ + int index = selected_slot(wolf->player, inv); + int direction = 0; + + if (joystick_slot_previous(wolf->settings, event)) + direction = -1; + if (joystick_slot_next(wolf->settings, event)) + direction = 1; + if (direction == 0) + return; + for (int i = 0; i < 8; i++) { + index = (index + direction + 8) % 8; + if (!inv->slot[index].item.data) + continue; + select_inventory_slot(inv, index); + return use_inventory_item(wolf->player, &inv->slot[index]); + } +} diff --git a/src/player/joystick.c b/src/player/joystick.c index 88fac2c..d82236b 100644 --- a/src/player/joystick.c +++ b/src/player/joystick.c @@ -24,39 +24,59 @@ float joystick_axis(sfJoystickAxis axis) return position / 100.0f; } -static int action_button(control_action_t action) +static int default_action_button(control_action_t action) { switch (action) { case CTRL_SPRINT: return JOY_BTN_LSTICK; case CTRL_INVENTORY: - return JOY_BTN_B; + return JOY_BTN_INVENTORY; case CTRL_EQUIP: + return JOY_BTN_EQUIP; case CTRL_HEAL: - return JOY_BTN_A; + return JOY_BTN_HEAL; case CTRL_RELOAD: - return JOY_BTN_X; + return JOY_BTN_RELOAD; case CTRL_FLASHLIGHT: - return JOY_BTN_BACK; + return JOY_BTN_FLASHLIGHT; case CTRL_PAUSE: - return JOY_BTN_START; + return JOY_BTN_OPTIONS; default: return -1; } } -sfBool joystick_action_down(control_action_t action) +static int action_button(settings_game_t *settings, control_action_t action) { - int button = action_button(action); + if (action >= CTRL_SLOT_1) + return -1; + if (settings && settings->joystick_bindings[action] >= 0) + return settings->joystick_bindings[action]; + return default_action_button(action); +} + +int joystick_bound_button(settings_game_t *settings, control_action_t action, + int fallback) +{ + if (settings && settings->joystick_bindings[action] >= 0) + return settings->joystick_bindings[action]; + return fallback; +} + +sfBool joystick_action_down(settings_game_t *settings, + control_action_t action) +{ + int button = action_button(settings, action); if (button < 0 || !joystick_connected()) return sfFalse; return sfJoystick_isButtonPressed(JOYSTICK_ID, button); } -sfBool joystick_action_event(control_action_t action, sfEvent event) +sfBool joystick_action_event(settings_game_t *settings, + control_action_t action, sfEvent event) { - int button = action_button(action); + int button = action_button(settings, action); return button >= 0 && event.type == sfEvtJoystickButtonPressed && event.joystickButton.joystickId == JOYSTICK_ID && @@ -66,6 +86,5 @@ sfBool joystick_action_event(control_action_t action, sfEvent event) sfBool joystick_shoot_down(void) { return joystick_connected() && - (sfJoystick_isButtonPressed(JOYSTICK_ID, JOY_BTN_RB) || - joystick_axis(sfJoystickZ) > 0.45f); + sfJoystick_isButtonPressed(JOYSTICK_ID, JOY_BTN_SHOOT); } diff --git a/src/player/joystick_actions.c b/src/player/joystick_actions.c index 188c2af..98871e4 100644 --- a/src/player/joystick_actions.c +++ b/src/player/joystick_actions.c @@ -7,38 +7,49 @@ #include "../../include/wolf3d.h" -static sfBool joystick_pov_event(sfEvent event, float direction) +static sfBool joystick_axis_event(sfEvent event, sfJoystickAxis axis, + float direction) { - return event.type == sfEvtJoystickMoved && - event.joystickMove.joystickId == JOYSTICK_ID && - event.joystickMove.axis == sfJoystickPovY && - event.joystickMove.position * direction > 50.0f; + float position; + + (void)direction; + if (event.type != sfEvtJoystickMoved || + event.joystickMove.joystickId != JOYSTICK_ID || + event.joystickMove.axis != axis) + return sfFalse; + position = event.joystickMove.position / 100.0f; + return position * direction > JOY_TRIGGER_THRESHOLD; } sfBool joystick_menu_up(sfEvent event) { - return joystick_pov_event(event, 1.0f); + return joystick_axis_event(event, sfJoystickPovY, 1.0f) || + joystick_axis_event(event, sfJoystickY, -1.0f) || + joystick_axis_event(event, sfJoystickV, -1.0f); } sfBool joystick_menu_down(sfEvent event) { - return joystick_pov_event(event, -1.0f); + return joystick_axis_event(event, sfJoystickPovY, -1.0f) || + joystick_axis_event(event, sfJoystickY, 1.0f) || + joystick_axis_event(event, sfJoystickV, 1.0f); } -sfBool joystick_menu_confirm(sfEvent event) +sfBool joystick_menu_left(sfEvent event) { - return joystick_action_event(CTRL_EQUIP, event); + return joystick_axis_event(event, sfJoystickPovX, -1.0f) || + joystick_axis_event(event, sfJoystickX, -1.0f) || + joystick_axis_event(event, sfJoystickU, -1.0f); } -sfBool isactiondown(settings_game_t *settings, control_action_t action) +sfBool joystick_menu_right(sfEvent event) { - return (settings && sfKeyboard_isKeyPressed(settings->key_bindings[action])) - || joystick_action_down(action); + return joystick_axis_event(event, sfJoystickPovX, 1.0f) || + joystick_axis_event(event, sfJoystickX, 1.0f) || + joystick_axis_event(event, sfJoystickU, 1.0f); } -sfBool isactionpressed(settings_game_t *settings, control_action_t action, - sfEvent event) +sfBool joystick_menu_confirm(settings_game_t *settings, sfEvent event) { - return (settings && iskeypressed(settings->key_bindings[action], event)) || - joystick_action_event(action, event); + return joystick_action_event(settings, CTRL_EQUIP, event); } diff --git a/src/player/joystick_slots.c b/src/player/joystick_slots.c new file mode 100644 index 0000000..8a8203e --- /dev/null +++ b/src/player/joystick_slots.c @@ -0,0 +1,28 @@ +/* +** EPITECH PROJECT, 2026 +** wolf3d +** File description: +** Joystick weapon slot helpers +*/ + +#include "../../include/wolf3d.h" + +sfBool joystick_slot_previous(settings_game_t *settings, sfEvent event) +{ + int button = joystick_bound_button(settings, CTRL_SLOT_1, + JOY_BTN_SLOT_PREV); + + return event.type == sfEvtJoystickButtonPressed && + event.joystickButton.joystickId == JOYSTICK_ID && + event.joystickButton.button == (unsigned int)button; +} + +sfBool joystick_slot_next(settings_game_t *settings, sfEvent event) +{ + int button = joystick_bound_button(settings, CTRL_SLOT_2, + JOY_BTN_SLOT_NEXT); + + return event.type == sfEvtJoystickButtonPressed && + event.joystickButton.joystickId == JOYSTICK_ID && + event.joystickButton.button == (unsigned int)button; +} diff --git a/src/player/weapon/weapon.c b/src/player/weapon/weapon.c index d2d5dd1..301da2b 100644 --- a/src/player/weapon/weapon.c +++ b/src/player/weapon/weapon.c @@ -9,7 +9,7 @@ static void handle_monster_death(wolf_t *wolf, player_t *monster) { - if (rand() % 6 == 0) + if (rand() % 3 == 0) push_front(&wolf->list[GAME][HEAL], init_player(NULL, HEAL_T, &(sfVector2f){monster->x, monster->y})); monster->alive = sfFalse; diff --git a/src/render/camera.c b/src/render/camera.c index 53f897f..6168092 100644 --- a/src/render/camera.c +++ b/src/render/camera.c @@ -13,6 +13,10 @@ static sfBool is_sprint_moving(wolf_t *wolf, player_t *player) if (!player->running) return sfFalse; + if (joystick_axis(sfJoystickY) < 0.0f) + return sfTrue; + if (joystick_axis(sfJoystickX) != 0.0f) + return sfTrue; if (sfKeyboard_isKeyPressed(settings->key_bindings[CTRL_FORWARD])) return sfTrue; if (sfKeyboard_isKeyPressed(settings->key_bindings[CTRL_LEFT])) diff --git a/src/render/draw_crosshair.c b/src/render/draw_crosshair.c index 6303cc6..35a048c 100644 --- a/src/render/draw_crosshair.c +++ b/src/render/draw_crosshair.c @@ -7,21 +7,32 @@ #include "../../include/wolf3d.h" -void draw_crosshair(wolf_t *wolf, window_t *win) +static void update_crosshair_view(wolf_t *wolf, window_t *win, + int dx, int dy) { - sfSprite *sprite = wolf->game->crosshair.cursor->sprite; - sfVector2i pos = sfMouse_getPositionRenderWindow(win->window); - sfVector2i center = {win->width / 2, win->height / 2}; - sfVector2i vector = {pos.x - center.x, pos.y - center.y}; - - wolf->player->angle += (float)(vector.x / 400.f); - wolf->player->z -= (float)(vector.y / 2.f); + wolf->player->angle += (float)(dx / 400.f); + wolf->player->z -= (float)(dy / 2.f); wolf->player->z -= joystick_axis(sfJoystickV) * 22.0f; if (wolf->player->z >= 600) wolf->player->z = 600; if (wolf->player->z <= -800) wolf->player->z = -800; - sfMouse_setPositionRenderWindow(center, win->window); + sfMouse_setPositionRenderWindow( + (sfVector2i){win->width / 2, win->height / 2}, win->window); +} + +void draw_crosshair(wolf_t *wolf, window_t *win) +{ + sfSprite *sprite = NULL; + sfVector2i pos = sfMouse_getPositionRenderWindow(win->window); + sfVector2i center = {win->width / 2, win->height / 2}; + + if (!wolf || !wolf->game || !wolf->game->crosshair.cursor) + return; + sprite = wolf->game->crosshair.cursor->sprite; + if (!sprite) + return; + update_crosshair_view(wolf, win, pos.x - center.x, pos.y - center.y); if (!wolf->settings || wolf->settings->crosshair) sfRenderWindow_drawSprite(win->window, sprite, NULL); } diff --git a/src/save/save_entities.c b/src/save/save_entities.c index 51b7ec6..25b481e 100644 --- a/src/save/save_entities.c +++ b/src/save/save_entities.c @@ -53,6 +53,13 @@ static void free_entity_list(list_t **list) *list = NULL; } +static void free_loaded_entity_list(wolf_t *wolf) +{ + free_entity_list(&wolf->list[GAME][MONSTER]); + free_entity_list(&wolf->list[GAME][GARBAGE]); + free_entity_list(&wolf->list[GAME][HEAL]); +} + static int read_entity_line(FILE *file, player_t *entity) { int type = 0; @@ -77,7 +84,7 @@ static int push_loaded_entity(wolf_t *wolf, FILE *file) if (!entity) return -1; if (read_entity_line(file, entity) < 0) { - free(entity); + destroy_player(entity); return -1; } if (entity->type == GARBAGE_T) @@ -95,11 +102,11 @@ int save_read_entities(FILE *file, wolf_t *wolf) if (fscanf(file, " entities %d\n", &count) != 1) return 0; - free_entity_list(&wolf->list[GAME][MONSTER]); - free_entity_list(&wolf->list[GAME][GARBAGE]); - free_entity_list(&wolf->list[GAME][HEAL]); + free_loaded_entity_list(wolf); for (int i = 0; i < count; i++) - if (push_loaded_entity(wolf, file) < 0) + if (push_loaded_entity(wolf, file) < 0) { + free_loaded_entity_list(wolf); return -1; + } return 0; } diff --git a/src/settings/controller_controls.c b/src/settings/controller_controls.c new file mode 100644 index 0000000..8f49638 --- /dev/null +++ b/src/settings/controller_controls.c @@ -0,0 +1,146 @@ +/* +** EPITECH PROJECT, 2026 +** wolf3d +** File description: +** Controller controls settings +*/ + +#include "wolf3d.h" + +static const char *get_controller_label(int id) +{ + const char *labels[] = { + "MOVE FORWARD", "MOVE BACKWARD", "MOVE LEFT", "MOVE RIGHT", + "TURN LEFT", "TURN RIGHT", "SPRINT", "INVENTORY", "EQUIP / USE", + "RELOAD", "FLASHLIGHT", "HEAL", "PAUSE", "SLOT 1", + "SLOT 2", "SLOT 3", "SLOT 4", "SLOT 5", "SLOT 6", "SLOT 7", + "SLOT 8" + }; + + return labels[id]; +} + +static const char *get_controller_name(int id) +{ + const char *names[] = { + "controller_forward_value", "controller_backward_value", + "controller_left_value", "controller_right_value", + "controller_turn_left_value", "controller_turn_right_value", + "controller_sprint_value", "controller_inventory_value", + "controller_equip_value", "controller_reload_value", + "controller_flashlight_value", "controller_heal_value", + "controller_pause_value", "controller_slot_prev_value", + "controller_slot_next_value", "controller_slot_3_value", + "controller_slot_4_value", "controller_slot_5_value", + "controller_slot_6_value", "controller_slot_7_value", + "controller_slot_8_value" + }; + + return names[id]; +} + +static text_t *find_controller_text(wolf_t *wolf, const char *name) +{ + text_t *text = NULL; + + for (list_t *c = wolf->list[SETTINGS][TEXT]; c; c = c->next) { + text = (text_t *)c->data; + if (text && text->name && strcmp(text->name, name) == 0) + return text; + } + return NULL; +} + +static const char *get_controller_value(int id) +{ + const char *values[] = { + "LEFT STICK UP", "LEFT STICK DOWN", "LEFT STICK LEFT", + "LEFT STICK RIGHT", "RIGHT STICK LEFT", "RIGHT STICK RIGHT", + "BUTTON 11", "BUTTON 2", "BUTTON 0", "BUTTON 3", "BUTTON 6", + "BUTTON 1", "BUTTON 9", "BUTTON 4", "BUTTON 5", "FIXED", + "FIXED", "FIXED", "FIXED", "FIXED", "FIXED" + }; + + return values[id]; +} + +void sync_controller_values(wolf_t *wolf, settings_game_t *settings) +{ + text_t *text = NULL; + char buffer[16]; + + if (!wolf || !settings) + return; + for (int i = 0; i < CTRL_COUNT; i++) { + text = find_controller_text(wolf, get_controller_name(i)); + if (!text || !text->text) + continue; + if (i < CTRL_SPRINT) + sfText_setString(text->text, get_controller_value(i)); + else { + snprintf(buffer, sizeof(buffer), "BUTTON %d", + settings->joystick_bindings[i]); + sfText_setString(text->text, buffer); + } + } +} + +static void add_controller_text(wolf_t *wolf, int i, sfVector2f *pos, + sfBool is_value) +{ + char *content = is_value ? (char *)get_controller_value(i) : + (char *)get_controller_label(i); + char *name = is_value ? (char *)get_controller_name(i) : + (char *)get_controller_label(i); + text_t data = {name, content, CONTROLLER, TYPE_SETTINGS, NULL, sfFalse, + NULL, NULL}; + + push_front(&wolf->list[SETTINGS][TEXT], + create_text(&data, wolf->data->font, pos, + &(sfVector2f){0.75f, 0.75f})); +} + +static void add_controller_line(wolf_t *wolf, window_t *window, int i, + sfVector2f *pos) +{ + rect_t data = {(char *)get_controller_label(i), CONTROLLER, NULL, NULL, + NULL, TYPE_SETTINGS, sfFalse}; + + push_front(&wolf->list[SETTINGS][LINE], + create_line(&data, pos, &(sfVector2f){window->width / 3.1f, 1.0f})); +} + +static void add_controller_row(wolf_t *wolf, window_t *window, int i, int rows) +{ + float step = (window->height / 1.25f - 88.0f - + window->height / 3.05f) / (float)(rows - 1); + float x = window->width / 6.8f + window->width * 0.38f * (i / rows); + float y = window->height / 3.05f + step * (i % rows); + + if (step > 39.0f) + step = 39.0f; + if (step < 29.0f) + step = 29.0f; + add_controller_text(wolf, i, &(sfVector2f){x + window->width * 0.22f, y}, + sfTrue); + add_controller_text(wolf, i, &(sfVector2f){x, y}, sfFalse); + add_controller_line(wolf, window, i, &(sfVector2f){x - 11, y + 24}); +} + +void init_controller_controls(wolf_t *wolf, window_t *window) +{ + int rows = (CTRL_COUNT + 1) / 2; + + for (int i = 0; i < CTRL_COUNT; i++) + add_controller_row(wolf, window, i, rows); +} + +int get_controller_action_by_name(const char *name) +{ + if (!name) + return -1; + for (int i = CTRL_SPRINT; i <= CTRL_SLOT_2; i++) + if (strcmp(name, get_controller_name(i)) == 0) + return i; + return -1; +} diff --git a/src/settings/manage_settings.c b/src/settings/manage_settings.c index 87e20a1..5673233 100644 --- a/src/settings/manage_settings.c +++ b/src/settings/manage_settings.c @@ -10,9 +10,9 @@ static int in_list_top(char *name) { char *names[] = {"graphics", "audio", "gameplay", "controls", - "accessibility"}; + "controller", "accessibility"}; - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 6; i++) { if (strcmp(name, names[i]) == 0) return 0; } @@ -69,67 +69,23 @@ static void hover_button(wolf_t *wolf, sfEvent event) } } -static sfFloatRect get_triangle_bounds(text_t *text) -{ - sfFloatRect bounds = sfText_getGlobalBounds(text->text); - - bounds.left -= 25; - bounds.top -= 12; - bounds.width += 50; - bounds.height += 24; - return bounds; -} - -static sfBool in_contains(wolf_t *wolf, text_t *text, int action) -{ - wolf->rebinding_control_action = action; - sfText_setString(text->text, "PRESS KEY"); - return sfTrue; -} - -static sfBool click_controls_value(wolf_t *wolf, sfEvent event) +static sfBool handle_settings_back(wolf_t *wolf, sfEvent event) { - text_t *text = NULL; - sfFloatRect bounds; - int action = -1; - - if (wolf->settings_state != CONTROLS) + if (wolf->rebinding_control_action >= 0) return sfFalse; - for (list_t *c = wolf->list[SETTINGS][TEXT]; c; c = c->next) { - text = (text_t *)c->data; - if (!text || !text->text || text->state != CONTROLS) - continue; - action = get_control_action_by_name(text->name); - if (action < 0) - continue; - bounds = get_triangle_bounds(text); - if (sfFloatRect_contains(&bounds, event.mouseButton.x, - event.mouseButton.y)) { - return in_contains(wolf, text, action); - } - } - return sfFalse; -} - -static sfBool handle_rebind_key(wolf_t *wolf, sfEvent event) -{ - if (event.type != sfEvtKeyPressed || wolf->rebinding_control_action < 0) + if (!is_pause_key(wolf->tmp_settings, event) && + !joystick_action_event(wolf->tmp_settings, CTRL_PAUSE, event)) return sfFalse; - wolf->tmp_settings->key_bindings[wolf->rebinding_control_action] = - event.key.code; - wolf->rebinding_control_action = -1; - sync_controls_values(wolf, wolf->tmp_settings); + wolf->state = wolf->return_state; return sfTrue; } -static sfBool handle_settings_back(wolf_t *wolf, sfEvent event) +static void handle_settings_joystick(wolf_t *wolf, sfEvent event) { - if (event.type != sfEvtKeyPressed || wolf->rebinding_control_action >= 0) - return sfFalse; - if (!is_pause_key(wolf->tmp_settings, event)) - return sfFalse; - wolf->state = wolf->return_state; - return sfTrue; + if (joystick_menu_right(event) && wolf->settings_state < ACCESSIBILITY) + wolf->settings_state++; + if (joystick_menu_left(event) && wolf->settings_state > GRAPHICS) + wolf->settings_state--; } void manage_settings(wolf_t *wolf, sfEvent event) @@ -138,10 +94,13 @@ void manage_settings(wolf_t *wolf, sfEvent event) return; if (handle_rebind_key(wolf, event)) return; + if (handle_rebind_joystick(wolf, event)) + return; if (iskeypressed(sfKeyRight, event) && wolf->settings_state < ACCESSIBILITY) wolf->settings_state++; if (iskeypressed(sfKeyLeft, event) && wolf->settings_state > GRAPHICS) wolf->settings_state--; + handle_settings_joystick(wolf, event); if (event.type == sfEvtMouseButtonPressed && event.mouseButton.button == sfMouseLeft && click_controls_value(wolf, event) == sfFalse diff --git a/src/settings/settings.c b/src/settings/settings.c index cbb7c66..25e4da4 100644 --- a/src/settings/settings.c +++ b/src/settings/settings.c @@ -118,6 +118,7 @@ static void sync_settings_values(wolf_t *wolf, settings_game_t *settings) sync_audio_values(wolf, settings); sync_gameplay_values(wolf, settings); sync_controls_values(wolf, settings); + sync_controller_values(wolf, settings); } void reset_settings(wolf_t *wolf) diff --git a/src/settings/settings_buttons.c b/src/settings/settings_buttons.c index 41dd683..dea2be4 100644 --- a/src/settings/settings_buttons.c +++ b/src/settings/settings_buttons.c @@ -52,21 +52,21 @@ static void create_settings_title(wolf_t *wolf, float x, float y, text_t *info) static void create_settings(wolf_t *wolf, text_t *infos, window_t *window) { - float start_x = (window->width - (5 * 300.0f)) / 2.0f + 150.0f; + float start_x = (window->width - (6 * 250.0f)) / 2.0f + 125.0f; float y = window->height / 4.8f; float second_y = window->height / 1.25f; - for (int i = 0; i < 9; i++) { - if (i == 8) + for (int i = 0; i < 10; i++) { + if (i == 9) create_settings_title(wolf, window->width / 2.5f, window->height / 3.7f, &infos[i]); - if (i < 5) + if (i < 6) create_settings_box(wolf, - &(sfVector2f){start_x + i * 300.0f, y}, &infos[i], - &(sfVector2f){280.0f, 70.0f}); - if (i >= 5 && i < 8) + &(sfVector2f){start_x + i * 250.0f, y}, &infos[i], + &(sfVector2f){230.0f, 70.0f}); + if (i >= 6 && i < 9) create_settings_box(wolf, - &(sfVector2f){start_x + 150.0f + (i - 5) * 450.0f, second_y}, + &(sfVector2f){start_x + 150.0f + (i - 6) * 450.0f, second_y}, &infos[i], &(sfVector2f){380.0f, 70.0f}); } } @@ -74,14 +74,16 @@ static void create_settings(wolf_t *wolf, text_t *infos, window_t *window) void init_settings_buttons(wolf_t *wolf, window_t *window) { char *names[] = {"graphics", "audio", "gameplay", "controls", - "accessibility", "reset", "back", "apply", "settings"}; + "controller", "accessibility", "reset", "back", "apply", "settings"}; char *contents[] = {"GRAPHICS", "AUDIO", "GAMEPLAY", "CONTROLS", - "ACCESSIBILITY", "RESET TO DEFAULT", "BACK", "APPLY", "SETTINGS"}; - int states[] = {GRAPHICS, AUDIO, GAMEPLAY, CONTROLS, ACCESSIBILITY, + "CONTROLLER", "ACCESSIBILITY", "RESET TO DEFAULT", "BACK", "APPLY", + "SETTINGS"}; + int states[] = {GRAPHICS, AUDIO, GAMEPLAY, CONTROLS, CONTROLLER, + ACCESSIBILITY, -1, -1, -1, -1}; - text_t infos[9]; + text_t infos[10]; - for (int i = 0; i < 9; i++) + for (int i = 0; i < 10; i++) infos[i] = (text_t){names[i], contents[i], states[i], TYPE_SETTINGS, NULL, sfTrue, NULL, NULL}; create_settings(wolf, infos, window); diff --git a/src/settings/settings_rebind.c b/src/settings/settings_rebind.c new file mode 100644 index 0000000..c9cd8ab --- /dev/null +++ b/src/settings/settings_rebind.c @@ -0,0 +1,92 @@ +/* +** EPITECH PROJECT, 2026 +** wolf3d +** File description: +** Settings controls rebinding +*/ + +#include "wolf3d.h" + +static sfFloatRect get_control_bounds(text_t *text) +{ + sfFloatRect bounds = sfText_getGlobalBounds(text->text); + + bounds.left -= 25; + bounds.top -= 12; + bounds.width += 50; + bounds.height += 24; + return bounds; +} + +static int get_rebind_action(wolf_t *wolf, text_t *text) +{ + if (wolf->settings_state == CONTROLS) + return get_control_action_by_name(text->name); + return get_controller_action_by_name(text->name); +} + +static sfBool select_rebind_action(wolf_t *wolf, text_t *text, int action) +{ + wolf->rebinding_control_action = action; + sfText_setString(text->text, action >= CTRL_COUNT ? "PRESS BUTTON" : + "PRESS KEY"); + return sfTrue; +} + +static sfBool try_click_rebind(wolf_t *wolf, text_t *text, sfEvent event) +{ + sfFloatRect bounds; + int action = get_rebind_action(wolf, text); + + if (action < 0) + return sfFalse; + bounds = get_control_bounds(text); + if (!sfFloatRect_contains(&bounds, event.mouseButton.x, + event.mouseButton.y)) + return sfFalse; + if (wolf->settings_state == CONTROLLER) + action += CTRL_COUNT; + return select_rebind_action(wolf, text, action); +} + +sfBool click_controls_value(wolf_t *wolf, sfEvent event) +{ + text_t *text = NULL; + + if (wolf->settings_state != CONTROLS && wolf->settings_state != CONTROLLER) + return sfFalse; + for (list_t *c = wolf->list[SETTINGS][TEXT]; c; c = c->next) { + text = (text_t *)c->data; + if (!text || !text->text || text->state != wolf->settings_state) + continue; + if (try_click_rebind(wolf, text, event)) + return sfTrue; + } + return sfFalse; +} + +sfBool handle_rebind_key(wolf_t *wolf, sfEvent event) +{ + int action = wolf->rebinding_control_action; + + if (event.type != sfEvtKeyPressed || action < 0 || action >= CTRL_COUNT) + return sfFalse; + wolf->tmp_settings->key_bindings[action] = event.key.code; + wolf->rebinding_control_action = -1; + sync_controls_values(wolf, wolf->tmp_settings); + return sfTrue; +} + +sfBool handle_rebind_joystick(wolf_t *wolf, sfEvent event) +{ + int action = wolf->rebinding_control_action - CTRL_COUNT; + + if (event.type != sfEvtJoystickButtonPressed || action < 0 || + action >= CTRL_COUNT) + return sfFalse; + wolf->tmp_settings->joystick_bindings[action] = event.joystickButton.button; + wolf->settings->joystick_bindings[action] = event.joystickButton.button; + wolf->rebinding_control_action = -1; + sync_controller_values(wolf, wolf->tmp_settings); + return sfTrue; +} diff --git a/src/stage/bsp_room_shapes.c b/src/stage/bsp_room_shapes.c new file mode 100644 index 0000000..ad65242 --- /dev/null +++ b/src/stage/bsp_room_shapes.c @@ -0,0 +1,111 @@ +/* +** EPITECH PROJECT, 2026 +** wolf3d +** File description: +** BSP room shape carving helpers +*/ + +#include "../include/wolf3d.h" + +static void carve_room_rect(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH]) +{ + int y; + + for (y = node->ry; y < node->ry + node->rh; y++) + carve_h(map, node->rx, node->rx + node->rw - 1, y); +} + +static void carve_room_diamond(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH]) +{ + int cx; + int cy; + int rx; + int ry; + int limit; + int x; + int y; + + cx = node->rx + node->rw / 2; + cy = node->ry + node->rh / 2; + rx = node->rw / 2; + ry = node->rh / 2; + limit = rx * ry; + for (y = node->ry; y < node->ry + node->rh; y++) + for (x = node->rx; x < node->rx + node->rw; x++) + map[y][x] = abs(x - cx) * ry + abs(y - cy) * rx <= limit ? 0 : + map[y][x]; +} + +static void carve_room_cross(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH]) +{ + int cx; + int cy; + + cx = node->rx + node->rw / 2; + cy = node->ry + node->rh / 2; + carve_h(map, node->rx, node->rx + node->rw - 1, cy); + carve_v(map, node->ry, node->ry + node->rh - 1, cx); + if (node->rw > 4) + carve_h(map, node->rx, node->rx + node->rw - 1, cy - 1); + if (node->rh > 4) + carve_v(map, node->ry, node->ry + node->rh - 1, cx - 1); +} + +int bsp_room_shape_from_roll(int roll) +{ + if (roll < BSP_ROOM_RECT_RATE) + return 0; + if (roll < BSP_ROOM_RECT_RATE + BSP_ROOM_DIAMOND_RATE) + return 1; + if (roll < BSP_ROOM_RECT_RATE + BSP_ROOM_DIAMOND_RATE + + BSP_ROOM_CROSS_RATE) + return 2; + return 3; +} + +static void carve_blob_row(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH], + int y) +{ + int cx = node->rx + node->rw / 2; + int cy = node->ry + node->rh / 2; + int span = node->rw / 2; + int start; + int end; + + span = span - (abs(y - cy) * span) / (node->rh / 2 + 1); + span += (rand() % 3) - 1; + if (span < 1) + span = 1; + start = cx - span < node->rx + 1 ? node->rx + 1 : cx - span; + end = cx + span > node->rx + node->rw - 2 ? node->rx + node->rw - 2 : + cx + span; + for (int x = start; x <= end; x++) + map[y][x] = 0; +} + +static void carve_room_blob(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH]) +{ + int y; + + for (y = node->ry; y < node->ry + node->rh; y++) { + carve_blob_row(node, map, y); + } +} + +void bsp_carve_room_shape(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH], + int shape) +{ + if (shape == 0) { + carve_room_rect(node, map); + return; + } + if (shape == 1) { + carve_room_diamond(node, map); + return; + } + if (shape == 2) { + carve_room_cross(node, map); + return; + } + carve_room_blob(node, map); +} diff --git a/src/stage/bsp_rooms.c b/src/stage/bsp_rooms.c index 6351146..7100bc4 100644 --- a/src/stage/bsp_rooms.c +++ b/src/stage/bsp_rooms.c @@ -27,24 +27,28 @@ void carve_v(int map[MAP_HEIGHT][MAP_WIDTH], int y1, int y2, int x) map[y][x] = 0; } -static void carve_room(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH]) -{ - for (int y = node->ry; y < node->ry + node->rh; y++) - carve_h(map, node->rx, node->rx + node->rw - 1, y); -} - static void bsp_place_room(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH]) { - int max_w = node->w - BSP_ROOM_MARGIN * 2; - int max_h = node->h - BSP_ROOM_MARGIN * 2; + int max_w; + int max_h; + int min_w; + int min_h; + int roll; + int shape; + max_w = node->w - BSP_ROOM_MARGIN * 2; + max_h = node->h - BSP_ROOM_MARGIN * 2; if (max_w < 3 || max_h < 3) return; - node->rw = 3 + rand() % (max_w - 2); - node->rh = 3 + rand() % (max_h - 2); + min_w = BSP_ROOM_MIN_SIZE > max_w ? max_w : BSP_ROOM_MIN_SIZE; + min_h = BSP_ROOM_MIN_SIZE > max_h ? max_h : BSP_ROOM_MIN_SIZE; + node->rw = min_w + rand() % (max_w - min_w + 1); + node->rh = min_h + rand() % (max_h - min_h + 1); node->rx = node->x + BSP_ROOM_MARGIN + rand() % (max_w - node->rw + 1); node->ry = node->y + BSP_ROOM_MARGIN + rand() % (max_h - node->rh + 1); - carve_room(node, map); + roll = rand() % 100; + shape = bsp_room_shape_from_roll(roll); + bsp_carve_room_shape(node, map, shape); } void bsp_place_rooms(bsp_t *node, int map[MAP_HEIGHT][MAP_WIDTH]) diff --git a/src/stage/card.c b/src/stage/card.c index aa11767..fd3cbb5 100644 --- a/src/stage/card.c +++ b/src/stage/card.c @@ -35,14 +35,18 @@ void create_card(wolf_t *wolf, card_t *card, sfVector2f *pos, card->hoover = sfFalse; wolf->player->weapon = NULL; card->text = sfText_create(); + if (!card->text) + return; sfText_setFont(card->text, wolf->data->font); create_bonus(card, rd, &wolf->game->inv); c = get_color(card->bonus, rd); txt = sfText_getGlobalBounds(card->text); - sfText_setOutlineThickness(card->text, 3); - sfText_setOrigin(card->text, (sfVector2f){ - txt.left + txt.width / 2.0f, txt.top + txt.height / 2.0f}); + card_setup_text(card, &txt); card->rect = create_rect(size, pos, &c); + if (!card->rect) { + sfText_destroy(card->text); + card->text = NULL; + } } static void destroy_card_set(intermission_t *inter) @@ -84,19 +88,6 @@ void init_card(wolf_t *wolf, intermission_t *inter) reset_card_state(wolf, inter); } -static void move_card(window_t *win, card_t *card, float step) -{ - sfVector2f pos = sfRectangleShape_getPosition(card->rect); - float target_y = win->height / 2.0f; - - if (pos.y <= target_y) - return; - pos.y -= step; - if (pos.y < target_y) - pos.y = target_y; - sfRectangleShape_setPosition(card->rect, pos); -} - void recreate_card_set(wolf_t *wolf, window_t *win, intermission_t *inter) { float card_w = win->width * CARD_W; @@ -138,6 +129,8 @@ void draw_card(wolf_t *wolf, intermission_t *inter) sfVector2f pos; for (int i = 0; i < 3; i++) { + if (!inter->card[i].rect || !inter->card[i].text) + continue; pos = sfRectangleShape_getPosition(inter->card[i].rect); sfRenderWindow_drawRectangleShape(wolf->window_data->window, inter->card[i].rect, NULL); @@ -155,13 +148,16 @@ void rise_card(wolf_t *wolf, intermission_t *inter) float dt = sfTime_asSeconds(sfClock_restart(inter->clock)); float step = CARD_SPEED * dt; int done = 0; + sfVector2f pos; if (inter->done) return; for (int i = 0; i < 3; i++) { - move_card(win, &inter->card[i], step); - if (sfRectangleShape_getPosition(inter->card[i].rect).y - <= win->height / 2.0f) + if (!inter->card[i].rect) + continue; + card_rise_single(win, &inter->card[i], step); + pos = sfRectangleShape_getPosition(inter->card[i].rect); + if (pos.y <= win->height / 2.0f) done++; } inter->done = (done == 3) ? sfTrue : sfFalse; diff --git a/src/stage/card_cursor.c b/src/stage/card_cursor.c new file mode 100644 index 0000000..5a2d1f1 --- /dev/null +++ b/src/stage/card_cursor.c @@ -0,0 +1,51 @@ +/* +** EPITECH PROJECT, 2026 +** wolf3d +** File description: +** Card cursor helpers +*/ + +#include "../include/wolf3d.h" + +int card_under_cursor(wolf_t *wolf, intermission_t *inter) +{ + sfVector2i pos = sfMouse_getPositionRenderWindow( + wolf->window_data->window); + sfFloatRect bounds; + + for (int i = 0; i < 3; i++) { + bounds = sfRectangleShape_getGlobalBounds(inter->card[i].rect); + if (sfFloatRect_contains(&bounds, pos.x, pos.y)) + return i; + } + return -1; +} + +int hovered_card(intermission_t *inter) +{ + for (int i = 0; i < 3; i++) + if (inter->card[i].hoover) + return i; + return -1; +} + +void move_card_cursor(wolf_t *wolf) +{ + sfVector2i pos = sfMouse_getPositionRenderWindow( + wolf->window_data->window); + float x = joystick_axis(sfJoystickX); + float y = joystick_axis(sfJoystickY); + float speed = 18.0f; + + pos.x += x * speed; + pos.y += y * speed; + if (pos.x < 0) + pos.x = 0; + if (pos.y < 0) + pos.y = 0; + if (pos.x > wolf->window_data->width) + pos.x = wolf->window_data->width; + if (pos.y > wolf->window_data->height) + pos.y = wolf->window_data->height; + sfMouse_setPositionRenderWindow(pos, wolf->window_data->window); +} diff --git a/src/stage/card_helpers.c b/src/stage/card_helpers.c new file mode 100644 index 0000000..8e5eb4e --- /dev/null +++ b/src/stage/card_helpers.c @@ -0,0 +1,33 @@ +/* +** EPITECH PROJECT, 2026 +** stage +** File description: +** card helpers +*/ + +#include "../../include/wolf3d.h" +#include "stage_card.h" + +void card_setup_text(card_t *card, const sfFloatRect *txt) +{ + sfText_setOutlineThickness(card->text, 3); + sfText_setOrigin(card->text, (sfVector2f){txt->left + txt->width / 2.0f, + txt->top + txt->height / 2.0f}); +} + +void card_rise_single(window_t *win, card_t *card, float step) +{ + sfVector2f pos; + float target_y; + + if (!card->rect) + return; + pos = sfRectangleShape_getPosition(card->rect); + target_y = win->height / 2.0f; + if (pos.y <= target_y) + return; + pos.y -= step; + if (pos.y < target_y) + pos.y = target_y; + sfRectangleShape_setPosition(card->rect, pos); +} diff --git a/src/stage/card_input.c b/src/stage/card_input.c index aa9f01e..fa8a450 100644 --- a/src/stage/card_input.c +++ b/src/stage/card_input.c @@ -119,9 +119,31 @@ static void select_card_by_key(wolf_t *wolf, intermission_t *inter, after_card_pick(wolf, inter); } +static void handle_card_joystick(wolf_t *wolf, intermission_t *inter, + sfEvent event) +{ + int index = hovered_card(inter); + sfBool pressed = joystick_action_down(wolf->settings, CTRL_EQUIP); + + (void)event; + if (!pressed) { + inter->confirm_down = sfFalse; + return; + } + if (inter->confirm_down) + return; + inter->confirm_down = sfTrue; + if (index < 0) + index = card_under_cursor(wolf, inter); + if (index >= 0) + select_card_by_key(wolf, inter, index); +} + void handle_card_input(wolf_t *wolf, intermission_t *inter, sfEvent event) { + move_card_cursor(wolf); click_card(wolf, wolf->window_data, inter); + handle_card_joystick(wolf, inter, event); if (iskeypressed(sfKeyA, event)) select_card_by_key(wolf, inter, 0); if (iskeypressed(sfKeyC, event)) diff --git a/src/stage/reset_network.c b/src/stage/reset_network.c index 0c26dab..a9d17e4 100644 --- a/src/stage/reset_network.c +++ b/src/stage/reset_network.c @@ -58,6 +58,7 @@ void notify_card_ready(wolf_t *wolf) return; memset(&pkt, 0, sizeof(pkt)); pkt.type = PKT_GAME_STATE; + pkt.player_id = wolf->net.player_id; pkt.data[0] = 'K'; client_send_packet(&wolf->net, &pkt); }