remove game/ directory
This commit is contained in:
parent
73116da632
commit
7b25ae028b
48 changed files with 1 additions and 1 deletions
25
src/GameState.gd
Normal file
25
src/GameState.gd
Normal file
|
@ -0,0 +1,25 @@
|
|||
extends Node
|
||||
class_name GameState
|
||||
|
||||
# https://github.com/GDQuest/godot-demos/blob/master/2018/04-24-finite-state-machine/player_v2/state_machine.gd
|
||||
# http://www.gameprogrammingpatterns.com/state.html
|
||||
|
||||
enum State { MAIN, WINNING, GAME_OVER }
|
||||
var transitions = {
|
||||
State.MAIN : [ State.WINNING ],
|
||||
State.WINNING : [ State.GAME_OVER ],
|
||||
State.GAME_OVER : [ State.MAIN ]
|
||||
}
|
||||
|
||||
signal state_changed(previous, current)
|
||||
|
||||
export(State) var current_state = State.MAIN
|
||||
|
||||
func current_state_name() -> String:
|
||||
return State.keys()[current_state]
|
||||
|
||||
func transition_to(state):
|
||||
assert(state in transitions[current_state])
|
||||
var previous_state = current_state
|
||||
current_state = state
|
||||
emit_signal("state_changed", previous_state, current_state)
|
29
src/Main.gd
Normal file
29
src/Main.gd
Normal file
|
@ -0,0 +1,29 @@
|
|||
extends Control
|
||||
|
||||
export var blur: int = 3
|
||||
export var blur_transition_duration: float = 1
|
||||
var blur_amount: float = 0
|
||||
var blur_step: float = 0
|
||||
|
||||
func _ready():
|
||||
$ColorRect.visible = false
|
||||
blur_amount = 0
|
||||
blur_step = blur / blur_transition_duration
|
||||
print("Starting state: ", $GameState.current_state_name())
|
||||
|
||||
func _process(delta):
|
||||
if blur_amount < blur:
|
||||
blur_amount += delta * blur_step
|
||||
print("blur: ", blur_amount)
|
||||
$ColorRect.material.set_shader_param("blur_amount", blur_amount)
|
||||
|
||||
func _on_GameState_state_changed(previous, current):
|
||||
print("GameState: ", $GameState.State.keys()[previous], " -> ", $GameState.State.keys()[current])
|
||||
match current:
|
||||
GameState.State.WINNING:
|
||||
print("Solved!")
|
||||
# TODO: wait for the animation to finish before game over
|
||||
#$GameState.transition_to(GameState.State.GAME_OVER)
|
||||
GameState.State.GAME_OVER:
|
||||
$ColorRect.visible = true
|
||||
$ColorRect.material.set_shader_param("blur_amount", blur_amount)
|
126
src/Main.tscn
Normal file
126
src/Main.tscn
Normal file
|
@ -0,0 +1,126 @@
|
|||
[gd_scene load_steps=7 format=2]
|
||||
|
||||
[ext_resource path="res://src/Main.gd" type="Script" id=1]
|
||||
[ext_resource path="res://src/Taquin.tscn" type="PackedScene" id=2]
|
||||
[ext_resource path="res://src/GameState.gd" type="Script" id=3]
|
||||
[ext_resource path="res://assets/fonts/Montserrat-ExtraBolt-48.tres" type="DynamicFont" id=4]
|
||||
|
||||
[sub_resource type="Shader" id=1]
|
||||
code = "shader_type canvas_item;
|
||||
|
||||
uniform float blur_amount : hint_range(0, 5);
|
||||
|
||||
void fragment() {
|
||||
COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, blur_amount);
|
||||
}"
|
||||
|
||||
[sub_resource type="ShaderMaterial" id=2]
|
||||
shader = SubResource( 1 )
|
||||
shader_param/blur_amount = null
|
||||
|
||||
[node name="Main" type="Control"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="HSplitContainer" type="HSplitContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 20.0
|
||||
margin_top = 20.0
|
||||
margin_right = -20.0
|
||||
margin_bottom = -20.0
|
||||
custom_constants/separation = 0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Taquin" parent="HSplitContainer" instance=ExtResource( 2 )]
|
||||
margin_right = 540.0
|
||||
margin_bottom = 560.0
|
||||
rows = 4
|
||||
columns = 4
|
||||
|
||||
[node name="VSplitContainer" type="VBoxContainer" parent="HSplitContainer"]
|
||||
margin_left = 548.0
|
||||
margin_right = 984.0
|
||||
margin_bottom = 560.0
|
||||
alignment = 1
|
||||
|
||||
[node name="New Game" type="Button" parent="HSplitContainer/VSplitContainer"]
|
||||
margin_top = 228.0
|
||||
margin_right = 436.0
|
||||
margin_bottom = 278.0
|
||||
rect_min_size = Vector2( 0, 50 )
|
||||
flat = true
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="HSplitContainer/VSplitContainer/New Game"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
color = Color( 0.0705882, 0.294118, 0.396078, 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="HSplitContainer/VSplitContainer/New Game"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
custom_fonts/font = ExtResource( 4 )
|
||||
custom_colors/font_color_shadow = Color( 0.827451, 0.423529, 0.0666667, 1 )
|
||||
custom_constants/shadow_offset_x = 0
|
||||
custom_constants/shadow_offset_y = 2
|
||||
text = "New Game"
|
||||
align = 1
|
||||
valign = 1
|
||||
uppercase = true
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Hints" type="Button" parent="HSplitContainer/VSplitContainer"]
|
||||
margin_top = 282.0
|
||||
margin_right = 436.0
|
||||
margin_bottom = 332.0
|
||||
rect_min_size = Vector2( 0, 50 )
|
||||
flat = true
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="HSplitContainer/VSplitContainer/Hints"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
color = Color( 0.0705882, 0.294118, 0.396078, 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="HSplitContainer/VSplitContainer/Hints"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
custom_fonts/font = ExtResource( 4 )
|
||||
custom_colors/font_color_shadow = Color( 0.827451, 0.423529, 0.0666667, 1 )
|
||||
custom_constants/shadow_offset_x = 0
|
||||
custom_constants/shadow_offset_y = 2
|
||||
text = "Hints"
|
||||
align = 1
|
||||
valign = 1
|
||||
uppercase = true
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="."]
|
||||
material = SubResource( 2 )
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 0.510254
|
||||
margin_right = 0.510254
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="GameState" type="Node" parent="."]
|
||||
script = ExtResource( 3 )
|
||||
[connection signal="state_changed" from="GameState" to="." method="_on_GameState_state_changed"]
|
||||
[connection signal="state_changed" from="GameState" to="HSplitContainer/Taquin" method="_on_GameState_state_changed"]
|
20
src/MainMobile.tscn
Normal file
20
src/MainMobile.tscn
Normal file
|
@ -0,0 +1,20 @@
|
|||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://src/Main.gd" type="Script" id=1]
|
||||
[ext_resource path="res://src/Taquin.tscn" type="PackedScene" id=2]
|
||||
|
||||
[node name="Main" type="Control"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Taquin" parent="." instance=ExtResource( 2 )]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -270.0
|
||||
margin_top = -280.0
|
||||
margin_right = 270.0
|
||||
margin_bottom = 280.0
|
||||
[connection signal="solved" from="Taquin" to="." method="_on_Taquin_solved"]
|
26
src/Piece.gd
Normal file
26
src/Piece.gd
Normal file
|
@ -0,0 +1,26 @@
|
|||
tool
|
||||
extends Node2D
|
||||
class_name Piece
|
||||
|
||||
export var size: int = 160
|
||||
|
||||
var order: int = 0
|
||||
var taquin_position := Vector2.ZERO
|
||||
var taquin_index := Vector2.ZERO
|
||||
var piece_scale := Vector2(0.25, 0.25)
|
||||
|
||||
func _ready() -> void:
|
||||
$ColorRect.rect_size = Vector2(size, size)
|
||||
$ColorRect/Label.text = str(order)
|
||||
|
||||
# We need a dedicated material to have separate uniform,
|
||||
# otherwise uniforms will be shared.
|
||||
var mat = $ColorRect.material.duplicate() as ShaderMaterial
|
||||
mat.set_shader_param("scale", Vector3(piece_scale.x, piece_scale.y, 1.0))
|
||||
mat.set_shader_param("offset", Vector3(taquin_position.x, taquin_position.y, 0.0))
|
||||
$ColorRect.material = mat
|
||||
|
||||
func debug_print(name: String):
|
||||
print("%s order: %s" % [name, order])
|
||||
print("%s taquin index: %s" % [name, taquin_index])
|
||||
print("%s taquin position: %s" % [name, position])
|
137
src/Piece.tscn
Normal file
137
src/Piece.tscn
Normal file
|
@ -0,0 +1,137 @@
|
|||
[gd_scene load_steps=13 format=2]
|
||||
|
||||
[ext_resource path="res://src/Piece.gd" type="Script" id=1]
|
||||
[ext_resource path="res://assets/escher_lizards.jpg" type="Texture" id=2]
|
||||
|
||||
[sub_resource type="VisualShaderNodeVec3Constant" id=1]
|
||||
constant = Vector3( 0.3, 0.3, 0 )
|
||||
|
||||
[sub_resource type="VisualShaderNodeTexture" id=2]
|
||||
output_port_for_preview = 0
|
||||
texture = ExtResource( 2 )
|
||||
|
||||
[sub_resource type="VisualShaderNodeInput" id=3]
|
||||
output_port_for_preview = 0
|
||||
input_name = "uv"
|
||||
|
||||
[sub_resource type="VisualShaderNodeVectorOp" id=4]
|
||||
output_port_for_preview = 0
|
||||
default_input_values = [ 0, Vector3( 0, 0, 0 ), 1, Vector3( 0.25, 0.25, 1 ) ]
|
||||
operator = 2
|
||||
|
||||
[sub_resource type="VisualShaderNodeVectorOp" id=5]
|
||||
default_input_values = [ 0, Vector3( 0, 0, 0 ), 1, Vector3( 0.8, 0.1, 0 ) ]
|
||||
|
||||
[sub_resource type="VisualShaderNodeVec3Uniform" id=6]
|
||||
uniform_name = "scale"
|
||||
|
||||
[sub_resource type="VisualShaderNodeVec3Uniform" id=7]
|
||||
uniform_name = "offset"
|
||||
|
||||
[sub_resource type="VisualShaderNodeVec3Constant" id=8]
|
||||
constant = Vector3( 0.25, 0.25, 1 )
|
||||
|
||||
[sub_resource type="VisualShader" id=9]
|
||||
code = "shader_type canvas_item;
|
||||
uniform vec3 scale;
|
||||
uniform vec3 offset;
|
||||
uniform sampler2D tex_frg_3;
|
||||
|
||||
|
||||
void vertex() {
|
||||
// Output:0
|
||||
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
// Input:4
|
||||
vec3 n_out4p0;
|
||||
n_out4p0 = vec3(UV,0.0);
|
||||
|
||||
// VectorUniform:7
|
||||
vec3 n_out7p0;
|
||||
n_out7p0 = scale;
|
||||
|
||||
// VectorOp:5
|
||||
vec3 n_out5p0;
|
||||
n_out5p0 = n_out4p0 * n_out7p0;
|
||||
|
||||
// VectorUniform:8
|
||||
vec3 n_out8p0;
|
||||
n_out8p0 = offset;
|
||||
|
||||
// VectorOp:6
|
||||
vec3 n_out6p0;
|
||||
n_out6p0 = n_out5p0 + n_out8p0;
|
||||
|
||||
// Texture:3
|
||||
vec3 n_out3p0;
|
||||
float n_out3p1;
|
||||
vec4 tex_frg_3_read = texture( tex_frg_3 , n_out6p0.xy );
|
||||
n_out3p0 = tex_frg_3_read.rgb;
|
||||
n_out3p1 = tex_frg_3_read.a;
|
||||
|
||||
// Output:0
|
||||
COLOR.rgb = n_out3p0;
|
||||
|
||||
}
|
||||
|
||||
void light() {
|
||||
// Output:0
|
||||
|
||||
}
|
||||
"
|
||||
graph_offset = Vector2( 0, 75 )
|
||||
mode = 1
|
||||
flags/light_only = false
|
||||
nodes/fragment/0/position = Vector2( 1080, 160 )
|
||||
nodes/fragment/3/node = SubResource( 2 )
|
||||
nodes/fragment/3/position = Vector2( 820, 120 )
|
||||
nodes/fragment/4/node = SubResource( 3 )
|
||||
nodes/fragment/4/position = Vector2( 40, 60 )
|
||||
nodes/fragment/5/node = SubResource( 4 )
|
||||
nodes/fragment/5/position = Vector2( 360, 80 )
|
||||
nodes/fragment/6/node = SubResource( 5 )
|
||||
nodes/fragment/6/position = Vector2( 620, 240 )
|
||||
nodes/fragment/7/node = SubResource( 6 )
|
||||
nodes/fragment/7/position = Vector2( 40, 240 )
|
||||
nodes/fragment/8/node = SubResource( 7 )
|
||||
nodes/fragment/8/position = Vector2( 360, 380 )
|
||||
nodes/fragment/9/node = SubResource( 8 )
|
||||
nodes/fragment/9/position = Vector2( 40, 300 )
|
||||
nodes/fragment/11/node = SubResource( 1 )
|
||||
nodes/fragment/11/position = Vector2( 360, 440 )
|
||||
nodes/fragment/connections = PoolIntArray( 3, 0, 0, 0, 4, 0, 5, 0, 5, 0, 6, 0, 6, 0, 3, 0, 7, 0, 5, 1, 8, 0, 6, 1 )
|
||||
|
||||
[sub_resource type="ShaderMaterial" id=10]
|
||||
shader = SubResource( 9 )
|
||||
shader_param/scale = Vector3( 0.25, 0.25, 1 )
|
||||
shader_param/offset = Vector3( 0, 0, 0 )
|
||||
|
||||
[node name="Piece" type="Node2D"]
|
||||
script = ExtResource( 1 )
|
||||
size = 64
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="."]
|
||||
material = SubResource( 10 )
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_right = 64.0
|
||||
margin_bottom = 64.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="ColorRect"]
|
||||
margin_left = 10.0
|
||||
margin_top = 10.0
|
||||
margin_right = 50.0
|
||||
margin_bottom = 50.0
|
||||
rect_scale = Vector2( 3, 3 )
|
||||
rect_clip_content = true
|
||||
size_flags_vertical = 1
|
||||
custom_colors/font_color = Color( 0, 0, 0, 1 )
|
||||
text = "0"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
312
src/Taquin.gd
Normal file
312
src/Taquin.gd
Normal file
|
@ -0,0 +1,312 @@
|
|||
tool
|
||||
extends Control
|
||||
class_name Taquin
|
||||
|
||||
var Piece = preload("res://src/Piece.tscn")
|
||||
|
||||
export var rows: int = 4
|
||||
export var columns: int = 4
|
||||
export var width: int = 512
|
||||
export var height: int = 512
|
||||
export var difficulty: int = 10
|
||||
|
||||
enum Direction { UP, DOWN, LEFT, RIGHT }
|
||||
|
||||
var interpiece := 4
|
||||
var min_padding := 15
|
||||
var padding := Vector2(min_padding, min_padding)
|
||||
|
||||
var pieces: Array = []
|
||||
var rng := RandomNumberGenerator.new()
|
||||
|
||||
var current_animation_path := "AnimationPlayer/MockPiece:position"
|
||||
|
||||
var swipe := Vector2(0, 0)
|
||||
var is_sliding := false
|
||||
var minimum_slide_length := 5.0
|
||||
|
||||
var current_sliding_piece: Piece = null
|
||||
var missing_piece: Piece = null
|
||||
|
||||
var current_origin := Vector2.ZERO
|
||||
var current_goal := Vector2.ZERO
|
||||
var current_axis := Vector2.ZERO
|
||||
var current_touch_slide := Vector2.ZERO
|
||||
var local_min_position := Vector2.ZERO
|
||||
var local_max_position := Vector2.ZERO
|
||||
|
||||
func position_for_index(index: Vector2, size: int) -> Vector2:
|
||||
return padding + Vector2(index.x * (size + interpiece), index.y * (size + interpiece))
|
||||
|
||||
func compute_piece_size() -> int:
|
||||
var w_size: int = (width - (2 * min_padding) - ((columns - 1) * interpiece)) / columns
|
||||
var h_size: int = (height - (2 * min_padding) - ((rows - 1) * interpiece)) / rows
|
||||
return int(min(w_size, h_size))
|
||||
|
||||
func compute_padding(piece_size: int) -> Vector2:
|
||||
var p = Vector2(0, 0)
|
||||
p.x = width - columns * piece_size - (columns - 1) * interpiece
|
||||
p.y = height - rows * piece_size - (rows - 1) * interpiece
|
||||
p = p / Vector2(2, 2)
|
||||
return p
|
||||
|
||||
func _ready() -> void:
|
||||
$AnimationPlayer/MockPiece.visible = false
|
||||
$Particles2D.emitting = false
|
||||
|
||||
$Background.rect_size.x = width
|
||||
$Background.rect_size.y = height
|
||||
|
||||
rng.randomize()
|
||||
var piece_size: int = compute_piece_size()
|
||||
padding = compute_padding(piece_size)
|
||||
print("piece size: ", piece_size)
|
||||
print("padding: ", padding)
|
||||
|
||||
for c in range(columns):
|
||||
var pieces_row: Array = []
|
||||
for r in range(rows):
|
||||
var piece = Piece.instance()
|
||||
|
||||
piece.size = piece_size
|
||||
piece.position = position_for_index(Vector2(c, r), piece.size)
|
||||
piece.order = 1 + c + r * columns
|
||||
piece.taquin_index = Vector2(c, r)
|
||||
piece.piece_scale = Vector2((float(piece_size) / width), (float(piece_size) / height))
|
||||
piece.taquin_position = Vector2(float(piece.position.x) / width, float(piece.position.y) / height)
|
||||
|
||||
if r == rows - 1 && c == columns - 1:
|
||||
piece.visible = false
|
||||
missing_piece = piece
|
||||
|
||||
$Background.add_child(piece)
|
||||
pieces_row.append(piece)
|
||||
|
||||
pieces.append(pieces_row)
|
||||
|
||||
shuffle(difficulty)
|
||||
|
||||
func _process(delta):
|
||||
var game_state = get_node_or_null("/root/Main/GameState") as GameState
|
||||
if game_state != null:
|
||||
match game_state.current_state:
|
||||
# If we are in the winning animation, fast-forward to game over screen
|
||||
GameState.State.WINNING:
|
||||
game_state.transition_to(GameState.State.GAME_OVER)
|
||||
return
|
||||
GameState.State.GAME_OVER:
|
||||
return
|
||||
|
||||
func _input(event):
|
||||
if $AnimationPlayer.is_playing():
|
||||
# Disable input during animation
|
||||
return
|
||||
|
||||
#
|
||||
# Handle keyboard input
|
||||
#
|
||||
if event.is_action_pressed("ui_up"):
|
||||
move_piece(Direction.UP)
|
||||
if event.is_action_pressed("ui_down"):
|
||||
move_piece(Direction.DOWN)
|
||||
if event.is_action_pressed("ui_left"):
|
||||
move_piece(Direction.LEFT)
|
||||
if event.is_action_pressed("ui_right"):
|
||||
move_piece(Direction.RIGHT)
|
||||
|
||||
#
|
||||
# Handle touch input
|
||||
#
|
||||
if event is InputEventScreenDrag:
|
||||
swipe = event.relative
|
||||
# We check that the slide to have a minimum length
|
||||
# before detecting the direction to avoid jittering.
|
||||
if swipe.length() > minimum_slide_length and not is_sliding:
|
||||
print("sliding started")
|
||||
is_sliding = true
|
||||
current_touch_slide = Vector2.ZERO
|
||||
var angle = swipe.angle()
|
||||
var direction = direction_for_angle(angle)
|
||||
debug_print_direction(direction)
|
||||
current_sliding_piece = sliding_piece_for_direction(direction)
|
||||
if current_sliding_piece != null:
|
||||
current_axis = axis_for_direction(direction)
|
||||
var local_end_position = current_axis * (current_sliding_piece.size + interpiece)
|
||||
local_min_position = Vector2(min(0, local_end_position.x), min(0, local_end_position.y))
|
||||
local_max_position = Vector2(max(0, local_end_position.x), max(0, local_end_position.y))
|
||||
current_goal = current_sliding_piece.position + local_end_position
|
||||
current_touch_slide += swipe
|
||||
if current_sliding_piece != null:
|
||||
var delta = current_touch_slide.project(current_axis)
|
||||
delta.x = clamp(delta.x, local_min_position.x, local_max_position.x)
|
||||
delta.y = clamp(delta.y, local_min_position.y, local_max_position.y)
|
||||
current_sliding_piece.position = current_origin + delta
|
||||
|
||||
if event is InputEventScreenTouch:
|
||||
if not event.pressed: # Touch released
|
||||
is_sliding = false
|
||||
if current_sliding_piece != null:
|
||||
var current_position = current_sliding_piece.position
|
||||
if current_position.distance_to(current_origin) > current_position.distance_to(current_goal):
|
||||
current_sliding_piece.position = current_goal
|
||||
commit_slide()
|
||||
else:
|
||||
reset_position(current_sliding_piece)
|
||||
reset_slide()
|
||||
|
||||
func debug_print_direction(direction: int):
|
||||
match direction:
|
||||
Direction.UP:
|
||||
print("Direction ⬆️ UP")
|
||||
Direction.DOWN:
|
||||
print("Direction ⬇️ DOWN")
|
||||
Direction.LEFT:
|
||||
print("Direction ⬅️ LEFT")
|
||||
Direction.RIGHT:
|
||||
print("Direction ➡️ RIGHT")
|
||||
_:
|
||||
assert(false)
|
||||
|
||||
func axis_for_direction(direction: int) -> Vector2:
|
||||
match direction:
|
||||
Direction.UP:
|
||||
return Vector2.UP
|
||||
Direction.DOWN:
|
||||
return Vector2.DOWN
|
||||
Direction.LEFT:
|
||||
return Vector2.LEFT
|
||||
Direction.RIGHT:
|
||||
return Vector2.RIGHT
|
||||
_:
|
||||
assert(false)
|
||||
return Vector2.ZERO
|
||||
|
||||
func direction_for_angle(angle: float) -> int:
|
||||
if angle < PI / 4 and angle >= - PI / 4:
|
||||
return Direction.RIGHT
|
||||
if angle >= PI / 4 and angle < PI - PI / 4:
|
||||
return Direction.DOWN
|
||||
if angle >= - PI + PI / 4 and angle < - PI / 4:
|
||||
return Direction.UP
|
||||
if angle >= PI - PI / 4 or angle < -PI + PI / 4:
|
||||
return Direction.LEFT
|
||||
assert(false)
|
||||
return Direction.DOWN
|
||||
|
||||
func sliding_piece_for_direction(direction) -> Piece:
|
||||
var destination: Vector2 = missing_piece.taquin_index
|
||||
match direction:
|
||||
Direction.UP:
|
||||
destination.y += 1
|
||||
Direction.DOWN:
|
||||
destination.y -= 1
|
||||
Direction.LEFT:
|
||||
destination.x += 1
|
||||
Direction.RIGHT:
|
||||
destination.x -= 1
|
||||
|
||||
if (destination.x < 0 || destination.x >= columns
|
||||
|| destination.y < 0 || destination.y >= rows):
|
||||
print("\/!\\ Impossible move")
|
||||
return null
|
||||
|
||||
var piece = pieces[destination.x][destination.y]
|
||||
current_origin = piece.position
|
||||
|
||||
return piece
|
||||
|
||||
func move_piece(direction) -> void:
|
||||
current_sliding_piece = sliding_piece_for_direction(direction)
|
||||
if current_sliding_piece == null:
|
||||
reset_slide()
|
||||
return
|
||||
|
||||
var moving_piece_animation: Animation = $AnimationPlayer.get_animation("MovingPiece")
|
||||
|
||||
assert(moving_piece_animation != null)
|
||||
assert(moving_piece_animation.get_track_count() > 0)
|
||||
|
||||
var moving_piece_track_index: int = moving_piece_animation.find_track(current_animation_path)
|
||||
|
||||
assert(moving_piece_track_index != -1)
|
||||
|
||||
var new_animation_path: String = str($AnimationPlayer.get_parent().get_path_to(current_sliding_piece), ":position")
|
||||
moving_piece_animation.track_set_path(moving_piece_track_index, new_animation_path)
|
||||
current_animation_path = new_animation_path
|
||||
|
||||
moving_piece_animation.track_set_key_value(moving_piece_track_index, 0, current_sliding_piece.position)
|
||||
moving_piece_animation.track_set_key_value(moving_piece_track_index, 1, missing_piece.position)
|
||||
$AnimationPlayer.play("MovingPiece")
|
||||
|
||||
commit_slide()
|
||||
|
||||
update()
|
||||
check_solved()
|
||||
|
||||
func commit_slide():
|
||||
assert(current_sliding_piece != null)
|
||||
assert(current_origin != Vector2.ZERO)
|
||||
|
||||
var x_delta = missing_piece.taquin_index.x - current_sliding_piece.taquin_index.x
|
||||
var y_delta = missing_piece.taquin_index.y - current_sliding_piece.taquin_index.y
|
||||
assert(abs(x_delta) + abs(y_delta) == 1)
|
||||
|
||||
swap_pieces(missing_piece, current_sliding_piece)
|
||||
reset_position(missing_piece)
|
||||
|
||||
ensure_validity()
|
||||
reset_slide()
|
||||
check_solved()
|
||||
|
||||
func reset_slide():
|
||||
current_sliding_piece = null
|
||||
current_origin = Vector2.ZERO
|
||||
|
||||
func reset_position(p: Piece):
|
||||
p.position = position_for_index(p.taquin_index, p.size)
|
||||
|
||||
func swap_pieces(a: Piece, b: Piece) -> void:
|
||||
var a_index := a.taquin_index
|
||||
a.taquin_index = b.taquin_index
|
||||
pieces[b.taquin_index.x][b.taquin_index.y] = a
|
||||
b.taquin_index = a_index
|
||||
pieces[a_index.x][a_index.y] = b
|
||||
|
||||
func shuffle(count: int) -> void:
|
||||
while count > 0:
|
||||
count -= 1
|
||||
var direction = rng.randi_range(Direction.UP, Direction.RIGHT)
|
||||
move_piece(direction)
|
||||
|
||||
func check_solved() -> bool:
|
||||
for c in range(columns):
|
||||
for r in range(rows):
|
||||
if pieces[c][r].order != 1 + c + r * columns:
|
||||
return false
|
||||
|
||||
var game_state = get_node_or_null("/root/Main/GameState") as GameState
|
||||
if game_state != null:
|
||||
game_state.transition_to(GameState.State.WINNING)
|
||||
|
||||
return true
|
||||
|
||||
func ensure_validity() -> void:
|
||||
for c in range(columns):
|
||||
for r in range(rows):
|
||||
var piece = pieces[c][r]
|
||||
assert(piece.taquin_index.x == c)
|
||||
assert(piece.taquin_index.y == r)
|
||||
|
||||
func _on_GameState_state_changed(previous, current):
|
||||
match current:
|
||||
GameState.State.WINNING:
|
||||
$Particles2D.emitting = true
|
||||
$Timer.start(-1)
|
||||
GameState.State.GAME_OVER:
|
||||
$Particles2D.emitting = false
|
||||
$Timer.stop()
|
||||
|
||||
func _on_Timer_timeout():
|
||||
var game_state = get_node("/root/Main/GameState") as GameState
|
||||
if game_state != null:
|
||||
game_state.transition_to(GameState.State.GAME_OVER)
|
89
src/Taquin.tscn
Normal file
89
src/Taquin.tscn
Normal file
|
@ -0,0 +1,89 @@
|
|||
[gd_scene load_steps=7 format=2]
|
||||
|
||||
[ext_resource path="res://src/Taquin.gd" type="Script" id=1]
|
||||
[ext_resource path="res://src/Piece.tscn" type="PackedScene" id=2]
|
||||
|
||||
[sub_resource type="Animation" id=1]
|
||||
resource_name = "MovingPiece"
|
||||
length = 0.2
|
||||
step = 0.01
|
||||
tracks/0/type = "value"
|
||||
tracks/0/path = NodePath("AnimationPlayer/MockPiece:position")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/keys = {
|
||||
"times": PoolRealArray( 0, 0.2 ),
|
||||
"transitions": PoolRealArray( 1, 1 ),
|
||||
"update": 0,
|
||||
"values": [ Vector2( 15, 15 ), Vector2( 175, 15 ) ]
|
||||
}
|
||||
|
||||
[sub_resource type="Gradient" id=2]
|
||||
colors = PoolColorArray( 1, 1, 1, 1, 1, 0.140625, 0.140625, 1 )
|
||||
|
||||
[sub_resource type="GradientTexture" id=3]
|
||||
gradient = SubResource( 2 )
|
||||
|
||||
[sub_resource type="ParticlesMaterial" id=4]
|
||||
emission_shape = 2
|
||||
emission_box_extents = Vector3( 10, 5, 1 )
|
||||
flag_disable_z = true
|
||||
spread = 180.0
|
||||
gravity = Vector3( 0, 10, 0 )
|
||||
initial_velocity = 50.0
|
||||
angular_velocity = 28.0
|
||||
orbit_velocity = 0.0
|
||||
orbit_velocity_random = 0.0
|
||||
linear_accel = 1.0
|
||||
damping = 22.0
|
||||
angle = 45.0
|
||||
angle_random = 1.0
|
||||
scale = 5.0
|
||||
color_ramp = SubResource( 3 )
|
||||
|
||||
[node name="Taquin" type="Control"]
|
||||
rect_min_size = Vector2( 540, 540 )
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
rows = 3
|
||||
columns = 3
|
||||
difficulty = 0
|
||||
|
||||
[node name="Background" type="ColorRect" parent="."]
|
||||
margin_right = 512.0
|
||||
margin_bottom = 512.0
|
||||
color = Color( 0.254902, 0.329412, 0.45098, 1 )
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
anims/MovingPiece = SubResource( 1 )
|
||||
|
||||
[node name="MockPiece" parent="AnimationPlayer" instance=ExtResource( 2 )]
|
||||
visible = false
|
||||
position = Vector2( 175, 15 )
|
||||
size = 160
|
||||
|
||||
[node name="PlaceholderTexture" type="ColorRect" parent="AnimationPlayer/MockPiece"]
|
||||
margin_right = 160.0
|
||||
margin_bottom = 160.0
|
||||
color = Color( 0.137255, 0.976471, 0.0196078, 1 )
|
||||
|
||||
[node name="Particles2D" type="Particles2D" parent="."]
|
||||
position = Vector2( 253, 262 )
|
||||
rotation = -1.5708
|
||||
emitting = false
|
||||
amount = 30
|
||||
lifetime = 1.6
|
||||
preprocess = 0.25
|
||||
speed_scale = 1.25
|
||||
explosiveness = 1.0
|
||||
local_coords = false
|
||||
process_material = SubResource( 4 )
|
||||
|
||||
[node name="Timer" type="Timer" parent="."]
|
||||
wait_time = 2.0
|
||||
one_shot = true
|
||||
[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"]
|
Loading…
Add table
Add a link
Reference in a new issue