mirror of
https://github.com/godotengine/godot-demo-projects.git
synced 2026-01-04 23:10:08 +01:00
Update 2D role playing game
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
extends Node
|
||||
|
||||
export (NodePath) var combat_screen
|
||||
export (NodePath) var exploration_screen
|
||||
export(NodePath) var combat_screen
|
||||
export(NodePath) var exploration_screen
|
||||
|
||||
const PLAYER_WIN = "res://dialogue/dialogue_data/player_won.json"
|
||||
const PLAYER_LOSE = "res://dialogue/dialogue_data/player_lose.json"
|
||||
@@ -19,12 +19,6 @@ func _ready():
|
||||
"_on_opponent_dialogue_finished", [n])
|
||||
remove_child(combat_screen)
|
||||
|
||||
func _on_opponent_dialogue_finished(opponent):
|
||||
if opponent.lost:
|
||||
return
|
||||
var player = $Exploration/Grid/Player
|
||||
var combatents = [player.combat_actor, opponent.combat_actor]
|
||||
start_combat(combatents)
|
||||
|
||||
func start_combat(combat_actors):
|
||||
remove_child($Exploration)
|
||||
@@ -35,6 +29,15 @@ func start_combat(combat_actors):
|
||||
combat_screen.initialize(combat_actors)
|
||||
$AnimationPlayer.play_backwards("fade")
|
||||
|
||||
|
||||
func _on_opponent_dialogue_finished(opponent):
|
||||
if opponent.lost:
|
||||
return
|
||||
var player = $Exploration/Grid/Player
|
||||
var combatents = [player.combat_actor, opponent.combat_actor]
|
||||
start_combat(combatents)
|
||||
|
||||
|
||||
func _on_combat_finished(winner, _loser):
|
||||
remove_child(combat_screen)
|
||||
$AnimationPlayer.play_backwards("fade")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[gd_scene load_steps=5 format=2]
|
||||
|
||||
[ext_resource path="res://Game.gd" type="Script" id=1]
|
||||
[ext_resource path="res://screens/exploration/Exploration.tscn" type="PackedScene" id=2]
|
||||
[ext_resource path="res://screens/combat/Combat.tscn" type="PackedScene" id=3]
|
||||
[ext_resource path="res://screens/combat/Combat.tscn" type="PackedScene" id=2]
|
||||
[ext_resource path="res://screens/exploration/Exploration.tscn" type="PackedScene" id=3]
|
||||
|
||||
[sub_resource type="Animation" id=1]
|
||||
length = 0.5
|
||||
@@ -38,10 +38,10 @@ __meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Combat" parent="." instance=ExtResource( 3 )]
|
||||
[node name="Combat" parent="." instance=ExtResource( 2 )]
|
||||
visible = false
|
||||
|
||||
[node name="Exploration" parent="." instance=ExtResource( 2 )]
|
||||
[node name="Exploration" parent="." instance=ExtResource( 3 )]
|
||||
|
||||
[node name="Camera2D" type="Camera2D" parent="."]
|
||||
offset = Vector2( 640, 360 )
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extends Node
|
||||
|
||||
export (String, FILE, "*.json") var dialogue_file
|
||||
export(String, FILE, "*.json") var dialogue_file
|
||||
var dialogue_keys = []
|
||||
var dialogue_name = ""
|
||||
var current = 0
|
||||
@@ -15,7 +15,8 @@ func start_dialogue():
|
||||
index_dialogue()
|
||||
dialogue_text = dialogue_keys[current].text
|
||||
dialogue_name = dialogue_keys[current].name
|
||||
|
||||
|
||||
|
||||
func next_dialogue():
|
||||
current += 1
|
||||
if current == dialogue_keys.size():
|
||||
@@ -23,13 +24,15 @@ func next_dialogue():
|
||||
return
|
||||
dialogue_text = dialogue_keys[current].text
|
||||
dialogue_name = dialogue_keys[current].name
|
||||
|
||||
|
||||
|
||||
func index_dialogue():
|
||||
var dialogue = load_dialogue(dialogue_file)
|
||||
dialogue_keys.clear()
|
||||
for key in dialogue:
|
||||
dialogue_keys.append(dialogue[key])
|
||||
|
||||
|
||||
|
||||
func load_dialogue(file_path):
|
||||
var file = File.new()
|
||||
if file.file_exists(file_path):
|
||||
|
||||
@@ -4,4 +4,3 @@
|
||||
|
||||
[node name="DialoguePlayer" type="Node"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
extends Control
|
||||
|
||||
var dialogue_node = null
|
||||
|
||||
func _ready():
|
||||
hide()
|
||||
|
||||
|
||||
func show_dialogue(player, dialogue):
|
||||
show()
|
||||
$Button.grab_focus()
|
||||
@@ -21,12 +23,13 @@ func show_dialogue(player, dialogue):
|
||||
$Name.text = dialogue_node.dialogue_name
|
||||
$Text.text = dialogue_node.dialogue_text
|
||||
|
||||
|
||||
|
||||
func _on_Button_button_up():
|
||||
dialogue_node.next_dialogue()
|
||||
$Name.text = dialogue_node.dialogue_name
|
||||
$Text.text = dialogue_node.dialogue_text
|
||||
|
||||
|
||||
func _on_dialogue_finished(player):
|
||||
dialogue_node.disconnect("dialogue_started", player, "set_active")
|
||||
dialogue_node.disconnect("dialogue_finished", player, "set_active")
|
||||
|
||||
@@ -31,5 +31,4 @@ margin_top = 32.0
|
||||
margin_right = 1264.0
|
||||
margin_bottom = 151.0
|
||||
text = "Next"
|
||||
|
||||
[connection signal="button_up" from="Button" to="." method="_on_Button_button_up"]
|
||||
|
||||
@@ -33,4 +33,3 @@ func request_move(pawn, direction):
|
||||
if not target_pawn.has_node("DialoguePlayer"):
|
||||
return
|
||||
get_node(dialogue_ui).show_dialogue(pawn, target_pawn.get_node("DialoguePlayer"))
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ onready var Grid = get_parent()
|
||||
var lost = false
|
||||
|
||||
func _ready():
|
||||
update_look_direction(Vector2(1, 0))
|
||||
update_look_direction(Vector2.RIGHT)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
var input_direction = get_input_direction()
|
||||
@@ -19,15 +20,18 @@ func _process(delta):
|
||||
else:
|
||||
bump()
|
||||
|
||||
|
||||
func get_input_direction():
|
||||
return Vector2(
|
||||
int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left")),
|
||||
int(Input.is_action_pressed("ui_down")) - int(Input.is_action_pressed("ui_up"))
|
||||
Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left"),
|
||||
Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
|
||||
)
|
||||
|
||||
|
||||
func update_look_direction(direction):
|
||||
$Pivot/Sprite.rotation = direction.angle()
|
||||
|
||||
|
||||
func move_to(target_position):
|
||||
set_process(false)
|
||||
$AnimationPlayer.play("walk")
|
||||
@@ -40,5 +44,6 @@ func move_to(target_position):
|
||||
|
||||
set_process(true)
|
||||
|
||||
|
||||
func bump():
|
||||
$AnimationPlayer.play("bump")
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
extends 'Pawn.gd'
|
||||
|
||||
#warning-ignore:unused_class_variable
|
||||
export (PackedScene) var combat_actor
|
||||
#warning-ignore:unused_class_variable
|
||||
var lost = false
|
||||
|
||||
func _ready():
|
||||
|
||||
@@ -68,9 +68,6 @@ script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_group_": true
|
||||
}
|
||||
_sections_unfolded = [ "Offset", "Transform", "Z Index" ]
|
||||
type = 0
|
||||
combat_actor = null
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
anims/bump = SubResource( 1 )
|
||||
@@ -84,4 +81,3 @@ anims/walk = SubResource( 2 )
|
||||
texture = ExtResource( 2 )
|
||||
centered = false
|
||||
offset = Vector2( -32, -32 )
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
extends 'actor.gd'
|
||||
extends 'Actor.gd'
|
||||
|
||||
func _ready():
|
||||
set_process(false)
|
||||
|
||||
|
||||
|
||||
func get_input_direction():
|
||||
return Vector2(0, 0)
|
||||
return Vector2.ZERO
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
extends Node2D
|
||||
|
||||
enum CellType { ACTOR, OBSTACLE, OBJECT }
|
||||
#warning-ignore:unused_class_variable
|
||||
export(CellType) var type = CellType.ACTOR
|
||||
|
||||
var active = true setget set_active
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
extends "actor.gd"
|
||||
extends "Actor.gd"
|
||||
|
||||
const DIRECTIONS = [-1, 1]
|
||||
|
||||
@@ -8,7 +8,7 @@ func get_input_direction():
|
||||
var random_x = DIRECTIONS[randi() % DIRECTIONS.size()]
|
||||
var random_y = DIRECTIONS[randi() % DIRECTIONS.size()]
|
||||
|
||||
var random_axis = randi()%2
|
||||
var random_axis = randi() % 2
|
||||
if random_axis > 0:
|
||||
random_x = 0
|
||||
else:
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
extends 'Pawn.gd'
|
||||
|
||||
onready var Grid = get_parent()
|
||||
#warning-ignore:unused_class_variable
|
||||
export (PackedScene) var combat_actor
|
||||
#warning-ignore:unused_class_variable
|
||||
var lost = false
|
||||
|
||||
func _ready():
|
||||
update_look_direction(Vector2(1, 0))
|
||||
update_look_direction(Vector2.RIGHT)
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
var input_direction = get_input_direction()
|
||||
@@ -20,15 +23,18 @@ func _process(_delta):
|
||||
else:
|
||||
bump()
|
||||
|
||||
|
||||
func get_input_direction():
|
||||
return Vector2(
|
||||
int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left")),
|
||||
int(Input.is_action_pressed("ui_down")) - int(Input.is_action_pressed("ui_up"))
|
||||
Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left"),
|
||||
Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
|
||||
)
|
||||
|
||||
|
||||
func update_look_direction(direction):
|
||||
$Pivot/Sprite.rotation = direction.angle()
|
||||
|
||||
|
||||
func move_to(target_position):
|
||||
set_process(false)
|
||||
$AnimationPlayer.play("walk")
|
||||
@@ -41,5 +47,6 @@ func move_to(target_position):
|
||||
|
||||
set_process(true)
|
||||
|
||||
|
||||
func bump():
|
||||
$AnimationPlayer.play("bump")
|
||||
|
||||
@@ -26,6 +26,41 @@ window/size/height=720
|
||||
window/stretch/mode="2d"
|
||||
window/stretch/aspect="expand"
|
||||
|
||||
[input]
|
||||
|
||||
ui_left={
|
||||
"deadzone": 0.5,
|
||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null)
|
||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null)
|
||||
]
|
||||
}
|
||||
ui_right={
|
||||
"deadzone": 0.5,
|
||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":15,"pressure":0.0,"pressed":false,"script":null)
|
||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null)
|
||||
]
|
||||
}
|
||||
ui_up={
|
||||
"deadzone": 0.5,
|
||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null)
|
||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null)
|
||||
]
|
||||
}
|
||||
ui_down={
|
||||
"deadzone": 0.5,
|
||||
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777234,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null)
|
||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[rendering]
|
||||
|
||||
environment/default_environment="res://default_env.tres"
|
||||
|
||||
@@ -14,6 +14,18 @@ func initialize(combat_combatants):
|
||||
$UI.initialize()
|
||||
$TurnQueue.initialize()
|
||||
|
||||
|
||||
func clear_combat():
|
||||
for n in $Combatants.get_children():
|
||||
n.queue_free()
|
||||
for n in $UI/Combatants.get_children():
|
||||
n.queue_free()
|
||||
|
||||
|
||||
func finish_combat(winner, loser):
|
||||
emit_signal("combat_finished", winner, loser)
|
||||
|
||||
|
||||
func _on_combatant_death(combatant):
|
||||
var winner
|
||||
if not combatant.name == "Player":
|
||||
@@ -24,13 +36,3 @@ func _on_combatant_death(combatant):
|
||||
winner = n
|
||||
break
|
||||
finish_combat(winner, combatant)
|
||||
|
||||
func clear_combat():
|
||||
for n in $Combatants.get_children():
|
||||
n.queue_free()
|
||||
for n in $UI/Combatants.get_children():
|
||||
n.queue_free()
|
||||
|
||||
func finish_combat(winner, loser):
|
||||
emit_signal("combat_finished", winner, loser)
|
||||
|
||||
|
||||
@@ -12,4 +12,3 @@ damage = 2
|
||||
life = 10
|
||||
|
||||
[node name="Sprite" parent="." instance=ExtResource( 3 )]
|
||||
|
||||
|
||||
@@ -36,5 +36,4 @@ texture = ExtResource( 2 )
|
||||
wait_time = 0.25
|
||||
one_shot = true
|
||||
|
||||
|
||||
[editable path="Sprite"]
|
||||
|
||||
@@ -4,4 +4,3 @@
|
||||
|
||||
[node name="Player" instance=ExtResource( 1 )]
|
||||
defense = 2
|
||||
|
||||
|
||||
@@ -31,4 +31,3 @@ step = 1.0
|
||||
value = 5.0
|
||||
rounded = true
|
||||
percent_visible = false
|
||||
|
||||
|
||||
@@ -31,4 +31,3 @@ step = 1.0
|
||||
value = 5.0
|
||||
rounded = true
|
||||
percent_visible = false
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ export (PackedScene) var info_scene
|
||||
|
||||
func _ready():
|
||||
combatants_node = get_node(combatants_node)
|
||||
|
||||
|
||||
|
||||
func initialize():
|
||||
for combatant in combatants_node.get_children():
|
||||
var health = combatant.get_node("Health")
|
||||
@@ -18,16 +19,19 @@ func initialize():
|
||||
$Combatants.add_child(info)
|
||||
$Buttons/GridContainer/Attack.grab_focus()
|
||||
|
||||
|
||||
func _on_Attack_button_up():
|
||||
if not combatants_node.get_node("Player").active:
|
||||
return
|
||||
combatants_node.get_node("Player").attack(combatants_node.get_node("Opponent"))
|
||||
|
||||
|
||||
func _on_Defend_button_up():
|
||||
if not combatants_node.get_node("Player").active:
|
||||
return
|
||||
combatants_node.get_node("Player").defend()
|
||||
|
||||
|
||||
func _on_Flee_button_up():
|
||||
if not combatants_node.get_node("Player").active:
|
||||
return
|
||||
|
||||
@@ -96,6 +96,5 @@ size_flags_horizontal = 2
|
||||
size_flags_vertical = 4
|
||||
size_flags_stretch_ratio = 0.0
|
||||
text = "Add"
|
||||
|
||||
[connection signal="button_down" from="Panel/Button" to="Panel/VBoxContainer" method="_on_Button_button_down"]
|
||||
[connection signal="button_up" from="Panel/Button" to="Panel/VBoxContainer" method="_on_Button_button_up"]
|
||||
|
||||
@@ -13,4 +13,3 @@ text = "Title"
|
||||
align = 1
|
||||
autowrap = true
|
||||
clip_text = true
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
extends Node
|
||||
|
||||
export (int) var damage = 1
|
||||
export (int) var defense = 1
|
||||
export(int) var damage = 1
|
||||
export(int) var defense = 1
|
||||
var active = false setget set_active
|
||||
|
||||
signal turn_finished
|
||||
@@ -15,22 +15,27 @@ func set_active(value):
|
||||
return
|
||||
if $Health.armor >= $Health.base_armor + defense:
|
||||
$Health.armor = $Health.base_armor
|
||||
|
||||
|
||||
|
||||
func attack(target):
|
||||
target.take_damage(damage)
|
||||
emit_signal("turn_finished")
|
||||
|
||||
|
||||
func consume(item):
|
||||
item.use(self)
|
||||
emit_signal("turn_finished")
|
||||
|
||||
|
||||
func defend():
|
||||
$Health.armor += defense
|
||||
emit_signal("turn_finished")
|
||||
|
||||
func consume(item):
|
||||
item.use(self)
|
||||
emit_signal("turn_finished")
|
||||
|
||||
|
||||
func flee():
|
||||
emit_signal("turn_finished")
|
||||
|
||||
func take_damage(damage):
|
||||
$Health.take_damage(damage)
|
||||
|
||||
|
||||
func take_damage(damage_to_take):
|
||||
$Health.take_damage(damage_to_take)
|
||||
$Sprite/AnimationPlayer.play("take_damage")
|
||||
|
||||
@@ -19,5 +19,4 @@ texture = ExtResource( 3 )
|
||||
wait_time = 0.25
|
||||
one_shot = true
|
||||
|
||||
|
||||
[editable path="Sprite"]
|
||||
|
||||
@@ -6,4 +6,3 @@
|
||||
|
||||
[node name="Health" parent="." index="0"]
|
||||
base_armor = 1
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ var armor = 0
|
||||
func _ready():
|
||||
armor = base_armor
|
||||
|
||||
|
||||
func take_damage(damage):
|
||||
life = life - damage + armor
|
||||
if life <= 0:
|
||||
@@ -18,10 +19,12 @@ func take_damage(damage):
|
||||
else:
|
||||
emit_signal("health_changed", life)
|
||||
|
||||
|
||||
func heal(amount):
|
||||
life += amount
|
||||
life = clamp(life, life, max_life)
|
||||
emit_signal("health_changed", life)
|
||||
|
||||
|
||||
func get_health_ratio():
|
||||
return life / max_life
|
||||
|
||||
@@ -4,4 +4,3 @@
|
||||
|
||||
[node name="Health" type="Node"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
|
||||
@@ -36,4 +36,3 @@ texture = ExtResource( 1 )
|
||||
[node name="Body" type="Sprite" parent="Pivot"]
|
||||
position = Vector2( 0, -76 )
|
||||
texture = ExtResource( 2 )
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ extends Node
|
||||
|
||||
const combatant = preload("../combatants/Combatant.gd")
|
||||
|
||||
export (NodePath) var combatants_list
|
||||
export(NodePath) var combatants_list
|
||||
var queue = [] setget set_queue
|
||||
var active_combatant = null setget _set_active_combatant
|
||||
|
||||
@@ -11,22 +11,26 @@ signal active_combatant_changed(active_combatant)
|
||||
func _ready():
|
||||
combatants_list = get_node(combatants_list)
|
||||
|
||||
|
||||
func initialize():
|
||||
set_queue(combatants_list.get_children())
|
||||
play_turn()
|
||||
|
||||
|
||||
func play_turn():
|
||||
yield(active_combatant, "turn_finished")
|
||||
get_next_in_queue()
|
||||
play_turn()
|
||||
|
||||
|
||||
func get_next_in_queue():
|
||||
var current_combatant = queue.pop_front()
|
||||
current_combatant.active = false
|
||||
queue.append(current_combatant)
|
||||
self.active_combatant = queue[0]
|
||||
return active_combatant
|
||||
|
||||
|
||||
|
||||
func remove(combatant):
|
||||
var new_queue = []
|
||||
for n in queue:
|
||||
@@ -35,6 +39,7 @@ func remove(combatant):
|
||||
combatant.queue_free()
|
||||
self.queue = new_queue
|
||||
|
||||
|
||||
func set_queue(new_queue):
|
||||
queue.clear()
|
||||
for node in new_queue:
|
||||
@@ -45,6 +50,7 @@ func set_queue(new_queue):
|
||||
if queue.size() > 0:
|
||||
self.active_combatant = queue[0]
|
||||
|
||||
|
||||
func _set_active_combatant(new_combatant):
|
||||
active_combatant = new_combatant
|
||||
active_combatant.active = true
|
||||
|
||||
@@ -4,4 +4,3 @@
|
||||
|
||||
[node name="TurnQueue" type="Node"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
|
||||
Reference in New Issue
Block a user