commit 51fb7f81d3065a6b1dcb9ad5ed8c0e8e8a226b2d Author: Daniel Lehmann <3004405@stud.hs-mannheim.de> Date: Wed Mar 27 18:02:45 2024 +0100 v.0 TicTacToe vorlage diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4709183 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Godot 4+ specific ignores +.godot/ diff --git a/assets/sprites.png b/assets/sprites.png new file mode 100644 index 0000000..ebbfc16 Binary files /dev/null and b/assets/sprites.png differ diff --git a/assets/sprites.png.import b/assets/sprites.png.import new file mode 100644 index 0000000..73a5193 --- /dev/null +++ b/assets/sprites.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://do2vfb2cj7fk" +path="res://.godot/imported/sprites.png-bb36f92a96c2b9ad48a8528dbf01963e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/sprites.png" +dest_files=["res://.godot/imported/sprites.png-bb36f92a96c2b9ad48a8528dbf01963e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/sprites.svg b/assets/sprites.svg new file mode 100644 index 0000000..048f98c --- /dev/null +++ b/assets/sprites.svg @@ -0,0 +1,451 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/sprites.svg.import b/assets/sprites.svg.import new file mode 100644 index 0000000..4f334bc --- /dev/null +++ b/assets/sprites.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddkkkb6k0j8pl" +path="res://.godot/imported/sprites.svg-3de0a23bb576a309499d2855bd837d3b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/sprites.svg" +dest_files=["res://.godot/imported/sprites.svg-3de0a23bb576a309499d2855bd837d3b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/board/board.gd b/board/board.gd new file mode 100644 index 0000000..267fa49 --- /dev/null +++ b/board/board.gd @@ -0,0 +1,65 @@ +extends TileMap + +class_name Board + +var tile_by_name := {} +var layer_by_name := {} + +@export var state_layer: String = "States" +@export var board_layer: String = "Board" +@export var state_name: String = "Name" +@export var empty_state: String = "Empty" +var off_limits: String = "" +@export var active: bool = true + + +func _ready(): + print("Initializing board...") + _index_atlas_coordinates_by_name() + _index_layers_by_name() + $Highlight.size = tile_set.tile_size + print("Tiles: ", ", ".join(tile_by_name.keys())) + + +func get_state(grid_pos: Vector2) -> String: + var state_tile_data: TileData = self.get_cell_tile_data(layer_by_name[state_layer], grid_pos) + var board_tile_data: TileData = self.get_cell_tile_data(layer_by_name[board_layer], grid_pos) + if not board_tile_data: + return off_limits + if not state_tile_data or not state_tile_data.get_custom_data(state_name): + return empty_state + return state_tile_data.get_custom_data(state_name) + +func set_state(grid_pos: Vector2, state: String) -> bool: + if get_state(grid_pos) != empty_state: + return false + self.set_cell(layer_by_name[state_layer], grid_pos, tile_by_name[state]["source_id"], tile_by_name[state]["atlas_coords"]) + return true + +func global_to_map(global_pos: Vector2) -> Vector2: + return self.local_to_map(self.to_local(global_pos)) + +func gridpos_at_mouse() -> Vector2: + return global_to_map(get_viewport().get_mouse_position()) + +func reset() -> void: + for cell in get_used_cells(layer_by_name[state_layer]): + set_cell(layer_by_name[state_layer], cell, -1) + +func _index_atlas_coordinates_by_name(): + var tileset = self.tile_set + for source_index in range(tileset.get_source_count()): + var source_id = tileset.get_source_id(source_index) + var atlas:TileSetAtlasSource = tileset.get_source(source_id) + for tile_index in range(atlas.get_tiles_count()): + var atlas_coords = atlas.get_tile_id(tile_index) + var tile_data : TileData = atlas.get_tile_data(atlas_coords, 0) + if tile_data and tile_data.get_custom_data(state_name): + tile_by_name[tile_data.get_custom_data(state_name)] = { + "source_id": source_id, + "atlas_coords": atlas_coords + } + +func _index_layers_by_name(): + for i in range(get_layers_count()): + layer_by_name[get_layer_name(i)] = i diff --git a/board/board.tscn b/board/board.tscn new file mode 100644 index 0000000..4d13652 --- /dev/null +++ b/board/board.tscn @@ -0,0 +1,70 @@ +[gd_scene load_steps=6 format=3 uid="uid://dvlk7imglgpms"] + +[ext_resource type="Texture2D" uid="uid://do2vfb2cj7fk" path="res://assets/sprites.png" id="1_lagx3"] +[ext_resource type="Script" path="res://board/board.gd" id="2_vjb6q"] +[ext_resource type="Script" path="res://board/highlight.gd" id="3_m02pi"] + +[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_cta1l"] +resource_name = "sprites" +texture = ExtResource("1_lagx3") +texture_region_size = Vector2i(100, 100) +0:0/0 = 0 +0:0/0/custom_data_0 = "Blue1" +1:0/0 = 0 +1:0/0/custom_data_0 = "Blue2" +2:0/0 = 0 +2:0/0/custom_data_0 = "Red1" +3:0/0 = 0 +3:0/0/custom_data_0 = "Red2" +0:1/0 = 0 +0:1/0/custom_data_0 = "Circle" +1:1/0 = 0 +1:1/0/custom_data_0 = "Square" +2:1/0 = 0 +2:1/0/custom_data_0 = "Triangle" +3:1/0 = 0 +3:1/0/custom_data_0 = "Cross" +0:2/0 = 0 +0:2/0/custom_data_0 = "Circle2" +1:2/0 = 0 +1:2/0/custom_data_0 = "Square2" +2:2/0 = 0 +2:2/0/custom_data_0 = "Triangle2" +3:2/0 = 0 +3:2/0/custom_data_0 = "Cross2" +0:3/0 = 0 +0:3/0/custom_data_0 = "D1" +1:3/0 = 0 +1:3/0/custom_data_0 = "D2" +2:3/0 = 0 +2:3/0/custom_data_0 = "D3" +3:3/0 = 0 +3:3/0/custom_data_0 = "D4" +0:4/0 = 0 +0:4/0/custom_data_0 = "White" +1:4/0 = 0 +1:4/0/custom_data_0 = "D5" +2:4/0 = 0 +2:4/0/custom_data_0 = "D6" +3:4/0 = 0 +3:4/0/custom_data_0 = "Grey" + +[sub_resource type="TileSet" id="TileSet_exrha"] +tile_size = Vector2i(100, 100) +custom_data_layer_0/name = "Name" +custom_data_layer_0/type = 4 +sources/1 = SubResource("TileSetAtlasSource_cta1l") + +[node name="Board" type="TileMap"] +texture_filter = 1 +tile_set = SubResource("TileSet_exrha") +rendering_quadrant_size = 100 +format = 2 +layer_0/name = "Board" +layer_0/tile_data = PackedInt32Array(65537, 1, 0, 131073, 1, 0, 131074, 1, 0, 0, 1, 0, 65536, 1, 0, 131072, 1, 0, 1, 1, 0, 2, 1, 0, 65538, 1, 0) +layer_1/name = "States" +layer_1/tile_data = PackedInt32Array() +script = ExtResource("2_vjb6q") + +[node name="Highlight" type="Node2D" parent="."] +script = ExtResource("3_m02pi") diff --git a/board/highlight.gd b/board/highlight.gd new file mode 100644 index 0000000..9dea4f6 --- /dev/null +++ b/board/highlight.gd @@ -0,0 +1,22 @@ +extends Node2D + +@export var size := Vector2(16,16): + set(value): + size = value + queue_redraw() + +@onready var board := get_parent() as Board + + +func _draw(): + draw_rect(Rect2(0,0,size.x,size.y), Color(1,0,0,0.7), false, 2) + + +func _process(_delta): + var grid_pos = board.global_to_map(get_viewport().get_mouse_position()) + var state = board.get_state(grid_pos) + if state == board.empty_state and board.active: + position = board.map_to_local(grid_pos) - size / 2 + show() + else: + hide() diff --git a/game.gd b/game.gd new file mode 100644 index 0000000..73e2190 --- /dev/null +++ b/game.gd @@ -0,0 +1,65 @@ +extends Node2D + +var current_player: int = 0 +var player_states := ["Circle", "Cross"] +var won := "" + +@onready var board := $Board as Board +@onready var nextPlayer := $GUI/NextPlayer as Sprite2D +@onready var wonPlayer := $GUI/WonPlayer as Sprite2D + +# Called when the node enters the scene tree for the first time. +func _ready(): + current_player = 0 + board.reset() + won = "" + wonPlayer.hide() + nextPlayer.region_rect.position.x = current_player * 300 + nextPlayer.show() + board.active = true + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(_delta): + if won == "": + if Input.is_action_just_pressed("Click"): + var grid_pos = board.gridpos_at_mouse() + if board.get_state(grid_pos) == board.empty_state: + if board.set_state(grid_pos, player_states[current_player]): + won = _check_win() + if won=="": + current_player = 1 - current_player + nextPlayer.region_rect.position.x = current_player * 300 + else: + print("Winner: ", won) + wonPlayer.region_rect.position.x = player_states.find(won) * 300 + wonPlayer.show() + nextPlayer.hide() + board.active = false + +func _on_restart_button_pressed(): + _ready() + + +var _checks = [ + [Vector2i(-1,0), Vector2i(1,0)], + [Vector2i(0,-1), Vector2i(0,1)], + [Vector2i(-1,-1), Vector2i(1,1)], + [Vector2i(-1,1), Vector2i(1,-1)], +] + +func _check_win() -> String: + for cell in board.get_used_cells(board.layer_by_name[board.state_layer]): + var state = board.get_state(cell) + for check in _checks: + var same = true + for neighbour in check: + if board.get_state(cell + neighbour) != state: + same = false + continue + if same: + return state + return "" + + + diff --git a/game.tscn b/game.tscn new file mode 100644 index 0000000..21073b3 --- /dev/null +++ b/game.tscn @@ -0,0 +1,64 @@ +[gd_scene load_steps=4 format=3 uid="uid://dmahmxghpm47w"] + +[ext_resource type="Script" path="res://game.gd" id="1_g1ie7"] +[ext_resource type="PackedScene" uid="uid://dvlk7imglgpms" path="res://board/board.tscn" id="1_ym80r"] +[ext_resource type="Texture2D" uid="uid://do2vfb2cj7fk" path="res://assets/sprites.png" id="3_0333r"] + +[node name="Game" type="Node2D"] +script = ExtResource("1_g1ie7") + +[node name="Board" parent="." instance=ExtResource("1_ym80r")] +position = Vector2(420, 222) + +[node name="GUI" type="Node2D" parent="."] + +[node name="Title" type="Label" parent="GUI"] +offset_left = 28.0 +offset_top = 31.0 +offset_right = 718.0 +offset_bottom = 103.0 +theme_override_font_sizes/font_size = 32 +text = "Tic Tac Toe Rapid Prototyper" + +[node name="NextPlayer" type="Sprite2D" parent="GUI"] +position = Vector2(690, 59) +scale = Vector2(0.5, 0.5) +texture = ExtResource("3_0333r") +region_enabled = true +region_rect = Rect2(0, 100, 100, 100) + +[node name="NextPlayerLabel" type="Label" parent="GUI"] +offset_left = 602.0 +offset_top = 47.0 +offset_right = 644.0 +offset_bottom = 73.0 +size_flags_horizontal = 4 +text = "Next:" +metadata/_edit_use_anchors_ = true + +[node name="WonPlayer" type="Sprite2D" parent="GUI"] +visible = false +position = Vector2(891, 58) +scale = Vector2(0.5, 0.5) +texture = ExtResource("3_0333r") +region_enabled = true +region_rect = Rect2(0, 100, 100, 100) + +[node name="WonPlayerLabel" type="Label" parent="GUI"] +offset_left = 807.0 +offset_top = 47.0 +offset_right = 849.0 +offset_bottom = 73.0 +size_flags_horizontal = 4 +text = "Won:" +metadata/_edit_use_anchors_ = true + +[node name="RestartButton" type="Button" parent="GUI"] +offset_left = 983.0 +offset_top = 44.0 +offset_right = 1086.0 +offset_bottom = 75.0 +text = "Restart" +metadata/_edit_use_anchors_ = true + +[connection signal="pressed" from="GUI/RestartButton" to="." method="_on_restart_button_pressed"] diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..afd6a84 --- /dev/null +++ b/icon.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..16de692 --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cqc52ufiirdtv" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..c7a99ff --- /dev/null +++ b/project.godot @@ -0,0 +1,31 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Tic Tac Toe" +run/main_scene="res://game.tscn" +config/features=PackedStringArray("4.2", "GL Compatibility") +config/icon="res://icon.svg" + +[input] + +Click={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) +] +} + +[rendering] + +textures/canvas_textures/default_texture_filter=0 +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" +environment/defaults/default_clear_color=Color(0.203922, 0.203922, 0.203922, 1)