Initial commit. Matching game

This commit is contained in:
Feufochmar 2022-05-24 18:12:10 +02:00
commit 3ee40cae0c
21 changed files with 2553 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.import/

20
Main.gd Normal file
View File

@ -0,0 +1,20 @@
extends Node
# Called when the node enters the scene tree for the first time.
func _ready():
pass
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
if Input.is_action_just_pressed("ui_up"):
$GemGrid.up()
elif Input.is_action_just_pressed("ui_down"):
$GemGrid.down()
elif Input.is_action_just_pressed("ui_left"):
$GemGrid.left()
elif Input.is_action_just_pressed("ui_right"):
$GemGrid.right()
elif Input.is_action_just_pressed("ui_accept"):
$GemGrid.select()
elif Input.is_action_just_pressed("ui_cancel"):
$GemGrid.cancel()

126
Main.tscn Normal file
View File

@ -0,0 +1,126 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://entities/gem_grid.tscn" type="PackedScene" id=1]
[ext_resource path="res://entities/mana_counter.tscn" type="PackedScene" id=2]
[ext_resource path="res://Main.gd" type="Script" id=3]
[node name="Main" type="Node"]
script = ExtResource( 3 )
[node name="GemGrid" parent="." instance=ExtResource( 1 )]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -256.0
margin_top = -256.0
margin_right = 256.0
margin_bottom = 256.0
[node name="Counters" type="Control" parent="."]
margin_right = 256.0
margin_bottom = 600.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ManaCounter" parent="Counters" instance=ExtResource( 2 )]
[node name="ManaCounter2" parent="Counters" instance=ExtResource( 2 )]
margin_top = 32.0
margin_bottom = 64.0
type = 1
[node name="ManaCounter3" parent="Counters" instance=ExtResource( 2 )]
margin_top = 64.0
margin_bottom = 96.0
type = 2
[node name="ManaCounter4" parent="Counters" instance=ExtResource( 2 )]
margin_top = 96.0
margin_bottom = 128.0
type = 3
[node name="ManaCounter5" parent="Counters" instance=ExtResource( 2 )]
margin_top = 128.0
margin_bottom = 160.0
type = 4
[node name="ManaCounter6" parent="Counters" instance=ExtResource( 2 )]
margin_top = 160.0
margin_bottom = 192.0
type = 5
[node name="ManaCounter7" parent="Counters" instance=ExtResource( 2 )]
margin_top = 192.0
margin_bottom = 224.0
type = 6
[node name="ManaCounter8" parent="Counters" instance=ExtResource( 2 )]
margin_top = 224.0
margin_bottom = 256.0
type = 7
[node name="ManaCounter9" parent="Counters" instance=ExtResource( 2 )]
margin_top = 256.0
margin_bottom = 288.0
effect = 1
[node name="ManaCounter10" parent="Counters" instance=ExtResource( 2 )]
margin_top = 288.0
margin_bottom = 320.0
type = 1
effect = 1
[node name="ManaCounter11" parent="Counters" instance=ExtResource( 2 )]
margin_top = 320.0
margin_bottom = 352.0
type = 2
effect = 1
[node name="ManaCounter12" parent="Counters" instance=ExtResource( 2 )]
margin_top = 352.0
margin_bottom = 384.0
type = 3
effect = 1
[node name="ManaCounter13" parent="Counters" instance=ExtResource( 2 )]
margin_top = 384.0
margin_bottom = 416.0
type = 4
effect = 1
[node name="ManaCounter14" parent="Counters" instance=ExtResource( 2 )]
margin_top = 416.0
margin_bottom = 448.0
type = 5
effect = 1
[node name="ManaCounter15" parent="Counters" instance=ExtResource( 2 )]
margin_top = 448.0
margin_bottom = 480.0
type = 6
effect = 1
[node name="ManaCounter16" parent="Counters" instance=ExtResource( 2 )]
margin_top = 480.0
margin_bottom = 512.0
type = 7
effect = 1
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter2" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter3" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter4" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter5" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter6" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter7" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter8" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter9" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter10" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter11" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter12" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter13" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter14" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter15" method="update_count"]
[connection signal="gem_matched" from="GemGrid" to="Counters/ManaCounter16" method="update_count"]

7
default_env.tres Normal file
View File

@ -0,0 +1,7 @@
[gd_resource type="Environment" load_steps=2 format=2]
[sub_resource type="ProceduralSky" id=1]
[resource]
background_mode = 2
background_sky = SubResource( 1 )

110
entities/gem.gd Normal file
View File

@ -0,0 +1,110 @@
extends Control
class_name Gem
signal position_reached()
# Gem type enum
enum Type { Thermic, Optic, Kinetic, Electromagnetic, Acoustic, Biologic, Chemic } #, Skull }
# Gem effect enum
enum Effect { ManaNegative, ManaPositive, EffectNegative, EffectPositive }
const effect_distribution = {
Effect.ManaNegative: 45,
Effect.ManaPositive: 45,
Effect.EffectNegative: 5,
Effect.EffectPositive: 5
}
# Variables
const unscaled_size = 128
export(Type) var type = Type.Thermic
export(Effect) var effect = Effect.ManaNegative
# Indicate if animation are played
var animate = false
# For removing
var matched: bool = false
var bonus: int = 0
# For moving
var next_i: int = 0
var next_j: int = 0
var current_i: int = 0
var current_j: int = 0
var move_dist: float = 0.0
var move_inc: float = 0.0
var moving: bool = false
# Called when the node enters the scene tree for the first time.
func _ready():
update_sprite()
# Called periodically
func _process(delta):
if moving:
move_inc += 4*delta
if (move_inc >= move_dist) or ((current_i == next_i) and (current_j == next_j)):
reach_position()
else:
var pos = size() * lerp(Vector2(current_i, current_j), Vector2(next_i, next_j), move_inc / move_dist)
set_position(pos)
# Randomize a gem
func randomize(rnd: Random):
type = rnd.pick_from_array(Type.values())
effect = rnd.pick_from_distribution(effect_distribution)
update_sprite()
# Update the sprite from its type and effect
func update_sprite():
$Sprite.region_rect.position.x = unscaled_size * (type % Type.size())
$Sprite.region_rect.position.y = unscaled_size * (effect % Effect.size())
# Used for movement and position
func size():
return $Sprite.transform.get_scale().x * unscaled_size
# Match a gem
func set_matched():
if !matched:
matched = true
if animate:
$Sprite.hide()
$ParticlesMatch.emitting = true
$ParticlesTimer.start()
else:
queue_free()
# Bonus value
func set_bonus():
bonus += 1
# Enable animations and particles
func enable_animations():
animate = true
# Set the position on the grid
func set_grid_position(i, j):
next_i = i
next_j = j
current_i = i
current_j = j
set_position(size() * Vector2(i,j))
# Move to grid position
func move_to_position(i, j):
next_i = i
next_j = j
move_dist = Vector2(current_i, current_j).distance_to(Vector2(next_i, next_j))
func update_position():
if animate:
moving = true
else:
reach_position()
func reach_position():
move_inc = 0.0
moving = false
current_i = next_i
current_j = next_j
set_position(size() * Vector2(next_i, next_j))
if animate:
emit_signal("position_reached")

44
entities/gem.tscn Normal file
View File

@ -0,0 +1,44 @@
[gd_scene load_steps=5 format=2]
[ext_resource path="res://entities/gem.gd" type="Script" id=1]
[ext_resource path="res://entities/gems.svg" type="Texture" id=2]
[ext_resource path="res://entities/matched-particle.svg" type="Texture" id=3]
[sub_resource type="AtlasTexture" id=1]
flags = 4
atlas = ExtResource( 2 )
region = Rect2( 0, 0, 1024, 512 )
[node name="Gem" type="Control"]
margin_right = 64.0
margin_bottom = 64.0
rect_min_size = Vector2( 64, 64 )
script = ExtResource( 1 )
[node name="Sprite" type="Sprite" parent="."]
scale = Vector2( 0.5, 0.5 )
texture = SubResource( 1 )
centered = false
region_enabled = true
region_rect = Rect2( 0, 0, 128, 128 )
[node name="ParticlesMatch" type="CPUParticles2D" parent="."]
position = Vector2( 32, 32 )
z_index = 1
emitting = false
amount = 25
one_shot = true
explosiveness = 0.5
texture = ExtResource( 3 )
emission_shape = 1
emission_sphere_radius = 32.0
angular_velocity = 90.0
angular_velocity_random = 0.5
scale_amount = 0.5
scale_amount_random = 0.5
[node name="ParticlesTimer" type="Timer" parent="."]
wait_time = 5.0
one_shot = true
[connection signal="timeout" from="ParticlesTimer" to="." method="queue_free"]

328
entities/gem_grid.gd Normal file
View File

@ -0,0 +1,328 @@
extends Control
signal is_ready()
signal gem_matched(matched_results)
signal gems_swapped()
const GemScene = preload("res://entities/gem.tscn")
# Random generator
var rnd = Random.new()
# Grid dimensions
const grid_width = 8
const grid_height = 8
# The grid
var grid = LogicalGrid.new(grid_width, grid_height)
#var grid: Array = [Array(), Array(), Array(), Array(), Array(), Array(), Array(), Array()]
# Cursor position
var cursor_i: int = 0
var cursor_j: int = 0
# Status of the cursor
var gem_selected: bool = false
# Counter for events sent by gems
var event_count: int = 0
# State machine for animations and event handling
# NONE : Do not change state
# INIT : Initial state, only here before the end of the _ready function
# READY : Accept input
# SWAPPING : Reject input, change the position of gems
# MATCHING : Reject input, match gems
#
# --------- ------------ ------------
# | READY |--(gem swapped)-->| SWAPPING |--(swap ended, gems are matching)-->| MATCHING |
# --------- ------------ ------------
# ^ ^ | |
# | ------------(match occured)----------------- |
# | |
# -------------------------------(no match)-----------------------------------
#
enum State { NONE, INIT, READY, SWAPPING, MATCHING }
var current_state = State.INIT
var next_state = State.NONE
#
# Called when the node enters the scene tree for the first time.
func _ready():
# Init the grid
for i in range(grid_width):
for j in range(grid_height):
add_gem(i, j, i, j)
# match gems (without emitting events)
match_gems()
# change the direction
grid.set_flow_direction(LogicalGrid.FlowDirection.Down)
# State is ready
current_state = State.READY
# Tell all gems the grid is ready
emit_signal("is_ready")
# State machine
func _process(delta):
if (next_state != State.NONE) and (current_state != next_state):
match next_state:
State.READY:
show_cursor()
current_state = next_state
next_state = State.NONE
State.SWAPPING:
hide_cursor()
event_count = 0
emit_signal("gems_swapped")
current_state = next_state
next_state = State.NONE
State.MATCHING:
current_state = next_state
match_gems() # Computes next state
else:
next_state = State.NONE
# Add a new gem on the board to replace a gem at i,j
# On screen initialy displayed at position screen_i,screen_j
func add_gem(i, j, screen_i, screen_j):
var g = GemScene.instance()
g.randomize(rnd)
g.set_grid_position(screen_i, screen_j)
if current_state != State.INIT:
g.enable_animations()
else:
connect("is_ready", g, "enable_animations")
connect("gems_swapped", g, "update_position")
g.connect("position_reached", self, "on_gem_moved")
$Gems.add_child(g)
grid.push_value(i, j, g)
# Swap gems between cursor position and given position
func gem_swap(i_to, j_to):
if (i_to >= 0) and (j_to >= 0) and (i_to < grid_width) and (j_to < grid_width):
# Swap gem
grid.swap_values(cursor_i, cursor_j, i_to, j_to)
grid.at(cursor_i, cursor_j).move_to_position(cursor_i, cursor_j)
grid.at(i_to, j_to).move_to_position(i_to, j_to)
# Move cursor
gem_selected = false
cursor_i = i_to
cursor_j = j_to
update_cursor()
# Change state
next_state = State.SWAPPING
# Called each time a gems have finished moving
func on_gem_moved():
event_count += 1
if event_count >= (grid_width * grid_height):
# All gems have responded : matching
next_state = State.MATCHING
func match_gems():
# Init signaled result
var results = Dictionary()
for t in Gem.Type.values():
for e in Gem.Effect.values():
results[[t, e]] = 0
# Check loop
var check_loop: bool = true
var no_match = true
while check_loop:
no_match = true
check_loop = false
# Mark the gems to match.
check_matched()
# Collect the gems to match in results
var to_remove = []
for i in range(grid_width):
for j in range(grid_height):
var g = grid.at(i, j)
if g.matched:
# Loop if we are in the init state
check_loop = (current_state == State.INIT)
no_match = false
results[[g.type, g.effect]] = results[[g.type, g.effect]] + 1 + g.bonus
# The indexes to remove are placed by pushing front,
# so that the last indexes are removed first
to_remove.push_front([i, j])
# Remove the gems
grid.remove_values(to_remove)
# Add new gems. The initial positions depends on the flow direction.
var counts = [0]
var dir = grid.get_flow_direction()
match dir:
LogicalGrid.FlowDirection.Up:
counts.resize(grid_width)
LogicalGrid.FlowDirection.Down:
counts.resize(grid_width)
LogicalGrid.FlowDirection.Left:
counts.resize(grid_height)
LogicalGrid.FlowDirection.Right:
counts.resize(grid_height)
for i in range(counts.size()):
counts[i] = 0
for idx in to_remove:
var screen_idx = []
var i = 0
match dir:
LogicalGrid.FlowDirection.Up:
i = idx[0]
screen_idx = [i, grid_height + counts[i]]
LogicalGrid.FlowDirection.Down:
i = idx[0]
screen_idx = [i, -1 - counts[i]]
LogicalGrid.FlowDirection.Left:
i = idx[1]
screen_idx = [grid_width + counts[i], i]
LogicalGrid.FlowDirection.Right:
i = idx[1]
screen_idx = [-1 - counts[i], i]
counts[i] = counts[i] + 1
add_gem(idx[0], idx[1], screen_idx[0], screen_idx[1])
## Add new gems to fill the empty spaces
#for i in range(grid_width):
# var count = 0
# for _j in range(grid[i].size(), grid_height):
# # gems are not added on the position they are on the grid,
# # but outside so that animation plays correctly
# add_gem(i, count + grid_height)
# count += 1
# Update gems next position
for i in range(grid_width):
for j in range(grid_height):
grid.at(i, j).move_to_position(i, j)
if current_state == State.INIT:
grid.at(i, j).update_position()
# Emit events and compute next state
if current_state != State.INIT:
emit_signal("gem_matched", results)
if no_match:
next_state = State.READY
else:
next_state = State.SWAPPING
# If a gem is surrounded by two gem of the same type vertically or horizontally,
# mark the gem and its surronding gems as matched
func check_matched():
for i in range(grid_width):
for j in range(grid_height):
check_matched_at(i, j)
func check_matched_at(i, j):
# check vertically
var up_i = i
var up_j = j - 1
var down_i = i
var down_j = j + 1
if (up_j >= 0) and (down_j < grid_height):
if same_type(i, j, up_i, up_j) and same_type(i, j, down_i, down_j):
# match
mark_matched(i, j)
mark_matched(up_i, up_j)
mark_matched(down_i, down_j)
# Add a bonus if the effect is the same
if same_effect(i, j, up_i, up_j) and same_effect(i, j, down_i, down_j):
mark_bonus(i, j)
# Check horizontally
var left_i = i - 1
var left_j = j
var right_i = i + 1
var right_j = j
if (left_i >= 0) and (right_i < grid_width):
if same_type(i, j, left_i, left_j) and same_type(i, j, right_i, right_j):
# match
mark_matched(i, j)
mark_matched(left_i, left_j)
mark_matched(right_i, right_j)
# Add a bonus if the effect is the same
if same_effect(i, j, left_i, left_j) and same_effect(i, j, right_i, right_j):
mark_bonus(i, j)
func mark_matched(i, j):
var g = grid.at(i, j)
g.set_matched()
# disconnect signals related to gem displacement
if is_connected("gems_swapped", g, "update_position"):
disconnect("gems_swapped", g, "update_position")
if g.is_connected("position_reached", self, "on_gem_moved"):
g.disconnect("position_reached", self, "on_gem_moved")
func mark_bonus(i, j):
var g = grid.at(i, j)
g.set_bonus()
# True if gems at (i,j) and (x,y) have the same type
func same_type(i, j, x, y):
return grid.at(i, j).type == grid.at(x, y).type
# True is gems at (i,j) and (x,y) have the same effect
func same_effect(i, j, x, y):
return grid.at(i, j).effect == grid.at(x, y).effect
# Cursor management
func update_cursor():
# Position
$Cursor.set_position(64 * Vector2(cursor_i,cursor_j))
# Selected ?
var offset = 0
if gem_selected:
offset = 128
$Cursor.texture.region.position.x = offset
func show_cursor():
$Cursor.show()
func hide_cursor():
$Cursor.hide()
# Input handling
# Only handle events if state is READY and not in transition
func accept_input():
return (current_state == State.READY) and (next_state == State.NONE)
func up():
if accept_input():
if gem_selected:
gem_swap(cursor_i, cursor_j - 1)
else:
cursor_j = cursor_j - 1
if cursor_j < 0:
cursor_j = grid_height - 1
update_cursor()
func down():
if accept_input():
if gem_selected:
gem_swap(cursor_i, cursor_j + 1)
else:
cursor_j = (cursor_j + 1) % grid_height
update_cursor()
func left():
if accept_input():
if gem_selected:
gem_swap(cursor_i - 1, cursor_j)
else:
cursor_i = cursor_i - 1
if cursor_i < 0:
cursor_i = grid_width - 1
update_cursor()
func right():
if accept_input():
if gem_selected:
gem_swap(cursor_i + 1, cursor_j)
else:
cursor_i = (cursor_i + 1) % grid_width
update_cursor()
func select():
if accept_input():
gem_selected = !gem_selected
update_cursor()
func cancel():
if accept_input():
gem_selected = false
update_cursor()

44
entities/gem_grid.tscn Normal file
View File

@ -0,0 +1,44 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://entities/gem_grid.gd" type="Script" id=1]
[ext_resource path="res://entities/gem_selector.svg" type="Texture" id=2]
[sub_resource type="AtlasTexture" id=1]
flags = 4
atlas = ExtResource( 2 )
region = Rect2( 0, 0, 128, 128 )
[node name="GemGrid" type="Control"]
margin_right = 512.0
margin_bottom = 512.0
rect_clip_content = true
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ColorRect" type="ColorRect" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
color = Color( 0.603922, 0.647059, 0.65098, 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Gems" type="Control" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Cursor" type="TextureRect" parent="."]
margin_right = 64.0
margin_bottom = 64.0
rect_min_size = Vector2( 64, 64 )
texture = SubResource( 1 )
expand = true
stretch_mode = 1
__meta__ = {
"_edit_use_anchors_": false
}

52
entities/gem_selector.svg Normal file
View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="256"
height="128"
viewBox="0 0 67.733332 33.866668"
version="1.1"
id="svg41030"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)"
sodipodi:docname="gem_selector.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview41032"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="px"
showgrid="false"
units="px"
inkscape:zoom="4.4328844"
inkscape:cx="93.392916"
inkscape:cy="63.502671"
inkscape:window-width="1920"
inkscape:window-height="1049"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs41027" />
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="rect41113"
style="fill:#e5bf00;fill-opacity:1;stroke:none;stroke-width:5.00031;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers fill stroke"
d="M 0,0 V 54 H 10 V 10 H 54 V 0 Z m 74,0 v 10 h 44 v 44 h 10 V 0 Z M 0,74 v 54 H 54 V 118 H 10 V 74 Z m 118,0 v 44 H 74 v 10 h 54 V 74 Z"
transform="scale(0.26458334)" />
<path
id="path41877"
style="fill:#00e5e5;fill-opacity:1;stroke:none;stroke-width:1.323;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers fill stroke"
d="m 33.866667,0 v 14.2875 h 2.645834 V 2.6458334 H 48.154168 V 0 Z M 53.445835,0 V 2.6458334 H 65.087502 V 14.2875 h 2.645833 V 0 Z M 33.866667,19.579167 V 33.866668 H 48.154168 V 31.220834 H 36.512501 V 19.579167 Z m 31.220835,0 V 31.220834 H 53.445835 v 2.645834 h 14.2875 V 19.579167 Z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/gem_selector.svg-7ce7dce652630ac09e42a628d1b9123f.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://entities/gem_selector.svg"
dest_files=[ "res://.import/gem_selector.svg-7ce7dce652630ac09e42a628d1b9123f.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

1399
entities/gems.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 99 KiB

35
entities/gems.svg.import Normal file
View File

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/gems.svg-4ba00769f724f70394c92013b0796a53.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://entities/gems.svg"
dest_files=[ "res://.import/gems.svg-4ba00769f724f70394c92013b0796a53.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

20
entities/mana_counter.gd Normal file
View File

@ -0,0 +1,20 @@
extends Control
export(Gem.Type) var type = Gem.Type.Thermic
export(Gem.Effect) var effect = Gem.Effect.ManaNegative
var count: int = 0
# Called when the node enters the scene tree for the first time.
func _ready():
$Gem.type = type
$Gem.effect = effect
$Gem.update_sprite()
$Label.text = str(count)
func update_count(matched: Dictionary):
for k in matched.keys():
if (k[0] == type) and (k[1] == effect):
count += matched[k]
break
$Label.text = str(count)

View File

@ -0,0 +1,23 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://entities/gem.tscn" type="PackedScene" id=1]
[ext_resource path="res://entities/mana_counter.gd" type="Script" id=2]
[node name="ManaCounter" type="Control"]
margin_right = 192.0
margin_bottom = 32.0
script = ExtResource( 2 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Gem" parent="." instance=ExtResource( 1 )]
rect_scale = Vector2( 0.5, 0.5 )
[node name="Label" type="Label" parent="."]
margin_left = 40.0
margin_top = 8.0
margin_right = 192.0
margin_bottom = 22.0
text = "0000"
clip_text = true

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
version="1.1"
id="svg5"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)"
sodipodi:docname="matched-particle.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#cccccc"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:document-units="px"
showgrid="false"
units="px"
inkscape:zoom="35.463075"
inkscape:cx="5.3858838"
inkscape:cy="8.5299991"
inkscape:window-width="1920"
inkscape:window-height="1049"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="path1233"
style="fill:#ffffff;stroke-width:5.00032;stroke-linecap:round;stroke-linejoin:round;paint-order:markers fill stroke;fill-opacity:1"
d="M 15.640625 0.052734375 C 14.307469 0.11206338 8.7924422 3.5389988 8 3.5390625 C 7.1404017 3.5391316 0.72301334 -0.49264224 0.11523438 0.11523438 C -0.49254457 0.72311097 3.5389934 7.1404017 3.5390625 8 C 3.5391316 8.8595983 -0.49264224 15.276987 0.11523438 15.884766 C 0.72311097 16.492545 7.1404017 12.461007 8 12.460938 C 8.8595983 12.460868 15.276987 16.492642 15.884766 15.884766 C 16.492545 15.276889 12.461007 8.8595983 12.460938 8 C 12.460868 7.1404017 16.492642 0.72301334 15.884766 0.11523438 C 15.837275 0.067751645 15.753604 0.047706493 15.640625 0.052734375 z M 8 5 A 2.9999999 2.9999999 0 0 1 11 8 A 2.9999999 2.9999999 0 0 1 8 11 A 2.9999999 2.9999999 0 0 1 5 8 A 2.9999999 2.9999999 0 0 1 8 5 z "
transform="scale(0.26458333)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/matched-particle.svg-416c5e1325ce98f7ffcae31d9908d4ad.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://entities/matched-particle.svg"
dest_files=[ "res://.import/matched-particle.svg-416c5e1325ce98f7ffcae31d9908d4ad.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

35
icon.png.import Normal file
View File

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.png"
dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

53
project.godot Normal file
View File

@ -0,0 +1,53 @@
; 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=4
_global_script_classes=[ {
"base": "Control",
"class": "Gem",
"language": "GDScript",
"path": "res://entities/gem.gd"
}, {
"base": "Reference",
"class": "LogicalGrid",
"language": "GDScript",
"path": "res://tools/logical_grid.gd"
}, {
"base": "Reference",
"class": "Random",
"language": "GDScript",
"path": "res://tools/random.gd"
} ]
_global_script_class_icons={
"Gem": "",
"LogicalGrid": "",
"Random": ""
}
[application]
config/name="Match Battle"
run/main_scene="res://Main.tscn"
config/icon="res://icon.png"
[display]
window/stretch/mode="2d"
window/stretch/aspect="keep"
[physics]
common/enable_pause_aware_picking=true
[rendering]
quality/driver/driver_name="GLES2"
vram_compression/import_etc=true
vram_compression/import_etc2=false
environment/default_environment="res://default_env.tres"

102
tools/logical_grid.gd Normal file
View File

@ -0,0 +1,102 @@
extends Reference
class_name LogicalGrid
# Grid with a flow direction for the item added / removed
# The position on grid must be translated as the position in the array is different
enum FlowDirection { Up, Down, Left, Right }
# Fields
var width: int # width
var height: int # height
var table: Array = [] # table = array of columns or rows depending on the direction
var flow_direction = FlowDirection.Up
# Constructor
func _init(width: int, height: int):
self.width = width
self.height = height
# By default, array is initialized with empty arrays
for x in range(0, width):
table.push_back(Array())
# Get flow direction
func get_flow_direction():
return self.flow_direction
# Set flow direction
func set_flow_direction(dir):
# Keep old values
var old_table = self.table
var old_dir = self.flow_direction
# Update the flow direction
self.flow_direction = dir
# Update the array to reflect the change in flow direction
self.table = []
if (dir == FlowDirection.Up) or (dir == FlowDirection.Down):
for nx in range(width):
self.table.push_back(Array())
for ny in range(height):
self.table[nx].push_back(false)
else:
for ny in range(height):
self.table.push_back(Array())
for nx in range(width):
self.table[ny].push_back(false)
# Set the real values
for i in range(width):
for j in range(height):
var old_idx = get_idx(i, j, old_dir)
var new_idx = get_idx(i, j, dir)
self.table[new_idx[0]][new_idx[1]] = old_table[old_idx[0]][old_idx[1]]
# Get the index in the table depending on the flow direction
func get_idx(x, y, dir = self.flow_direction):
match dir:
FlowDirection.Up:
return [x, y]
FlowDirection.Down:
return [x, height - 1 - y]
FlowDirection.Left:
return [y, x]
FlowDirection.Right:
return [y, width - 1 - x]
# Get value at position
func at(x: int, y: int):
var idx = get_idx(x, y)
return self.table[idx[0]][idx[1]]
# Set value at position
func set_at(x: int, y: int, val):
var idx = get_idx(x, y)
self.table[idx[0]][idx[1]] = val
# Remove a value from the table
#func remove_at(x: int, y:int):
# var idx = get_idx(x, y)
# self.table[idx[0]].remove(idx[1])
# Remove the values from a list of indexes
class IdxSorter:
static func higher_idx_first(a, b):
return (a[0] > b[0]) or ((a[0] == b[0]) and (a[1] > b[1]))
func remove_values(lst_idx: Array):
# Tranform the values
var new_idx = []
for idx in lst_idx:
new_idx.push_back(get_idx(idx[0], idx[1]))
new_idx.sort_custom(IdxSorter, "higher_idx_first")
for idx in new_idx:
self.table[idx[0]].remove(idx[1])
# Push a value to the grid, the position indicate the row or column to push to
func push_value(x: int, y: int, val):
var idx = get_idx(x, y)
self.table[idx[0]].push_back(val)
# Swap two values
func swap_values(x1: int, y1: int, x2: int, y2: int):
var v = self.at(x1, y1)
self.set_at(x1, y1, self.at(x2, y2))
self.set_at(x2, y2, v)

36
tools/random.gd Normal file
View File

@ -0,0 +1,36 @@
extends Reference
class_name Random
# Generator
var generator: RandomNumberGenerator = RandomNumberGenerator.new()
# Init
func _init():
generator.randomize()
# Generate from a range (inclusive)
func pick_from_range(v1: int, v2: int):
return generator.randi_range(v1, v2)
# Generate from an Array
func pick_from_array(a: Array):
var idx = generator.randi_range(0, a.size() - 1)
return a[idx]
# Generate from a dictionary of (value, weight)
func pick_from_distribution(d: Dictionary):
var keys = d.keys()
var weight_sum = 0
for k in keys:
weight_sum += d[k]
# pick a number between 0 and the total minus 1
var val = generator.randi_range(0, weight_sum - 1)
# Compute the sums until val < current_sum
var current_sum = 0
var ret = keys.front()
for k in keys:
current_sum += d[k]
ret = k
if val < current_sum:
break
return ret