summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig4
-rw-r--r--.gitattributes2
-rw-r--r--.gitignore4
-rw-r--r--ball.gd35
-rw-r--r--ball.gd.uid1
-rw-r--r--ball.tscn25
-rw-r--r--board.krabin0 -> 46459 bytes
-rw-r--r--board.pngbin0 -> 1779 bytes
-rw-r--r--board.png.import34
-rw-r--r--board2.krabin0 -> 47197 bytes
-rw-r--r--board2.pngbin0 -> 2840 bytes
-rw-r--r--board2.png.import34
-rw-r--r--bumper.gd22
-rw-r--r--bumper.gd.uid1
-rw-r--r--bumper.tscn11
-rw-r--r--bumper_a.tscn82
-rw-r--r--bumper_b.tscn84
-rw-r--r--bumper_c.tscn80
-rw-r--r--flipper.gd13
-rw-r--r--flipper.gd.uid1
-rw-r--r--flipper.tscn39
-rw-r--r--guide.gd54
-rw-r--r--guide.gd.uid1
-rw-r--r--guide.tscn117
-rw-r--r--hit.oggbin0 -> 5214 bytes
-rw-r--r--hit.ogg.import19
-rw-r--r--hud.gd9
-rw-r--r--hud.gd.uid1
-rw-r--r--hud.tscn11
-rw-r--r--icon.svg1
-rw-r--r--icon.svg.import37
-rw-r--r--pinball.gd103
-rw-r--r--pinball.gd.uid1
-rw-r--r--pinball.tscn163
-rw-r--r--project.godot59
-rw-r--r--tuxeball.pngbin0 -> 5748 bytes
-rw-r--r--tuxeball.png.import34
37 files changed, 1082 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..f28239b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,4 @@
+root = true
+
+[*]
+charset = utf-8
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..1aa53c2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+# Godot 4+ specific ignores
+.godot/
+/android/
+/assets/placeholders/
diff --git a/ball.gd b/ball.gd
new file mode 100644
index 0000000..d9037a8
--- /dev/null
+++ b/ball.gd
@@ -0,0 +1,35 @@
+extends RigidBody2D
+
+
+var reset_state := false
+var target_vector := Vector2.ZERO
+
+var collision_buffer: Array
+
+
+func _integrate_forces(state: PhysicsDirectBodyState2D) -> void:
+ if reset_state:
+ reset_state = false
+ state.transform = Transform2D(0.0, target_vector)
+
+ for idx in state.get_contact_count():
+ var body := state.get_contact_collider_object(idx)
+
+ if body.is_in_group("bumper"):
+ body.animate_bump()
+ apply_central_impulse(
+ state.get_contact_local_normal(idx) *
+ body.get_impulse()
+ )
+
+ if not collision_buffer.has(body):
+ collision_buffer.append(body)
+ get_tree().current_scene.get_node("HUD").score += 50
+
+ if state.get_contact_count() == 0:
+ collision_buffer.clear()
+
+
+func move_body(target_position: Vector2) -> void:
+ target_vector = target_position
+ reset_state = true
diff --git a/ball.gd.uid b/ball.gd.uid
new file mode 100644
index 0000000..7a5b0c6
--- /dev/null
+++ b/ball.gd.uid
@@ -0,0 +1 @@
+uid://bqxlt5ql0pfbt
diff --git a/ball.tscn b/ball.tscn
new file mode 100644
index 0000000..d3816a4
--- /dev/null
+++ b/ball.tscn
@@ -0,0 +1,25 @@
+[gd_scene load_steps=5 format=3 uid="uid://b1nhd5h4tie07"]
+
+[ext_resource type="Script" uid="uid://bqxlt5ql0pfbt" path="res://ball.gd" id="1_41u45"]
+[ext_resource type="Texture2D" uid="uid://1j0d0qpa7qxt" path="res://tuxeball.png" id="2_41u45"]
+
+[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_48gu3"]
+bounce = 0.1
+
+[sub_resource type="CircleShape2D" id="CircleShape2D_t6ii5"]
+radius = 6.0
+
+[node name="Ball" type="RigidBody2D"]
+mass = 0.1
+physics_material_override = SubResource("PhysicsMaterial_48gu3")
+contact_monitor = true
+max_contacts_reported = 5
+script = ExtResource("1_41u45")
+metadata/_edit_group_ = true
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("CircleShape2D_t6ii5")
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+position = Vector2(4.76837e-07, -9)
+texture = ExtResource("2_41u45")
diff --git a/board.kra b/board.kra
new file mode 100644
index 0000000..2622aac
--- /dev/null
+++ b/board.kra
Binary files differ
diff --git a/board.png b/board.png
new file mode 100644
index 0000000..beeef89
--- /dev/null
+++ b/board.png
Binary files differ
diff --git a/board.png.import b/board.png.import
new file mode 100644
index 0000000..d8babf9
--- /dev/null
+++ b/board.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cihildfmaodiu"
+path="res://.godot/imported/board.png-aeeee8e2906231b92d271d02de0f6106.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://board.png"
+dest_files=["res://.godot/imported/board.png-aeeee8e2906231b92d271d02de0f6106.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/board2.kra b/board2.kra
new file mode 100644
index 0000000..2e621ce
--- /dev/null
+++ b/board2.kra
Binary files differ
diff --git a/board2.png b/board2.png
new file mode 100644
index 0000000..77fb471
--- /dev/null
+++ b/board2.png
Binary files differ
diff --git a/board2.png.import b/board2.png.import
new file mode 100644
index 0000000..1fefaf5
--- /dev/null
+++ b/board2.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://f8qmjbc1h2tr"
+path="res://.godot/imported/board2.png-c2b98e3c04672b79c70a1627368cee33.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://board2.png"
+dest_files=["res://.godot/imported/board2.png-c2b98e3c04672b79c70a1627368cee33.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/bumper.gd b/bumper.gd
new file mode 100644
index 0000000..ab4bdb2
--- /dev/null
+++ b/bumper.gd
@@ -0,0 +1,22 @@
+extends StaticBody2D
+
+
+@export var force: float = 3.0
+@export var hit_sound: AudioStreamPlayer
+@export var animation_player: AnimationPlayer
+
+
+func _ready() -> void:
+ animation_player.animation_finished.connect(func(anim_name: StringName):
+ if anim_name == "hit":
+ animation_player.play("default")
+ )
+
+
+func animate_bump() -> void:
+ animation_player.play("hit")
+ hit_sound.play()
+
+
+func get_impulse() -> Vector2:
+ return Vector2(force, force)
diff --git a/bumper.gd.uid b/bumper.gd.uid
new file mode 100644
index 0000000..8e83139
--- /dev/null
+++ b/bumper.gd.uid
@@ -0,0 +1 @@
+uid://d2kqjyn0ty35r
diff --git a/bumper.tscn b/bumper.tscn
new file mode 100644
index 0000000..aea9727
--- /dev/null
+++ b/bumper.tscn
@@ -0,0 +1,11 @@
+[gd_scene load_steps=3 format=3 uid="uid://t27td5jbdv28"]
+
+[ext_resource type="Script" uid="uid://d2kqjyn0ty35r" path="res://bumper.gd" id="1_h31vo"]
+
+[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_hrt4a"]
+bounce = 0.25
+
+[node name="Bumper" type="StaticBody2D" groups=["bumper"]]
+physics_material_override = SubResource("PhysicsMaterial_hrt4a")
+script = ExtResource("1_h31vo")
+metadata/_edit_group_ = true
diff --git a/bumper_a.tscn b/bumper_a.tscn
new file mode 100644
index 0000000..153fb11
--- /dev/null
+++ b/bumper_a.tscn
@@ -0,0 +1,82 @@
+[gd_scene load_steps=9 format=3 uid="uid://bfv0i5cqbhysy"]
+
+[ext_resource type="PackedScene" uid="uid://t27td5jbdv28" path="res://bumper.tscn" id="1_q5peq"]
+[ext_resource type="AudioStream" uid="uid://bwy4ji0hb47n" path="res://hit.ogg" id="2_uandx"]
+[ext_resource type="Texture2D" uid="uid://bmra2qpibkr2u" path="res://assets/placeholders/6266.png" id="3_k7wrx"]
+
+[sub_resource type="Animation" id="Animation_uandx"]
+resource_name = "default"
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(200, 666, 16, 16)]
+}
+
+[sub_resource type="Animation" id="Animation_k7wrx"]
+length = 0.001
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(200, 666, 16, 16)]
+}
+
+[sub_resource type="Animation" id="Animation_7sf2s"]
+resource_name = "hit"
+length = 0.1
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(218, 666, 16, 16)]
+}
+
+[sub_resource type="AnimationLibrary" id="AnimationLibrary_7sf2s"]
+_data = {
+&"RESET": SubResource("Animation_k7wrx"),
+&"default": SubResource("Animation_uandx"),
+&"hit": SubResource("Animation_7sf2s")
+}
+
+[sub_resource type="CircleShape2D" id="CircleShape2D_uandx"]
+radius = 6.0
+
+[node name="Bumper-a" node_paths=PackedStringArray("hit_sound", "animation_player") instance=ExtResource("1_q5peq")]
+force = 6.0
+hit_sound = NodePath("Hit")
+animation_player = NodePath("AnimationPlayer")
+
+[node name="Hit" type="AudioStreamPlayer" parent="." index="0"]
+stream = ExtResource("2_uandx")
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="." index="1"]
+libraries = {
+&"": SubResource("AnimationLibrary_7sf2s")
+}
+
+[node name="Sprite2D" type="Sprite2D" parent="." index="2"]
+texture = ExtResource("3_k7wrx")
+region_enabled = true
+region_rect = Rect2(200, 666, 16, 16)
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="." index="3"]
+shape = SubResource("CircleShape2D_uandx")
diff --git a/bumper_b.tscn b/bumper_b.tscn
new file mode 100644
index 0000000..fe9aff3
--- /dev/null
+++ b/bumper_b.tscn
@@ -0,0 +1,84 @@
+[gd_scene load_steps=8 format=3 uid="uid://c8vbuly76q4cr"]
+
+[ext_resource type="PackedScene" uid="uid://t27td5jbdv28" path="res://bumper.tscn" id="1_ft11y"]
+[ext_resource type="AudioStream" uid="uid://bwy4ji0hb47n" path="res://hit.ogg" id="2_8nwkk"]
+[ext_resource type="Texture2D" uid="uid://bmra2qpibkr2u" path="res://assets/placeholders/6266.png" id="3_apqca"]
+
+[sub_resource type="Animation" id="Animation_8nwkk"]
+resource_name = "default"
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(8, 408, 24, 32)]
+}
+
+[sub_resource type="Animation" id="Animation_apqca"]
+length = 0.001
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(8, 408, 24, 32)]
+}
+
+[sub_resource type="Animation" id="Animation_sgpq0"]
+resource_name = "hit"
+length = 0.1
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(66, 408, 24, 32)]
+}
+
+[sub_resource type="AnimationLibrary" id="AnimationLibrary_sgpq0"]
+_data = {
+&"RESET": SubResource("Animation_apqca"),
+&"default": SubResource("Animation_8nwkk"),
+&"hit": SubResource("Animation_sgpq0")
+}
+
+[node name="Bumper-B" node_paths=PackedStringArray("hit_sound", "animation_player") instance=ExtResource("1_ft11y")]
+force = 5.0
+hit_sound = NodePath("Hit")
+animation_player = NodePath("AnimationPlayer")
+
+[node name="Hit" type="AudioStreamPlayer" parent="." index="0"]
+stream = ExtResource("2_8nwkk")
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="." index="1"]
+libraries = {
+&"": SubResource("AnimationLibrary_sgpq0")
+}
+
+[node name="Sprite2D" type="Sprite2D" parent="." index="2"]
+texture = ExtResource("3_apqca")
+region_enabled = true
+region_rect = Rect2(8, 408, 24, 32)
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="." index="3"]
+polygon = PackedVector2Array(-6, -10, -6, -8, -5, -6, -4, -4, 3, 9, 4, 9, 4, 8, -5, -10)
+
+[node name="StaticBody2D" type="StaticBody2D" parent="." index="4"]
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="StaticBody2D" index="0"]
+polygon = PackedVector2Array(-6, -12, -8, -12, -7, 3, 2, 10, 5, 12, 5, 10, 3, 9, -6, -8)
diff --git a/bumper_c.tscn b/bumper_c.tscn
new file mode 100644
index 0000000..21ed569
--- /dev/null
+++ b/bumper_c.tscn
@@ -0,0 +1,80 @@
+[gd_scene load_steps=8 format=3 uid="uid://du5c3h14wyygb"]
+
+[ext_resource type="PackedScene" uid="uid://t27td5jbdv28" path="res://bumper.tscn" id="1_dxpye"]
+[ext_resource type="AudioStream" uid="uid://bwy4ji0hb47n" path="res://hit.ogg" id="2_2akie"]
+[ext_resource type="Texture2D" uid="uid://bmra2qpibkr2u" path="res://assets/placeholders/6266.png" id="3_4ath6"]
+
+[sub_resource type="Animation" id="Animation_1ja52"]
+resource_name = "default"
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(11, 386, 14, 10)]
+}
+
+[sub_resource type="Animation" id="Animation_p5cxw"]
+length = 0.001
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(11, 386, 14, 10)]
+}
+
+[sub_resource type="Animation" id="Animation_2akie"]
+resource_name = "hit"
+length = 0.1
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(69, 386, 14, 10)]
+}
+
+[sub_resource type="AnimationLibrary" id="AnimationLibrary_vjgm0"]
+_data = {
+&"RESET": SubResource("Animation_p5cxw"),
+&"default": SubResource("Animation_1ja52"),
+&"hit": SubResource("Animation_2akie")
+}
+
+[node name="Bumper-C" node_paths=PackedStringArray("hit_sound", "animation_player") instance=ExtResource("1_dxpye")]
+hit_sound = NodePath("Hit")
+animation_player = NodePath("AnimationPlayer")
+
+[node name="Hit" type="AudioStreamPlayer" parent="." index="0"]
+stream = ExtResource("2_2akie")
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="." index="1"]
+libraries = {
+&"": SubResource("AnimationLibrary_vjgm0")
+}
+
+[node name="Sprite2D" type="Sprite2D" parent="." index="2"]
+texture = ExtResource("3_4ath6")
+centered = false
+region_enabled = true
+region_rect = Rect2(11, 386, 14, 10)
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="." index="3"]
+position = Vector2(7, 5)
+polygon = PackedVector2Array(6, -4, 3, -4, -6, 1, -6, 3, -4, 3, 6, -2)
diff --git a/flipper.gd b/flipper.gd
new file mode 100644
index 0000000..0dc214b
--- /dev/null
+++ b/flipper.gd
@@ -0,0 +1,13 @@
+extends StaticBody2D
+
+
+func is_colliding(node: CollisionObject2D) -> bool:
+ return $Area2D.get_overlapping_bodies().has(node)
+
+
+func get_center() -> Vector2:
+ return $Center.global_position
+
+
+func get_lowest_point() -> Vector2:
+ return $LowestPoint.global_position
diff --git a/flipper.gd.uid b/flipper.gd.uid
new file mode 100644
index 0000000..0acb4ad
--- /dev/null
+++ b/flipper.gd.uid
@@ -0,0 +1 @@
+uid://bdndvmqxotild
diff --git a/flipper.tscn b/flipper.tscn
new file mode 100644
index 0000000..4fe95f6
--- /dev/null
+++ b/flipper.tscn
@@ -0,0 +1,39 @@
+[gd_scene load_steps=5 format=3 uid="uid://0wgt426mn3tt"]
+
+[ext_resource type="Script" uid="uid://bdndvmqxotild" path="res://flipper.gd" id="1_qmqmj"]
+[ext_resource type="Texture2D" uid="uid://bmra2qpibkr2u" path="res://assets/placeholders/6266.png" id="2_62qx3"]
+
+[sub_resource type="BoxMesh" id="BoxMesh_48gu3"]
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_ia8el"]
+size = Vector2(24, 5)
+
+[node name="Flipper" type="StaticBody2D"]
+script = ExtResource("1_qmqmj")
+metadata/_edit_group_ = true
+
+[node name="MeshInstance2D" type="MeshInstance2D" parent="."]
+position = Vector2(6.5, 1.90735e-06)
+scale = Vector2(13, 2)
+mesh = SubResource("BoxMesh_48gu3")
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+position = Vector2(8, 0)
+texture = ExtResource("2_62qx3")
+region_enabled = true
+region_rect = Rect2(34, 1046, 24, 24)
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="."]
+polygon = PackedVector2Array(-2, -3, 4, -3, 4, -2, 12, -2, 12, -1, 19, -1, 19, 1, 14, 1, 14, 2, 9, 2, 9, 3, 4, 3, 4, 4, -1, 3, -3, 3, -3, -2, -2, -2)
+
+[node name="Area2D" type="Area2D" parent="."]
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
+position = Vector2(9, -1.5)
+shape = SubResource("RectangleShape2D_ia8el")
+
+[node name="Center" type="Marker2D" parent="."]
+position = Vector2(7, 0)
+
+[node name="LowestPoint" type="Marker2D" parent="."]
+position = Vector2(19, 2)
diff --git a/guide.gd b/guide.gd
new file mode 100644
index 0000000..aae9deb
--- /dev/null
+++ b/guide.gd
@@ -0,0 +1,54 @@
+extends Area2D
+
+
+@export var force: float = 10.0
+
+var current_direction := Vector2.UP: set = set_current_direction
+var base_gravity: float = gravity
+
+
+func set_current_direction(value: Vector2) -> void:
+ current_direction = value
+
+ gravity_direction = current_direction
+ gravity = base_gravity
+
+ if current_direction == Vector2.UP:
+ $AnimationPlayer.play("up")
+ gravity = base_gravity * 0.25
+ elif current_direction == Vector2.DOWN:
+ $AnimationPlayer.play("down")
+ elif current_direction == Vector2.LEFT:
+ $AnimationPlayer.play("left")
+ elif current_direction == Vector2.RIGHT:
+ $AnimationPlayer.play("right")
+
+
+func get_impulse() -> Vector2:
+ return Vector2(force, force)
+
+
+func apply_guiding_impulse(body: Node2D) -> void:
+ if (
+ current_direction == Vector2.LEFT or
+ current_direction == Vector2.RIGHT or
+ (current_direction == Vector2.UP and body.global_position.y > global_position.y)
+ ):
+ body.apply_central_impulse(
+ (global_position - body.global_position).normalized() *
+ get_impulse()
+ )
+
+
+func _on_timer_timeout() -> void:
+ current_direction = [
+ Vector2.UP,
+ Vector2.DOWN,
+ Vector2.LEFT,
+ Vector2.RIGHT,
+ ].pick_random()
+ $Timer.wait_time = randf_range(5.0, 10.0)
+
+
+func _on_body_entered(body: Node2D) -> void:
+ apply_guiding_impulse(body)
diff --git a/guide.gd.uid b/guide.gd.uid
new file mode 100644
index 0000000..c28a73f
--- /dev/null
+++ b/guide.gd.uid
@@ -0,0 +1 @@
+uid://bm8spheg1euql
diff --git a/guide.tscn b/guide.tscn
new file mode 100644
index 0000000..c15a9c8
--- /dev/null
+++ b/guide.tscn
@@ -0,0 +1,117 @@
+[gd_scene load_steps=10 format=3 uid="uid://cik38e6miyh17"]
+
+[ext_resource type="Script" uid="uid://bm8spheg1euql" path="res://guide.gd" id="1_4mqed"]
+[ext_resource type="Texture2D" uid="uid://bmra2qpibkr2u" path="res://assets/placeholders/6266.png" id="1_kce4j"]
+
+[sub_resource type="CircleShape2D" id="CircleShape2D_kce4j"]
+radius = 9.0
+
+[sub_resource type="Animation" id="Animation_4mqed"]
+resource_name = "up"
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(144, 310, 16, 24)]
+}
+
+[sub_resource type="Animation" id="Animation_feuw2"]
+length = 0.001
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(144, 310, 16, 24)]
+}
+
+[sub_resource type="Animation" id="Animation_gajip"]
+resource_name = "down"
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(162, 310, 16, 24)]
+}
+
+[sub_resource type="Animation" id="Animation_83y8a"]
+resource_name = "left"
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(144, 336, 16, 24)]
+}
+
+[sub_resource type="Animation" id="Animation_utvnm"]
+resource_name = "right"
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Sprite2D:region_rect")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [Rect2(162, 336, 16, 24)]
+}
+
+[sub_resource type="AnimationLibrary" id="AnimationLibrary_gajip"]
+_data = {
+&"RESET": SubResource("Animation_feuw2"),
+&"down": SubResource("Animation_gajip"),
+&"left": SubResource("Animation_83y8a"),
+&"right": SubResource("Animation_utvnm"),
+&"up": SubResource("Animation_4mqed")
+}
+
+[node name="Guide" type="Area2D" groups=["guide"]]
+gravity_space_override = 1
+gravity = 300.0
+script = ExtResource("1_4mqed")
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+texture = ExtResource("1_kce4j")
+centered = false
+region_enabled = true
+region_rect = Rect2(144, 310, 16, 24)
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+position = Vector2(8, 11)
+shape = SubResource("CircleShape2D_kce4j")
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
+libraries = {
+&"": SubResource("AnimationLibrary_gajip")
+}
+
+[node name="Timer" type="Timer" parent="."]
+autostart = true
+
+[connection signal="body_entered" from="." to="." method="_on_body_entered"]
+[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]
diff --git a/hit.ogg b/hit.ogg
new file mode 100644
index 0000000..218b7fb
--- /dev/null
+++ b/hit.ogg
Binary files differ
diff --git a/hit.ogg.import b/hit.ogg.import
new file mode 100644
index 0000000..046c8d8
--- /dev/null
+++ b/hit.ogg.import
@@ -0,0 +1,19 @@
+[remap]
+
+importer="oggvorbisstr"
+type="AudioStreamOggVorbis"
+uid="uid://bwy4ji0hb47n"
+path="res://.godot/imported/hit.ogg-609794b2f0b9184c55860d7e8196e521.oggvorbisstr"
+
+[deps]
+
+source_file="res://hit.ogg"
+dest_files=["res://.godot/imported/hit.ogg-609794b2f0b9184c55860d7e8196e521.oggvorbisstr"]
+
+[params]
+
+loop=false
+loop_offset=0
+bpm=0
+beat_count=0
+bar_beats=4
diff --git a/hud.gd b/hud.gd
new file mode 100644
index 0000000..86ca7da
--- /dev/null
+++ b/hud.gd
@@ -0,0 +1,9 @@
+extends CanvasLayer
+
+
+var score: int = 0: set = set_score
+
+
+func set_score(value: int) -> void:
+ score = value
+ $Score.text = str(score)
diff --git a/hud.gd.uid b/hud.gd.uid
new file mode 100644
index 0000000..5976f57
--- /dev/null
+++ b/hud.gd.uid
@@ -0,0 +1 @@
+uid://jnx31tack2nb
diff --git a/hud.tscn b/hud.tscn
new file mode 100644
index 0000000..6dd3380
--- /dev/null
+++ b/hud.tscn
@@ -0,0 +1,11 @@
+[gd_scene load_steps=2 format=3 uid="uid://bwo077rca3xmb"]
+
+[ext_resource type="Script" uid="uid://jnx31tack2nb" path="res://hud.gd" id="1_37p78"]
+
+[node name="HUD" type="CanvasLayer"]
+script = ExtResource("1_37p78")
+
+[node name="Score" type="Label" parent="."]
+offset_right = 40.0
+offset_bottom = 23.0
+text = "0"
diff --git a/icon.svg b/icon.svg
new file mode 100644
index 0000000..9d8b7fa
--- /dev/null
+++ b/icon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg> \ No newline at end of file
diff --git a/icon.svg.import b/icon.svg.import
new file mode 100644
index 0000000..29f6182
--- /dev/null
+++ b/icon.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cgdo5iepsrvtu"
+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/pinball.gd b/pinball.gd
new file mode 100644
index 0000000..3a3a37c
--- /dev/null
+++ b/pinball.gd
@@ -0,0 +1,103 @@
+extends Node2D
+
+
+var launch_speed := -35
+var flipper_launch_speed := Vector2(
+ 1.0,
+ -30,
+)
+
+var shake_tween: Tween
+var camera_base_position: Vector2
+
+
+func _ready() -> void:
+ shake_tween = get_tree().create_tween()
+ shake_tween.kill()
+
+ camera_base_position = $Camera2D.position
+
+ $FlipperLeft.rotation_degrees = 30
+ $FlipperRight.rotation_degrees = -30
+
+ launch_ball()
+
+
+func _process(_delta: float) -> void:
+ if Input.is_action_pressed("flipper_left"):
+ var tween := get_tree().create_tween()
+ tween.tween_property($FlipperLeft, "rotation_degrees", -45, 0.1)
+
+ if Input.is_action_just_released("flipper_left"):
+ var tween := get_tree().create_tween()
+ tween.tween_property($FlipperLeft, "rotation_degrees", 30, 0.1)
+
+ if Input.is_action_just_pressed("flipper_left"):
+ if $FlipperLeft.is_colliding(%Ball):
+ if %Ball.position.y < $FlipperLeft.get_lowest_point().y:
+ %Ball.apply_central_impulse(
+ Vector2(abs(%Ball.global_position.x - $FlipperLeft.get_center().x), 1.0)
+ *
+ flipper_launch_speed
+ )
+
+
+ if Input.is_action_pressed("flipper_right"):
+ var tween := get_tree().create_tween()
+ tween.tween_property($FlipperRight, "rotation_degrees", 45, 0.1)
+
+ if Input.is_action_just_released("flipper_right"):
+ var tween := get_tree().create_tween()
+ tween.tween_property($FlipperRight, "rotation_degrees", -30, 0.1)
+
+ if Input.is_action_just_pressed("flipper_right"):
+ if $FlipperRight.is_colliding(%Ball):
+ if %Ball.position.y < $FlipperRight.get_lowest_point().y:
+ %Ball.apply_central_impulse(
+ Vector2(-abs(%Ball.global_position.x - $FlipperRight.get_center().x), 1.0)
+ *
+ flipper_launch_speed
+ )
+
+
+ if Input.is_action_just_pressed("shake"):
+ %Ball.apply_central_impulse(Vector2(randf_range(-1.5, 1.5), randf_range(-1.5, -0.5)))
+ shake_tween.stop()
+ shake_tween = get_tree().create_tween()
+ var shake_direction = Vector2(randf_range(-1.0, 1.0), randf_range(-1.0, 1.0))
+ shake_tween.tween_property($Camera2D, "position", camera_base_position + shake_direction, 0.05)
+ shake_tween.tween_property($Camera2D, "position", camera_base_position, 0.05)
+
+ # fail-safe for non-launched ball. TODO: use different action
+ # TODO: make ball launch manual in general
+ if $LaunchArea.get_overlapping_bodies().has(%Ball):
+ launch_ball()
+
+
+ if %Ball.position.y > get_viewport_rect().size.y:
+ $Camera2D.position.y = get_viewport_rect().size.y + get_viewport_rect().size.y / 2.0
+ camera_base_position.y = $Camera2D.position.y
+ else:
+ $Camera2D.position.y = get_viewport_rect().size.y / 2.0
+ camera_base_position.y = $Camera2D.position.y
+
+
+func launch_ball() -> void:
+ $Wall.visible = false
+ $Wall.process_mode = Node.PROCESS_MODE_DISABLED
+
+ var tween := get_tree().create_tween()
+ tween.tween_property(%Ball, "position:y", %Ball.position.y + 5, 0.5)
+ await tween.finished
+ %Ball.apply_central_impulse(Vector2(0, launch_speed))
+
+
+func _on_outside_body_entered(_body: Node2D) -> void:
+ %Ball.move_body($LaunchArea.global_position)
+ await get_tree().create_timer(0.5).timeout
+ launch_ball()
+
+
+func _on_wall_area_body_exited(_body: Node2D) -> void:
+ $Wall.visible = true
+ $Wall.process_mode = Node.PROCESS_MODE_INHERIT
diff --git a/pinball.gd.uid b/pinball.gd.uid
new file mode 100644
index 0000000..84e864d
--- /dev/null
+++ b/pinball.gd.uid
@@ -0,0 +1 @@
+uid://drgww31jt6bor
diff --git a/pinball.tscn b/pinball.tscn
new file mode 100644
index 0000000..f23b631
--- /dev/null
+++ b/pinball.tscn
@@ -0,0 +1,163 @@
+[gd_scene load_steps=13 format=3 uid="uid://r0g3nkf417ff"]
+
+[ext_resource type="Script" uid="uid://drgww31jt6bor" path="res://pinball.gd" id="1_e78sp"]
+[ext_resource type="Texture2D" uid="uid://bmra2qpibkr2u" path="res://assets/placeholders/6266.png" id="2_ia8el"]
+[ext_resource type="PackedScene" uid="uid://b1nhd5h4tie07" path="res://ball.tscn" id="3_t6ii5"]
+[ext_resource type="PackedScene" uid="uid://0wgt426mn3tt" path="res://flipper.tscn" id="4_7dlyx"]
+[ext_resource type="PackedScene" uid="uid://bfv0i5cqbhysy" path="res://bumper_a.tscn" id="5_ia8el"]
+[ext_resource type="PackedScene" uid="uid://bwo077rca3xmb" path="res://hud.tscn" id="6_ia8el"]
+[ext_resource type="PackedScene" uid="uid://c8vbuly76q4cr" path="res://bumper_b.tscn" id="6_r4l6h"]
+[ext_resource type="PackedScene" uid="uid://du5c3h14wyygb" path="res://bumper_c.tscn" id="7_55ysk"]
+[ext_resource type="PackedScene" uid="uid://cik38e6miyh17" path="res://guide.tscn" id="8_q2wd1"]
+
+[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_ia8el"]
+friction = 0.2
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_7dlyx"]
+size = Vector2(161, 20)
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_r4l6h"]
+size = Vector2(14, 9.75)
+
+[node name="Pinball" type="Node2D"]
+script = ExtResource("1_e78sp")
+
+[node name="HUD" parent="." instance=ExtResource("6_ia8el")]
+
+[node name="Camera2D" type="Camera2D" parent="."]
+position = Vector2(96, 70)
+offset = Vector2(0, 6)
+
+[node name="Board" type="StaticBody2D" parent="."]
+physics_material_override = SubResource("PhysicsMaterial_ia8el")
+
+[node name="Board" type="Sprite2D" parent="Board"]
+position = Vector2(0, 6)
+texture = ExtResource("2_ia8el")
+centered = false
+region_enabled = true
+region_rect = Rect2(8, 24, 192, 278)
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Board"]
+position = Vector2(0, 6)
+polygon = PackedVector2Array(56, 278, 8, 246, 8, 208, 11, 201, 17, 197, 24, 196, 24, 163, 20, 158, 15, 151, 11, 143, 8, 134, 6, 120, 5, 110, 5, 86, 6, 76, 7, 68, 10, 54, 17, 39, 34, 19, 55, 7, 67, 4, 72, 3, 101, 3, 108, 4, 118, 7, 129, 11, 139, 17, 146, 22, 156, 32, 168, 49, 175, 68, 176, 72, 176, 278, 192, 278, 192, 0, 0, 0, 0, 278)
+
+[node name="CollisionPolygon2D2" type="CollisionPolygon2D" parent="Board"]
+position = Vector2(0, 6)
+polygon = PackedVector2Array(104, 278, 152, 246, 152, 208, 149, 203, 148, 201, 145, 199, 140, 196, 136, 196, 136, 163, 139, 158, 142, 153, 146, 145, 149, 140, 151, 134, 153, 120, 155, 110, 155, 86, 153, 76, 152, 68, 151, 62, 149, 54, 145, 46, 146, 45, 149, 49, 153, 55, 155, 61, 157, 67, 159, 74, 160, 80, 160, 278)
+
+[node name="CollisionPolygon2D3" type="CollisionPolygon2D" parent="Board"]
+position = Vector2(0, 6)
+polygon = PackedVector2Array(160, 278, 176, 278, 176, 279, 160, 279)
+
+[node name="CollisionPolygon2D4" type="CollisionPolygon2D" parent="Board"]
+position = Vector2(0, 6)
+polygon = PackedVector2Array(22, 206, 20, 207, 20, 240, 54, 262, 56, 262, 56, 254, 25, 232, 24, 230, 24, 207)
+
+[node name="CollisionPolygon2D5" type="CollisionPolygon2D" parent="Board"]
+position = Vector2(0, 6)
+polygon = PackedVector2Array(138, 206, 136, 207, 136, 230, 136, 232, 104, 254, 104, 262, 108, 261, 138, 242, 140, 240, 140, 207)
+
+[node name="CollisionPolygon2D6" type="CollisionPolygon2D" parent="Board"]
+position = Vector2(0, 6)
+polygon = PackedVector2Array(68, 24, 66, 25, 66, 41, 70, 41, 70, 25)
+
+[node name="CollisionPolygon2D7" type="CollisionPolygon2D" parent="Board"]
+position = Vector2(24, 6)
+polygon = PackedVector2Array(68, 24, 66, 25, 66, 41, 70, 41, 70, 25)
+
+[node name="CollisionPolygon2D8" type="CollisionPolygon2D" parent="Board"]
+position = Vector2(0, 6)
+polygon = PackedVector2Array(48, 32, 45, 33, 40, 38, 35, 44, 30, 52, 27, 60, 25, 69, 24, 75, 23, 82, 23, 114, 24, 123, 25, 129, 26, 134, 28, 139, 30, 144, 32, 148, 34, 151, 48, 142, 46, 137, 45, 134, 45, 130, 48, 126, 52, 123, 58, 120, 64, 117, 70, 114, 72, 114, 72, 113, 71, 111, 32, 111, 32, 96, 40, 96, 40, 97, 48, 97, 56, 98, 64, 99, 72, 99, 72, 98, 70, 96, 52, 86, 44, 81, 42, 80, 40, 75, 40, 72, 41, 68, 42, 64, 43, 62, 45, 58, 47, 55, 48, 54)
+
+[node name="CollisionPolygon2D9" type="CollisionPolygon2D" parent="Board"]
+position = Vector2(0, 6)
+polygon = PackedVector2Array(112, 32, 115, 33, 119, 37, 123, 41, 125, 44, 127, 47, 129, 50, 131, 54, 133, 60, 134, 64, 135, 69, 136, 75, 137, 82, 137, 114, 136, 121, 135, 127, 134, 132, 132, 137, 131, 140, 128, 147, 126, 151, 112, 143, 113, 140, 114, 137, 115, 132, 113, 127, 110, 124, 90, 114, 88, 114, 88, 113, 89, 111, 128, 111, 128, 96, 120, 96, 112, 97, 104, 98, 96, 99, 88, 99, 88, 98, 90, 96, 94, 94, 98, 92, 102, 90, 106, 88, 110, 86, 114, 84, 117, 81, 119, 78, 121, 75, 121, 70, 119, 66, 118, 62, 116, 58, 114, 54, 112, 53)
+
+[node name="CollisionPolygon2D10" type="CollisionPolygon2D" parent="Board"]
+position = Vector2(0, 8)
+polygon = PackedVector2Array(80, 277, 78, 278, 82, 278)
+
+[node name="Wall" type="StaticBody2D" parent="."]
+position = Vector2(0, 6)
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Wall"]
+scale = Vector2(1, 1)
+polygon = PackedVector2Array(145, 45, 139, 36, 136, 32, 130, 26, 122, 19, 114, 14, 107, 10, 101, 7, 98, 6, 94, 6, 101, 6, 116, 9, 131, 15, 141, 21, 145, 24, 146, 25, 146, 45, 145, 46)
+
+[node name="WallArea" type="Area2D" parent="."]
+position = Vector2(0, 6)
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="WallArea"]
+polygon = PackedVector2Array(145, 45, 139, 36, 136, 32, 130, 26, 122, 19, 114, 14, 101, 7, 98, 6, 94, 6, 88, 3, 101, 3, 116, 9, 131, 15, 141, 21, 145, 24, 146, 25, 146, 45, 145, 46)
+
+[node name="FlipperLeft" parent="." instance=ExtResource("4_7dlyx")]
+position = Vector2(56, 264)
+
+[node name="FlipperRight" parent="." instance=ExtResource("4_7dlyx")]
+position = Vector2(104, 264)
+scale = Vector2(-1, 1)
+
+[node name="Outside" type="Area2D" parent="."]
+position = Vector2(60, 294)
+metadata/_edit_group_ = true
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="Outside"]
+position = Vector2(20.5, 6)
+shape = SubResource("RectangleShape2D_7dlyx")
+
+[node name="LaunchArea" type="Area2D" parent="."]
+position = Vector2(168, 278)
+metadata/_edit_group_ = true
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="LaunchArea"]
+position = Vector2(0, 0.125)
+shape = SubResource("RectangleShape2D_r4l6h")
+
+[node name="Objects" type="Node2D" parent="."]
+
+[node name="Bumper-a" parent="Objects" instance=ExtResource("5_ia8el")]
+position = Vector2(80, 62)
+physics_material_override = null
+
+[node name="Bumper-a2" parent="Objects" instance=ExtResource("5_ia8el")]
+position = Vector2(61, 76)
+physics_material_override = null
+
+[node name="Bumper-a3" parent="Objects" instance=ExtResource("5_ia8el")]
+position = Vector2(100, 76)
+physics_material_override = null
+
+[node name="Bumper-B" parent="Objects" instance=ExtResource("6_r4l6h")]
+position = Vector2(44, 228)
+physics_material_override = null
+
+[node name="Bumper-B2" parent="Objects" instance=ExtResource("6_r4l6h")]
+position = Vector2(116, 228)
+scale = Vector2(-1, 1)
+physics_material_override = null
+
+[node name="Bumper-C" parent="Objects" instance=ExtResource("7_55ysk")]
+position = Vector2(35, 150)
+physics_material_override = null
+
+[node name="Bumper-C2" parent="Objects" instance=ExtResource("7_55ysk")]
+position = Vector2(125, 150)
+scale = Vector2(-1, 1)
+physics_material_override = null
+
+[node name="Guide" parent="Objects" instance=ExtResource("8_q2wd1")]
+position = Vector2(72, 100)
+
+[node name="Sprite2D" type="Sprite2D" parent="Objects"]
+position = Vector2(81, 188)
+texture = ExtResource("2_ia8el")
+region_enabled = true
+region_rect = Rect2(297, 747, 62, 11)
+
+[node name="Ball" parent="." instance=ExtResource("3_t6ii5")]
+unique_name_in_owner = true
+position = Vector2(168, 276)
+
+[connection signal="body_exited" from="WallArea" to="." method="_on_wall_area_body_exited"]
+[connection signal="body_entered" from="Outside" to="." method="_on_outside_body_entered"]
diff --git a/project.godot b/project.godot
new file mode 100644
index 0000000..97669af
--- /dev/null
+++ b/project.godot
@@ -0,0 +1,59 @@
+; 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="tuxemon-pinball"
+run/main_scene="uid://r0g3nkf417ff"
+config/features=PackedStringArray("4.4", "GL Compatibility")
+config/icon="res://icon.svg"
+
+[display]
+
+window/size/viewport_width=192
+window/size/viewport_height=139
+window/stretch/mode="canvas_items"
+window/stretch/scale_mode="integer"
+
+[global_group]
+
+bumper=""
+guide=""
+
+[input]
+
+flipper_left={
+"deadzone": 0.2,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+]
+}
+flipper_right={
+"deadzone": 0.2,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+]
+}
+shake={
+"deadzone": 0.2,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+]
+}
+
+[physics]
+
+2d/default_gravity=100.0
+
+[rendering]
+
+textures/canvas_textures/default_texture_filter=0
+renderer/rendering_method="gl_compatibility"
+renderer/rendering_method.mobile="gl_compatibility"
diff --git a/tuxeball.png b/tuxeball.png
new file mode 100644
index 0000000..feadd4b
--- /dev/null
+++ b/tuxeball.png
Binary files differ
diff --git a/tuxeball.png.import b/tuxeball.png.import
new file mode 100644
index 0000000..f75e84d
--- /dev/null
+++ b/tuxeball.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://1j0d0qpa7qxt"
+path="res://.godot/imported/tuxeball.png-27d49a57c763ac5b9671c068ad5332da.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://tuxeball.png"
+dest_files=["res://.godot/imported/tuxeball.png-27d49a57c763ac5b9671c068ad5332da.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