stats and better collision dropping dev, interface
This commit is contained in:
parent
006e83d5bf
commit
15b4c5d320
9
HUD.gd
9
HUD.gd
@ -16,7 +16,9 @@ func _on_PopupMenu_id_pressed(id):
|
|||||||
2:
|
2:
|
||||||
$"../SaveDialog".popup_centered()
|
$"../SaveDialog".popup_centered()
|
||||||
4:
|
4:
|
||||||
$"../SettingsWindow".popup_centered()
|
$"../SettingsWindow".popup()
|
||||||
|
5:
|
||||||
|
$"../StatsWindow".popup()
|
||||||
|
|
||||||
func _process(delta):
|
func _process(delta):
|
||||||
elapsed_time += delta # Increment the elapsed time by the frame's delta time
|
elapsed_time += delta # Increment the elapsed time by the frame's delta time
|
||||||
@ -35,3 +37,8 @@ func _on_settings_window_close_requested():
|
|||||||
|
|
||||||
func _on_control_stop_button_pressed():
|
func _on_control_stop_button_pressed():
|
||||||
%Map.controlling=null
|
%Map.controlling=null
|
||||||
|
|
||||||
|
func _on_kill_all_pressed():
|
||||||
|
for node in %Map.get_children():
|
||||||
|
if node is Player:
|
||||||
|
node.queue_free()
|
||||||
|
@ -29,5 +29,5 @@ func _on_confirmed():
|
|||||||
$"../SaveDialog/LineEdit".text=$ItemList.get_item_text(item)
|
$"../SaveDialog/LineEdit".text=$ItemList.get_item_text(item)
|
||||||
var filename = "user://saves/" + $ItemList.get_item_text(item)
|
var filename = "user://saves/" + $ItemList.get_item_text(item)
|
||||||
print("load:", filename)
|
print("load:", filename)
|
||||||
get_tree().root.get_node("Sim/Map").load_map(filename)
|
%Map.load_map(filename)
|
||||||
break
|
break
|
||||||
|
28
Player.gd
28
Player.gd
@ -2,6 +2,7 @@ class_name Player
|
|||||||
extends RigidBody2D
|
extends RigidBody2D
|
||||||
|
|
||||||
var health=60
|
var health=60
|
||||||
|
var can_pickup=true
|
||||||
|
|
||||||
func _integrate_forces(state: PhysicsDirectBodyState2D):
|
func _integrate_forces(state: PhysicsDirectBodyState2D):
|
||||||
var contact_count = state.get_contact_count()
|
var contact_count = state.get_contact_count()
|
||||||
@ -13,6 +14,8 @@ func _integrate_forces(state: PhysicsDirectBodyState2D):
|
|||||||
handle_pool_collision(collider_object)
|
handle_pool_collision(collider_object)
|
||||||
|
|
||||||
func handle_player_collision(player: Player):
|
func handle_player_collision(player: Player):
|
||||||
|
if not get_tree().root.get_node("Sim/ScreenOverlay/SettingsWindow/VBoxContainer/DropOnColl").is_pressed():
|
||||||
|
return
|
||||||
if is_carrying() and player.is_carrying():
|
if is_carrying() and player.is_carrying():
|
||||||
doubledrop(player)
|
doubledrop(player)
|
||||||
drop()
|
drop()
|
||||||
@ -62,10 +65,11 @@ func drop():
|
|||||||
new_pool.mana_r=carrying_r
|
new_pool.mana_r=carrying_r
|
||||||
new_pool.mana_g=carrying_g
|
new_pool.mana_g=carrying_g
|
||||||
new_pool.mana_b=carrying_b
|
new_pool.mana_b=carrying_b
|
||||||
new_pool.get_node("CollisionShape2D2").disabled=true;
|
#new_pool.get_node("CollisionShape2D2").disabled=true;
|
||||||
carrying_r=0
|
carrying_r=0
|
||||||
carrying_g=0
|
carrying_g=0
|
||||||
carrying_b=0
|
carrying_b=0
|
||||||
|
$PickupTimer.start()
|
||||||
get_tree().root.get_node("Sim/Map").call_deferred("add_child",new_pool)
|
get_tree().root.get_node("Sim/Map").call_deferred("add_child",new_pool)
|
||||||
update()
|
update()
|
||||||
|
|
||||||
@ -84,11 +88,24 @@ func doubledrop(player: Player):
|
|||||||
player.carrying_r=0
|
player.carrying_r=0
|
||||||
player.carrying_g=0
|
player.carrying_g=0
|
||||||
player.carrying_b=0
|
player.carrying_b=0
|
||||||
|
can_pickup=false
|
||||||
|
$PickupTimer.start()
|
||||||
|
player.can_pickup=false
|
||||||
|
player.getNode("PickupTimer").start()
|
||||||
get_tree().root.get_node("Sim/Map").call_deferred("add_child",new_pool)
|
get_tree().root.get_node("Sim/Map").call_deferred("add_child",new_pool)
|
||||||
update()
|
update()
|
||||||
player.update()
|
player.update()
|
||||||
|
|
||||||
|
func _on_Timer_timeout():
|
||||||
|
# This function will be called after 5 seconds
|
||||||
|
print("Timer finished!")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func exchange_with(pool: Pool):
|
func exchange_with(pool: Pool):
|
||||||
|
if ! can_pickup:
|
||||||
|
return
|
||||||
|
|
||||||
if is_carrying():
|
if is_carrying():
|
||||||
pool.mana_r+=carrying_r
|
pool.mana_r+=carrying_r
|
||||||
pool.mana_g+=carrying_g
|
pool.mana_g+=carrying_g
|
||||||
@ -107,8 +124,10 @@ func exchange_with(pool: Pool):
|
|||||||
elif pool.mana_b>0:
|
elif pool.mana_b>0:
|
||||||
pool.mana_b -= 1
|
pool.mana_b -= 1
|
||||||
carrying_b=1
|
carrying_b=1
|
||||||
|
can_pickup=false
|
||||||
|
$PickupTimer.start()
|
||||||
update()
|
update()
|
||||||
|
|
||||||
func update():
|
func update():
|
||||||
var max_value = max(carrying_r, carrying_g, carrying_b)
|
var max_value = max(carrying_r, carrying_g, carrying_b)
|
||||||
if max_value==0:
|
if max_value==0:
|
||||||
@ -143,6 +162,7 @@ func load_data(data):
|
|||||||
carrying_r=data["r"]
|
carrying_r=data["r"]
|
||||||
carrying_g=data["g"]
|
carrying_g=data["g"]
|
||||||
carrying_b=data["b"]
|
carrying_b=data["b"]
|
||||||
|
update()
|
||||||
|
|
||||||
func _on_health_timer_timeout():
|
func _on_health_timer_timeout():
|
||||||
if not get_tree().root.get_node("Sim/ScreenOverlay/SettingsWindow/VBoxContainer/Health").is_pressed():
|
if not get_tree().root.get_node("Sim/ScreenOverlay/SettingsWindow/VBoxContainer/Health").is_pressed():
|
||||||
@ -155,3 +175,7 @@ func _on_health_timer_timeout():
|
|||||||
|
|
||||||
if(health<1):
|
if(health<1):
|
||||||
self.queue_free()
|
self.queue_free()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_pickup_timer_timeout():
|
||||||
|
can_pickup=true
|
||||||
|
54
Pool.gd
54
Pool.gd
@ -29,6 +29,59 @@ func _process(_delta):
|
|||||||
$CollisionShape2D2.disabled=false
|
$CollisionShape2D2.disabled=false
|
||||||
update()
|
update()
|
||||||
|
|
||||||
|
func pool_coll_check():
|
||||||
|
var query_parameters = PhysicsShapeQueryParameters2D.new()
|
||||||
|
var collision_shape = $CollisionShape2D2.shape
|
||||||
|
query_parameters.set_shape(collision_shape)
|
||||||
|
query_parameters.set_transform(Transform2D(0, scale, 0, position))
|
||||||
|
query_parameters.set_collision_mask(2) #2=pools
|
||||||
|
|
||||||
|
# Get the Physics2DDirectSpaceState for collision checking
|
||||||
|
var space_state = get_world_2d().direct_space_state
|
||||||
|
|
||||||
|
# Check for collision
|
||||||
|
var collision_results = space_state.intersect_shape(query_parameters)
|
||||||
|
for result in collision_results:
|
||||||
|
if result.collider is Pool and result.collider != self:
|
||||||
|
if is_fully_overlapping(self, result.collider):
|
||||||
|
merge_with(result.collider)
|
||||||
|
|
||||||
|
func is_fully_overlapping(pool1, pool2):
|
||||||
|
var radius1 = pool1.scale[0]*pool1.get_node("CollisionShape2D2").scale[0]*pool1.get_node("CollisionShape2D2").shape.radius
|
||||||
|
var radius2 = pool2.scale[0]*pool2.get_node("CollisionShape2D2").scale[0]*pool1.get_node("CollisionShape2D2").shape.radius
|
||||||
|
var center_distance = pool1.position.distance_to(pool2.position)
|
||||||
|
|
||||||
|
# Check if one circle is completely inside the other
|
||||||
|
return center_distance <= abs(radius1 - radius2)
|
||||||
|
|
||||||
|
func merge_with(p: Pool):
|
||||||
|
# Calculate RGB sums for both pools
|
||||||
|
var sum_rgb_self = mana_r + mana_g + mana_b
|
||||||
|
var sum_rgb_other = p.mana_r + p.mana_g + p.mana_b
|
||||||
|
|
||||||
|
# Avoid division by zero
|
||||||
|
var total_sum = sum_rgb_self + sum_rgb_other
|
||||||
|
if total_sum == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Calculate weighted positions
|
||||||
|
var weighted_position_self = position * sum_rgb_self
|
||||||
|
var weighted_position_other = p.position * sum_rgb_other
|
||||||
|
|
||||||
|
# Compute new position as weighted average
|
||||||
|
position = (weighted_position_self + weighted_position_other) / total_sum
|
||||||
|
|
||||||
|
# Merge mana values
|
||||||
|
mana_r += p.mana_r
|
||||||
|
mana_g += p.mana_g
|
||||||
|
mana_b += p.mana_b
|
||||||
|
p.mana_r = 0
|
||||||
|
p.mana_g = 0
|
||||||
|
p.mana_b = 0
|
||||||
|
|
||||||
|
#update()
|
||||||
|
#p.update()
|
||||||
|
|
||||||
func update():
|
func update():
|
||||||
var max_value: float = max(mana_r, mana_g, mana_b)
|
var max_value: float = max(mana_r, mana_g, mana_b)
|
||||||
if max_value==0:
|
if max_value==0:
|
||||||
@ -42,6 +95,7 @@ func update():
|
|||||||
var s=sqrt((mana_r+mana_g+mana_b)/1000.0)
|
var s=sqrt((mana_r+mana_g+mana_b)/1000.0)
|
||||||
self.scale=Vector2(s,s)
|
self.scale=Vector2(s,s)
|
||||||
self.queue_redraw() # Request to redraw the node
|
self.queue_redraw() # Request to redraw the node
|
||||||
|
pool_coll_check()
|
||||||
|
|
||||||
func decay():
|
func decay():
|
||||||
var new_player = load("res://player.tscn").instantiate()
|
var new_player = load("res://player.tscn").instantiate()
|
||||||
|
43
StatsWindow.gd
Normal file
43
StatsWindow.gd
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
extends Window
|
||||||
|
|
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
func _ready():
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
|
||||||
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
func _process(delta):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func _on_timer_timeout():
|
||||||
|
var pl=0
|
||||||
|
var po=0
|
||||||
|
var rpl=0
|
||||||
|
var gpl=0
|
||||||
|
var bpl=0
|
||||||
|
var rpo=0
|
||||||
|
var gpo=0
|
||||||
|
var bpo=0
|
||||||
|
|
||||||
|
for node in %Map.get_children():
|
||||||
|
if node is Player:
|
||||||
|
pl+=1
|
||||||
|
rpl+=node.carrying_r
|
||||||
|
gpl+=node.carrying_g
|
||||||
|
bpl+=node.carrying_b
|
||||||
|
if node is Pool:
|
||||||
|
po+=1
|
||||||
|
rpo+=node.mana_r
|
||||||
|
gpo+=node.mana_g
|
||||||
|
bpo+=node.mana_b
|
||||||
|
$Label.text=\
|
||||||
|
"Players: "+str(pl)+"\n"+ \
|
||||||
|
"Pools: "+str(po)+"\n"+ \
|
||||||
|
"R: "+str(rpo+rpl)+" ("+str(rpo)+"+"+str(rpl)+")\n"+\
|
||||||
|
"G: "+str(gpo+gpl)+" ("+str(gpo)+"+"+str(gpl)+")\n"+\
|
||||||
|
"B: "+str(bpo+bpl)+" ("+str(bpo)+"+"+str(bpl)+")\n"
|
||||||
|
|
||||||
|
func _on_close_requested():
|
||||||
|
hide()
|
14
hud.tscn
14
hud.tscn
@ -120,7 +120,7 @@ text = "Radius:"
|
|||||||
|
|
||||||
[node name="Radius" type="LineEdit" parent="HBoxContainer/ToolSelect/VBoxContainer/Create Player Circle"]
|
[node name="Radius" type="LineEdit" parent="HBoxContainer/ToolSelect/VBoxContainer/Create Player Circle"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "200"
|
text = "50"
|
||||||
placeholder_text = "0"
|
placeholder_text = "0"
|
||||||
max_length = 5
|
max_length = 5
|
||||||
|
|
||||||
@ -147,6 +147,13 @@ layout_mode = 2
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Stop"
|
text = "Stop"
|
||||||
|
|
||||||
|
[node name="GOD" type="VBoxContainer" parent="HBoxContainer/ToolSelect/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="kill all" type="Button" parent="HBoxContainer/ToolSelect/VBoxContainer/GOD"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Kill All"
|
||||||
|
|
||||||
[node name="Debug" type="Label" parent="HBoxContainer"]
|
[node name="Debug" type="Label" parent="HBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 6
|
size_flags_horizontal = 6
|
||||||
@ -159,7 +166,7 @@ size_flags_horizontal = 10
|
|||||||
[node name="File" type="MenuButton" parent="HBoxContainer/Right"]
|
[node name="File" type="MenuButton" parent="HBoxContainer/Right"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "File..."
|
text = "File..."
|
||||||
item_count = 5
|
item_count = 6
|
||||||
popup/item_0/text = "New..."
|
popup/item_0/text = "New..."
|
||||||
popup/item_0/id = 0
|
popup/item_0/id = 0
|
||||||
popup/item_1/text = "Load..."
|
popup/item_1/text = "Load..."
|
||||||
@ -171,6 +178,9 @@ popup/item_3/id = 3
|
|||||||
popup/item_3/separator = true
|
popup/item_3/separator = true
|
||||||
popup/item_4/text = "Settings..."
|
popup/item_4/text = "Settings..."
|
||||||
popup/item_4/id = 4
|
popup/item_4/id = 4
|
||||||
|
popup/item_5/text = "Stats..."
|
||||||
|
popup/item_5/id = 5
|
||||||
|
|
||||||
[connection signal="item_selected" from="HBoxContainer/ToolSelect/VBoxContainer/OptionButton" to="HBoxContainer/ToolSelect" method="_on_option_button_item_selected"]
|
[connection signal="item_selected" from="HBoxContainer/ToolSelect/VBoxContainer/OptionButton" to="HBoxContainer/ToolSelect" method="_on_option_button_item_selected"]
|
||||||
[connection signal="pressed" from="HBoxContainer/ToolSelect/VBoxContainer/Control Player/Button" to="." method="_on_control_stop_button_pressed"]
|
[connection signal="pressed" from="HBoxContainer/ToolSelect/VBoxContainer/Control Player/Button" to="." method="_on_control_stop_button_pressed"]
|
||||||
|
[connection signal="pressed" from="HBoxContainer/ToolSelect/VBoxContainer/GOD/kill all" to="." method="_on_kill_all_pressed"]
|
||||||
|
@ -24,4 +24,9 @@ shape = SubResource("CircleShape2D_yt706")
|
|||||||
[node name="HealthTimer" type="Timer" parent="."]
|
[node name="HealthTimer" type="Timer" parent="."]
|
||||||
autostart = true
|
autostart = true
|
||||||
|
|
||||||
|
[node name="PickupTimer" type="Timer" parent="."]
|
||||||
|
wait_time = 0.25
|
||||||
|
one_shot = true
|
||||||
|
|
||||||
[connection signal="timeout" from="HealthTimer" to="." method="_on_health_timer_timeout"]
|
[connection signal="timeout" from="HealthTimer" to="." method="_on_health_timer_timeout"]
|
||||||
|
[connection signal="timeout" from="PickupTimer" to="." method="_on_pickup_timer_timeout"]
|
||||||
|
@ -67,4 +67,5 @@ escape={
|
|||||||
|
|
||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
|
renderer/rendering_method="gl_compatibility"
|
||||||
textures/vram_compression/import_etc2_astc=true
|
textures/vram_compression/import_etc2_astc=true
|
||||||
|
27
sim.tscn
27
sim.tscn
@ -1,4 +1,4 @@
|
|||||||
[gd_scene load_steps=8 format=3 uid="uid://bpy475nyq5ea1"]
|
[gd_scene load_steps=9 format=3 uid="uid://bpy475nyq5ea1"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://Map.gd" id="2_rolej"]
|
[ext_resource type="Script" path="res://Map.gd" id="2_rolej"]
|
||||||
[ext_resource type="Script" path="res://ScreenShots.gd" id="3_7b3mn"]
|
[ext_resource type="Script" path="res://ScreenShots.gd" id="3_7b3mn"]
|
||||||
@ -7,6 +7,7 @@
|
|||||||
[ext_resource type="PackedScene" uid="uid://i5hw0qow332a" path="res://hud.tscn" id="4_u63sk"]
|
[ext_resource type="PackedScene" uid="uid://i5hw0qow332a" path="res://hud.tscn" id="4_u63sk"]
|
||||||
[ext_resource type="Script" path="res://LoadDialog.gd" id="6_0mayr"]
|
[ext_resource type="Script" path="res://LoadDialog.gd" id="6_0mayr"]
|
||||||
[ext_resource type="Script" path="res://SaveDialog.gd" id="6_v2wl8"]
|
[ext_resource type="Script" path="res://SaveDialog.gd" id="6_v2wl8"]
|
||||||
|
[ext_resource type="Script" path="res://StatsWindow.gd" id="7_8peal"]
|
||||||
|
|
||||||
[node name="Sim" type="Node2D"]
|
[node name="Sim" type="Node2D"]
|
||||||
|
|
||||||
@ -54,11 +55,23 @@ placeholder_text = "Filename"
|
|||||||
[node name="NewDialog" type="ConfirmationDialog" parent="ScreenOverlay"]
|
[node name="NewDialog" type="ConfirmationDialog" parent="ScreenOverlay"]
|
||||||
dialog_text = "Clear current map?"
|
dialog_text = "Clear current map?"
|
||||||
|
|
||||||
|
[node name="StatsWindow" type="Window" parent="ScreenOverlay"]
|
||||||
|
title = "Stats"
|
||||||
|
position = Vector2i(15, 320)
|
||||||
|
size = Vector2i(200, 200)
|
||||||
|
script = ExtResource("7_8peal")
|
||||||
|
|
||||||
|
[node name="Timer" type="Timer" parent="ScreenOverlay/StatsWindow"]
|
||||||
|
autostart = true
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="ScreenOverlay/StatsWindow"]
|
||||||
|
offset_right = 40.0
|
||||||
|
offset_bottom = 23.0
|
||||||
|
|
||||||
[node name="SettingsWindow" type="Window" parent="ScreenOverlay"]
|
[node name="SettingsWindow" type="Window" parent="ScreenOverlay"]
|
||||||
title = "Settings"
|
title = "Settings"
|
||||||
position = Vector2i(18, 50)
|
position = Vector2i(18, 75)
|
||||||
size = Vector2i(300, 200)
|
size = Vector2i(250, 200)
|
||||||
visible = false
|
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="ScreenOverlay/SettingsWindow"]
|
[node name="VBoxContainer" type="VBoxContainer" parent="ScreenOverlay/SettingsWindow"]
|
||||||
offset_right = 123.0
|
offset_right = 123.0
|
||||||
@ -72,6 +85,10 @@ text = "Decay"
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Health"
|
text = "Health"
|
||||||
|
|
||||||
|
[node name="DropOnColl" type="CheckBox" parent="ScreenOverlay/SettingsWindow/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Drop on Coll"
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="ScreenOverlay/SettingsWindow/VBoxContainer"]
|
[node name="HBoxContainer" type="HBoxContainer" parent="ScreenOverlay/SettingsWindow/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
@ -96,6 +113,8 @@ script = ExtResource("3_7b3mn")
|
|||||||
[connection signal="about_to_popup" from="ScreenOverlay/SaveDialog" to="ScreenOverlay/SaveDialog" method="_on_about_to_popup"]
|
[connection signal="about_to_popup" from="ScreenOverlay/SaveDialog" to="ScreenOverlay/SaveDialog" method="_on_about_to_popup"]
|
||||||
[connection signal="confirmed" from="ScreenOverlay/SaveDialog" to="ScreenOverlay/SaveDialog" method="_on_confirmed"]
|
[connection signal="confirmed" from="ScreenOverlay/SaveDialog" to="ScreenOverlay/SaveDialog" method="_on_confirmed"]
|
||||||
[connection signal="confirmed" from="ScreenOverlay/NewDialog" to="Map" method="_on_new_dialog_confirmed"]
|
[connection signal="confirmed" from="ScreenOverlay/NewDialog" to="Map" method="_on_new_dialog_confirmed"]
|
||||||
|
[connection signal="close_requested" from="ScreenOverlay/StatsWindow" to="ScreenOverlay/StatsWindow" method="_on_close_requested"]
|
||||||
|
[connection signal="timeout" from="ScreenOverlay/StatsWindow/Timer" to="ScreenOverlay/StatsWindow" method="_on_timer_timeout"]
|
||||||
[connection signal="close_requested" from="ScreenOverlay/SettingsWindow" to="ScreenOverlay/HUD" method="_on_settings_window_close_requested"]
|
[connection signal="close_requested" from="ScreenOverlay/SettingsWindow" to="ScreenOverlay/HUD" method="_on_settings_window_close_requested"]
|
||||||
[connection signal="toggled" from="ScreenOverlay/SettingsWindow/VBoxContainer/HBoxContainer/Screenshots" to="ScreenShots" method="_on_screenshots_toggled"]
|
[connection signal="toggled" from="ScreenOverlay/SettingsWindow/VBoxContainer/HBoxContainer/Screenshots" to="ScreenShots" method="_on_screenshots_toggled"]
|
||||||
[connection signal="text_changed" from="ScreenOverlay/SettingsWindow/VBoxContainer/HBoxContainer/Interval" to="ScreenShots" method="_on_interval_text_changed"]
|
[connection signal="text_changed" from="ScreenOverlay/SettingsWindow/VBoxContainer/HBoxContainer/Interval" to="ScreenShots" method="_on_interval_text_changed"]
|
||||||
|
Loading…
Reference in New Issue
Block a user