tetris
| -rw-r--r-- | .github/workflows/export.yml | 3 | ||||
| -rw-r--r-- | .gitmodules | 3 | ||||
| -rw-r--r-- | Logic.gd | 66 | ||||
| -rw-r--r-- | Main.tscn | 14 | ||||
| -rw-r--r-- | README.md | 38 | ||||
| -rw-r--r-- | Tetris.gd | 121 | ||||
| -rw-r--r-- | TetrisPiece.gd | 74 | ||||
| l--------- | addons/gdcli | 1 | ||||
| -rw-r--r-- | autoloads/CLI.gd | 11 | ||||
| -rw-r--r-- | project.godot | 39 | ||||
| -rw-r--r-- | submodules/.gdignore | 0 | ||||
| m--------- | submodules/gdcli | 0 |
12 files changed, 298 insertions, 72 deletions
diff --git a/.github/workflows/export.yml b/.github/workflows/export.yml index d3a34af..80bc4ec 100644 --- a/.github/workflows/export.yml +++ b/.github/workflows/export.yml @@ -14,14 +14,13 @@ on: env: GODOT_VERSION: 3.5 - RELEASE: rc NAME: ${{ github.event.repository.name }} jobs: export: runs-on: ubuntu-latest container: - image: ghcr.io/bend-n/godot-2d:3.5.rc + image: ghcr.io/bend-n/godot-2d:3.5 name: ${{ matrix.name }} strategy: matrix: diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 68b663d..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "gdcli"] - path = submodules/gdcli - url = https://github.com/bend-n/gdcli diff --git a/Logic.gd b/Logic.gd new file mode 100644 index 0000000..946604d --- /dev/null +++ b/Logic.gd @@ -0,0 +1,66 @@ +extends Reference +class_name Logic + +const shapes = [ + [ 0, 0, 0, 0, + 1, 1, 1, 1], + [ 0, 0, 0, 0, + 1, 1, 1, 0, + 1 ], + [ 1, 1, 1, 0, + 0, 0, 1 ], + [ 0, 1, 1, 0, + 0, 1, 1 ], + [ 1, 1, 0, 0, + 0, 1, 1 ], + [ 0, 1, 1, 0, + 1, 1 ], + [ 0, 1, 0, 0, + 1, 1, 1 ] +]; + + +static func create_matrix(rows: int, columns: int) -> Array: + var matrix = [] + for y in range(rows): + matrix.append([]) + for _x in range(columns): + matrix[y].append(0) + + return matrix + + +static func valid_move(matrix: Array, piece, offset := Vector2()) -> bool: + offset += piece.position + for y in range(4): + for x in range(4): + if get_index_or_null(piece.shape, y, x) > 0: + #warning-ignore-all:narrowing_conversion + if ( + get_index_or_null(matrix, y + offset.y, x + offset.x) == null + or get_index_or_null(matrix, y + offset.y, x + offset.x) > 0 + or x + offset.x < 0 + or y + offset.y >= len(matrix) + or x + offset.x >= len(matrix[y]) + ): + return false + return true + + +static func clear_lines(matrix: Array) -> void: + var y = len(matrix) + while y > 0: + y -= 1 + if matrix[y].min() > 0: + for yy in range(y, 1, -1): + matrix[yy] = matrix[yy - 1] + y += 1 + + +static func get_index_or_null(arr: Array, y: int, x: int) -> int: + return arr[y][x] if y < len(arr) and x < len(arr[y]) else null + + +static func print_matrix(mat: Array): + for i in mat: + print(i) @@ -1,3 +1,13 @@ -[gd_scene format=2] +[gd_scene load_steps=2 format=2] -[node name="Main" type="Node2D"] +[ext_resource path="res://Tetris.gd" type="Script" id=1] + +[node name="Main" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="Tetris" type="AspectRatioContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 1 ) +colors = PoolColorArray( 0.811765, 0.176471, 0.176471, 1, 0.427451, 0.721569, 0.278431, 1, 0.156863, 0.270588, 0.709804, 1, 0.615686, 0.164706, 0.611765, 1, 0.717647, 0.713726, 0.282353, 1, 0.47451, 0.47451, 0.47451, 1, 0.835294, 0.588235, 0.113725, 1 ) @@ -1,37 +1,5 @@ -# godot-template +# tetris -Godot template repository for my programs +[](https://godotengine.org "Made with godot") ---- - -# How to use - -- To use: click use this template button. -- Clone your repository -- Add your files -- Commit -- Push - -<details> -<summary>For itch.io depoloyment</summary> -<br> -Add a secret called BUTLER_CREDENTIALS with your butler api key. -</details> - -<details> -<summary>For android builds</summary> -<br> -Add two secrets: - ANDROID_KEYSTORE_BASE64 - ANDROID_KEYSTORE_PASSWORD -</details> - ---- - -### Availability: - -| windows | ios | linux | android | mac | html | | -|:------------------:|:---:|:------------------:|:------------------:|:------------------:|:------------------:|:-------------:| -| :x: | :x: | :x: | :x: | :x: | :heavy_check_mark: | github pages | -| :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | itch.io | -| :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | godot exports | +simple tetris diff --git a/Tetris.gd b/Tetris.gd new file mode 100644 index 0000000..459b088 --- /dev/null +++ b/Tetris.gd @@ -0,0 +1,121 @@ +class_name Tetris +extends AspectRatioContainer + +const ROWS = 20 +const COLUMNS = 10 + +export(PoolColorArray) var colors + +var current_shape: TetrisPiece +var matrix := Logic.create_matrix(ROWS, COLUMNS) +var bg_squares := [] +var squares := [] + + +func get_square(at: Vector2) -> Control: + return squares[at.y * COLUMNS + at.x] + + +func init_squares(): + var bg := create_grid_container("background") + add_child(bg) + var fg := create_grid_container("foreground") + add_child(fg) + for _i in range(ROWS * COLUMNS): + var bg_square := ReferenceRect.new() + bg_square.editor_only = false + bg_square.border_width = 1.5 + bg_square.border_color = Color.ghostwhite + bg_square.name = "%d" % _i + expand_control(bg_square) + bg.add_child(bg_square) + var fg_square = ColorRect.new() + fg_square.color = Color.transparent + fg_square.name = "%d" % _i + expand_control(fg_square) + fg.add_child(fg_square) + squares.append(fg_square) + + +func create_grid_container(name: String) -> GridContainer: + var g := GridContainer.new() + g.columns = COLUMNS + g.name = name + g.add_constant_override("hseparation", 0) + g.add_constant_override("vseparation", 0) + g.set_anchors_and_margins_preset(PRESET_WIDE) + return g + + +func expand_control(c: Control) -> void: + c.size_flags_horizontal = SIZE_EXPAND_FILL + c.size_flags_vertical = SIZE_EXPAND_FILL + c.set_anchors_and_margins_preset(PRESET_WIDE) + + +func _ready() -> void: + set_process(false) + assert(len(colors) == Logic.shapes.size()) # 7 + ratio = float(COLUMNS) / float(ROWS) + init_squares() + start() + + +func start() -> void: + set_process(true) + # warning-ignore-all:integer_division + current_shape = TetrisPiece.new(Vector2((COLUMNS / 2) - 2, 0)) + tick() + + +func draw_board(mat: Array = matrix.duplicate(true), shape: TetrisPiece = current_shape) -> void: + shape.embed(mat) + for y in range(ROWS): + for x in range(COLUMNS): + get_square(Vector2(x, y)).color = colors[mat[y][x] - 1] if mat[y][x] > 0 else Color.transparent + + +func _process(_delta): + if Input.is_action_just_pressed("ui_left"): + current_shape.move(Vector2(-1, 0), matrix) + elif Input.is_action_just_pressed("ui_right"): + current_shape.move(Vector2(1, 0), matrix) + if Input.is_action_just_pressed("ui_up"): + current_shape.rotate(matrix, true) + if Input.is_action_just_pressed("ui_down"): + while current_shape.move(Vector2(0, 1), matrix): + continue + tick(false) + else: + draw_board() + + +func tick(create_timer := true): + if create_timer: + var t := get_tree().create_timer(.25) + t.connect("timeout", self, "tick") + + if not current_shape.move(Vector2(0, 1), matrix): # invalid move: cant go down + current_shape.embed(matrix) + current_shape = TetrisPiece.new(Vector2(randi() % (COLUMNS - 4), 0)) + Logic.clear_lines(matrix) + print(ascii(matrix)) + draw_board() + + +#warning-ignore:shadowed_variable +func ascii(matrix): + var s = "+" + for _x in range(COLUMNS): + s += "-" + s += "+" + #warning-ignore:shadowed_variable + for y in range(ROWS): + s += "\n" + ("|" if y != ROWS - 1 else "+") + for x in range(COLUMNS): + if matrix[y][x] > 0: + s += "ﱢ" + else: + s += "-" + s += ("|" if y != ROWS - 1 else "+") + return s diff --git a/TetrisPiece.gd b/TetrisPiece.gd new file mode 100644 index 0000000..9d9dc38 --- /dev/null +++ b/TetrisPiece.gd @@ -0,0 +1,74 @@ +extends Resource +class_name TetrisPiece + +var shape_type: int +var shape := [] +var position: Vector2 + + +func _init(pos: Vector2) -> void: + position = pos + randomize() + shape_type = randi() % len(Logic.shapes) + var s: PoolIntArray = [0, 0, 0, 0] + s.append_array(Logic.shapes[shape_type]) + + for y in range(4): + shape.append([]) + for x in range(4): + var i: int = y * 4 + x + if len(s) > i and s[i] > 0: + shape[y].append(1) + else: + shape[y].append(0) + + +# 90 deg +func _rotate_cw() -> void: + var new_shape = shape.duplicate(true) + new_shape[0][0] = shape[3][0] + new_shape[0][1] = shape[2][0] + new_shape[0][2] = shape[1][0] + new_shape[0][3] = shape[0][0] + new_shape[1][3] = shape[0][1] + new_shape[2][3] = shape[0][2] + new_shape[3][3] = shape[0][3] + new_shape[3][2] = shape[1][3] + new_shape[3][1] = shape[2][3] + new_shape[3][0] = shape[3][3] + new_shape[2][0] = shape[3][2] + new_shape[1][0] = shape[3][1] + + new_shape[1][1] = shape[2][1] + new_shape[1][2] = shape[1][1] + new_shape[2][2] = shape[1][2] + new_shape[2][1] = shape[2][2] + shape = new_shape + + +func embed(matrix: Array) -> Array: + for y in range(4): + for x in range(4): + if shape[y][x] > 0: + matrix[y + position.y][x + position.x] = 1 + shape_type + return matrix + + +func move(direction: Vector2, matrix: Array) -> bool: + if Logic.valid_move(matrix, self, direction): + position += direction + return true + return false + + +func rotate(matrix: Array, cw := true): + var s = shape.duplicate(true) + if cw: + _rotate_cw() + else: + for _i in range(3): + _rotate_cw() + if !Logic.valid_move(matrix, self): + shape = s + return false + return true diff --git a/addons/gdcli b/addons/gdcli deleted file mode 120000 index ac119d8..0000000 --- a/addons/gdcli +++ /dev/null @@ -1 +0,0 @@ -../submodules/gdcli/addons/gdcli/
\ No newline at end of file diff --git a/autoloads/CLI.gd b/autoloads/CLI.gd deleted file mode 100644 index 095bfd3..0000000 --- a/autoloads/CLI.gd +++ /dev/null @@ -1,11 +0,0 @@ -extends Node - -func _ready() -> void: - var p := Parser.new() - p.add_argument(Arg.new({triggers=["-h", "--help", "-?"], help="show this help message and exit", action="store_true"})) - var args = p.parse_arguments() - if args == null: - get_tree().quit() - elif args.get("help", false): - print(p.help()) - get_tree().quit()
\ No newline at end of file diff --git a/project.godot b/project.godot index 663e841..308345a 100644 --- a/project.godot +++ b/project.godot @@ -10,30 +10,32 @@ config_version=4 _global_script_classes=[ { "base": "Reference", -"class": "Arg", +"class": "Logic", "language": "GDScript", -"path": "res://addons/gdcli/Arg.gd" +"path": "res://Logic.gd" }, { -"base": "Reference", -"class": "Parser", +"base": "AspectRatioContainer", +"class": "Tetris", +"language": "GDScript", +"path": "res://Tetris.gd" +}, { +"base": "Resource", +"class": "TetrisPiece", "language": "GDScript", -"path": "res://addons/gdcli/Parser.gd" +"path": "res://TetrisPiece.gd" } ] _global_script_class_icons={ -"Arg": "", -"Parser": "" +"Logic": "", +"Tetris": "", +"TetrisPiece": "" } [application] -config/name="Godot Template" +config/name="tetris" run/main_scene="res://Main.tscn" config/use_custom_user_dir=true -config/custom_user_dir_name="GodotTemplate" - -[autoload] - -CLI="*res://autoloads/CLI.gd" +config/custom_user_dir_name="tetris" [debug] @@ -41,13 +43,13 @@ gdscript/warnings/return_value_discarded=false [display] -window/size/width=320 -window/size/height=180 -window/size/test_width=1280 -window/size/test_height=720 +window/size/width=180 +window/size/height=320 +window/size/test_width=720 +window/size/test_height=1280 window/dpi/allow_hidpi=true window/stretch/mode="2d" -window/stretch/aspect="keep" +window/stretch/aspect="expand" [logging] @@ -61,3 +63,4 @@ quality/intended_usage/framebuffer_allocation=0 quality/intended_usage/framebuffer_allocation.mobile=0 2d/snapping/use_gpu_pixel_snap=true vram_compression/import_etc=true +environment/default_clear_color=Color( 0.937255, 0.937255, 0.937255, 1 ) diff --git a/submodules/.gdignore b/submodules/.gdignore deleted file mode 100644 index e69de29..0000000 --- a/submodules/.gdignore +++ /dev/null diff --git a/submodules/gdcli b/submodules/gdcli deleted file mode 160000 -Subproject 6040364dae05d331f8d6c9d3d52186df1d0399c |