player character has move and dodge
This commit is contained in:
@@ -2,7 +2,9 @@
|
||||
|
||||
[ext_resource type="Script" uid="uid://bici4k5vkuoos" path="res://Scripts/player_camera.gd" id="1_ocbkm"]
|
||||
|
||||
[node name="Camera3D" type="Camera3D"]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.25038, 0.968148, 0, -0.968148, 0.25038, 0, 0, 0)
|
||||
fov = 80.0
|
||||
[node name="CameraFollow" type="Node3D"]
|
||||
script = ExtResource("1_ocbkm")
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.34202, 0.939693, 0, -0.939693, 0.34202, 0, 10, 3)
|
||||
fov = 80.0
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
[gd_scene load_steps=8 format=4 uid="uid://bacgd5lmvaxea"]
|
||||
[gd_scene load_steps=15 format=4 uid="uid://bacgd5lmvaxea"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bcgt64gerg8pv" path="res://Scripts/player.gd" id="1_5wxv7"]
|
||||
[ext_resource type="Material" uid="uid://b4rpp31c0dx2m" path="res://Assets/Materials/BasicMat.tres" id="1_y6xdh"]
|
||||
[ext_resource type="Script" uid="uid://bk6205bvyl0to" path="res://Scripts/player_state_machine.gd" id="2_y5hcb"]
|
||||
[ext_resource type="Script" uid="uid://c2xwiexy5b3b3" path="res://Scripts/player_attack.gd" id="3_knrd4"]
|
||||
[ext_resource type="Script" uid="uid://wnisqyoyai2h" path="res://Scripts/player_on_floor.gd" id="4_hck1p"]
|
||||
[ext_resource type="Script" uid="uid://dhnl0penaqfkx" path="res://Scripts/player_dodge_roll.gd" id="5_hck1p"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_i3sml"]
|
||||
resource_name = "Material"
|
||||
@@ -54,11 +58,66 @@ size = Vector3(1.59, 0.4, 0.375)
|
||||
radius = 0.501894
|
||||
height = 1.30188
|
||||
|
||||
[sub_resource type="Animation" id="Animation_hck1p"]
|
||||
resource_name = "dodge"
|
||||
step = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Body:rotation")
|
||||
tracks/0/interp = 2
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.3),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0, 0), Vector3(0, 0, 6.28319)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_juxdh"]
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Body:rotation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector3(0, 0, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_4qbli"]
|
||||
_data = {
|
||||
&"RESET": SubResource("Animation_juxdh"),
|
||||
&"dodge": SubResource("Animation_hck1p")
|
||||
}
|
||||
|
||||
[node name="Player" type="CharacterBody3D"]
|
||||
script = ExtResource("1_5wxv7")
|
||||
|
||||
[node name="OmniLight3D" type="OmniLight3D" parent="."]
|
||||
transform = Transform3D(1, -1.74846e-07, 0, 1.74846e-07, 1, 0, 0, 0, 1, -2.406e-07, 1.87607, 0.0537765)
|
||||
light_energy = 0.1
|
||||
|
||||
[node name="State Machine" type="Node" parent="." node_paths=PackedStringArray("character", "initial_state")]
|
||||
script = ExtResource("2_y5hcb")
|
||||
character = NodePath("..")
|
||||
initial_state = NodePath("On Floor")
|
||||
|
||||
[node name="Attack" type="Node" parent="State Machine"]
|
||||
script = ExtResource("3_knrd4")
|
||||
|
||||
[node name="On Floor" type="Node" parent="State Machine"]
|
||||
script = ExtResource("4_hck1p")
|
||||
|
||||
[node name="Dodge Roll" type="Node" parent="State Machine"]
|
||||
script = ExtResource("5_hck1p")
|
||||
|
||||
[node name="Body" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.0537765)
|
||||
material_override = ExtResource("1_y6xdh")
|
||||
mesh = SubResource("ArrayMesh_5wxv7")
|
||||
skeleton = NodePath("")
|
||||
@@ -67,10 +126,6 @@ metadata/extras = {
|
||||
"rr_curve_obj": {}
|
||||
}
|
||||
|
||||
[node name="OmniLight3D" type="OmniLight3D" parent="Body"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.37607, 0)
|
||||
light_energy = 0.1
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Body"]
|
||||
transform = Transform3D(-0.487982, 0, -0.872854, 0, 1, 0, 0.872854, 0, -0.487982, 0.351417, 0.11253, -0.601462)
|
||||
mesh = SubResource("BoxMesh_5wxv7")
|
||||
@@ -79,3 +134,8 @@ skeleton = NodePath("../..")
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||
shape = SubResource("CapsuleShape3D_y5hcb")
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
libraries = {
|
||||
&"": SubResource("AnimationLibrary_4qbli")
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=25 format=4 uid="uid://dbqyyunvx6fdj"]
|
||||
[gd_scene load_steps=24 format=4 uid="uid://dbqyyunvx6fdj"]
|
||||
|
||||
[ext_resource type="Material" uid="uid://b4rpp31c0dx2m" path="res://Assets/Materials/BasicMat.tres" id="1_8lor2"]
|
||||
[ext_resource type="PackedScene" uid="uid://hmyrn2pbbhpm" path="res://Prefabs/PlayerCamera.tscn" id="2_qq11w"]
|
||||
@@ -27,9 +27,6 @@ fog_light_color = Color(0.117, 0.14115, 0.18, 1)
|
||||
fog_light_energy = 1.65
|
||||
fog_density = 1.0
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_qq11w"]
|
||||
size = Vector3(100, 1, 100)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_cre2v"]
|
||||
|
||||
[sub_resource type="ArrayMesh" id="ArrayMesh_eow34"]
|
||||
@@ -201,19 +198,11 @@ _surfaces = [{
|
||||
blend_shape_mode = 0
|
||||
shadow_mesh = SubResource("ArrayMesh_ib0jm")
|
||||
|
||||
[node name="Node3D" type="Node3D"]
|
||||
[node name="TestLevel" type="Node3D"]
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||
environment = SubResource("Environment_vab8d")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.196, 7.041, 1.942)
|
||||
collision_layer = 128
|
||||
collision_mask = 128
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D"]
|
||||
shape = SubResource("BoxShape3D_qq11w")
|
||||
|
||||
[node name="Test1" type="Node3D" parent="."]
|
||||
|
||||
[node name="Cube_001" type="MeshInstance3D" parent="Test1"]
|
||||
@@ -272,7 +261,7 @@ metadata/extras = {
|
||||
}
|
||||
|
||||
[node name="Camera3D" parent="." node_paths=PackedStringArray("player") instance=ExtResource("2_qq11w")]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.25038, 0.968148, 0, -0.968148, 0.25038, 0.196, 7.041, 1.942)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||
player = NodePath("../Player")
|
||||
|
||||
[node name="OmniLight3D" type="OmniLight3D" parent="."]
|
||||
@@ -291,7 +280,45 @@ light_color = Color(0.833261, 0.5957, 0.32611, 1)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.26995, 0.974748, 4.40131)
|
||||
light_color = Color(0.833261, 0.5957, 0.32611, 1)
|
||||
|
||||
[node name="OmniLight3D5" type="OmniLight3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.66639, 0.974749, -33.6377)
|
||||
light_color = Color(0.833261, 0.5957, 0.32611, 1)
|
||||
|
||||
[node name="OmniLight3D6" type="OmniLight3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.90079, 0.974749, -34.0342)
|
||||
light_color = Color(0.833261, 0.5957, 0.32611, 1)
|
||||
|
||||
[node name="OmniLight3D7" type="OmniLight3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.90218, 0.974748, -25.5942)
|
||||
light_color = Color(0.833261, 0.5957, 0.32611, 1)
|
||||
|
||||
[node name="OmniLight3D8" type="OmniLight3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.26995, 0.974748, -25.5011)
|
||||
light_color = Color(0.833261, 0.5957, 0.32611, 1)
|
||||
|
||||
[node name="PostProcessing" parent="." instance=ExtResource("2_vab8d")]
|
||||
|
||||
[node name="Player" parent="." instance=ExtResource("3_hfnh2")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0717504, 0.311536, 0.0210209)
|
||||
|
||||
[node name="Blockout" type="Node3D" parent="."]
|
||||
|
||||
[node name="CSGBox3D" type="CSGBox3D" parent="Blockout"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.296387, -13.9739)
|
||||
use_collision = true
|
||||
size = Vector3(0.328125, 0.407227, 18.4945)
|
||||
|
||||
[node name="CSGBox3D4" type="CSGBox3D" parent="Blockout"]
|
||||
transform = Transform3D(0.888377, 0, 0.459115, 0, 1, 0, -0.459115, 0, 0.888377, -3.97738, -0.296386, -15.7424)
|
||||
use_collision = true
|
||||
size = Vector3(0.859375, 0.407227, 18.4945)
|
||||
|
||||
[node name="CSGBox3D3" type="CSGBox3D" parent="Blockout"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.296387, -13.9739)
|
||||
use_collision = true
|
||||
size = Vector3(0.328125, 0.407227, 18.4945)
|
||||
|
||||
[node name="CSGBox3D2" type="CSGBox3D" parent="Blockout"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.296387, -30.2649)
|
||||
use_collision = true
|
||||
size = Vector3(20.9199, 0.407227, 14.3326)
|
||||
|
||||
16
Scripts/State.gd
Normal file
16
Scripts/State.gd
Normal file
@@ -0,0 +1,16 @@
|
||||
extends Node
|
||||
class_name State
|
||||
|
||||
signal Transitioned
|
||||
|
||||
func Enter():
|
||||
pass
|
||||
|
||||
func Exit():
|
||||
pass
|
||||
|
||||
func Update(_delta : float):
|
||||
pass
|
||||
|
||||
func Physics_Update(_delta : float):
|
||||
pass
|
||||
1
Scripts/State.gd.uid
Normal file
1
Scripts/State.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://55lxwlg155dq
|
||||
@@ -1,34 +1,23 @@
|
||||
extends CharacterBody3D
|
||||
class_name Player
|
||||
|
||||
var input_dir
|
||||
var dodge_direction
|
||||
|
||||
@onready var body: MeshInstance3D = $Body
|
||||
@onready var anim_player: AnimationPlayer = $AnimationPlayer
|
||||
|
||||
const MAX_STAMINA : float = 100
|
||||
var stamina = MAX_STAMINA
|
||||
const STAMINA_REGEN_RATE = 10.0
|
||||
|
||||
const MOVE_SPEED = 10.0
|
||||
const MOVE_TRANSITION_SPEED = 7.0
|
||||
## Gameplay Settings
|
||||
const DODGE_STAMINA_COST = 30.0
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if is_on_floor():
|
||||
standard_movement(delta)
|
||||
|
||||
body_look_at_mouse()
|
||||
apply_gravity(delta)
|
||||
stamina_regen(delta)
|
||||
move_and_slide()
|
||||
|
||||
func body_look_at_mouse():
|
||||
var mouse_raycast = MousePos.get_mouse_world_position()
|
||||
if mouse_raycast != null:
|
||||
body.look_at(Vector3(mouse_raycast.x,body.global_position.y,mouse_raycast.y),Vector3.UP)
|
||||
|
||||
func apply_gravity(delta):
|
||||
if !is_on_floor():
|
||||
velocity.y -= 9.8 * delta
|
||||
|
||||
func standard_movement(delta):
|
||||
input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_backward")
|
||||
|
||||
if input_dir != null:
|
||||
velocity.x = lerp(velocity.x, input_dir.x * MOVE_SPEED,delta * MOVE_TRANSITION_SPEED)
|
||||
velocity.z = lerp(velocity.z, input_dir.y * MOVE_SPEED,delta * MOVE_TRANSITION_SPEED)
|
||||
func stamina_regen(delta):
|
||||
if stamina < MAX_STAMINA:
|
||||
print("STAMINA : ", stamina)
|
||||
stamina = clamp(stamina + delta * STAMINA_REGEN_RATE,0,MAX_STAMINA)
|
||||
|
||||
2
Scripts/player_attack.gd
Normal file
2
Scripts/player_attack.gd
Normal file
@@ -0,0 +1,2 @@
|
||||
extends State
|
||||
class_name PlayerAttack
|
||||
1
Scripts/player_attack.gd.uid
Normal file
1
Scripts/player_attack.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c2xwiexy5b3b3
|
||||
@@ -1,5 +1,4 @@
|
||||
extends Camera3D
|
||||
class_name PlayerCamera
|
||||
extends Node3D
|
||||
|
||||
@export var player : Player
|
||||
|
||||
|
||||
21
Scripts/player_dodge_roll.gd
Normal file
21
Scripts/player_dodge_roll.gd
Normal file
@@ -0,0 +1,21 @@
|
||||
extends PlayerState
|
||||
class_name PlayerDodgeRoll
|
||||
|
||||
|
||||
|
||||
const DODGE_SPEED : float = 20.0
|
||||
const DODGE_TIME : float = .1
|
||||
var dodge_timer = DODGE_TIME
|
||||
|
||||
func Enter():
|
||||
character.anim_player.play("dodge")
|
||||
|
||||
func Physics_Update(delta):
|
||||
if dodge_timer > 0:
|
||||
dodge_timer -= delta
|
||||
else:
|
||||
dodge_timer = DODGE_TIME
|
||||
Transitioned.emit(self,"on floor")
|
||||
|
||||
character.velocity.x = character.dodge_direction.x * DODGE_SPEED
|
||||
character.velocity.z = character.dodge_direction.y * DODGE_SPEED
|
||||
1
Scripts/player_dodge_roll.gd.uid
Normal file
1
Scripts/player_dodge_roll.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dhnl0penaqfkx
|
||||
19
Scripts/player_on_floor.gd
Normal file
19
Scripts/player_on_floor.gd
Normal file
@@ -0,0 +1,19 @@
|
||||
extends PlayerState
|
||||
class_name PlayerOnFloor
|
||||
|
||||
func Physics_Update(delta):
|
||||
dodge_roll()
|
||||
standard_movement(delta)
|
||||
body_look_at_mouse()
|
||||
apply_gravity(delta)
|
||||
respawn_on_fall(delta)
|
||||
|
||||
func dodge_roll():
|
||||
if character.is_on_floor():
|
||||
if character.stamina > character.DODGE_STAMINA_COST:
|
||||
if Input.is_action_just_pressed("dodge"):
|
||||
var dodge_direction = Input.get_vector("move_left", "move_right", "move_forward", "move_backward")
|
||||
if dodge_direction:
|
||||
character.stamina -= character.DODGE_STAMINA_COST
|
||||
character.dodge_direction = dodge_direction
|
||||
Transitioned.emit(self,"dodge roll")
|
||||
1
Scripts/player_on_floor.gd.uid
Normal file
1
Scripts/player_on_floor.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://wnisqyoyai2h
|
||||
42
Scripts/player_state_machine.gd
Normal file
42
Scripts/player_state_machine.gd
Normal file
@@ -0,0 +1,42 @@
|
||||
extends Node
|
||||
class_name PlayerStateMachine
|
||||
|
||||
@export var character : Player
|
||||
@export var initial_state : State
|
||||
|
||||
var current_state : State
|
||||
var states : Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
for child in get_children():
|
||||
if child is State:
|
||||
states[child.name.to_lower()] = child
|
||||
child.Transitioned.connect(on_child_transition)
|
||||
|
||||
if initial_state:
|
||||
initial_state.Enter()
|
||||
current_state = initial_state
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if current_state:
|
||||
current_state.Update(delta)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if current_state:
|
||||
current_state.Physics_Update(delta)
|
||||
|
||||
func on_child_transition(state,new_state_name):
|
||||
if state != current_state:
|
||||
return
|
||||
|
||||
var new_state = states.get(new_state_name.to_lower())
|
||||
if !new_state:
|
||||
return
|
||||
|
||||
if current_state:
|
||||
current_state.Exit()
|
||||
|
||||
new_state.Enter()
|
||||
|
||||
current_state = new_state
|
||||
print("PLAYER STATE CHANGED TO : ",current_state)
|
||||
1
Scripts/player_state_machine.gd.uid
Normal file
1
Scripts/player_state_machine.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bk6205bvyl0to
|
||||
56
Scripts/player_states.gd
Normal file
56
Scripts/player_states.gd
Normal file
@@ -0,0 +1,56 @@
|
||||
extends State
|
||||
class_name PlayerState
|
||||
|
||||
@export var move_speed = 10.0
|
||||
@export var move_transition_speed = 7.0
|
||||
@export var health_lost_on_fall = 2.0
|
||||
|
||||
# respawn after falling
|
||||
const FALL_TIME_TO_RESPAWN : float = 4.0
|
||||
const TIME_TO_CACHE_POSITION : float = 2.0
|
||||
var ground_pos_cached = []
|
||||
var fall_timer = FALL_TIME_TO_RESPAWN
|
||||
var ground_pos_timer = 0
|
||||
|
||||
@onready var character = get_parent().character
|
||||
|
||||
func Update(delta):
|
||||
pass
|
||||
|
||||
func respawn_on_fall(delta):
|
||||
if character.is_on_floor():
|
||||
if ground_pos_timer > 0:
|
||||
ground_pos_timer -= delta
|
||||
else:
|
||||
ground_pos_timer = TIME_TO_CACHE_POSITION
|
||||
ground_pos_cached.append(character.global_position)
|
||||
if ground_pos_cached.size() > 2:
|
||||
ground_pos_cached.pop_front()
|
||||
else:
|
||||
if fall_timer > 0:
|
||||
fall_timer -= delta
|
||||
else:
|
||||
fall_timer = FALL_TIME_TO_RESPAWN
|
||||
character.global_position = ground_pos_cached[0]
|
||||
|
||||
func apply_gravity(delta):
|
||||
if !character.is_on_floor():
|
||||
character.velocity.y -= 9.8 * delta
|
||||
|
||||
func body_look_at_mouse():
|
||||
var mouse_raycast = MousePos.get_mouse_world_position()
|
||||
if mouse_raycast != null:
|
||||
character.body.look_at(Vector3(mouse_raycast.x,character.body.global_position.y,mouse_raycast.y),Vector3.UP)
|
||||
|
||||
func standard_movement(delta):
|
||||
if character.is_on_floor():
|
||||
var input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_backward")
|
||||
|
||||
if input_dir != null:
|
||||
character.velocity.x = lerp(character.velocity.x, input_dir.x * move_speed, delta * move_transition_speed)
|
||||
character.velocity.z = lerp(character.velocity.z, input_dir.y * move_speed,delta * move_transition_speed)
|
||||
else:
|
||||
if character.velocity:
|
||||
const MOMENTUM = 2.0
|
||||
character.velocity.x = lerp(character.velocity.x, 0.0,delta * MOMENTUM)
|
||||
character.velocity.z = lerp(character.velocity.z, 0.0,delta * MOMENTUM)
|
||||
1
Scripts/player_states.gd.uid
Normal file
1
Scripts/player_states.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://mmr40sx0iij5
|
||||
@@ -45,6 +45,11 @@ move_backward={
|
||||
"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":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
dodge={
|
||||
"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)
|
||||
]
|
||||
}
|
||||
|
||||
[layer_names]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user