so_long 구현하기

이민규·2023년 6월 9일
0

42seoul

목록 보기
8/24

완성된 프로젝트


과제 설명

so_long과제는 map에서 수집품을모아 포탈로 탈출하면되는 게임을 만드는 것이다


map 유효성 검사

map은 5가지 요소로 구성되어져 있다

  • 0(빈공간) : 플레이어가 지나다닐 수 있는 빈 공간이다
  • 1(벽) : 플레이어가 지나다닐 수 없게 막혀져 있는 벽을 의미한다
  • C(수집품) : 플레이어가 수집해야할 수집품이다 map에 있는 모든 수집품을 모으기 전에 플레이어는 탈출 할 수 없다
  • E(포탈) : 플레이어가 탈출 할 포탈이다 맵에 있는 모든 수집품을 모으기 전에는 포탈을 이용하지 못한다
  • P(플레이어) : 플레이어의 시작 위치를 정해준다

유효한 맵의 조건

  1. 지도는 하나의 포탈(E) 하나의 플레이어(P) 최소한 하나의 수집품(C)을 포함하고 있어야한다
  2. 지도는 반드시 직사각형 모양이어야 한다
  3. 지도는 벽(1)으로 둘러싸여 있어야한다
  4. 지도에 유효한 경로가 있는지 확인해야 한다
    👀 예를들어 수집품이 벽에 막혀있어서 수집품을 다 모으지 못하거나 포탈이 벽이 막혀 탈출을 못하는 경우등이 해당된다
  5. .ber 형식으로 되어있어야 한다

map 유효성 검사 코드

Check_map_name

지도가 .ber파일이 맞는지 검사한다

int	check_map_name(char *arr)
{
	unsigned long long	len;

	len = ft_strlen(arr); //map 파일이름길이를 구한다
	if (len <= 4) // map파일이 4글자 이하라면 오류이므로 1을 반환한다
		return (1);
	if (arr[len - 1] == 'r' && arr[len - 2] == 'e' && \
			arr[len - 3] == 'b' && arr[len - 4] == '.')
            // 맵이름의 맨 뒤에서부터 한글자씩 비교해 .ber파일이 맞는지 검사한다
		return (0); // 맞으면 정상맵으로 0을 반환한다
	return (1); // 아니면 맵 오류로 1을 반환한다
}

check_map_item

지도에 포탈과 플레이어 수집품이 정상 갯수만큼 존재하는지 확인하는 부분이다

int	check_map_item(char *map, t_map_check *map_check, int i)
{
	while (map[i]) // map을 전체 탐색한다
	{
		if (map[i] == 'C') // 수집품(C)가 발견되었으면 map_check->c의 값을 1증가시킨다
			map_check->c += 1;
		else if (map[i] == 'P') // 플레이어(P)가 발견되었으면 map_check->p의 값을 증가시킨다
		{
			map_check->player = i;
			map_check->p += 1;
		}
		else if (map[i] == 'E')// 포탈(E)가 발견되었으면 map_check->p의 값을 증가시킨다
			map_check->e += 1;
		else if (map[i] == '0' || map[i] == '1')
			;
		else if (map[i] == '\n')
			map_check->y += 1;
		else
			return (1);
		i++;
	}
	if (map_check->p != 1 || map_check->e != 1 || map_check->c <= 0)
    //플레이어, 포탈, 수집품을 갯수에 이상이 있을 시 1을 반환하여 오류를 표시한다
		return (1);
	return (0);
}

check_map_closed

지도가 벽으로 둘러싸여 있는지 확인한다

int	check_map_closed(char *map, t_map_check *map_check, int i)
{
	while (map[i]) //map을 전체 탐색한다
	{
		if (i < map_check->x && map[i] != '1')
        // i가 map_check->x 보다 작다는 말은 map의 맨 윗줄을 의미한다
			return (1);
		else if (i % (map_check->x + 1) == 0 && map[i] != '1')
        // i % map_check->x + 1 == 0이라는 말은 맵의 맨 왼쪽줄을 의미한다 
        // +1을 해준이유는 map안에 개행이 포함되어져 있어서 1을 더해준다
			return (1);
		else if ((i % (map_check->x + 1)) == map_check->x - 1 && map[i] != '1')
        // i % map_check->x + 1 == map_check->x - 1 이라는 말은 맵의 맨 오른쪽줄을 의미한다
        // -1을 해준이유는 map안에 개행이 포함되어져 있어서 1을 빼준다
			return (1);
		else if (i >= (map_check->x + 1) * (map_check->y - 1) && \
				i < ((map_check->x + 1) * map_check->y) - 1)
                // x에 y - 1을 곱해준 위치와 x에 y를 곱한위치에 1을 뺀 사이를 검사하여 맵의 맨 아랫줄을 검사한다
		{
			if (map[i] != '1')
				return (1);
		}
		i++;
	}
	return (0); // 이상이 없으면 0이 반환된다
}

dfs

맵에 유효한 경로가 있는지 확인하는 코드이다 dfs를 사용하여 유효한지 검사한다
👀 dfs(깊이우선탐색) : dfs란 전체 탐색을 하는 알고리즘 중 하나로 하나의 경로로 쭉 파고들어 끝까지 확인한 후 다시 이전으로 돌아와서 다음경로를 끝까지 파고든다 이러한 동작은 모든 장소를 확인할때 까지 반복해서 동작한다

void	dfs(char *map, t_map_check *map_check, int player)
{
	int	move[4];
	int	i;

	move[0] = player - map_check->x - 1; // 현재 플레이어를 위로 한칸 움직인다
	move[1] = player + 1; // 현재 플레이어를 오른쪽으로 한칸 움직인다
	move[2] = player - 1; // 현재 플레이어를 왼쪽으로 한칸 움직인다
	move[3] = player + map_check->x + 1; // 현재 플레이어를 아랫쪽으로 한칸 움직인다
	i = 0;
	map_check->dfs_map[player] = 1; // 플레이어가 방문했으면 dfs_map에 해당위치를 1로 갱신해준다
	if (map[player] == 'C') // 현재위치에 수집품이 있다면 dfs_c 갯수를 하나 늘려준다
		map_check->dfs_c += 1;
	while (i != 4) // 배열을 0~3까지넣으면서 플레이어를 현재위치에서 상하좌우로 이동시켜보며 검사를 진행한다
	{
		if (map[move[i]] != '1' && map_check->dfs_map[move[i]] != 1)
        // 플레이어를 움직인 위치가 1(벽)이 아니고 dfs_map != 1 방문한적이 없으면 이동시킨다
		{
			if (map[move[i]] == 'E') // 이동한위치에 포탈이 있다면 dfs_e 갯수를 하나 늘린다
				map_check->dfs_e += 1;
			else
				dfs(map, map_check, move[i]); 
                // 현재 플레이어 위치를 갱신하여 재귀함수를 호출한다
		}
		i++;
	}
}

이렇게 dfs에서 얻은 값 들로 뒷부분 코드에서 맵에서 전체 탐색한 수집품 갯수와 dfs를 진행하면서 얻은 수집품 갯수를 비교 포탈에 방문했는지도 확인하여 맵에 유효한 경로가 존재하는지 검사한다

if (map_check.dfs_c == map_check.c && map_check.dfs_e >= 1)
	{
		free(map_check.dfs_map);
		return (0);
	}

game_map 설정

so_long

game이 실행되는 동안 모든 입력은 so_long함수에서 처리가 되어진다

int	so_long(char *game_map)
{
	t_param	maps;

	maps.map = game_map;
	map_cnt(&maps); // map의 수집품 갯수를 카운트 해준다, 디스플레이 크기보다 맵의 크기가 큰지 테스트한다
	enemy_add(&maps, 0, 0, 500); // BONUS과제 적을 랜덤하게 추가해준다
	maps.mlx = mlx_init(); // 디스플레이 장치와 연결하여 maps.mlx에 연결해준다
	image_init(&maps); // 게임에 쓸 image파일들을 연결해준다
	maps.user.x = 0;
	maps.user.direction = 1;
	maps.check_attack = 0;
	maps.waitting_attack = 0;
	mlx_key_hook(maps.win, &key_press, &maps); // key입력을 받아 동작할 hook함수이다
	mlx_loop_hook(maps.mlx, &draw, &maps); // loop를 진행할때마다 동작할 hook함수이다
	mlx_hook(maps.win, 17, 0, &red_botton_delete, &maps); // red_botton(종료버튼)을 눌렀을때 동작할 hook함수이다
	mlx_loop(maps.mlx); // loop함수를 호출하여 게임이 꺼지지않는 상태로 event를 받게 만들어준다
	return (0);
}

map_cnt

맵의 크기가 디스플레이 크기보다 큰지 테스트하고 맵의 수집품 갯수를 세팅해준다

void	map_cnt(t_param *maps)
{
	int	i;
	int	max_win_width;
	int	max_win_height;

	maps->win_height = 0;
	maps->win_width = 0;
	maps->collection = 0;
	i = 0;
	while (maps->map[i])
	{
		if (maps->map[i] == '\n')
			maps->win_height += 1; // 개행을 발견할때 마다 height를 증가시킨다
		else if (maps->map[i] == 'C')
			maps->collection += 1; // 수집품을 발견할때 마다 수집품 갯수를 늘려준다
		i++;
	}
	maps->win_width = (i - maps->win_height) 
    // maps->win_height; // height를 이용하여 맵의 width를 계산해준다
	mlx_get_screen_size(maps->mlx, &max_win_width, &max_win_height);
    // 현재 디스플레이 장치의 scrren size를 구해주는 함수이다
	if (max_win_width < maps->win_width * 64 || \
	max_win_height < (maps->win_height + 1) * 64)
    // 64x64픽셀 크기의 이미지를 사용하기 때문에 width 와 height에 64를 곱해서 
    // 그 크기를 이용해 맵의 크기가 디스플레이 크기보다 큰지 검사해준다
		error(4);
	maps->move_cnt = 0;
}

enemy_add

맵에는 적의 위치정보가 포함되어져 있지 않기 때문에 랜덤한 위치에 적을 생성시켜주는 함수이다

void	enemy_add(t_param *maps, int enemy, int enemy_position, int cnt)
{
	if (maps->win_width * maps->win_height < 90)
    // 맵의 크기에 맞게 임의로 적의 숫자를 세팅해준다
		enemy = 1;
	else if (maps->win_width * maps->win_height < 150)
		enemy = 2;
	else if (maps->win_width * maps->win_height < 210)
		enemy = 3;
	else if (maps->win_width * maps->win_height < 270)
		enemy = 4;
	else
		enemy = 5;
	srand(time(NULL)); // srand함수에 현재시간을 넣어줘서 rand함수의 seed값을 세팅해준다
	while (enemy != 0 && cnt != 0)
	{
		enemy_position = rand() % ((maps->win_width + 1) * maps->win_height);
        // rand함수를 이용하여 랜덤으로 적의 위치를 정해 만들어준다
		if ((maps->map)[enemy_position] == '0')
		{
			(maps->map)[enemy_position] = 'L';
			enemy--;
		}
		cnt--;
	}
}

image_init

프로그램 밖에 있는 image file들을 프로그램안에 있는 구조체에 연결해주는 함수이다

void	image_init(t_param *maps)
{
	maps->win = mlx_new_window(maps->mlx, maps->win_width * 64, \
			(maps->win_height + 1) * 64, "minkyole's so_long");
            // window size를 map_cnt에서 계산한 width height값을 이용하여 만들어준다
	maps->user.image = mlx_xpm_file_to_image(maps->mlx, \
			"texture/p.xpm", &maps->user.wi, &maps->user.he);
            // 각각의 구조체에 image를 매칭시켜준다
	maps->wall.image = mlx_xpm_file_to_image(maps->mlx, \
			"texture/1.xpm", &maps->wall.wi, &maps->wall.he);
	maps->land.image = mlx_xpm_file_to_image(maps->mlx, \
			"texture/0.xpm", &maps->land.wi, &maps->land.he);
	maps->chase.image = mlx_xpm_file_to_image(maps->mlx, \
			"texture/c.xpm", &maps->chase.wi, &maps->chase.he);
	maps->potal[0].image = mlx_xpm_file_to_image(maps->mlx, \
			"texture/close_potal.xpm", &maps->potal[0].wi, &maps->potal[0].he);
	maps->potal[1].image = mlx_xpm_file_to_image(maps->mlx, \
			"texture/open_potal.xpm", &maps->potal[1].wi, &maps->potal[1].he);
	user_sprite_init(maps); 
    // sprite들은 여러장의 image가 순차적으로 나와 움직이는것처럼 보여지므로 
    // 연결시켜줄 image가 많아 함수를 따로 빼서 초기화해주었다
	enemy_sprite_init(maps);
	attack_sprite_init1(maps);
	attack_sprite_init2(maps);
	attack_sprite_init3(maps);
	keybord_init(maps);
}

map draw

draw

loop_hook함수의 콜백함수로서 loop가 이루어질때마다 화면에 image를 그려준다

int	draw(t_param *maps)
{
	unsigned long long	i;
	static long long	cnt; // 동적인 image를 그려줄때 사용할 static변수이다

	i = 0;
	while (maps->map[i]) // 맵을 전체탐색하면서 순서대로 그려준다
	{
		draw_map(maps, i, cnt); // 맵을 그려준다
		i++;
	}
	draw_attack(maps, i, ((maps->win_width + 1) * \
	(maps->win_height + 1)) - 2, maps->move_cnt);
    // 공격모션을 그려준다
	cnt++; // 동적인 image를 그려줄때 사용할 cnt를 증가시켜준다
	return (0);
}

draw_map

현재 위치의 맵이 무었인지 확인하여 그에 맞는 이미지를 그려준다

void	draw_map(t_param *maps, unsigned long long i, int cnt)
{
	draw_image(1, maps, i);
	if (maps->map[i] == '1')
		draw_image(2, maps, i);
	else if (maps->map[i] == 'P' && \
	(maps->user.direction == 1 || maps->user.direction == 3))
    //user.direction에 맞춰 플레이어의 방향을 설정해서 그려줍니다
		draw_user_r(cnt, maps, i);
	else if (maps->map[i] == 'P' && \
	(maps->user.direction == 2 || maps->user.direction == 4))
		draw_user_l(cnt, maps, i);
	else if (maps->map[i] == 'C')
		draw_image(4, maps, i);
	else if (maps->map[i] == 'E' && maps->collection == 0)
		draw_image(6, maps, i);
	else if (maps->map[i] == 'E')
		draw_image(5, maps, i);
	else if (maps->map[i] == 'R')
		draw_enemy_r(cnt, maps, i);
	else if (maps->map[i] == 'L')
		draw_enemy_l(cnt, maps, i);
}

draw_image

flag에 맞춰서 해당하는 image를 mlx_put_image_to_window함수를 이용하여 그려준다 (정적인 image)

void	draw_image(int flag, t_param *maps, unsigned long long i)
{
	if (flag == 1)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->land).image, i % (maps->win_width + 1) * 64, \
			i / (maps->win_width + 1) * 64);
            // 해당하는 image와 좌표를 계산하여 그려줍니다
	else if (flag == 2)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->wall).image, i % (maps->win_width + 1) * 64, \
			i / (maps->win_width + 1) * 64);
	else if (flag == 3)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->user).image, i % (maps->win_width + 1) * 64, \
			i / (maps->win_width + 1) * 64);
	else if (flag == 4)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->chase).image, i % (maps->win_width + 1) * 64, \
			i / (maps->win_width + 1) * 64);
	else if (flag == 5)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->potal[0]).image, i % (maps->win_width + 1) * 64, \
			i / (maps->win_width + 1) * 64);
	else if (flag == 6)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->potal[1]).image, i % (maps->win_width + 1) * 64, \
			i / (maps->win_width + 1) * 64);
}

draw_user_r

동적인 image를 그릴때 사용하는 함수중 하나이다 loop를 할때마다 증가하는 static변수인 cnt를 사용하여 image를 번갈아가며 그려 애니메이션 느낌을 준다

void	draw_user_r(int cnt, t_param *maps, unsigned long long i)
{
	if (cnt % 80 <= 20 // cnt에 맞춰서 image들을 순서대로 그려준다
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->user_sprite[0]).image, i % (maps->win_width + 1) * 64, \
			i / (maps->win_width + 1) * 64);
	else if (cnt % 80 <= 40)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->user_sprite[1]).image, i % (maps->win_width + 1) * 64, \
			i / (maps->win_width + 1) * 64);
	else if (cnt % 80 <= 60)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->user_sprite[2]).image, i % (maps->win_width + 1) * 64, \
			i / (maps->win_width + 1) * 64);
	else
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->user_sprite[3]).image, i % (maps->win_width + 1) * 64, \
			i / (maps->win_width + 1) * 64);
	maps->user.x = i;
}

draw_attack

score를 그려주고 공격 버튼을 눌러졌는지 확인하여 공격모션을 그려준다

void	draw_attack(t_param *maps, int i, int score_end, int move_cnt)
{
	static int	attack_cnt;
    // cnt를 사용하면 공격이 처음부터 그려지지않고 3번 image부터 그려지는 등 오류가 발생하여 attack에서 사용할 attack_cnt를 선언해준다

	while (i <= score_end)
    // 플레이어가 이동할때마다 늘어날 score를 그려준다
	{
		draw_image(1, maps, score_end);
		draw_score1(move_cnt % 10, maps, score_end);
        // move_cnt % 10이 0 ~ 4일 경우 그려준다
		draw_score2(move_cnt % 10, maps, score_end);
        // move_cnt % 10이 5 ~ 9일 경우 그려준다
		move_cnt = move_cnt / 10
		score_end--;
	}
	if (maps->check_attack == 1)
    // check_attack이란 공격방향을 결정해주는 플래그로check_attack에 맞춰 함수를 호출한다
		draw_right_attack(attack_cnt, maps, \
		maps->user.x + 1, (maps->win_width + 1));
	else if (maps->check_attack == 2)
		draw_left_attack(attack_cnt, maps, \
		maps->user.x - 2, (maps->win_width + 1));
	else if (maps->check_attack == 3)
		draw_up_attack(attack_cnt, maps, \
		maps->user.x - maps->win_width - 1, (maps->win_width + 1));
	else if (maps->check_attack == 4)
		draw_down_attack(attack_cnt, maps, \
		maps->user.x, (maps->win_width + 1));
	else
		attack_cnt = 0; // 공격동작이 끝나면 attack_cnt를 초기화해준다
	attack_cnt++; // attack_cnt를 계속 증가시켜준다
}

draw_right_attack

공격모션을 그리는 방법이다

void	draw_right_attack(int cnt, t_param *maps, int i, int width)
{
	if (cnt % 30 <= 5)
    // attack_cnt를 이용하여 순차적으로 공격모션을 출력한다
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->attack_sprite[0]).image, i % width * 64, i / width * 64);
	else if (cnt % 30 <= 10)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->attack_sprite[1]).image, i % width * 64, i / width * 64);
	else if (cnt % 30 <= 15)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->attack_sprite[2]).image, i % width * 64, i / width * 64);
	else if (cnt % 30 <= 20)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->attack_sprite[3]).image, i % width * 64, i / width * 64);
	else if (cnt % 30 <= 25)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->attack_sprite[4]).image, i % width * 64, i / width * 64);
	else if (cnt % 30 <= 30)
		mlx_put_image_to_window(maps->mlx, maps->win, \
			(maps->attack_sprite[5]).image, i % width * 64, i / width * 64);
	if (cnt % 30 == 29)
		maps->check_attack = 0;
        // 공격모션이 모두 출력되었으면 check_attack을 0으로 초기화하여 공격을 마무리한다
}

key_hook

key_press

키보드 입력이 들어오면 mlx_key_hook 함수에서 콜백함수로 호출되어 동작한다

int	key_press(int keycode, t_param *maps)
{
	if (keycode == KEY_ESC) // ESC가 입력되었으면 프로그램을 종료시킨다
		exit(0);
	if (keycode == KEY_ATTACK) // 공격키가 눌러졌으면 waitting_attack 플래그를 1로바꾼다
	{
		maps->waitting_attack = 1;
		return (0);
	}
	if (key_attack_up_down(keycode, maps) || \
	key_attack_left_right(keycode, maps, maps->user.x))
		return (0); // 공격모션중이면 종료시킨다
	if (maps->move_cnt % 30 == 29) // 플레이어가 30걸음을 이동할 때마다 적을 추가로 생성한다
		enemy_add(maps, 0, 0, 500); // 랜덤범위에 적을 생성한다
	if (keycode == KEY_RIGHT) // 방향키가 입력되었으면 그에 맞춰서 direction을 설정한다
		maps->user.direction = 1;
	else if (keycode == KEY_LEFT)
		maps->user.direction = 2;
	else if (keycode == KEY_UP)
		maps->user.direction = 3;
	else if (keycode == KEY_DOWN)
		maps->user.direction = 4;
	else // 그외 입력이 들어오면 함수를 탈출한다
		return (0);
	move_map(keycode, maps); // 입력된 방향에맞게 유저를 이동시킨다
	return (0);
}

key_attack_up_down

공격버튼이 눌러지고 난 이후에 방향키 입력이 들어오면 해당방향에 공격을 진행하게 한다

int	key_attack_up_down(int keycode, t_param *maps)
{
	if (maps->waitting_attack == 1 && keycode == KEY_UP)
	{
		if (maps->map[maps->user.x - maps->win_width - 1] == 'L' || \
		maps->map[maps->user.x - maps->win_width - 1] == 'R')
			maps->map[maps->user.x - maps->win_width - 1] = '0';
            // 공격범위 안에 enemy가 있으면 없에버린다
		maps->user.direction = 3; // 플레이어 방향 변경
		maps->check_attack = 3; // 공격모션 방향 변경
		maps->waitting_attack = 0; // 공격대기 초기화
		return (1);
	}
	if (maps->waitting_attack == 1 && keycode == KEY_DOWN)
	{
		if (maps->map[maps->user.x + maps->win_width + 1] == 'L' || \
		maps->map[maps->user.x + maps->win_width + 1] == 'R')
			maps->map[maps->user.x + maps->win_width + 1] = '0';
		maps->user.direction = 4;
		maps->check_attack = 4;
		maps->waitting_attack = 0;
		return (1);
	}
	return (0);
}

move_map

입력된 방향에 맞춰서 해당 위치의 맵에 무슨요소가 있는지 확인하여 이동시켜준다

void	move_map(int keycode, t_param *maps)
{
	if (move_land(keycode, maps))
	{
		maps->move_cnt += 1;
		ft_printf("%d\n", maps->move_cnt);
	}
	if (move_chase(keycode, maps))
	{
		maps->move_cnt += 1;
		maps->collection -= 1; // collection 갯수를 하나 줄여준다
		ft_printf("%d\n", maps->move_cnt);
	}
	if (move_potal(keycode, maps))
	{
		maps->move_cnt += 1;
		ft_printf("%d\n", maps->move_cnt);
		exit(0);
	}
	if (check_enemy(keycode, maps)) // 해당위치에 적이 있으면 게임을 종료시킨다
		exit(0);
	check_enemy2(maps); // 적의 공격범위안에 들어왔는지 검사한다
	enemy_set(maps); // 맵에 있는 적들을 이동시켜준다
	maps->waitting_attack = 0;
}

move_land

이동하는 위치가 빈땅(0)이면 user_move를 호출한다

int	move_land(int keycode, t_param *maps)
{
	if (keycode == KEY_UP && \
			(maps->map)[maps->user.x - maps->win_width - 1] == '0')
	{
		user_move(maps, 1);
		return (1);
	}
	else if (keycode == KEY_DOWN && \
			(maps->map)[maps->user.x + maps->win_width + 1] == '0')
	{
		user_move(maps, 2);
		return (1);
	}
	else if (keycode == KEY_LEFT && (maps->map)[maps->user.x - 1] == '0')
	{
		user_move(maps, 3);
		return (1);
	}
	else if (keycode == KEY_RIGHT && (maps->map)[maps->user.x + 1] == '0')
	{
		user_move(maps, 4);
		return (1);
	}
	return (0);
}

user_move

flag에 맞춰서 해당위치를 플레이어로 바꾸고 기존 플레이어 위치를 빈땅으로 변경한다

void	user_move(t_param *maps, int flag)
{
	if (flag == 1)
	{
		(maps->map)[maps->user.x] = '0';
		(maps->map)[maps->user.x - maps->win_width - 1] = 'P';
	}
	else if (flag == 2)
	{
		(maps->map)[maps->user.x] = '0';
		(maps->map)[maps->user.x + maps->win_width + 1] = 'P';
	}
	else if (flag == 3)
	{
		(maps->map)[maps->user.x] = '0';
		(maps->map)[maps->user.x - 1] = 'P';
	}
	else if (flag == 4)
	{
		(maps->map)[maps->user.x] = '0';
		(maps->map)[maps->user.x + 1] = 'P';
	}
}

move_potal

maps->collection이 0인지 확인하여 수집품이 다 모아진 상태에서만 이동이 가능하게 조건을 추가로 지정했다

int	move_potal(int keycode, t_param *maps)
{
	if (keycode == KEY_UP && (maps->map)[maps->user.x - maps->win_width - 1] \
			== 'E' && maps->collection == 0)
		return (1);
	else if (keycode == KEY_DOWN && (maps->map)[maps->user.x + \
			maps->win_width + 1] == 'E' && maps->collection == 0)
		return (1);
	else if (keycode == KEY_LEFT && \
			(maps->map)[maps->user.x - 1] == 'E' && maps->collection == 0)
		return (1);
	else if (keycode == KEY_RIGHT && \
			(maps->map)[maps->user.x + 1] == 'E' && maps->collection == 0)
		return (1);
	return (0);
}

check_enemy

유저가 이동한 위치에 enemy가 있으면 1을 반환하여 게임을 종료시킨다

int	check_enemy(int keycode, t_param *maps)
{
	if (keycode == KEY_UP && \
			((maps->map)[maps->user.x - maps->win_width - 1] == 'R' || \
			(maps->map)[maps->user.x - maps->win_width - 1] == 'L'))
		return (1);
	else if (keycode == KEY_DOWN && \
			((maps->map)[maps->user.x + maps->win_width + 1] == 'R' || \
			(maps->map)[maps->user.x + maps->win_width + 1] == 'L'))
		return (1);
	else if (keycode == KEY_LEFT && ((maps->map)[maps->user.x - 1] == 'R' || \
	(maps->map)[maps->user.x - 1] == 'L'))
		return (1);
	else if (keycode == KEY_RIGHT && ((maps->map)[maps->user.x + 1] == 'R' || \
	(maps->map)[maps->user.x + 1] == 'L'))
		return (1);
	return (0);
}

check_enemy2

유저가 이동한 범위가 적의 공격범위 (적 좌우 한칸)안에 들어간 경우 게임을 종료시킨다

void	check_enemy2(t_param *maps)
{
	int	i;

	i = 0;
	while (maps->map[i])
	{
		if (maps->map[i] == 'L' || maps->map[i] == 'R')
		{
			if (maps->map[i - 1] == 'P')
				exit(0);
			else if (maps->map[i + 1] == 'P')
				exit(0);
		}
		i++;
	}
}

enemy_set

맵에 존재하는 enemy들을 랜덤하게 이동시킨다

void	enemy_set(t_param *maps)
{
	int	i;

	i = 0;
	while (maps->map[i])
	{
		if (maps->map[i] == 'R' || maps->map[i] == 'L') // 해당위치가 enemy이면
			enemy_move(maps, i); // enemy 이동
		i++;
	}
	i = 0;
	while (maps->map[i])
	{
		if (maps->map[i] == 'r')
        // enemy가 여러칸을 한번에 이동하는것을 방지하고자
        // 이동중에는 소문자로 표시하고 이동이 종료되면 대문자로 변경
			maps->map[i] = 'R';
		else if (maps->map[i] == 'l')
			maps->map[i] = 'L';
		i++;
	}
}

enemy_move

void	enemy_move(t_param *maps, int enemy_position)
{
	int	keycode;
	int	cnt;

	cnt = 0;
	while (1)
	{
		keycode = (rand() % 4) + 123; // rand함수로 상하좌우 이동을 정한다
		if (keycode == KEY_UP && \
				(maps->map)[enemy_position - maps->win_width - 1] == '0')
                // 해당하는 위치가 빈땅이면 enemy를 이동시킨다
			return (move_enemy(maps, 1, enemy_position));
		else if (keycode == KEY_DOWN && \
				(maps->map)[enemy_position + maps->win_width + 1] == '0')
			return (move_enemy(maps, 2, enemy_position));
		else if (keycode == KEY_LEFT && (maps->map)[enemy_position - 1] == '0')
			return (move_enemy(maps, 3, enemy_position));
		else if (keycode == KEY_RIGHT && (maps->map)[enemy_position + 1] == '0')
			return (move_enemy(maps, 4, enemy_position));
		cnt++;
		if (cnt == 10) // 10번 반복할 동안 이동이 안 이루어졌으면 종료시킨다
			return ;
	}
}
profile
프로그래머 희망자(휴직중)

0개의 댓글