a game about throwing hammers made for the github game off
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
## Creates a maze using the [url=https://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_depth-first_search]recursive backtracker algorithm[/url].
extends Resource
class_name Maze

## This mazes size.
var size: Vector2i

## The maze, in a 2d array.
## Type: [Array][[Array][[PackedInt32Array]].
var maze := []

## The north constant.
const N := 1

## The east constant.
const E := 2

## The south constant.
const S := 4

## The west constant.
const W := 8

## The N E S W mappings to UP RIGHT DOWN LEFT
const cell_walls := {Vector2i.UP: N, Vector2i.RIGHT: E, Vector2i.DOWN: S, Vector2i.LEFT: W}
const _dirs := [Vector2i.UP, Vector2i.RIGHT, Vector2i.DOWN, Vector2i.LEFT]

## The image representation of this maze.
var image: Image = null:
	get:
		if not image:
				_gen_image()
		return image


func _init(p_size: Vector2i) -> void:
		size = p_size
		generate()
		erase_walls()

## Get a maze cells value.
func get_cellv(cell: Vector2i) -> int:
		return maze[cell.y][cell.x]

## Turns a 4b tile into a array of walls. [code]4[/code] => [code][Vector2i.DOWN][/code].
static func tile_4b_to_wall_array(tile_4b: int) -> Array[Vector2i]:
		var array: Array[Vector2i] = []
		for dir in _dirs:
				if tile_4b & cell_walls[dir]:
						array.append(dir)
		return array


## Turns a 4b tile into a array of paths. [code]4[/code] => [code][Vector2i.UP, Vector2i.LEFT, Vector2i.RIGHT][/code].
static func tile_4b_to_path_array(tile_4b: int) -> Array[Vector2i]:
		var array: Array[Vector2i] = []
		for dir in cell_walls.keys():
				if not tile_4b & cell_walls[dir]:
						array.append(dir)
		return array


func _check_neighbours(cell: Vector2i, unvisited: Array[Vector2i]) -> Array[Vector2i]:
		# checks if neighbour is visited.
		# returns array of unvisited neighbours.
		var list: Array[Vector2i] = []
		for n in cell_walls.keys():
				if cell + n in unvisited:
						list.append(cell + n)
		return list


func _set_cellv(cell: Vector2i, v: int) -> void:
		maze[cell.y][cell.x] = v

## Generates the maze using the [url=https://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_depth-first_search]recursive backtracker algorithm[/url].
func generate() -> void:
		randomize()
		var unvisited: Array[Vector2i] = []
		var stack: Array[Vector2i] = []

		# fill maze
		maze.clear()
		for x in range(size.x):
				var row: PackedInt32Array = []
				for y in range(size.y):
						row.append(N|E|S|W)
						unvisited.append(Vector2i(x, y))
				maze.append(row)

		var current := Vector2i(0, 0)
		unvisited.erase(current)

		# recurive backtracking algorithm
		while unvisited.size() > 0:
				# check neighbours of current cell.
				var neighbours := _check_neighbours(current, unvisited)

				# if neighbours exist, pick one at random and move in that direction.
				# remove wall between current cell and neighbour cell.
				if neighbours.size() > 0:
						var next := neighbours[randi() % neighbours.size()]
						stack.append(current)

						# remove wall from both cells.
						var dir := next - current
						var current_walls: int = get_cellv(current) - cell_walls[dir]
						var next_walls: int = get_cellv(next) - cell_walls[-dir]
						_set_cellv(current, current_walls)
						_set_cellv(next, next_walls)
						current = next
						unvisited.erase(current)
				elif stack:
						# if neighbours don't exist, retrieve new current from stack.
						current = stack.pop_back()

## Randomly remove walls. Prefers deleting 3 and 2 walled cells.
func erase_walls() -> void:
		randomize()
		const three_walls: Array[int] = [7, 11, 13, 14]
		const two_walls: Array[int] = [3, 5, 6, 9, 10, 12]

		for x in range(size.x):
				for y in range(size.y):
						if maze[y][x] in three_walls or maze[x][y] in two_walls or randi() % 3 == 0:
								var cell := Vector2i(x, y)
								var i := randi() % 4
								for _i in range(4):
										var n: Vector2i = cell_walls.keys()[i]
										i = wrapi(i + _i, 0, 3)
										if get_cellv(cell) & cell_walls[n]:
												var n_cell := (cell + n)
												if n_cell.x > size.x - 1 or n_cell.y > size.y - 1 or n_cell.x < 0 or n_cell.y < 0:
														continue
												var walls: int = get_cellv(cell) - cell_walls[n]
												var n_walls: int = get_cellv(n_cell) - cell_walls[-n]
												if (
														(x == 0 and walls & W)
														or (y == 0 and walls & N)
														or (x == size.x and walls & E)
														or (y == size.y and walls & S)
														or (n_cell.x == 0 and n_walls & W)
														or (n_cell.y == 0 and n_walls & N)
														or (n_cell.x == size.x and n_walls & E)
														or (n_cell.y == size.y and n_walls & S)
												):
														continue
												if walls in three_walls or n_walls in three_walls:
														continue
												_set_cellv(cell, walls)
												_set_cellv(n_cell, n_walls)
												break


func _gen_image() -> void:
		var img = Image.create(size.x * 3, size.y * 3, false, Image.FORMAT_L8)
		var position := Vector2i.ZERO
		for i in len(maze):
				for j in len(maze[i]):
						var paths := Maze.tile_4b_to_path_array(maze[i][j])
						if not paths.is_empty():
								var middle := position + Vector2i.ONE
								img.set_pixelv(middle, Color.WHITE)
								for path in paths:
										img.set_pixelv(middle + path, Color.WHITE)
						position.x += 3
				position.y += 3
				position.x = 0
		image = img