mantain mask, purple attack, star enemy
This commit is contained in:
parent
66ecb04bd1
commit
e32ed2f53a
10 changed files with 213 additions and 18 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
[gd_scene format=3 uid="uid://ya6jfltqnl1b"]
|
[gd_scene format=3 uid="uid://ya6jfltqnl1b"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://buamdls133c2e" path="res://scripts/star_enemy.gd" id="1_o614x"]
|
[ext_resource type="Script" uid="uid://bprbgsk73wmb8" path="res://scripts/heart_enemy.gd" id="1_o614x"]
|
||||||
[ext_resource type="Resource" uid="uid://jar2mye8do0c" path="res://resources/hearts.tres" id="2_2tr1s"]
|
[ext_resource type="Resource" uid="uid://jar2mye8do0c" path="res://resources/hearts.tres" id="2_2tr1s"]
|
||||||
[ext_resource type="PackedScene" uid="uid://c8l24d01bm2sg" path="res://scenes/enemy_explosion.tscn" id="3_n20dk"]
|
[ext_resource type="PackedScene" uid="uid://c8l24d01bm2sg" path="res://scenes/enemy_explosion.tscn" id="3_n20dk"]
|
||||||
[ext_resource type="Script" uid="uid://synocbtvgrf4" path="res://scripts/shadow.gd" id="4_ul27a"]
|
[ext_resource type="Script" uid="uid://synocbtvgrf4" path="res://scripts/shadow.gd" id="4_ul27a"]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[gd_scene format=3 uid="uid://ncgwx0yjn2gt"]
|
[gd_scene format=3 uid="uid://ncgwx0yjn2gt"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://sbseykg05177" path="res://scripts/player.gd" id="1_ur7pv"]
|
[ext_resource type="Script" uid="uid://sbseykg05177" path="res://scripts/player.gd" id="1_ur7pv"]
|
||||||
[ext_resource type="Resource" uid="uid://dquluu05ho3f4" path="res://resources/empty_mask.tres" id="2_3v2ag"]
|
[ext_resource type="Resource" uid="uid://butr81mog5qjy" path="res://resources/purple_attack.tres" id="2_3v2ag"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bp45yth1y3ia5" path="res://scenes/player_explosion.tscn" id="3_3v2ag"]
|
[ext_resource type="PackedScene" uid="uid://bp45yth1y3ia5" path="res://scenes/player_explosion.tscn" id="3_3v2ag"]
|
||||||
[ext_resource type="Script" uid="uid://synocbtvgrf4" path="res://scripts/shadow.gd" id="4_3v2ag"]
|
[ext_resource type="Script" uid="uid://synocbtvgrf4" path="res://scripts/shadow.gd" id="4_3v2ag"]
|
||||||
[ext_resource type="Texture2D" uid="uid://d3abeekjumqyb" path="res://assets/player/maske1.png" id="5_jej6c"]
|
[ext_resource type="Texture2D" uid="uid://d3abeekjumqyb" path="res://assets/player/maske1.png" id="5_jej6c"]
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
[ext_resource type="Texture2D" uid="uid://cabc77u0tblxv" path="res://assets/masks/purple_attack-2.png" id="2_yhwo8"]
|
[ext_resource type="Texture2D" uid="uid://cabc77u0tblxv" path="res://assets/masks/purple_attack-2.png" id="2_yhwo8"]
|
||||||
[ext_resource type="Texture2D" uid="uid://c4osojc1tqf77" path="res://assets/masks/purple_attack-1.png" id="3_7i1k2"]
|
[ext_resource type="Texture2D" uid="uid://c4osojc1tqf77" path="res://assets/masks/purple_attack-1.png" id="3_7i1k2"]
|
||||||
[ext_resource type="Script" uid="uid://ejd25ul4j5pp" path="res://scripts/hitbox.gd" id="4_7i1k2"]
|
[ext_resource type="Script" uid="uid://ejd25ul4j5pp" path="res://scripts/hitbox.gd" id="4_7i1k2"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://d1ybbm5tk1tbd" path="res://assets/vfx/particle_star.png" id="5_vnsf0"]
|
||||||
|
|
||||||
[sub_resource type="Animation" id="Animation_vnsf0"]
|
[sub_resource type="Animation" id="Animation_vnsf0"]
|
||||||
length = 0.001
|
length = 0.001
|
||||||
|
|
@ -55,6 +56,30 @@ tracks/3/keys = {
|
||||||
"update": 1,
|
"update": 1,
|
||||||
"values": [false]
|
"values": [false]
|
||||||
}
|
}
|
||||||
|
tracks/4/type = "value"
|
||||||
|
tracks/4/imported = false
|
||||||
|
tracks/4/enabled = true
|
||||||
|
tracks/4/path = NodePath("../GPUParticles2D:emitting")
|
||||||
|
tracks/4/interp = 1
|
||||||
|
tracks/4/loop_wrap = true
|
||||||
|
tracks/4/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [false]
|
||||||
|
}
|
||||||
|
tracks/5/type = "value"
|
||||||
|
tracks/5/imported = false
|
||||||
|
tracks/5/enabled = true
|
||||||
|
tracks/5/path = NodePath(".:modulate")
|
||||||
|
tracks/5/interp = 1
|
||||||
|
tracks/5/loop_wrap = true
|
||||||
|
tracks/5/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 0,
|
||||||
|
"values": [Color(1, 1, 1, 1)]
|
||||||
|
}
|
||||||
|
|
||||||
[sub_resource type="Animation" id="Animation_ev5qi"]
|
[sub_resource type="Animation" id="Animation_ev5qi"]
|
||||||
resource_name = "attack"
|
resource_name = "attack"
|
||||||
|
|
@ -101,7 +126,7 @@ tracks/3/path = NodePath("../..")
|
||||||
tracks/3/interp = 1
|
tracks/3/interp = 1
|
||||||
tracks/3/loop_wrap = true
|
tracks/3/loop_wrap = true
|
||||||
tracks/3/keys = {
|
tracks/3/keys = {
|
||||||
"times": PackedFloat32Array(0.53333336),
|
"times": PackedFloat32Array(1.0333333),
|
||||||
"transitions": PackedFloat32Array(1),
|
"transitions": PackedFloat32Array(1),
|
||||||
"values": [{
|
"values": [{
|
||||||
"args": [],
|
"args": [],
|
||||||
|
|
@ -115,10 +140,34 @@ tracks/4/path = NodePath("Hitbox:monitoring")
|
||||||
tracks/4/interp = 1
|
tracks/4/interp = 1
|
||||||
tracks/4/loop_wrap = true
|
tracks/4/loop_wrap = true
|
||||||
tracks/4/keys = {
|
tracks/4/keys = {
|
||||||
"times": PackedFloat32Array(0, 0.36666667),
|
"times": PackedFloat32Array(0, 0.36666667, 0.6333333),
|
||||||
"transitions": PackedFloat32Array(1, 1),
|
"transitions": PackedFloat32Array(1, 1, 1),
|
||||||
"update": 1,
|
"update": 1,
|
||||||
"values": [false, true]
|
"values": [false, true, false]
|
||||||
|
}
|
||||||
|
tracks/5/type = "value"
|
||||||
|
tracks/5/imported = false
|
||||||
|
tracks/5/enabled = true
|
||||||
|
tracks/5/path = NodePath("../GPUParticles2D:emitting")
|
||||||
|
tracks/5/interp = 1
|
||||||
|
tracks/5/loop_wrap = true
|
||||||
|
tracks/5/keys = {
|
||||||
|
"times": PackedFloat32Array(0.3),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [true]
|
||||||
|
}
|
||||||
|
tracks/6/type = "value"
|
||||||
|
tracks/6/imported = false
|
||||||
|
tracks/6/enabled = true
|
||||||
|
tracks/6/path = NodePath(".:modulate")
|
||||||
|
tracks/6/interp = 1
|
||||||
|
tracks/6/loop_wrap = true
|
||||||
|
tracks/6/keys = {
|
||||||
|
"times": PackedFloat32Array(0.6, 0.82),
|
||||||
|
"transitions": PackedFloat32Array(1, 1),
|
||||||
|
"update": 0,
|
||||||
|
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
|
||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_0qdgb"]
|
[sub_resource type="AnimationLibrary" id="AnimationLibrary_0qdgb"]
|
||||||
|
|
@ -127,6 +176,25 @@ _data = {
|
||||||
&"attack": SubResource("Animation_ev5qi")
|
&"attack": SubResource("Animation_ev5qi")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[sub_resource type="Curve" id="Curve_vnsf0"]
|
||||||
|
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.6351352, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
||||||
|
point_count = 3
|
||||||
|
|
||||||
|
[sub_resource type="CurveTexture" id="CurveTexture_ev5qi"]
|
||||||
|
curve = SubResource("Curve_vnsf0")
|
||||||
|
|
||||||
|
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_ev5qi"]
|
||||||
|
particle_flag_disable_z = true
|
||||||
|
angle_min = -720.0
|
||||||
|
angle_max = 720.0
|
||||||
|
spread = 180.0
|
||||||
|
initial_velocity_max = 186.28
|
||||||
|
gravity = Vector3(0, -98, 0)
|
||||||
|
scale_min = 2.0
|
||||||
|
scale_max = 10.0
|
||||||
|
color = Color(0.2231029, 0.2231029, 0.22310287, 1)
|
||||||
|
alpha_curve = SubResource("CurveTexture_ev5qi")
|
||||||
|
|
||||||
[sub_resource type="SpriteFrames" id="SpriteFrames_2rf1b"]
|
[sub_resource type="SpriteFrames" id="SpriteFrames_2rf1b"]
|
||||||
animations = [{
|
animations = [{
|
||||||
"frames": [{
|
"frames": [{
|
||||||
|
|
@ -156,6 +224,17 @@ root_node = NodePath("../AnimatedSprite2D")
|
||||||
libraries/ = SubResource("AnimationLibrary_0qdgb")
|
libraries/ = SubResource("AnimationLibrary_0qdgb")
|
||||||
autoplay = &"attack"
|
autoplay = &"attack"
|
||||||
|
|
||||||
|
[node name="GPUParticles2D" type="GPUParticles2D" parent="Node2D" unique_id=1940209552]
|
||||||
|
position = Vector2(3, 29.5)
|
||||||
|
scale = Vector2(0.5, 0.5)
|
||||||
|
emitting = false
|
||||||
|
amount = 64
|
||||||
|
texture = ExtResource("5_vnsf0")
|
||||||
|
lifetime = 0.5
|
||||||
|
one_shot = true
|
||||||
|
explosiveness = 1.0
|
||||||
|
process_material = SubResource("ParticleProcessMaterial_ev5qi")
|
||||||
|
|
||||||
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="Node2D" unique_id=291763539]
|
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="Node2D" unique_id=291763539]
|
||||||
position = Vector2(-3, -3)
|
position = Vector2(-3, -3)
|
||||||
sprite_frames = SubResource("SpriteFrames_2rf1b")
|
sprite_frames = SubResource("SpriteFrames_2rf1b")
|
||||||
|
|
|
||||||
83
scenes/star_enemy.tscn
Normal file
83
scenes/star_enemy.tscn
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
[gd_scene format=3 uid="uid://1fqyupmquole"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://buamdls133c2e" path="res://scripts/star_enemy.gd" id="1_2mruc"]
|
||||||
|
[ext_resource type="Resource" uid="uid://0mtm64s4jthn" path="res://resources/stars.tres" id="2_7vuyl"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://c8l24d01bm2sg" path="res://scenes/enemy_explosion.tscn" id="3_f4jcd"]
|
||||||
|
[ext_resource type="Script" uid="uid://synocbtvgrf4" path="res://scripts/shadow.gd" id="4_dlviu"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://ddakqxdesvjk5" path="res://assets/enemies/orange-4.png" id="5_7vuyl"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://b50fgyydmulf0" path="res://assets/enemies/orange-5.png" id="6_cwyt1"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bfbfyfcwftsvp" path="res://assets/enemies/orange-6.png" id="7_h1v7d"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://dwc2hmfei47bv" path="res://assets/enemies/orange-7.png" id="8_ihd2g"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://trbpyx771jjf" path="res://assets/enemies/orange-8.png" id="9_b7rn8"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://cool8ppma50uo" path="res://assets/enemies/orange-1.png" id="10_6vpiu"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://2qt27ivjbkma" path="res://assets/enemies/orange-2.png" id="11_jel63"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://cns03w1c34gq5" path="res://assets/enemies/orange-3.png" id="12_sonmy"]
|
||||||
|
|
||||||
|
[sub_resource type="CircleShape2D" id="CircleShape2D_rs3ab"]
|
||||||
|
radius = 16.0
|
||||||
|
|
||||||
|
[sub_resource type="SpriteFrames" id="SpriteFrames_w150n"]
|
||||||
|
animations = [{
|
||||||
|
"frames": [{
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": ExtResource("5_7vuyl")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": ExtResource("6_cwyt1")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": ExtResource("7_h1v7d")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": ExtResource("8_ihd2g")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": ExtResource("9_b7rn8")
|
||||||
|
}],
|
||||||
|
"loop": true,
|
||||||
|
"name": &"attack",
|
||||||
|
"speed": 5.0
|
||||||
|
}, {
|
||||||
|
"frames": [{
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": ExtResource("10_6vpiu")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": ExtResource("11_jel63")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": ExtResource("12_sonmy")
|
||||||
|
}],
|
||||||
|
"loop": true,
|
||||||
|
"name": &"default",
|
||||||
|
"speed": 5.0
|
||||||
|
}]
|
||||||
|
|
||||||
|
[node name="RangedEnemy" type="CharacterBody2D" unique_id=93153910 node_paths=PackedStringArray("main_visual") groups=["enemy"]]
|
||||||
|
collision_layer = 2
|
||||||
|
collision_mask = 3
|
||||||
|
script = ExtResource("1_2mruc")
|
||||||
|
attack_charge_time = 0.5
|
||||||
|
flee_range = 130.0
|
||||||
|
approach_range = 220.0
|
||||||
|
current_mask_data = ExtResource("2_7vuyl")
|
||||||
|
move_speed = 40.0
|
||||||
|
main_visual = NodePath("Anim")
|
||||||
|
death_scene = ExtResource("3_f4jcd")
|
||||||
|
|
||||||
|
[node name="Shadow" type="Node2D" parent="." unique_id=1651361569]
|
||||||
|
position = Vector2(0, 16)
|
||||||
|
script = ExtResource("4_dlviu")
|
||||||
|
metadata/_custom_type_script = "uid://synocbtvgrf4"
|
||||||
|
|
||||||
|
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="." unique_id=1882108293]
|
||||||
|
radius = 40.0
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1216246343]
|
||||||
|
shape = SubResource("CircleShape2D_rs3ab")
|
||||||
|
|
||||||
|
[node name="Anim" type="AnimatedSprite2D" parent="." unique_id=1041580697]
|
||||||
|
modulate = Color(0.94509804, 0.94509804, 0.94509804, 1)
|
||||||
|
sprite_frames = SubResource("SpriteFrames_w150n")
|
||||||
|
autoplay = "default"
|
||||||
|
frame_progress = 0.20596069
|
||||||
File diff suppressed because one or more lines are too long
12
scripts/heart_enemy.gd
Normal file
12
scripts/heart_enemy.gd
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
extends Enemy
|
||||||
|
|
||||||
|
@onready var sprite : AnimatedSprite2D = $Anim
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
super._process(delta)
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
func _draw():
|
||||||
|
if current_charge > 0 and target_node:
|
||||||
|
var color = Color.RED.lerp(Color.WHITE, current_charge / attack_charge_time)
|
||||||
|
draw_dashed_line(Vector2.ZERO, to_local(target_node.global_position), color, 1, 5)
|
||||||
1
scripts/heart_enemy.gd.uid
Normal file
1
scripts/heart_enemy.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://bprbgsk73wmb8
|
||||||
|
|
@ -11,6 +11,11 @@ func _ready():
|
||||||
rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
|
||||||
func change_scene(target_path: PackedScene, is_error: bool = false):
|
func change_scene(target_path: PackedScene, is_error: bool = false):
|
||||||
|
var player : Player= get_tree().get_first_node_in_group("player")
|
||||||
|
var player_mask : MaskData = null
|
||||||
|
if player:
|
||||||
|
player_mask = player.current_mask_data
|
||||||
|
|
||||||
rect.mouse_filter = Control.MOUSE_FILTER_STOP
|
rect.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
|
|
||||||
if is_error:
|
if is_error:
|
||||||
|
|
@ -25,10 +30,11 @@ func change_scene(target_path: PackedScene, is_error: bool = false):
|
||||||
var log_file = "res://hex.txt" if is_error else "res://log.txt"
|
var log_file = "res://hex.txt" if is_error else "res://log.txt"
|
||||||
display_log_file(log_file)
|
display_log_file(log_file)
|
||||||
|
|
||||||
await get_tree().create_timer(0.5).timeout
|
await get_tree().create_timer(0.1).timeout
|
||||||
var error = get_tree().change_scene_to_packed(target_path)
|
var error = get_tree().change_scene_to_packed(target_path)
|
||||||
if error != OK:
|
if error != OK:
|
||||||
push_error("Failed to load scene")
|
push_error("Failed to load scene")
|
||||||
|
await get_tree().scene_changed
|
||||||
|
|
||||||
log_display.visible = false
|
log_display.visible = false
|
||||||
log_display.text = ""
|
log_display.text = ""
|
||||||
|
|
@ -37,17 +43,22 @@ func change_scene(target_path: PackedScene, is_error: bool = false):
|
||||||
tween_out.tween_property(rect, "modulate:a", 0.0, 0.5)
|
tween_out.tween_property(rect, "modulate:a", 0.0, 0.5)
|
||||||
rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
|
||||||
|
player = get_tree().get_first_node_in_group("player")
|
||||||
|
if player and player_mask:
|
||||||
|
player.equip_mask(player_mask)
|
||||||
|
|
||||||
func display_log_file(file_path: String):
|
func display_log_file(file_path: String):
|
||||||
if not FileAccess.file_exists(file_path):
|
if not FileAccess.file_exists(file_path):
|
||||||
log_display.add_text("ERROR: " + file_path + " not found in build.\n")
|
log_display.add_text(file_path + " not found\n")
|
||||||
return
|
return
|
||||||
|
|
||||||
var file = FileAccess.open(file_path, FileAccess.READ)
|
var file = FileAccess.open(file_path, FileAccess.READ)
|
||||||
if not file:
|
if not file:
|
||||||
log_display.add_text("ERROR: Could not open file.\n")
|
log_display.add_text("cant open file\n")
|
||||||
return
|
return
|
||||||
|
|
||||||
while not file.eof_reached():
|
while not file.eof_reached():
|
||||||
|
for i in range(randi() % 4):
|
||||||
var line = file.get_line()
|
var line = file.get_line()
|
||||||
if line.strip_edges() == "": continue
|
if line.strip_edges() == "": continue
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
extends Enemy
|
extends Enemy
|
||||||
|
|
||||||
|
@onready var sprite : AnimatedSprite2D = $Anim
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
super._process(delta)
|
super._process(delta)
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|
@ -8,3 +10,10 @@ func _draw():
|
||||||
if current_charge > 0 and target_node:
|
if current_charge > 0 and target_node:
|
||||||
var color = Color.RED.lerp(Color.WHITE, current_charge / attack_charge_time)
|
var color = Color.RED.lerp(Color.WHITE, current_charge / attack_charge_time)
|
||||||
draw_dashed_line(Vector2.ZERO, to_local(target_node.global_position), color, 1, 5)
|
draw_dashed_line(Vector2.ZERO, to_local(target_node.global_position), color, 1, 5)
|
||||||
|
|
||||||
|
|
||||||
|
func _sprite_anim():
|
||||||
|
if current_charge > 0.1:
|
||||||
|
sprite.play("attack")
|
||||||
|
else:
|
||||||
|
sprite.play("default")
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ func mask_ready():
|
||||||
star.from_player = from_player
|
star.from_player = from_player
|
||||||
star.target_position = target_position
|
star.target_position = target_position
|
||||||
if not from_player:
|
if not from_player:
|
||||||
star.modulate = Color.BLACK
|
star.modulate = Color("#8888ff")
|
||||||
get_tree().current_scene.add_child(star)
|
get_tree().current_scene.add_child(star)
|
||||||
star.mask_ready()
|
star.mask_ready()
|
||||||
await get_tree().create_timer(0.05).timeout
|
await get_tree().create_timer(0.05).timeout
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue