Added rpg system for combat
added lots of loot to find added level up system
This commit is contained in:
76
src/scenes/attack_arrow.tscn
Normal file
76
src/scenes/attack_arrow.tscn
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
[gd_scene format=3 uid="uid://cvjj4wo2agd2k"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://ba772auc1t65n" path="res://assets/gfx/arrow.png" id="1_bey2v"]
|
||||||
|
[ext_resource type="Script" uid="uid://dqbctups3eri6" path="res://scripts/attack_arrow.gd" id="1_if6eb"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://hmci4kgvbqib" path="res://assets/audio/sfx/weapons/bow/arrow_fire_swosh.wav" id="3_o8cb2"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://b140nlsak4ub7" path="res://assets/audio/sfx/weapons/bow/arrow-hit-brick-wall-01.mp3" id="4_8l43l"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://dc7nt8gnjt5u5" path="res://assets/audio/sfx/weapons/melee_attack_12.wav.mp3" id="4_ol4b0"]
|
||||||
|
|
||||||
|
[sub_resource type="Gradient" id="Gradient_yp18a"]
|
||||||
|
colors = PackedColorArray(0, 0, 0, 1, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
[sub_resource type="GradientTexture2D" id="GradientTexture2D_gpny7"]
|
||||||
|
gradient = SubResource("Gradient_yp18a")
|
||||||
|
fill = 1
|
||||||
|
fill_from = Vector2(0.504587, 0.504587)
|
||||||
|
fill_to = Vector2(0.848624, 0.784404)
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_b6ybh"]
|
||||||
|
size = Vector2(2, 6)
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_wuwd8"]
|
||||||
|
size = Vector2(2, 8)
|
||||||
|
|
||||||
|
[node name="Arrow" type="CharacterBody2D" unique_id=1519950765]
|
||||||
|
z_index = 10
|
||||||
|
y_sort_enabled = true
|
||||||
|
collision_layer = 0
|
||||||
|
collision_mask = 0
|
||||||
|
motion_mode = 1
|
||||||
|
script = ExtResource("1_if6eb")
|
||||||
|
|
||||||
|
[node name="Shadow" type="Sprite2D" parent="." unique_id=1423726915]
|
||||||
|
z_index = 1
|
||||||
|
z_as_relative = false
|
||||||
|
position = Vector2(-2.98023e-08, 0)
|
||||||
|
scale = Vector2(0.09375, 0.0820313)
|
||||||
|
texture = SubResource("GradientTexture2D_gpny7")
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=1110211076]
|
||||||
|
texture = ExtResource("1_bey2v")
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1017444254]
|
||||||
|
shape = SubResource("RectangleShape2D_b6ybh")
|
||||||
|
|
||||||
|
[node name="ArrowArea" type="Area2D" parent="." unique_id=1075536929]
|
||||||
|
collision_layer = 0
|
||||||
|
collision_mask = 75
|
||||||
|
priority = 1
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="ArrowArea" unique_id=926133347]
|
||||||
|
physics_interpolation_mode = 1
|
||||||
|
position = Vector2(0, 1)
|
||||||
|
shape = SubResource("RectangleShape2D_wuwd8")
|
||||||
|
debug_color = Color(0.7, 0, 0.195726, 0.42)
|
||||||
|
|
||||||
|
[node name="SfxArrowFire" type="AudioStreamPlayer2D" parent="." unique_id=1413495156]
|
||||||
|
stream = ExtResource("3_o8cb2")
|
||||||
|
pitch_scale = 1.61
|
||||||
|
max_polyphony = 4
|
||||||
|
|
||||||
|
[node name="SfxImpactWall" type="AudioStreamPlayer2D" parent="." unique_id=1132726967]
|
||||||
|
stream = ExtResource("4_8l43l")
|
||||||
|
volume_db = -4.0
|
||||||
|
pitch_scale = 1.29
|
||||||
|
attenuation = 3.4822
|
||||||
|
max_polyphony = 4
|
||||||
|
panning_strength = 1.3
|
||||||
|
|
||||||
|
[node name="SfxImpactSound" type="AudioStreamPlayer2D" parent="." unique_id=463883211]
|
||||||
|
stream = ExtResource("4_ol4b0")
|
||||||
|
volume_db = -4.685
|
||||||
|
pitch_scale = 1.47
|
||||||
|
max_polyphony = 4
|
||||||
|
|
||||||
|
[connection signal="area_entered" from="ArrowArea" to="." method="_on_arrow_area_area_entered"]
|
||||||
|
[connection signal="body_entered" from="ArrowArea" to="." method="_on_arrow_area_body_entered"]
|
||||||
86
src/scenes/attack_axe_swing.tscn
Normal file
86
src/scenes/attack_axe_swing.tscn
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
[gd_scene format=3 uid="uid://tcobiw1iirdw"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://bqrtsr3mjvv3j" path="res://scripts/attack_axe_swing.gd" id="1_xo3v0"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bwxpic53sluul" path="res://assets/gfx/sword_slash.png" id="2_lwt2c"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://4vulahdsj4i2" path="res://assets/audio/sfx/swoosh/throw_01.wav.mp3" id="3_v2p0x"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://uerx5rib87a6" path="res://assets/audio/sfx/weapons/bone_hit_wall_01.wav.mp3" id="4_ul7bj"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://dc7nt8gnjt5u5" path="res://assets/audio/sfx/weapons/melee_attack_12.wav.mp3" id="5_whqew"]
|
||||||
|
|
||||||
|
[sub_resource type="Animation" id="Animation_6bxep"]
|
||||||
|
length = 0.001
|
||||||
|
tracks/0/type = "value"
|
||||||
|
tracks/0/imported = false
|
||||||
|
tracks/0/enabled = true
|
||||||
|
tracks/0/path = NodePath("Sprite2D:modulate")
|
||||||
|
tracks/0/interp = 1
|
||||||
|
tracks/0/loop_wrap = true
|
||||||
|
tracks/0/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 0,
|
||||||
|
"values": [Color(1, 1, 1, 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="Animation" id="Animation_p46b1"]
|
||||||
|
resource_name = "slash_anim"
|
||||||
|
length = 0.8
|
||||||
|
tracks/0/type = "value"
|
||||||
|
tracks/0/imported = false
|
||||||
|
tracks/0/enabled = true
|
||||||
|
tracks/0/path = NodePath("Sprite2D:modulate")
|
||||||
|
tracks/0/interp = 1
|
||||||
|
tracks/0/loop_wrap = true
|
||||||
|
tracks/0/keys = {
|
||||||
|
"times": PackedFloat32Array(0, 0.533333, 0.733333),
|
||||||
|
"transitions": PackedFloat32Array(1, 1, 1),
|
||||||
|
"update": 0,
|
||||||
|
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="AnimationLibrary" id="AnimationLibrary_hj6i2"]
|
||||||
|
_data = {
|
||||||
|
&"RESET": SubResource("Animation_6bxep"),
|
||||||
|
&"slash_anim": SubResource("Animation_p46b1")
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_3jdng"]
|
||||||
|
size = Vector2(12, 12)
|
||||||
|
|
||||||
|
[node name="AxeSwing" type="Node2D" unique_id=1568208090]
|
||||||
|
z_index = 10
|
||||||
|
y_sort_enabled = true
|
||||||
|
script = ExtResource("1_xo3v0")
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=461038063]
|
||||||
|
texture = ExtResource("2_lwt2c")
|
||||||
|
|
||||||
|
[node name="AnimationPlayer" type="AnimationPlayer" parent="." unique_id=691292922]
|
||||||
|
libraries/ = SubResource("AnimationLibrary_hj6i2")
|
||||||
|
|
||||||
|
[node name="AttackSwosh" type="AudioStreamPlayer2D" parent="." unique_id=515041905]
|
||||||
|
stream = ExtResource("3_v2p0x")
|
||||||
|
pitch_scale = 0.74
|
||||||
|
autoplay = true
|
||||||
|
|
||||||
|
[node name="DamageArea" type="Area2D" parent="." unique_id=985585639]
|
||||||
|
collision_layer = 0
|
||||||
|
collision_mask = 75
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="DamageArea" unique_id=805714782]
|
||||||
|
shape = SubResource("RectangleShape2D_3jdng")
|
||||||
|
debug_color = Color(0.7, 0, 0.18232, 0.42)
|
||||||
|
|
||||||
|
[node name="MeleeImpactWall" type="AudioStreamPlayer2D" parent="." unique_id=1016195072]
|
||||||
|
stream = ExtResource("4_ul7bj")
|
||||||
|
volume_db = -4.0
|
||||||
|
pitch_scale = 1.3
|
||||||
|
max_polyphony = 4
|
||||||
|
|
||||||
|
[node name="MeleeImpact" type="AudioStreamPlayer2D" parent="." unique_id=1548143473]
|
||||||
|
stream = ExtResource("5_whqew")
|
||||||
|
volume_db = -5.622
|
||||||
|
pitch_scale = 1.43
|
||||||
|
max_polyphony = 4
|
||||||
|
|
||||||
|
[connection signal="area_entered" from="DamageArea" to="." method="_on_damage_area_area_entered"]
|
||||||
|
[connection signal="body_entered" from="DamageArea" to="." method="_on_damage_area_body_entered"]
|
||||||
86
src/scenes/attack_spear_thrust.tscn
Normal file
86
src/scenes/attack_spear_thrust.tscn
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
[gd_scene format=3 uid="uid://b3my31y2ljai1"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://ddprn0wrasavr" path="res://scripts/attack_spear_thrust.gd" id="1_psi1x"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bwxpic53sluul" path="res://assets/gfx/sword_slash.png" id="2_rh1o6"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://4vulahdsj4i2" path="res://assets/audio/sfx/swoosh/throw_01.wav.mp3" id="3_j7ui3"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://uerx5rib87a6" path="res://assets/audio/sfx/weapons/bone_hit_wall_01.wav.mp3" id="4_cijfq"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://dc7nt8gnjt5u5" path="res://assets/audio/sfx/weapons/melee_attack_12.wav.mp3" id="5_h4gub"]
|
||||||
|
|
||||||
|
[sub_resource type="Animation" id="Animation_6bxep"]
|
||||||
|
length = 0.001
|
||||||
|
tracks/0/type = "value"
|
||||||
|
tracks/0/imported = false
|
||||||
|
tracks/0/enabled = true
|
||||||
|
tracks/0/path = NodePath("Sprite2D:modulate")
|
||||||
|
tracks/0/interp = 1
|
||||||
|
tracks/0/loop_wrap = true
|
||||||
|
tracks/0/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 0,
|
||||||
|
"values": [Color(1, 1, 1, 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="Animation" id="Animation_p46b1"]
|
||||||
|
resource_name = "slash_anim"
|
||||||
|
length = 0.8
|
||||||
|
tracks/0/type = "value"
|
||||||
|
tracks/0/imported = false
|
||||||
|
tracks/0/enabled = true
|
||||||
|
tracks/0/path = NodePath("Sprite2D:modulate")
|
||||||
|
tracks/0/interp = 1
|
||||||
|
tracks/0/loop_wrap = true
|
||||||
|
tracks/0/keys = {
|
||||||
|
"times": PackedFloat32Array(0, 0.533333, 0.733333),
|
||||||
|
"transitions": PackedFloat32Array(1, 1, 1),
|
||||||
|
"update": 0,
|
||||||
|
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="AnimationLibrary" id="AnimationLibrary_hj6i2"]
|
||||||
|
_data = {
|
||||||
|
&"RESET": SubResource("Animation_6bxep"),
|
||||||
|
&"slash_anim": SubResource("Animation_p46b1")
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_3jdng"]
|
||||||
|
size = Vector2(12, 12)
|
||||||
|
|
||||||
|
[node name="SpearThrust" type="Node2D" unique_id=785354531]
|
||||||
|
z_index = 10
|
||||||
|
y_sort_enabled = true
|
||||||
|
script = ExtResource("1_psi1x")
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=1051719548]
|
||||||
|
texture = ExtResource("2_rh1o6")
|
||||||
|
|
||||||
|
[node name="AnimationPlayer" type="AnimationPlayer" parent="." unique_id=907474922]
|
||||||
|
libraries/ = SubResource("AnimationLibrary_hj6i2")
|
||||||
|
|
||||||
|
[node name="AttackSwosh" type="AudioStreamPlayer2D" parent="." unique_id=1397921535]
|
||||||
|
stream = ExtResource("3_j7ui3")
|
||||||
|
pitch_scale = 1.4
|
||||||
|
autoplay = true
|
||||||
|
|
||||||
|
[node name="DamageArea" type="Area2D" parent="." unique_id=1687133888]
|
||||||
|
collision_layer = 0
|
||||||
|
collision_mask = 75
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="DamageArea" unique_id=376828642]
|
||||||
|
shape = SubResource("RectangleShape2D_3jdng")
|
||||||
|
debug_color = Color(0.7, 0, 0.18232, 0.42)
|
||||||
|
|
||||||
|
[node name="MeleeImpactWall" type="AudioStreamPlayer2D" parent="." unique_id=651866255]
|
||||||
|
stream = ExtResource("4_cijfq")
|
||||||
|
volume_db = -4.0
|
||||||
|
pitch_scale = 1.3
|
||||||
|
max_polyphony = 4
|
||||||
|
|
||||||
|
[node name="MeleeImpact" type="AudioStreamPlayer2D" parent="." unique_id=35588340]
|
||||||
|
stream = ExtResource("5_h4gub")
|
||||||
|
volume_db = -5.622
|
||||||
|
pitch_scale = 1.43
|
||||||
|
max_polyphony = 4
|
||||||
|
|
||||||
|
[connection signal="area_entered" from="DamageArea" to="." method="_on_damage_area_area_entered"]
|
||||||
|
[connection signal="body_entered" from="DamageArea" to="." method="_on_damage_area_body_entered"]
|
||||||
169
src/scripts/attack_arrow.gd
Normal file
169
src/scripts/attack_arrow.gd
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
extends CharacterBody2D
|
||||||
|
|
||||||
|
var speed = 300
|
||||||
|
var direction = Vector2.ZERO
|
||||||
|
var stick_duration = 3.0 # How long the arrow stays stuck to walls
|
||||||
|
var is_stuck = false
|
||||||
|
var stick_timer = 0.0
|
||||||
|
|
||||||
|
var initiated_by: Node2D = null
|
||||||
|
|
||||||
|
@onready var arrow_area = $ArrowArea # Assuming you have an Area2D node named ArrowArea
|
||||||
|
@onready var shadow = $Shadow # Assuming you have a Shadow node under the CharacterBody2D
|
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
func _ready() -> void:
|
||||||
|
arrow_area.set_deferred("monitoring", true)
|
||||||
|
#arrow_area.body_entered.connect(_on_body_entered)
|
||||||
|
$SfxArrowFire.play()
|
||||||
|
call_deferred("_initialize_arrow")
|
||||||
|
|
||||||
|
func _initialize_arrow() -> void:
|
||||||
|
var angle = direction.angle()
|
||||||
|
self.rotation = angle - PI / 2 # Adjust for sprite orientation
|
||||||
|
# Set initial rotation based on direction
|
||||||
|
velocity = direction * speed # Set initial velocity to move the arrow
|
||||||
|
|
||||||
|
# Calculate the offset for the shadow position, which should be below the arrow
|
||||||
|
var shadow_offset = Vector2(0, 4) # Adjust the 16 to how far you want the shadow from the arrow (this is just an example)
|
||||||
|
|
||||||
|
# Apply the rotation of the arrow to the shadow offset
|
||||||
|
shadow.position += shadow_offset.rotated(-self.rotation)
|
||||||
|
if abs(direction.x) == 1:
|
||||||
|
shadow.scale.x = 0.26
|
||||||
|
shadow.scale.y = 0.062
|
||||||
|
|
||||||
|
elif abs(direction.x) > 0:
|
||||||
|
shadow.scale.x = 0.18
|
||||||
|
shadow.scale.y = 0.08
|
||||||
|
else:
|
||||||
|
shadow.scale.x = 0.1
|
||||||
|
shadow.scale.y = 0.1
|
||||||
|
|
||||||
|
# Calculate the shadow's scale based on the velocity or direction of the arrow
|
||||||
|
#var velocity_magnitude = velocity.length()
|
||||||
|
|
||||||
|
# Scale more in the horizontal direction if moving diagonally or horizontally
|
||||||
|
#var scale_factor = 0.28 + abs(velocity.x) / velocity_magnitude # Adjust the factor to your preference
|
||||||
|
|
||||||
|
# Apply the scaling to the shadow
|
||||||
|
shadow.rotation = -(angle - PI / 2)
|
||||||
|
|
||||||
|
func shoot(shoot_direction: Vector2, start_pos: Vector2) -> void:
|
||||||
|
direction = shoot_direction.normalized()
|
||||||
|
global_position = start_pos
|
||||||
|
#position = start_pos
|
||||||
|
|
||||||
|
# Called every frame. 'delta' is the e lapsed time since the previous frame.
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
if is_stuck:
|
||||||
|
# Handle fade out here if it's stuck
|
||||||
|
stick_timer += delta
|
||||||
|
if stick_timer >= stick_duration:
|
||||||
|
# Start fading out after it sticks
|
||||||
|
modulate.a = max(0, 1 - (stick_timer - stick_duration) / 1.0) # Fade out over 1 second
|
||||||
|
if stick_timer >= stick_duration + 1.0: # Extra second for fade out
|
||||||
|
queue_free() # Remove the arrow after fade out
|
||||||
|
move_and_slide()
|
||||||
|
|
||||||
|
func _physics_process(_delta: float) -> void:
|
||||||
|
# If the arrow is stuck, stop it from moving
|
||||||
|
if is_stuck:
|
||||||
|
velocity = Vector2.ZERO # Stop movement
|
||||||
|
# Optional: disable further physics interaction by setting linear_velocity
|
||||||
|
# move_and_slide(Vector2.ZERO) # You can also use this to stop the character
|
||||||
|
|
||||||
|
func play_impact():
|
||||||
|
$SfxImpactSound.play()
|
||||||
|
|
||||||
|
# Called when the arrow hits a wall or another object
|
||||||
|
func _on_body_entered(body: Node) -> void:
|
||||||
|
if not is_stuck:
|
||||||
|
if body == initiated_by:
|
||||||
|
return
|
||||||
|
if body is CharacterBody2D and body.stats.is_invulnerable == false and body.stats.hp > 0: # hit an enemy
|
||||||
|
#if body is CharacterBody2D and body.collision_layer & (1 << 8) and body.taking_damage_timer <= 0 and body.stats.hp > 0: # Check if body is enemy (layer 9)
|
||||||
|
|
||||||
|
# Stop the arrow
|
||||||
|
velocity = Vector2.ZERO
|
||||||
|
is_stuck = true
|
||||||
|
stick_timer = 0.0
|
||||||
|
arrow_area.set_deferred("monitoring", false)
|
||||||
|
# Calculate the collision point - move arrow slightly back from its direction
|
||||||
|
var collision_normal = -direction # Opposite of arrow's direction
|
||||||
|
var offset_distance = 8 # Adjust this value based on your collision shape sizes
|
||||||
|
var stick_position = global_position + (collision_normal * offset_distance)
|
||||||
|
|
||||||
|
# Make arrow a child of the enemy to stick to it
|
||||||
|
var global_rot = global_rotation
|
||||||
|
get_parent().call_deferred("remove_child", self)
|
||||||
|
body.call_deferred("add_child", self)
|
||||||
|
self.set_deferred("global_position", stick_position)
|
||||||
|
self.set_deferred("global_rotation", global_rot)
|
||||||
|
#global_rotation = global_rot
|
||||||
|
body.call_deferred("take_damage", self, initiated_by)
|
||||||
|
self.call_deferred("play_impact") # need to play the sound on the next frame, because else it cuts it.
|
||||||
|
|
||||||
|
else:
|
||||||
|
$SfxImpactWall.play()
|
||||||
|
# Stop the arrow
|
||||||
|
velocity = Vector2.ZERO
|
||||||
|
is_stuck = true
|
||||||
|
stick_timer = 0.0
|
||||||
|
arrow_area.set_deferred("monitoring", false)
|
||||||
|
# You can optionally stick the arrow at the collision point if you want:
|
||||||
|
# position = body.position # Uncomment this if you want to "stick" it at the collision point
|
||||||
|
# Additional logic for handling interaction with walls or other objects
|
||||||
|
|
||||||
|
|
||||||
|
func _on_arrow_area_area_entered(area: Area2D) -> void:
|
||||||
|
if not is_stuck:
|
||||||
|
if area.get_parent() == initiated_by:
|
||||||
|
return
|
||||||
|
if area.get_parent() is CharacterBody2D and area.get_parent().stats.is_invulnerable == false and area.get_parent().stats.hp > 0: # hit an enemy
|
||||||
|
#if body is CharacterBody2D and body.collision_layer & (1 << 8) and body.taking_damage_timer <= 0 and body.stats.hp > 0: # Check if body is enemy (layer 9)
|
||||||
|
|
||||||
|
# Stop the arrow
|
||||||
|
velocity = Vector2.ZERO
|
||||||
|
is_stuck = true
|
||||||
|
stick_timer = 0.0
|
||||||
|
arrow_area.set_deferred("monitoring", false)
|
||||||
|
# Calculate the collision point - move arrow slightly back from its direction
|
||||||
|
var collision_normal = -direction # Opposite of arrow's direction
|
||||||
|
var offset_distance = 8 # Adjust this value based on your collision shape sizes
|
||||||
|
var stick_position = global_position + (collision_normal * offset_distance)
|
||||||
|
|
||||||
|
# Make arrow a child of the enemy to stick to it
|
||||||
|
var global_rot = global_rotation
|
||||||
|
get_parent().call_deferred("remove_child", self)
|
||||||
|
area.get_parent().call_deferred("add_child", self)
|
||||||
|
self.set_deferred("global_position", stick_position)
|
||||||
|
self.set_deferred("global_rotation", global_rot)
|
||||||
|
#global_rotation = global_rot
|
||||||
|
area.get_parent().call_deferred("take_damage", self, initiated_by)
|
||||||
|
self.call_deferred("play_impact") # need to play the sound on the next frame, because else it cuts it.
|
||||||
|
|
||||||
|
else:
|
||||||
|
$SfxImpactWall.play()
|
||||||
|
# Stop the arrow
|
||||||
|
velocity = Vector2.ZERO
|
||||||
|
is_stuck = true
|
||||||
|
stick_timer = 0.0
|
||||||
|
arrow_area.set_deferred("monitoring", false)
|
||||||
|
# You can optionally stick the arrow at the collision point if you want:
|
||||||
|
# position = body.position # Uncomment this if you want to "stick" it at the collision point
|
||||||
|
# Additional logic for handling interaction with walls or other objects
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
|
||||||
|
func _on_arrow_area_body_entered(body: Node2D) -> void:
|
||||||
|
if not is_stuck:
|
||||||
|
if body == initiated_by:
|
||||||
|
return
|
||||||
|
$SfxImpactWall.play()
|
||||||
|
# Stop the arrow
|
||||||
|
velocity = Vector2.ZERO
|
||||||
|
is_stuck = true
|
||||||
|
stick_timer = 0.0
|
||||||
|
arrow_area.set_deferred("monitoring", false)
|
||||||
|
pass # Replace with function body.
|
||||||
1
src/scripts/attack_arrow.gd.uid
Normal file
1
src/scripts/attack_arrow.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dqbctups3eri6
|
||||||
64
src/scripts/attack_axe_swing.gd
Normal file
64
src/scripts/attack_axe_swing.gd
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
extends Node2D
|
||||||
|
|
||||||
|
var direction := Vector2.ZERO # Default direction
|
||||||
|
var fade_delay := 0.14 # When to start fading (mid-move)
|
||||||
|
var move_duration := 0.2 # Slash exists for 0.3 seconds
|
||||||
|
var fade_duration := 0.06 # Time to fade out
|
||||||
|
var stretch_amount := Vector2(1, 1.4) # How much to stretch the sprite
|
||||||
|
var slash_amount = 8
|
||||||
|
var initiated_by: Node2D = null
|
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
func _ready() -> void:
|
||||||
|
call_deferred("_initialize_swing")
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
func _initialize_swing():
|
||||||
|
var tween = create_tween()
|
||||||
|
var move_target = global_position + (direction.normalized() * slash_amount) # Moves in given direction
|
||||||
|
tween.set_trans(Tween.TRANS_CUBIC) # Smooth acceleration & deceleration
|
||||||
|
tween.set_ease(Tween.EASE_OUT) # Fast start, then slows down
|
||||||
|
tween.tween_property(self, "global_position", move_target, move_duration)
|
||||||
|
'
|
||||||
|
# Create stretch tween (grow and shrink slightly)
|
||||||
|
var stretch_tween = create_tween()
|
||||||
|
stretch_tween.set_trans(Tween.TRANS_CUBIC)
|
||||||
|
stretch_tween.set_ease(Tween.EASE_OUT)
|
||||||
|
stretch_tween.tween_property($Sprite2D, "scale", Vector2.ONE, move_duration / 2) # start normal
|
||||||
|
stretch_tween.tween_property($Sprite2D, "scale", stretch_amount, move_duration / 2)
|
||||||
|
'
|
||||||
|
|
||||||
|
# Wait until mid-move to start fade
|
||||||
|
await get_tree().create_timer(fade_delay).timeout
|
||||||
|
|
||||||
|
# Start fade-out effect
|
||||||
|
var fade_tween = create_tween()
|
||||||
|
fade_tween.tween_property($Sprite2D, "modulate:a", 0.0, fade_duration) # Fade to transparent
|
||||||
|
await fade_tween.finished
|
||||||
|
queue_free()
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _on_damage_area_body_entered(body: Node2D) -> void:
|
||||||
|
if body.get_parent() == initiated_by or body == initiated_by:
|
||||||
|
return
|
||||||
|
if body.get_parent() is CharacterBody2D and body.get_parent().stats.is_invulnerable == false and body.get_parent().stats.hp > 0: # hit an enemy
|
||||||
|
$MeleeImpact.play()
|
||||||
|
body.take_damage(self, initiated_by)
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
$MeleeImpactWall.play()
|
||||||
|
pass
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
|
||||||
|
func _on_damage_area_area_entered(body: Area2D) -> void:
|
||||||
|
if body.get_parent() == initiated_by:
|
||||||
|
return
|
||||||
|
if body.get_parent() is CharacterBody2D and body.get_parent().stats.is_invulnerable == false and body.get_parent().stats.hp > 0: # hit an enemy
|
||||||
|
$MeleeImpact.play()
|
||||||
|
body.get_parent().take_damage(self, initiated_by)
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
$MeleeImpactWall.play()
|
||||||
|
pass
|
||||||
|
pass # Replace with function body.
|
||||||
1
src/scripts/attack_axe_swing.gd.uid
Normal file
1
src/scripts/attack_axe_swing.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bqrtsr3mjvv3j
|
||||||
64
src/scripts/attack_spear_thrust.gd
Normal file
64
src/scripts/attack_spear_thrust.gd
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
extends Node2D
|
||||||
|
|
||||||
|
var direction := Vector2.ZERO # Default direction
|
||||||
|
var fade_delay := 0.14 # When to start fading (mid-move)
|
||||||
|
var move_duration := 0.2 # Slash exists for 0.3 seconds
|
||||||
|
var fade_duration := 0.06 # Time to fade out
|
||||||
|
var stretch_amount := Vector2(1, 1.4) # How much to stretch the sprite
|
||||||
|
var slash_amount = 8
|
||||||
|
var initiated_by: Node2D = null
|
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
func _ready() -> void:
|
||||||
|
call_deferred("_initialize_thrust")
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
func _initialize_thrust():
|
||||||
|
var tween = create_tween()
|
||||||
|
var move_target = global_position + (direction.normalized() * slash_amount) # Moves in given direction
|
||||||
|
tween.set_trans(Tween.TRANS_CUBIC) # Smooth acceleration & deceleration
|
||||||
|
tween.set_ease(Tween.EASE_OUT) # Fast start, then slows down
|
||||||
|
tween.tween_property(self, "global_position", move_target, move_duration)
|
||||||
|
'
|
||||||
|
# Create stretch tween (grow and shrink slightly)
|
||||||
|
var stretch_tween = create_tween()
|
||||||
|
stretch_tween.set_trans(Tween.TRANS_CUBIC)
|
||||||
|
stretch_tween.set_ease(Tween.EASE_OUT)
|
||||||
|
stretch_tween.tween_property($Sprite2D, "scale", Vector2.ONE, move_duration / 2) # start normal
|
||||||
|
stretch_tween.tween_property($Sprite2D, "scale", stretch_amount, move_duration / 2)
|
||||||
|
'
|
||||||
|
|
||||||
|
# Wait until mid-move to start fade
|
||||||
|
await get_tree().create_timer(fade_delay).timeout
|
||||||
|
|
||||||
|
# Start fade-out effect
|
||||||
|
var fade_tween = create_tween()
|
||||||
|
fade_tween.tween_property($Sprite2D, "modulate:a", 0.0, fade_duration) # Fade to transparent
|
||||||
|
await fade_tween.finished
|
||||||
|
queue_free()
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _on_damage_area_body_entered(body: Node2D) -> void:
|
||||||
|
if body.get_parent() == initiated_by or body == initiated_by:
|
||||||
|
return
|
||||||
|
if body.get_parent() is CharacterBody2D and body.get_parent().stats.is_invulnerable == false and body.get_parent().stats.hp > 0: # hit an enemy
|
||||||
|
$MeleeImpact.play()
|
||||||
|
body.take_damage(self, initiated_by)
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
$MeleeImpactWall.play()
|
||||||
|
pass
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
|
||||||
|
func _on_damage_area_area_entered(body: Area2D) -> void:
|
||||||
|
if body.get_parent() == initiated_by:
|
||||||
|
return
|
||||||
|
if body.get_parent() is CharacterBody2D and body.get_parent().stats.is_invulnerable == false and body.get_parent().stats.hp > 0: # hit an enemy
|
||||||
|
$MeleeImpact.play()
|
||||||
|
body.get_parent().take_damage(self, initiated_by)
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
$MeleeImpactWall.play()
|
||||||
|
pass
|
||||||
|
pass # Replace with function body.
|
||||||
1
src/scripts/attack_spear_thrust.gd.uid
Normal file
1
src/scripts/attack_spear_thrust.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ddprn0wrasavr
|
||||||
@@ -1266,7 +1266,7 @@ func _place_enemies_in_room(room: Dictionary, grid: Array, map_size: Vector2i, r
|
|||||||
|
|
||||||
# Randomize stats (similar to player but weaker)
|
# Randomize stats (similar to player but weaker)
|
||||||
# Base stats vary by enemy type
|
# Base stats vary by enemy type
|
||||||
var max_health = rng.randf_range(30.0, 60.0)
|
var max_health = rng.randf_range(18.0, 35.0) # Reduced from 30.0-60.0 for better balance
|
||||||
var move_speed: float
|
var move_speed: float
|
||||||
var damage = rng.randf_range(5.0, 15.0)
|
var damage = rng.randf_range(5.0, 15.0)
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ extends CharacterBody2D
|
|||||||
|
|
||||||
@export var max_health: float = 50.0
|
@export var max_health: float = 50.0
|
||||||
@export var move_speed: float = 80.0
|
@export var move_speed: float = 80.0
|
||||||
@export var damage: float = 10.0
|
@export var damage: float = 10.0 # Legacy - use character_stats.damage instead
|
||||||
@export var attack_cooldown: float = 1.0
|
@export var attack_cooldown: float = 1.0
|
||||||
|
@export var exp_reward: float = 10.0 # EXP granted when this enemy is defeated
|
||||||
|
|
||||||
var current_health: float = 50.0
|
var current_health: float = 50.0
|
||||||
|
var character_stats: CharacterStats # RPG stats system (same as players)
|
||||||
var is_dead: bool = false
|
var is_dead: bool = false
|
||||||
var target_player: Node = null
|
var target_player: Node = null
|
||||||
var attack_timer: float = 0.0
|
var attack_timer: float = 0.0
|
||||||
@@ -35,6 +37,9 @@ var anim_speed: float = 0.15 # Seconds per frame
|
|||||||
@onready var collision_shape = get_node_or_null("CollisionShape2D")
|
@onready var collision_shape = get_node_or_null("CollisionShape2D")
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
# Initialize CharacterStats for RPG system
|
||||||
|
_initialize_character_stats()
|
||||||
|
|
||||||
current_health = max_health
|
current_health = max_health
|
||||||
add_to_group("enemy")
|
add_to_group("enemy")
|
||||||
|
|
||||||
@@ -51,6 +56,27 @@ func _ready():
|
|||||||
# Walls are on layer 7 (bit 6 = 64), not layer 4!
|
# Walls are on layer 7 (bit 6 = 64), not layer 4!
|
||||||
collision_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64)
|
collision_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64)
|
||||||
|
|
||||||
|
# Initialize CharacterStats for this enemy
|
||||||
|
# Override in subclasses to set specific baseStats
|
||||||
|
func _initialize_character_stats():
|
||||||
|
character_stats = CharacterStats.new()
|
||||||
|
character_stats.character_type = "enemy"
|
||||||
|
character_stats.character_name = name
|
||||||
|
# Default stats - override in subclasses
|
||||||
|
character_stats.baseStats.str = 10
|
||||||
|
character_stats.baseStats.dex = 10
|
||||||
|
character_stats.baseStats.int = 10
|
||||||
|
character_stats.baseStats.end = 10
|
||||||
|
character_stats.baseStats.wis = 10
|
||||||
|
character_stats.baseStats.cha = 10
|
||||||
|
character_stats.baseStats.lck = 10
|
||||||
|
# Initialize hp and mp
|
||||||
|
character_stats.hp = character_stats.maxhp
|
||||||
|
character_stats.mp = character_stats.maxmp
|
||||||
|
# Sync max_health and current_health from character_stats (for backwards compatibility)
|
||||||
|
max_health = character_stats.maxhp
|
||||||
|
current_health = character_stats.hp
|
||||||
|
|
||||||
func _physics_process(delta):
|
func _physics_process(delta):
|
||||||
if is_dead:
|
if is_dead:
|
||||||
# Even when dead, allow knockback to continue briefly
|
# Even when dead, allow knockback to continue briefly
|
||||||
@@ -259,7 +285,7 @@ func _find_nearest_player_to_position(pos: Vector2, max_range: float = 100.0) ->
|
|||||||
|
|
||||||
return nearest
|
return nearest
|
||||||
|
|
||||||
func take_damage(amount: float, from_position: Vector2):
|
func take_damage(amount: float, from_position: Vector2, is_critical: bool = false):
|
||||||
# Only process damage on server/authority
|
# Only process damage on server/authority
|
||||||
if not is_multiplayer_authority():
|
if not is_multiplayer_authority():
|
||||||
return
|
return
|
||||||
@@ -273,8 +299,41 @@ func take_damage(amount: float, from_position: Vector2):
|
|||||||
if nearest_player:
|
if nearest_player:
|
||||||
killer_player = nearest_player # Update killer to the most recent attacker
|
killer_player = nearest_player # Update killer to the most recent attacker
|
||||||
|
|
||||||
current_health -= amount
|
# Check for dodge chance (based on DEX) - same as players
|
||||||
print(name, " took ", amount, " damage! Health: ", current_health)
|
var _was_dodged = false
|
||||||
|
if character_stats:
|
||||||
|
var dodge_roll = randf()
|
||||||
|
var dodge_chance = character_stats.dodge_chance
|
||||||
|
if dodge_roll < dodge_chance:
|
||||||
|
_was_dodged = true
|
||||||
|
print(name, " DODGED the attack! (DEX: ", character_stats.baseStats.dex + character_stats.get_pass("dex"), ", dodge chance: ", dodge_chance * 100.0, "%)")
|
||||||
|
# Show "DODGED" text
|
||||||
|
_show_damage_number(0.0, from_position, false, false, true) # is_dodged = true
|
||||||
|
# Sync dodge visual to clients
|
||||||
|
if multiplayer.has_multiplayer_peer() and is_inside_tree():
|
||||||
|
var enemy_name = name
|
||||||
|
var enemy_index = get_meta("enemy_index") if has_meta("enemy_index") else -1
|
||||||
|
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||||
|
if game_world and game_world.has_method("_sync_enemy_damage_visual"):
|
||||||
|
game_world._sync_enemy_damage_visual.rpc(enemy_name, enemy_index)
|
||||||
|
return # No damage taken, exit early
|
||||||
|
|
||||||
|
# If not dodged, apply damage with DEF reduction
|
||||||
|
var actual_damage = amount
|
||||||
|
if character_stats:
|
||||||
|
# Calculate damage after DEF reduction (critical hits pierce 80% of DEF)
|
||||||
|
actual_damage = character_stats.calculate_damage(amount, false, is_critical) # false = not magical, is_critical = crit pierce
|
||||||
|
character_stats.modify_health(-actual_damage)
|
||||||
|
current_health = character_stats.hp
|
||||||
|
if character_stats.hp <= 0:
|
||||||
|
character_stats.no_health.emit()
|
||||||
|
var effective_def = character_stats.defense * (0.2 if is_critical else 1.0)
|
||||||
|
print(name, " took ", actual_damage, " damage (", amount, " base - ", effective_def, " DEF = ", actual_damage, ")! Health: ", current_health, "/", character_stats.maxhp)
|
||||||
|
else:
|
||||||
|
# Fallback for legacy (shouldn't happen if _initialize_character_stats is called)
|
||||||
|
current_health -= amount
|
||||||
|
actual_damage = amount
|
||||||
|
print(name, " took ", amount, " damage! Health: ", current_health, " (critical: ", is_critical, ")")
|
||||||
|
|
||||||
# Calculate knockback direction (away from attacker)
|
# Calculate knockback direction (away from attacker)
|
||||||
var knockback_direction = (global_position - from_position).normalized()
|
var knockback_direction = (global_position - from_position).normalized()
|
||||||
@@ -287,10 +346,9 @@ func take_damage(amount: float, from_position: Vector2):
|
|||||||
# Flash red (even if dying, show the hit)
|
# Flash red (even if dying, show the hit)
|
||||||
_flash_damage()
|
_flash_damage()
|
||||||
|
|
||||||
# Show damage number (red, using dmg_numbers.png font) above enemy
|
# Show damage number (red/orange, using dmg_numbers.png font) above enemy
|
||||||
# Only show if damage > 0
|
# Always show damage number, even if 0
|
||||||
if amount > 0:
|
_show_damage_number(actual_damage, from_position, is_critical)
|
||||||
_show_damage_number(amount, from_position)
|
|
||||||
|
|
||||||
# Sync damage visual to clients
|
# Sync damage visual to clients
|
||||||
# Use game_world to route damage visual sync instead of direct RPC to avoid node path issues
|
# Use game_world to route damage visual sync instead of direct RPC to avoid node path issues
|
||||||
@@ -321,16 +379,14 @@ func take_damage(amount: float, from_position: Vector2):
|
|||||||
call_deferred("_notify_doors_enemy_died")
|
call_deferred("_notify_doors_enemy_died")
|
||||||
|
|
||||||
@rpc("any_peer", "reliable")
|
@rpc("any_peer", "reliable")
|
||||||
func rpc_take_damage(amount: float, from_position: Vector2):
|
func rpc_take_damage(amount: float, from_position: Vector2, is_critical: bool = false):
|
||||||
# RPC version - only process on server/authority
|
# RPC version - only process on server/authority
|
||||||
if is_multiplayer_authority():
|
if is_multiplayer_authority():
|
||||||
take_damage(amount, from_position)
|
take_damage(amount, from_position, is_critical)
|
||||||
|
|
||||||
func _show_damage_number(amount: float, from_position: Vector2):
|
func _show_damage_number(amount: float, from_position: Vector2, is_critical: bool = false, is_miss: bool = false, is_dodged: bool = false):
|
||||||
# Show damage number (red, using dmg_numbers.png font) above enemy
|
# Show damage number (red/orange for crits, cyan for dodge, gray for miss, using dmg_numbers.png font) above enemy
|
||||||
# Only show if damage > 0
|
# Show even if amount is 0 for MISS/DODGED
|
||||||
if amount <= 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
var damage_number_scene = preload("res://scenes/damage_number.tscn")
|
var damage_number_scene = preload("res://scenes/damage_number.tscn")
|
||||||
if not damage_number_scene:
|
if not damage_number_scene:
|
||||||
@@ -340,9 +396,16 @@ func _show_damage_number(amount: float, from_position: Vector2):
|
|||||||
if not damage_label:
|
if not damage_label:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set damage text and red color
|
# Set text and color based on type
|
||||||
damage_label.label = str(int(amount))
|
if is_dodged:
|
||||||
damage_label.color = Color.RED
|
damage_label.label = "DODGED"
|
||||||
|
damage_label.color = Color.CYAN
|
||||||
|
elif is_miss:
|
||||||
|
damage_label.label = "MISS"
|
||||||
|
damage_label.color = Color.GRAY
|
||||||
|
else:
|
||||||
|
damage_label.label = str(int(amount))
|
||||||
|
damage_label.color = Color.ORANGE if is_critical else Color.RED
|
||||||
|
|
||||||
# Calculate direction from attacker (slight upward variation)
|
# Calculate direction from attacker (slight upward variation)
|
||||||
var direction_from_attacker = (global_position - from_position).normalized()
|
var direction_from_attacker = (global_position - from_position).normalized()
|
||||||
@@ -418,11 +481,16 @@ func _die():
|
|||||||
is_dead = true
|
is_dead = true
|
||||||
print(name, " died!")
|
print(name, " died!")
|
||||||
|
|
||||||
# Credit kill to the player who dealt the fatal damage
|
# Credit kill and grant EXP to the player who dealt the fatal damage
|
||||||
if killer_player and is_instance_valid(killer_player) and killer_player.character_stats:
|
if killer_player and is_instance_valid(killer_player) and killer_player.character_stats:
|
||||||
killer_player.character_stats.kills += 1
|
killer_player.character_stats.kills += 1
|
||||||
print(name, " kill credited to ", killer_player.name, " (total kills: ", killer_player.character_stats.kills, ")")
|
print(name, " kill credited to ", killer_player.name, " (total kills: ", killer_player.character_stats.kills, ")")
|
||||||
|
|
||||||
|
# Grant EXP to the killer
|
||||||
|
if exp_reward > 0:
|
||||||
|
killer_player.character_stats.add_xp(exp_reward)
|
||||||
|
print(name, " granted ", exp_reward, " EXP to ", killer_player.name)
|
||||||
|
|
||||||
# Sync kill update to client if this player belongs to a client
|
# Sync kill update to client if this player belongs to a client
|
||||||
# Only sync if we're on the server and the killer is a client's player
|
# Only sync if we're on the server and the killer is a client's player
|
||||||
if multiplayer.has_multiplayer_peer() and is_multiplayer_authority():
|
if multiplayer.has_multiplayer_peer() and is_multiplayer_authority():
|
||||||
@@ -475,16 +543,21 @@ func _spawn_loot():
|
|||||||
var loot_chance = randf()
|
var loot_chance = randf()
|
||||||
print(name, " loot chance roll: ", loot_chance, " (need > 0.3)")
|
print(name, " loot chance roll: ", loot_chance, " (need > 0.3)")
|
||||||
if loot_chance > 0.3:
|
if loot_chance > 0.3:
|
||||||
# Random loot type
|
# Decide what to drop: 30% coin, 30% food, 40% item
|
||||||
|
var drop_roll = randf()
|
||||||
var loot_type = 0
|
var loot_type = 0
|
||||||
|
var drop_item = false
|
||||||
|
|
||||||
# 50% chance for coin
|
if drop_roll < 0.3:
|
||||||
if randf() < 0.5:
|
# 30% chance for coin
|
||||||
loot_type = 0 # COIN
|
loot_type = 0 # COIN
|
||||||
# 50% chance for food item
|
elif drop_roll < 0.6:
|
||||||
else:
|
# 30% chance for food item
|
||||||
var food_types = [1, 2, 3] # APPLE, BANANA, CHERRY
|
var food_types = [1, 2, 3] # APPLE, BANANA, CHERRY
|
||||||
loot_type = food_types[randi() % food_types.size()]
|
loot_type = food_types[randi() % food_types.size()]
|
||||||
|
else:
|
||||||
|
# 40% chance for Item instance
|
||||||
|
drop_item = true
|
||||||
|
|
||||||
# Generate random velocity values (same on all clients)
|
# Generate random velocity values (same on all clients)
|
||||||
var random_angle = randf() * PI * 2
|
var random_angle = randf() * PI * 2
|
||||||
@@ -500,10 +573,20 @@ func _spawn_loot():
|
|||||||
if game_world and game_world.has_method("_find_nearby_safe_spawn_position"):
|
if game_world and game_world.has_method("_find_nearby_safe_spawn_position"):
|
||||||
safe_spawn_pos = game_world._find_nearby_safe_spawn_position(global_position, 64.0)
|
safe_spawn_pos = game_world._find_nearby_safe_spawn_position(global_position, 64.0)
|
||||||
|
|
||||||
# Spawn on server
|
|
||||||
var loot = loot_scene.instantiate()
|
|
||||||
var entities_node = get_parent()
|
var entities_node = get_parent()
|
||||||
if entities_node:
|
if not entities_node:
|
||||||
|
print(name, " ERROR: entities_node is null! Cannot spawn loot!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if drop_item:
|
||||||
|
# Spawn Item instance as loot
|
||||||
|
var item = ItemDatabase.get_random_enemy_drop()
|
||||||
|
if item:
|
||||||
|
ItemLootHelper.spawn_item_loot(item, global_position, entities_node, game_world)
|
||||||
|
print(name, " ✓ dropped item: ", item.item_name, " at ", safe_spawn_pos)
|
||||||
|
else:
|
||||||
|
# Spawn regular loot (coin or food)
|
||||||
|
var loot = loot_scene.instantiate()
|
||||||
entities_node.add_child(loot)
|
entities_node.add_child(loot)
|
||||||
loot.global_position = safe_spawn_pos
|
loot.global_position = safe_spawn_pos
|
||||||
loot.loot_type = loot_type
|
loot.loot_type = loot_type
|
||||||
@@ -519,8 +602,7 @@ func _spawn_loot():
|
|||||||
# Reuse game_world variable from above
|
# Reuse game_world variable from above
|
||||||
if game_world:
|
if game_world:
|
||||||
# Generate unique loot ID
|
# Generate unique loot ID
|
||||||
if not "loot_id_counter" in game_world:
|
# loot_id_counter is declared as a variable in game_world.gd, so it always exists
|
||||||
game_world.loot_id_counter = 0
|
|
||||||
var loot_id = game_world.loot_id_counter
|
var loot_id = game_world.loot_id_counter
|
||||||
game_world.loot_id_counter += 1
|
game_world.loot_id_counter += 1
|
||||||
# Store loot ID on server loot instance
|
# Store loot ID on server loot instance
|
||||||
@@ -530,8 +612,6 @@ func _spawn_loot():
|
|||||||
print(name, " ✓ synced loot spawn to clients")
|
print(name, " ✓ synced loot spawn to clients")
|
||||||
else:
|
else:
|
||||||
print(name, " ERROR: game_world not found for loot sync!")
|
print(name, " ERROR: game_world not found for loot sync!")
|
||||||
else:
|
|
||||||
print(name, " ERROR: entities_node is null! Cannot spawn loot!")
|
|
||||||
else:
|
else:
|
||||||
print(name, " loot chance failed (", loot_chance, " <= 0.3), no loot dropped")
|
print(name, " loot chance failed (", loot_chance, " <= 0.3), no loot dropped")
|
||||||
|
|
||||||
|
|||||||
@@ -15,16 +15,34 @@ var fly_height: float = 8.0 # Z position when flying
|
|||||||
func _ready():
|
func _ready():
|
||||||
super._ready()
|
super._ready()
|
||||||
|
|
||||||
max_health = 30.0
|
max_health = 18.0 # Reduced from 30.0 for better balance
|
||||||
current_health = max_health
|
current_health = max_health
|
||||||
move_speed = 40.0 # Reasonable speed for bats
|
move_speed = 40.0 # Reasonable speed for bats
|
||||||
damage = 5.0
|
damage = 5.0
|
||||||
|
exp_reward = 6.0 # Bats give low-moderate EXP
|
||||||
|
|
||||||
state_timer = idle_duration
|
state_timer = idle_duration
|
||||||
|
|
||||||
# CRITICAL: Ensure collision mask is set correctly (walls are on layer 7 = bit 6 = 64)
|
# CRITICAL: Ensure collision mask is set correctly (walls are on layer 7 = bit 6 = 64)
|
||||||
collision_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64)
|
collision_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64)
|
||||||
|
|
||||||
|
# Override to set weak stats for bats
|
||||||
|
func _initialize_character_stats():
|
||||||
|
super._initialize_character_stats()
|
||||||
|
# Bats are weak enemies - very low END for low DEF
|
||||||
|
character_stats.baseStats.end = 5 # END=5 gives DEF=1.0 (5 * 0.2)
|
||||||
|
character_stats.baseStats.str = 5 # Low STR = low damage
|
||||||
|
character_stats.baseStats.dex = 10 # Higher DEX (bats are agile)
|
||||||
|
character_stats.baseStats.int = 5
|
||||||
|
character_stats.baseStats.wis = 5
|
||||||
|
character_stats.baseStats.lck = 5
|
||||||
|
# Re-initialize hp and mp with new stats
|
||||||
|
character_stats.hp = character_stats.maxhp
|
||||||
|
character_stats.mp = character_stats.maxmp
|
||||||
|
# Sync max_health from character_stats
|
||||||
|
max_health = character_stats.maxhp
|
||||||
|
current_health = max_health
|
||||||
|
|
||||||
func _physics_process(delta):
|
func _physics_process(delta):
|
||||||
# Always update animation (even when dead, and on clients)
|
# Always update animation (even when dead, and on clients)
|
||||||
_update_animation(delta)
|
_update_animation(delta)
|
||||||
|
|||||||
@@ -740,40 +740,47 @@ func _setup_stats():
|
|||||||
# Set stats based on type
|
# Set stats based on type
|
||||||
match humanoid_type:
|
match humanoid_type:
|
||||||
HumanoidType.CYCLOPS:
|
HumanoidType.CYCLOPS:
|
||||||
max_health = 100.0
|
max_health = 55.0 # Reduced from 100.0 for better balance
|
||||||
move_speed = 40.0
|
move_speed = 40.0
|
||||||
damage = 15.0
|
damage = 15.0
|
||||||
dex = 8 # Slow, strong
|
dex = 8 # Slow, strong
|
||||||
|
exp_reward = 25.0 # Strong enemies give more EXP
|
||||||
HumanoidType.DEMON:
|
HumanoidType.DEMON:
|
||||||
max_health = 80.0
|
max_health = 45.0 # Reduced from 80.0 for better balance
|
||||||
move_speed = 45.0
|
move_speed = 45.0
|
||||||
damage = 12.0
|
damage = 12.0
|
||||||
dex = 12 # Medium speed
|
dex = 12 # Medium speed
|
||||||
|
exp_reward = 20.0
|
||||||
HumanoidType.HUMANOID:
|
HumanoidType.HUMANOID:
|
||||||
max_health = 60.0
|
max_health = 35.0 # Reduced from 60.0 for better balance
|
||||||
move_speed = 50.0
|
move_speed = 50.0
|
||||||
damage = 10.0
|
damage = 10.0
|
||||||
dex = 15 # Fast, agile
|
dex = 15 # Fast, agile
|
||||||
|
exp_reward = 15.0
|
||||||
HumanoidType.NIGHTELF:
|
HumanoidType.NIGHTELF:
|
||||||
max_health = 70.0
|
max_health = 40.0 # Reduced from 70.0 for better balance
|
||||||
move_speed = 55.0
|
move_speed = 55.0
|
||||||
damage = 11.0
|
damage = 11.0
|
||||||
dex = 18 # Very fast
|
dex = 18 # Very fast
|
||||||
|
exp_reward = 18.0
|
||||||
HumanoidType.GOBLIN:
|
HumanoidType.GOBLIN:
|
||||||
max_health = 40.0
|
max_health = 25.0 # Reduced from 40.0 for better balance
|
||||||
move_speed = 60.0
|
move_speed = 60.0
|
||||||
damage = 8.0
|
damage = 8.0
|
||||||
dex = 20 # Very fast, weak
|
dex = 20 # Very fast, weak
|
||||||
|
exp_reward = 10.0 # Weak enemies give less EXP
|
||||||
HumanoidType.ORC:
|
HumanoidType.ORC:
|
||||||
max_health = 90.0
|
max_health = 50.0 # Reduced from 90.0 for better balance
|
||||||
move_speed = 42.0
|
move_speed = 42.0
|
||||||
damage = 14.0
|
damage = 14.0
|
||||||
dex = 7 # Slow, very strong
|
dex = 7 # Slow, very strong
|
||||||
|
exp_reward = 22.0
|
||||||
HumanoidType.SKELETON:
|
HumanoidType.SKELETON:
|
||||||
max_health = 50.0
|
max_health = 30.0 # Reduced from 50.0 for better balance
|
||||||
move_speed = 48.0
|
move_speed = 48.0
|
||||||
damage = 9.0
|
damage = 9.0
|
||||||
dex = 14 # Medium-fast
|
dex = 14 # Medium-fast
|
||||||
|
exp_reward = 12.0
|
||||||
|
|
||||||
current_health = max_health
|
current_health = max_health
|
||||||
|
|
||||||
|
|||||||
@@ -13,16 +13,34 @@ var detection_range: float = 150.0
|
|||||||
func _ready():
|
func _ready():
|
||||||
super._ready()
|
super._ready()
|
||||||
|
|
||||||
max_health = 25.0
|
max_health = 15.0 # Reduced from 25.0 for better balance
|
||||||
current_health = max_health
|
current_health = max_health
|
||||||
move_speed = 40.0 # Much slower
|
move_speed = 40.0 # Much slower
|
||||||
damage = 8.0
|
damage = 8.0
|
||||||
|
exp_reward = 5.0 # Rats give low EXP
|
||||||
|
|
||||||
state_timer = idle_duration
|
state_timer = idle_duration
|
||||||
|
|
||||||
# CRITICAL: Ensure collision mask is set correctly (walls are on layer 7 = bit 6 = 64)
|
# CRITICAL: Ensure collision mask is set correctly (walls are on layer 7 = bit 6 = 64)
|
||||||
collision_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64)
|
collision_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64)
|
||||||
|
|
||||||
|
# Override to set weak stats for rats
|
||||||
|
func _initialize_character_stats():
|
||||||
|
super._initialize_character_stats()
|
||||||
|
# Rats are weak enemies - very low END for low DEF
|
||||||
|
character_stats.baseStats.end = 5 # END=5 gives DEF=1.0 (5 * 0.2)
|
||||||
|
character_stats.baseStats.str = 7 # Low STR = low damage
|
||||||
|
character_stats.baseStats.dex = 8 # Slightly higher DEX (rats are fast)
|
||||||
|
character_stats.baseStats.int = 5
|
||||||
|
character_stats.baseStats.wis = 5
|
||||||
|
character_stats.baseStats.lck = 5
|
||||||
|
# Re-initialize hp and mp with new stats
|
||||||
|
character_stats.hp = character_stats.maxhp
|
||||||
|
character_stats.mp = character_stats.maxmp
|
||||||
|
# Sync max_health from character_stats
|
||||||
|
max_health = character_stats.maxhp
|
||||||
|
current_health = max_health
|
||||||
|
|
||||||
func _ai_behavior(delta):
|
func _ai_behavior(delta):
|
||||||
# Update state timer
|
# Update state timer
|
||||||
state_timer -= delta
|
state_timer -= delta
|
||||||
|
|||||||
@@ -55,10 +55,11 @@ var time_since_last_frame = 0.0
|
|||||||
func _ready():
|
func _ready():
|
||||||
super._ready()
|
super._ready()
|
||||||
|
|
||||||
max_health = 20.0
|
max_health = 12.0 # Reduced from 20.0 for better balance
|
||||||
current_health = max_health
|
current_health = max_health
|
||||||
move_speed = 20.0 # Very slow (reduced from 35)
|
move_speed = 20.0 # Very slow (reduced from 35)
|
||||||
damage = 6.0
|
damage = 6.0
|
||||||
|
exp_reward = 8.0 # Slimes give moderate EXP
|
||||||
|
|
||||||
state_timer = idle_duration
|
state_timer = idle_duration
|
||||||
|
|
||||||
@@ -70,6 +71,23 @@ func _ready():
|
|||||||
if collision_shape and collision_shape.shape:
|
if collision_shape and collision_shape.shape:
|
||||||
collision_shape.shape.radius = 6.0 # 12x12 effective size
|
collision_shape.shape.radius = 6.0 # 12x12 effective size
|
||||||
|
|
||||||
|
# Override to set weak stats for slimes
|
||||||
|
func _initialize_character_stats():
|
||||||
|
super._initialize_character_stats()
|
||||||
|
# Slimes are weak enemies - very low END for low DEF
|
||||||
|
character_stats.baseStats.end = 5 # END=5 gives DEF=1.0 (5 * 0.2)
|
||||||
|
character_stats.baseStats.str = 6 # Low STR = low damage
|
||||||
|
character_stats.baseStats.dex = 5 # Low DEX
|
||||||
|
character_stats.baseStats.int = 5
|
||||||
|
character_stats.baseStats.wis = 5
|
||||||
|
character_stats.baseStats.lck = 5
|
||||||
|
# Re-initialize hp and mp with new stats
|
||||||
|
character_stats.hp = character_stats.maxhp
|
||||||
|
character_stats.mp = character_stats.maxmp
|
||||||
|
# Sync max_health from character_stats
|
||||||
|
max_health = character_stats.maxhp
|
||||||
|
current_health = max_health
|
||||||
|
|
||||||
func _physics_process(delta):
|
func _physics_process(delta):
|
||||||
# Always update animation (even when dead, and on clients)
|
# Always update animation (even when dead, and on clients)
|
||||||
_update_animation(delta)
|
_update_animation(delta)
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ func _ready():
|
|||||||
# Create chat UI
|
# Create chat UI
|
||||||
_create_chat_ui()
|
_create_chat_ui()
|
||||||
|
|
||||||
|
# Create inventory UI
|
||||||
|
_create_inventory_ui()
|
||||||
|
|
||||||
# Generate dungeon on host
|
# Generate dungeon on host
|
||||||
if multiplayer.is_server() or not multiplayer.has_multiplayer_peer():
|
if multiplayer.is_server() or not multiplayer.has_multiplayer_peer():
|
||||||
print("GameWorld: _ready() - Will generate dungeon (is_server: ", multiplayer.is_server(), ", has_peer: ", multiplayer.has_multiplayer_peer(), ")")
|
print("GameWorld: _ready() - Will generate dungeon (is_server: ", multiplayer.is_server(), ", has_peer: ", multiplayer.has_multiplayer_peer(), ")")
|
||||||
@@ -275,6 +278,37 @@ func _sync_loot_spawn(spawn_position: Vector2, loot_type: int, initial_velocity:
|
|||||||
loot.is_airborne = true
|
loot.is_airborne = true
|
||||||
print("Client spawned loot: ", loot_type, " at ", spawn_position, " authority: ", loot.get_multiplayer_authority())
|
print("Client spawned loot: ", loot_type, " at ", spawn_position, " authority: ", loot.get_multiplayer_authority())
|
||||||
|
|
||||||
|
@rpc("authority", "reliable")
|
||||||
|
func _sync_item_loot_spawn(spawn_position: Vector2, item_data: Dictionary, initial_velocity: Vector2, initial_velocity_z: float, loot_id: int = -1):
|
||||||
|
# Clients spawn item loot when server tells them to
|
||||||
|
if not multiplayer.is_server():
|
||||||
|
var loot_scene = preload("res://scenes/loot.tscn")
|
||||||
|
if not loot_scene:
|
||||||
|
return
|
||||||
|
|
||||||
|
var loot = loot_scene.instantiate()
|
||||||
|
var entities_node = get_node_or_null("Entities")
|
||||||
|
if entities_node:
|
||||||
|
# Create Item instance from data
|
||||||
|
var item = Item.new(item_data)
|
||||||
|
|
||||||
|
# Set multiplayer authority to server (peer 1) so RPCs work
|
||||||
|
if multiplayer.has_multiplayer_peer():
|
||||||
|
loot.set_multiplayer_authority(1)
|
||||||
|
# Store unique loot ID for identification
|
||||||
|
if loot_id >= 0:
|
||||||
|
loot.set_meta("loot_id", loot_id)
|
||||||
|
entities_node.add_child(loot)
|
||||||
|
loot.global_position = spawn_position
|
||||||
|
loot.loot_type = loot.LootType.ITEM
|
||||||
|
loot.item = item # Set the item instance
|
||||||
|
# Set initial velocity before _ready() processes
|
||||||
|
loot.velocity = initial_velocity
|
||||||
|
loot.velocity_z = initial_velocity_z
|
||||||
|
loot.velocity_set_by_spawner = true
|
||||||
|
loot.is_airborne = true
|
||||||
|
print("Client spawned item loot: ", item.item_name, " at ", spawn_position, " authority: ", loot.get_multiplayer_authority())
|
||||||
|
|
||||||
@rpc("authority", "unreliable")
|
@rpc("authority", "unreliable")
|
||||||
func _sync_enemy_position(enemy_name: String, enemy_index: int, pos: Vector2, vel: Vector2, z_pos: float, dir: int, frame: int, anim: String, frame_num: int, state_value: int):
|
func _sync_enemy_position(enemy_name: String, enemy_index: int, pos: Vector2, vel: Vector2, z_pos: float, dir: int, frame: int, anim: String, frame_num: int, state_value: int):
|
||||||
# Clients receive enemy position updates from server
|
# Clients receive enemy position updates from server
|
||||||
@@ -2056,6 +2090,19 @@ func _create_chat_ui():
|
|||||||
else:
|
else:
|
||||||
push_error("GameWorld: Failed to instantiate chat_ui.tscn!")
|
push_error("GameWorld: Failed to instantiate chat_ui.tscn!")
|
||||||
|
|
||||||
|
func _create_inventory_ui():
|
||||||
|
# Create inventory UI programmatically (using CanvasLayer)
|
||||||
|
var inventory_ui_script = load("res://scripts/inventory_ui.gd")
|
||||||
|
if not inventory_ui_script:
|
||||||
|
push_error("GameWorld: Could not load inventory_ui.gd script!")
|
||||||
|
return
|
||||||
|
|
||||||
|
var inventory_ui = CanvasLayer.new()
|
||||||
|
inventory_ui.set_script(inventory_ui_script)
|
||||||
|
inventory_ui.name = "InventoryUI"
|
||||||
|
add_child(inventory_ui)
|
||||||
|
print("GameWorld: Inventory UI created and added to scene tree")
|
||||||
|
|
||||||
func _send_player_join_message(peer_id: int, player_info: Dictionary):
|
func _send_player_join_message(peer_id: int, player_info: Dictionary):
|
||||||
# Send a chat message when a player joins
|
# Send a chat message when a player joins
|
||||||
# Only send from server to avoid duplicate messages
|
# Only send from server to avoid duplicate messages
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ extends Resource
|
|||||||
signal health_changed(new_health: float, max_health: float)
|
signal health_changed(new_health: float, max_health: float)
|
||||||
signal mana_changed(new_mana: float, max_mana: float)
|
signal mana_changed(new_mana: float, max_mana: float)
|
||||||
signal level_changed(new_level: int)
|
signal level_changed(new_level: int)
|
||||||
|
signal level_up_stats(stats_increased: Array) # Emitted when leveling up, contains array of stat names that were increased
|
||||||
signal xp_changed(new_xp: float, xp_to_next: float)
|
signal xp_changed(new_xp: float, xp_to_next: float)
|
||||||
signal no_health
|
signal no_health
|
||||||
|
|
||||||
@@ -152,11 +153,13 @@ var maxmp: float:
|
|||||||
|
|
||||||
var damage: float:
|
var damage: float:
|
||||||
get:
|
get:
|
||||||
return (baseStats.str + get_pass("str")) * 0.2 + get_pass("dmg")
|
# Increased damage scaling: 0.25 per STR point (was 0.2) - makes players stronger
|
||||||
|
return (baseStats.str + get_pass("str")) * 0.25 + get_pass("dmg")
|
||||||
|
|
||||||
var defense: float:
|
var defense: float:
|
||||||
get:
|
get:
|
||||||
return ((baseStats.end + get_pass("end")) * 0.3) + get_pass("def")
|
# Reduced DEF scaling: 0.2 per END point (was 0.3) to make it less overpowered for low-level enemies
|
||||||
|
return ((baseStats.end + get_pass("end")) * 0.2) + get_pass("def")
|
||||||
|
|
||||||
var spell_amp: float:
|
var spell_amp: float:
|
||||||
get:
|
get:
|
||||||
@@ -178,9 +181,23 @@ var crit_chance: float:
|
|||||||
get:
|
get:
|
||||||
return (baseStats.lck + get_pass("lck")) * 1.2
|
return (baseStats.lck + get_pass("lck")) * 1.2
|
||||||
|
|
||||||
|
var dodge_chance: float:
|
||||||
|
get:
|
||||||
|
# Dodge chance based on DEX (very low % per point, as per user request)
|
||||||
|
# Each point of DEX gives 0.5% dodge chance (so 20 DEX = 10% dodge)
|
||||||
|
return (baseStats.dex + get_pass("dex")) * 0.005
|
||||||
|
|
||||||
|
var hit_chance: float:
|
||||||
|
get:
|
||||||
|
# Hit chance based on DEX (higher DEX = better accuracy)
|
||||||
|
# Base hit chance is 95%, each DEX point adds 0.3% (so ~17 DEX = 100% base hit chance)
|
||||||
|
# Formula: 95% base + (DEX * 0.3%)
|
||||||
|
return 0.95 + ((baseStats.dex + get_pass("dex")) * 0.003)
|
||||||
|
|
||||||
var xp_to_next_level: float:
|
var xp_to_next_level: float:
|
||||||
get:
|
get:
|
||||||
return pow(level * 10, 1.5)
|
# Scale EXP requirements more aggressively - gets harder to level as you go
|
||||||
|
return pow(level * 15, 1.6)
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
hp = maxhp
|
hp = maxhp
|
||||||
@@ -193,25 +210,41 @@ func add_xp(amount: float) -> void:
|
|||||||
while xp >= xp_to_next_level:
|
while xp >= xp_to_next_level:
|
||||||
level_up()
|
level_up()
|
||||||
|
|
||||||
# instead of automatically update all stats, maybe let the player choose which stats to level up.
|
# Level up - randomly increases 2-3 stats instead of all
|
||||||
func level_up() -> void:
|
func level_up() -> void:
|
||||||
level += 1
|
level += 1
|
||||||
xp -= xp_to_next_level
|
xp -= xp_to_next_level
|
||||||
|
|
||||||
# Increase stats
|
# Randomly select 2-3 stats to increase (random 2 or 3)
|
||||||
baseStats.str += 1
|
var num_stats_to_increase = randi_range(2, 3)
|
||||||
baseStats.dex += 1
|
|
||||||
baseStats.int += 1
|
# All available stats (excluding cha for now as per user request)
|
||||||
baseStats.end += 1
|
var available_stats = ["str", "dex", "int", "end", "wis", "lck"]
|
||||||
baseStats.wis += 1
|
|
||||||
baseStats.cha += 1
|
# Shuffle and pick random stats
|
||||||
baseStats.lck += 1
|
var stats_to_increase = []
|
||||||
|
var shuffled_stats = available_stats.duplicate()
|
||||||
|
shuffled_stats.shuffle()
|
||||||
|
|
||||||
|
for i in range(min(num_stats_to_increase, shuffled_stats.size())):
|
||||||
|
stats_to_increase.append(shuffled_stats[i])
|
||||||
|
|
||||||
|
# Increase selected stats
|
||||||
|
for stat_name in stats_to_increase:
|
||||||
|
baseStats[stat_name] += 1
|
||||||
|
|
||||||
|
# Store which stats were increased (for display)
|
||||||
|
var stats_increased = stats_to_increase
|
||||||
|
|
||||||
# Restore health and mana on level up
|
# Restore health and mana on level up
|
||||||
hp = maxhp
|
hp = maxhp
|
||||||
mp = maxmp
|
mp = maxmp
|
||||||
|
|
||||||
|
# Emit level_changed signal
|
||||||
level_changed.emit(level)
|
level_changed.emit(level)
|
||||||
|
# Emit level_up_stats signal with which stats were increased
|
||||||
|
level_up_stats.emit(stats_increased)
|
||||||
|
|
||||||
health_changed.emit(hp, maxhp)
|
health_changed.emit(hp, maxhp)
|
||||||
mana_changed.emit(mp, maxmp)
|
mana_changed.emit(mp, maxmp)
|
||||||
|
|
||||||
@@ -225,12 +258,25 @@ func modify_mana(amount: float) -> void:
|
|||||||
mana_changed.emit(mp, maxmp)
|
mana_changed.emit(mp, maxmp)
|
||||||
character_changed.emit(self)
|
character_changed.emit(self)
|
||||||
|
|
||||||
func calculate_damage(base_damage: float, is_magical: bool = false) -> float:
|
func calculate_damage(base_damage: float, is_magical: bool = false, is_critical: bool = false) -> float:
|
||||||
if is_magical:
|
# Apply defense reduction (DEF reduces damage by a flat amount, not percentage)
|
||||||
return base_damage * (1 - (resistances.magic / 100.0))
|
# Defense formula: flat reduction based on END and equipment DEF
|
||||||
return base_damage * (1 - (defense / 100.0))
|
# Critical hits pierce 80% of DEF (only 20% of DEF applies to crits)
|
||||||
|
var final_damage = base_damage
|
||||||
|
if not is_magical:
|
||||||
|
# Physical damage: reduce by defense value (flat reduction)
|
||||||
|
var effective_defense = defense
|
||||||
|
if is_critical:
|
||||||
|
# Critical hits pierce 80% of DEF (only 20% applies)
|
||||||
|
effective_defense = defense * 0.2
|
||||||
|
final_damage = max(0.0, base_damage - effective_defense)
|
||||||
|
else:
|
||||||
|
# Magical damage: reduce by magic resistance percentage
|
||||||
|
final_damage = base_damage * (1 - (resistances.magic / 100.0))
|
||||||
|
return final_damage
|
||||||
|
|
||||||
func take_damage(amount: float, is_magical: bool = false) -> float:
|
func take_damage(amount: float, is_magical: bool = false) -> float:
|
||||||
|
# Calculate damage after DEF reduction
|
||||||
var actual_damage = calculate_damage(amount, is_magical)
|
var actual_damage = calculate_damage(amount, is_magical)
|
||||||
modify_health(-actual_damage)
|
modify_health(-actual_damage)
|
||||||
if hp <= 0:
|
if hp <= 0:
|
||||||
@@ -419,6 +465,28 @@ func drop_equipment(iItem:Item):
|
|||||||
|
|
||||||
func add_item(iItem:Item):
|
func add_item(iItem:Item):
|
||||||
self.inventory.push_back(iItem)
|
self.inventory.push_back(iItem)
|
||||||
|
|
||||||
|
# Auto-equip if slot is empty (only for equippable items)
|
||||||
|
if iItem.item_type == Item.ItemType.Equippable and iItem.equipment_type != Item.EquipmentType.NONE:
|
||||||
|
var slot_key = ""
|
||||||
|
match iItem.equipment_type:
|
||||||
|
Item.EquipmentType.MAINHAND:
|
||||||
|
slot_key = "mainhand"
|
||||||
|
Item.EquipmentType.OFFHAND:
|
||||||
|
slot_key = "offhand"
|
||||||
|
Item.EquipmentType.HEADGEAR:
|
||||||
|
slot_key = "headgear"
|
||||||
|
Item.EquipmentType.ARMOUR:
|
||||||
|
slot_key = "armour"
|
||||||
|
Item.EquipmentType.BOOTS:
|
||||||
|
slot_key = "boots"
|
||||||
|
Item.EquipmentType.ACCESSORY:
|
||||||
|
slot_key = "accessory"
|
||||||
|
|
||||||
|
# Auto-equip if slot is empty
|
||||||
|
if slot_key != "" and equipment[slot_key] == null:
|
||||||
|
equip_item(iItem)
|
||||||
|
|
||||||
emit_signal("character_changed", self)
|
emit_signal("character_changed", self)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
491
src/scripts/inspiration_scripts/inventory.gd
Normal file
491
src/scripts/inspiration_scripts/inventory.gd
Normal file
@@ -0,0 +1,491 @@
|
|||||||
|
extends CanvasLayer
|
||||||
|
|
||||||
|
var is_showing_inventory = true
|
||||||
|
var finished_tween = true
|
||||||
|
|
||||||
|
var selectedItem:Item = null
|
||||||
|
var selectedEquipment:Item = null
|
||||||
|
var lastPressedItem:Item = null
|
||||||
|
var lastPressedType = "Item"
|
||||||
|
|
||||||
|
var timerSelect:float = 0.0
|
||||||
|
|
||||||
|
func bindInventory():
|
||||||
|
if GameManager.character_data.is_connected("character_changed", _charChanged):
|
||||||
|
GameManager.character_data.disconnect("character_changed", _charChanged)
|
||||||
|
GameManager.character_data.connect("character_changed", _charChanged)
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
|
||||||
|
is_showing_inventory = false
|
||||||
|
var move_range = -119
|
||||||
|
$ControlContainer/ControlStats.position.x += move_range
|
||||||
|
move_range = 80
|
||||||
|
$ControlContainer/ControlInventory.position.x += move_range
|
||||||
|
move_range = 33
|
||||||
|
$ControlContainer/ControlInfo.position.y += move_range
|
||||||
|
|
||||||
|
$ControlContainer.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
|
||||||
|
|
||||||
|
# Set up multiplayer peer
|
||||||
|
'
|
||||||
|
GameManager.host(21212, false)
|
||||||
|
|
||||||
|
GameManager.character_data.connect("character_changed", _charChanged)
|
||||||
|
|
||||||
|
#$Player.position = Vector2(30, 30)
|
||||||
|
|
||||||
|
var item = Item.new()
|
||||||
|
item.item_type = Item.ItemType.Equippable
|
||||||
|
item.equipment_type = Item.EquipmentType.ARMOUR
|
||||||
|
item.item_name = "Leather Armour"
|
||||||
|
item.description = "A nice leather armour"
|
||||||
|
item.spriteFrame = 12
|
||||||
|
item.modifiers["def"] = 2
|
||||||
|
item.equipmentPath = "res://assets/gfx/Puny-Characters/Layer 2 - Clothes/Tunic Body/BrownTunic.png"
|
||||||
|
GameManager.character_data.add_item(item)
|
||||||
|
|
||||||
|
|
||||||
|
var item2 = Item.new()
|
||||||
|
item2.item_type = Item.ItemType.Equippable
|
||||||
|
item2.equipment_type = Item.EquipmentType.HEADGEAR
|
||||||
|
item2.item_name = "Leather helm"
|
||||||
|
item2.description = "A nice leather helm"
|
||||||
|
item2.spriteFrame = 31
|
||||||
|
item2.modifiers["def"] = 1
|
||||||
|
item2.equipmentPath = "res://assets/gfx/Puny-Characters/Layer 6 - Headgears/Basic Mage/MageHatRed.png"
|
||||||
|
item2.colorReplacements = [
|
||||||
|
{ "original": Color(255/255.0, 39/255.0, 44/255.0), "replace": Color(255/255.0,106/255.0,39/255.0)},
|
||||||
|
{ "original": Color(182/255.0, 0, 0), "replace": Color(182/255.0,106/255.0,0)},
|
||||||
|
{ "original": Color(118/255.0, 1/255.0, 0), "replace": Color(118/255.0,66/255.0,0)},
|
||||||
|
{ "original": Color(72/255.0, 0, 12/255.0), "replace": Color(72/255.0,34/255.0,0)}
|
||||||
|
]
|
||||||
|
GameManager.character_data.add_item(item2)
|
||||||
|
|
||||||
|
var item3 = Item.new()
|
||||||
|
item3.item_type = Item.ItemType.Equippable
|
||||||
|
item3.equipment_type = Item.EquipmentType.MAINHAND
|
||||||
|
item3.weapon_type = Item.WeaponType.SWORD
|
||||||
|
item3.item_name = "Dagger"
|
||||||
|
item3.description = "A sharp dagger"
|
||||||
|
item3.spriteFrame = 5*20 + 10
|
||||||
|
item3.modifiers["dmg"] = 2
|
||||||
|
GameManager.character_data.add_item(item3)
|
||||||
|
|
||||||
|
var item4 = Item.new()
|
||||||
|
item4.item_type = Item.ItemType.Equippable
|
||||||
|
item4.equipment_type = Item.EquipmentType.MAINHAND
|
||||||
|
item4.weapon_type = Item.WeaponType.AXE
|
||||||
|
item4.item_name = "Hand Axe"
|
||||||
|
item4.description = "A sharp hand axe"
|
||||||
|
item4.spriteFrame = 5*20 + 11
|
||||||
|
item4.modifiers["dmg"] = 4
|
||||||
|
GameManager.character_data.add_item(item4)
|
||||||
|
|
||||||
|
var item5 = Item.new()
|
||||||
|
item5.item_type = Item.ItemType.Equippable
|
||||||
|
item5.equipment_type = Item.EquipmentType.OFFHAND
|
||||||
|
item5.weapon_type = Item.WeaponType.AMMUNITION
|
||||||
|
item5.quantity = 13
|
||||||
|
item5.can_have_multiple_of = true
|
||||||
|
item5.item_name = "Iron Arrow"
|
||||||
|
item5.description = "A sharp arrow made of iron and feathers from pelican birds"
|
||||||
|
item5.spriteFrame = 7*20 + 11
|
||||||
|
item5.modifiers["dmg"] = 3
|
||||||
|
GameManager.character_data.add_item(item5)
|
||||||
|
|
||||||
|
var item6 = Item.new()
|
||||||
|
item6.item_type = Item.ItemType.Equippable
|
||||||
|
item6.equipment_type = Item.EquipmentType.MAINHAND
|
||||||
|
item6.weapon_type = Item.WeaponType.BOW
|
||||||
|
item6.item_name = "Wooden Bow"
|
||||||
|
item6.description = "A wooden bow made of elfish lembas trees"
|
||||||
|
item6.spriteFrame = 6*20 + 16
|
||||||
|
item6.modifiers["dmg"] = 3
|
||||||
|
GameManager.character_data.add_item(item6)
|
||||||
|
|
||||||
|
var item7 = Item.new()
|
||||||
|
item7.item_type = Item.ItemType.Equippable
|
||||||
|
item7.equipment_type = Item.EquipmentType.BOOTS
|
||||||
|
item7.weapon_type = Item.WeaponType.NONE
|
||||||
|
item7.item_name = "Sandals"
|
||||||
|
item7.description = "A pair of shitty sandals"
|
||||||
|
item7.equipmentPath = "res://assets/gfx/Puny-Characters/Layer 1 - Shoes/ShoesBrown.png"
|
||||||
|
item7.spriteFrame = 2*20 + 10
|
||||||
|
item7.modifiers["def"] = 1
|
||||||
|
GameManager.character_data.add_item(item7)
|
||||||
|
'
|
||||||
|
|
||||||
|
# clear default stuff
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.texture = null
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/LabelItemDescription.text = ""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _charChanged(iChar:CharacterStats):
|
||||||
|
# update all stats:
|
||||||
|
$ControlContainer/ControlStats/MarginContainer/VBoxContainer/HBoxContainer/LabelBaseStatsValue.text = str(iChar.level) + "\r\n" + \
|
||||||
|
"\r\n" + \
|
||||||
|
str(floori(iChar.hp)) + "/" + str(floori(iChar.maxhp)) + "\r\n" + \
|
||||||
|
str(floori(iChar.mp)) + "/" + str(floori(iChar.maxmp)) + "\r\n" + \
|
||||||
|
"\r\n" + \
|
||||||
|
str(iChar.baseStats.str) + "\r\n" + \
|
||||||
|
str(iChar.baseStats.dex) + "\r\n" + \
|
||||||
|
str(iChar.baseStats.end) + "\r\n" + \
|
||||||
|
str(iChar.baseStats.int) + "\r\n" + \
|
||||||
|
str(iChar.baseStats.wis) + "\r\n" + \
|
||||||
|
str(iChar.baseStats.lck)
|
||||||
|
|
||||||
|
$ControlContainer/ControlStats/MarginContainer/VBoxContainer/HBoxContainer/LabelDerivedStatsValue.text = str(floori(iChar.xp)) + "/" + str(floori(iChar.xp_to_next_level)) + "\r\n" + \
|
||||||
|
str(iChar.coin) + "\r\n" + \
|
||||||
|
"\r\n" + \
|
||||||
|
"\r\n" + \
|
||||||
|
"\r\n" + \
|
||||||
|
str(iChar.damage) + "\r\n" + \
|
||||||
|
str(iChar.defense) + "\r\n" + \
|
||||||
|
str(iChar.move_speed) + "\r\n" + \
|
||||||
|
str(iChar.attack_speed) + "\r\n" + \
|
||||||
|
str(iChar.sight) + "\r\n" + \
|
||||||
|
str(iChar.spell_amp) + "\r\n" + \
|
||||||
|
str(iChar.crit_chance) + "%"
|
||||||
|
|
||||||
|
# read inventory and populate inventory
|
||||||
|
var vboxInvent = $ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory
|
||||||
|
for child in vboxInvent.find_child("HBoxControl1").get_children():
|
||||||
|
child.get_parent().remove_child(child)
|
||||||
|
child.propagate_call("queue_free", [])
|
||||||
|
for child in vboxInvent.find_child("HBoxControl2").get_children():
|
||||||
|
child.get_parent().remove_child(child)
|
||||||
|
child.propagate_call("queue_free", [])
|
||||||
|
for child in vboxInvent.find_child("HBoxControl3").get_children():
|
||||||
|
child.get_parent().remove_child(child)
|
||||||
|
child.propagate_call("queue_free", [])
|
||||||
|
|
||||||
|
var selected_tex = preload("res://assets/gfx/ui/inventory_slot_kenny_white.png")
|
||||||
|
var styleBoxHover:StyleBox = StyleBoxTexture.new()
|
||||||
|
var styleBoxFocused:StyleBox = StyleBoxTexture.new()
|
||||||
|
var styleBoxPressed:StyleBox = StyleBoxTexture.new()
|
||||||
|
var styleBoxEmpty:StyleBox = StyleBoxEmpty.new()
|
||||||
|
styleBoxHover.texture = selected_tex
|
||||||
|
styleBoxFocused.texture = selected_tex
|
||||||
|
styleBoxPressed.texture = selected_tex
|
||||||
|
|
||||||
|
var quantityFont:Font = load("res://assets/fonts/dmg_numbers.png")
|
||||||
|
for item:Item in iChar.inventory:
|
||||||
|
var btn:Button = Button.new()
|
||||||
|
btn.add_theme_stylebox_override("normal", styleBoxEmpty)
|
||||||
|
btn.add_theme_stylebox_override("hover", styleBoxHover)
|
||||||
|
btn.add_theme_stylebox_override("focus", styleBoxFocused)
|
||||||
|
btn.add_theme_stylebox_override("pressed", styleBoxPressed)
|
||||||
|
btn.custom_minimum_size = Vector2(24, 24)
|
||||||
|
btn.size = Vector2(24, 24)
|
||||||
|
|
||||||
|
vboxInvent.find_child("HBoxControl1").add_child(btn)
|
||||||
|
var spr:Sprite2D = Sprite2D.new()
|
||||||
|
spr.texture = load(item.spritePath)
|
||||||
|
spr.hframes = item.spriteFrames.x
|
||||||
|
spr.vframes = item.spriteFrames.y
|
||||||
|
spr.frame = item.spriteFrame
|
||||||
|
spr.centered = false
|
||||||
|
spr.position = Vector2(4,4)
|
||||||
|
btn.add_child(spr)
|
||||||
|
|
||||||
|
if item.can_have_multiple_of:
|
||||||
|
var lblQuantity:Label = Label.new()
|
||||||
|
lblQuantity.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||||
|
lblQuantity.size = Vector2(24, 24)
|
||||||
|
lblQuantity.custom_minimum_size = Vector2(0,0)
|
||||||
|
lblQuantity.position = Vector2(10, 2)
|
||||||
|
lblQuantity.text = str(item.quantity)
|
||||||
|
lblQuantity.add_theme_font_override("font", quantityFont)
|
||||||
|
lblQuantity.add_theme_font_size_override("font", 8)
|
||||||
|
lblQuantity.scale = Vector2(0.5, 0.5)
|
||||||
|
btn.add_child(lblQuantity)
|
||||||
|
pass
|
||||||
|
|
||||||
|
btn.connect("pressed", _selectItem.bind(item, true))
|
||||||
|
btn.connect("focus_entered", _selectItem.bind(item, false))
|
||||||
|
|
||||||
|
pass
|
||||||
|
# clear eq container...
|
||||||
|
var eqContainer = $ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer
|
||||||
|
for child in eqContainer.get_children():
|
||||||
|
child.get_parent().remove_child(child)
|
||||||
|
child.propagate_call("queue_free", [])
|
||||||
|
'"mainhand": null,
|
||||||
|
"offhand": null,
|
||||||
|
"headgear": null,
|
||||||
|
"armour": null,
|
||||||
|
"boots": null,
|
||||||
|
"accessory": null'
|
||||||
|
addEq(iChar, "mainhand", eqContainer, styleBoxEmpty, styleBoxHover, styleBoxFocused, styleBoxPressed, quantityFont)
|
||||||
|
addEq(iChar, "offhand", eqContainer, styleBoxEmpty, styleBoxHover, styleBoxFocused, styleBoxPressed, quantityFont)
|
||||||
|
addEq(iChar, "headgear", eqContainer, styleBoxEmpty, styleBoxHover, styleBoxFocused, styleBoxPressed, quantityFont)
|
||||||
|
addEq(iChar, "armour", eqContainer, styleBoxEmpty, styleBoxHover, styleBoxFocused, styleBoxPressed, quantityFont)
|
||||||
|
addEq(iChar, "boots", eqContainer, styleBoxEmpty, styleBoxHover, styleBoxFocused, styleBoxPressed, quantityFont)
|
||||||
|
addEq(iChar, "accessory", eqContainer, styleBoxEmpty, styleBoxHover, styleBoxFocused, styleBoxPressed, quantityFont)
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
func addEq(iChar:CharacterStats, iPiece:String, eqContainer:Control, styleBoxEmpty: StyleBox, styleBoxHover: StyleBox, styleBoxFocused: StyleBox, styleBoxPressed: StyleBox, quantityFont: Font):
|
||||||
|
if iChar.equipment[iPiece] != null:
|
||||||
|
var btn:Button = Button.new()
|
||||||
|
btn.add_theme_stylebox_override("normal", styleBoxEmpty)
|
||||||
|
btn.add_theme_stylebox_override("hover", styleBoxHover)
|
||||||
|
btn.add_theme_stylebox_override("focus", styleBoxFocused)
|
||||||
|
btn.add_theme_stylebox_override("pressed", styleBoxPressed)
|
||||||
|
btn.custom_minimum_size = Vector2(24, 24)
|
||||||
|
btn.position = Vector2(1,9)
|
||||||
|
match iPiece:
|
||||||
|
"armour":
|
||||||
|
btn.position = Vector2(28, 9)
|
||||||
|
pass
|
||||||
|
"offhand":
|
||||||
|
btn.position = Vector2(55, 9)
|
||||||
|
pass
|
||||||
|
"headgear":
|
||||||
|
btn.position = Vector2(1, 36)
|
||||||
|
pass
|
||||||
|
"boots":
|
||||||
|
btn.position = Vector2(28, 36)
|
||||||
|
pass
|
||||||
|
"accessory":
|
||||||
|
btn.position = Vector2(55, 36)
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
|
||||||
|
eqContainer.add_child(btn)
|
||||||
|
var spr:Sprite2D = Sprite2D.new()
|
||||||
|
spr.texture = load(iChar.equipment[iPiece].spritePath)
|
||||||
|
spr.hframes = iChar.equipment[iPiece].spriteFrames.x
|
||||||
|
spr.vframes = iChar.equipment[iPiece].spriteFrames.y
|
||||||
|
spr.frame = iChar.equipment[iPiece].spriteFrame
|
||||||
|
spr.centered = false
|
||||||
|
spr.position = Vector2(4,4)
|
||||||
|
btn.add_child(spr)
|
||||||
|
btn.connect("pressed", _selectEquipment.bind(iChar.equipment[iPiece], true))
|
||||||
|
btn.connect("focus_entered", _selectEquipment.bind(iChar.equipment[iPiece], false))
|
||||||
|
|
||||||
|
if iChar.equipment[iPiece].can_have_multiple_of:
|
||||||
|
var lblQuantity:Label = Label.new()
|
||||||
|
lblQuantity.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||||
|
lblQuantity.size = Vector2(24, 24)
|
||||||
|
lblQuantity.custom_minimum_size = Vector2(0,0)
|
||||||
|
lblQuantity.position = Vector2(10, 2)
|
||||||
|
lblQuantity.text = str(iChar.equipment[iPiece].quantity)
|
||||||
|
lblQuantity.add_theme_font_override("font", quantityFont)
|
||||||
|
lblQuantity.add_theme_font_size_override("font", 8)
|
||||||
|
lblQuantity.scale = Vector2(0.5, 0.5)
|
||||||
|
btn.add_child(lblQuantity)
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
|
||||||
|
# draw equipment buttons (for unequipping)
|
||||||
|
func _selectEquipment(item:Item, isPress: bool):
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/ButtonDrop.visible = true
|
||||||
|
if !is_showing_inventory:
|
||||||
|
return
|
||||||
|
if lastPressedItem == item and lastPressedType == "Equipment" and timerSelect > 0:
|
||||||
|
$SfxUnequip.play()
|
||||||
|
GameManager.character_data.unequip_item(selectedEquipment)
|
||||||
|
selectedItem = selectedEquipment
|
||||||
|
lastPressedItem = null
|
||||||
|
selectedEquipment = null
|
||||||
|
lastPressedType = "Item"
|
||||||
|
var vboxInvent = $ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory
|
||||||
|
for child in vboxInvent.find_child("HBoxControl1").get_children():
|
||||||
|
child.grab_focus()
|
||||||
|
break
|
||||||
|
return
|
||||||
|
lastPressedType = "Equipment"
|
||||||
|
selectedEquipment = item
|
||||||
|
# update description in bottom
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.texture = load(item.spritePath)
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.hframes = item.spriteFrames.x
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.vframes = item.spriteFrames.y
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.frame = item.spriteFrame
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/LabelItemDescription.text = item.description
|
||||||
|
if isPress:
|
||||||
|
lastPressedItem = item
|
||||||
|
timerSelect = 0.4
|
||||||
|
# unequip item
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _selectItem(item:Item, isPress: bool):
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/ButtonDrop.visible = true
|
||||||
|
if !is_showing_inventory:
|
||||||
|
return
|
||||||
|
if lastPressedItem == item and lastPressedType == "Item" and timerSelect > 0:
|
||||||
|
timerSelect = 0
|
||||||
|
$SfxEquip.play()
|
||||||
|
GameManager.character_data.equip_item(selectedItem)
|
||||||
|
selectedEquipment = selectedItem
|
||||||
|
selectedItem = null
|
||||||
|
lastPressedItem = null
|
||||||
|
lastPressedType = "Equipment"
|
||||||
|
var eqContainer = $ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer
|
||||||
|
for child in eqContainer.get_children():
|
||||||
|
child.grab_focus()
|
||||||
|
break
|
||||||
|
return
|
||||||
|
lastPressedType = "Item"
|
||||||
|
selectedItem = item
|
||||||
|
# update description in bottom
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.texture = load(item.spritePath)
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.hframes = item.spriteFrames.x
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.vframes = item.spriteFrames.y
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.frame = item.spriteFrame
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/LabelItemDescription.text = item.description
|
||||||
|
if isPress:
|
||||||
|
lastPressedItem = item
|
||||||
|
timerSelect = 0.4
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
if timerSelect > 0:
|
||||||
|
timerSelect -= delta
|
||||||
|
if timerSelect <= 0:
|
||||||
|
timerSelect = 0
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _input(_event: InputEvent):
|
||||||
|
#print("connected to newtwork:", GameManager.is_network_connected)
|
||||||
|
#print("finished tween:", finished_tween)
|
||||||
|
if !GameManager.is_network_connected or finished_tween == false:
|
||||||
|
return
|
||||||
|
if is_showing_inventory:
|
||||||
|
if !$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/ButtonDrop.has_focus():
|
||||||
|
if lastPressedType == "Equipment" and selectedEquipment != null and Input.is_action_just_pressed("ui_accept"):
|
||||||
|
$SfxUnequip.play()
|
||||||
|
GameManager.character_data.unequip_item(selectedEquipment)
|
||||||
|
selectedItem = selectedEquipment
|
||||||
|
selectedEquipment = null
|
||||||
|
lastPressedItem = null
|
||||||
|
lastPressedType = "Item"
|
||||||
|
var vboxInvent = $ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory
|
||||||
|
for child in vboxInvent.find_child("HBoxControl1").get_children():
|
||||||
|
child.grab_focus()
|
||||||
|
break
|
||||||
|
return
|
||||||
|
if lastPressedType == "Item" and selectedItem != null and Input.is_action_just_pressed("ui_accept"):
|
||||||
|
$SfxEquip.play()
|
||||||
|
GameManager.character_data.equip_item(selectedItem)
|
||||||
|
selectedEquipment = selectedItem
|
||||||
|
selectedItem = null
|
||||||
|
lastPressedItem = null
|
||||||
|
lastPressedType = "Equipment"
|
||||||
|
var eqContainer = $ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer
|
||||||
|
for child in eqContainer.get_children():
|
||||||
|
child.grab_focus()
|
||||||
|
break
|
||||||
|
return
|
||||||
|
|
||||||
|
if Input.is_action_just_pressed("inventory") and finished_tween == true:
|
||||||
|
finished_tween = false
|
||||||
|
var move_range = 119
|
||||||
|
var move_duration = 0.3
|
||||||
|
if is_showing_inventory:
|
||||||
|
move_range = -move_range
|
||||||
|
#stats movement
|
||||||
|
var move_tween = get_tree().create_tween()
|
||||||
|
move_tween.tween_property($ControlContainer/ControlStats, "position:x",$ControlContainer/ControlStats.position.x + move_range,move_duration).from($ControlContainer/ControlStats.position.x).set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_IN_OUT)
|
||||||
|
move_range = -80
|
||||||
|
if is_showing_inventory:
|
||||||
|
move_range = -move_range
|
||||||
|
#inventory movement
|
||||||
|
var move_tween2 = get_tree().create_tween()
|
||||||
|
move_tween2.tween_property($ControlContainer/ControlInventory, "position:x",$ControlContainer/ControlInventory.position.x + move_range,move_duration).from($ControlContainer/ControlInventory.position.x).set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_IN_OUT)
|
||||||
|
|
||||||
|
move_range = -33
|
||||||
|
if is_showing_inventory:
|
||||||
|
$SfxInventoryClose.play()
|
||||||
|
move_range = -move_range
|
||||||
|
else:
|
||||||
|
$SfxInventoryOpen.play()
|
||||||
|
#info movement
|
||||||
|
var move_tween3 = get_tree().create_tween()
|
||||||
|
move_tween3.tween_property($ControlContainer/ControlInfo, "position:y",$ControlContainer/ControlInfo.position.y + move_range,move_duration).from($ControlContainer/ControlInfo.position.y).set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_IN_OUT)
|
||||||
|
|
||||||
|
is_showing_inventory = !is_showing_inventory
|
||||||
|
await move_tween3.finished
|
||||||
|
finished_tween = true
|
||||||
|
|
||||||
|
if is_showing_inventory:
|
||||||
|
# preselect first item
|
||||||
|
var vboxInvent = $ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory
|
||||||
|
var hadInventoryItem = false
|
||||||
|
for child in vboxInvent.find_child("HBoxControl1").get_children():
|
||||||
|
child.grab_focus()
|
||||||
|
hadInventoryItem = true
|
||||||
|
break
|
||||||
|
lastPressedType = "Item"
|
||||||
|
if hadInventoryItem == false:
|
||||||
|
# preselect something in equipment instead
|
||||||
|
selectedItem = null
|
||||||
|
lastPressedItem = null
|
||||||
|
lastPressedType = "Equipment"
|
||||||
|
var eqContainer = $ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer
|
||||||
|
for child in eqContainer.get_children():
|
||||||
|
child.grab_focus()
|
||||||
|
break
|
||||||
|
pass
|
||||||
|
|
||||||
|
pass
|
||||||
|
'
|
||||||
|
if Input.is_action_just_pressed("ui_right"):
|
||||||
|
$ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer.
|
||||||
|
pass
|
||||||
|
if Input.is_action_just_pressed("ui_left"):
|
||||||
|
|
||||||
|
pass'
|
||||||
|
'
|
||||||
|
if Input.is_action_just_pressed("ui_up"):
|
||||||
|
$ControlContainer/ControlInventory/Sprite2DSelector.position.y -= 20
|
||||||
|
pass
|
||||||
|
|
||||||
|
if Input.is_action_just_pressed("ui_down"):
|
||||||
|
$ControlContainer/ControlInventory/Sprite2DSelector.position.y += 20
|
||||||
|
pass'
|
||||||
|
|
||||||
|
|
||||||
|
func _on_button_drop_pressed() -> void:
|
||||||
|
if !is_showing_inventory:
|
||||||
|
return
|
||||||
|
if lastPressedType == "Item":
|
||||||
|
if selectedItem != null:
|
||||||
|
GameManager.character_data.drop_item(selectedItem)
|
||||||
|
selectedItem = null
|
||||||
|
# clear default stuff
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.texture = null
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/LabelItemDescription.text = ""
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/ButtonDrop.visible = false
|
||||||
|
else:
|
||||||
|
if selectedEquipment != null:
|
||||||
|
GameManager.character_data.drop_equipment(selectedEquipment)
|
||||||
|
selectedEquipment = null
|
||||||
|
# clear default stuff
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control/Sprite2DItem.texture = null
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/LabelItemDescription.text = ""
|
||||||
|
$ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/ButtonDrop.visible = false
|
||||||
|
|
||||||
|
var vboxInvent = $ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory
|
||||||
|
var hadInventoryItem = false
|
||||||
|
for child in vboxInvent.find_child("HBoxControl1").get_children():
|
||||||
|
child.grab_focus()
|
||||||
|
hadInventoryItem = true
|
||||||
|
break
|
||||||
|
lastPressedType = "Item"
|
||||||
|
if hadInventoryItem == false:
|
||||||
|
selectedItem = null
|
||||||
|
lastPressedItem = null
|
||||||
|
lastPressedType = "Equipment"
|
||||||
|
var eqContainer = $ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer
|
||||||
|
for child in eqContainer.get_children():
|
||||||
|
child.grab_focus()
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
pass # Replace with function body.
|
||||||
1
src/scripts/inspiration_scripts/inventory.gd.uid
Normal file
1
src/scripts/inspiration_scripts/inventory.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://20kfmxrtt20e
|
||||||
693
src/scripts/inspiration_scripts/inventory.tscn
Normal file
693
src/scripts/inspiration_scripts/inventory.tscn
Normal file
@@ -0,0 +1,693 @@
|
|||||||
|
[gd_scene format=3 uid="uid://du1cpug8yag6w"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://b7u7dbiaub8lp" path="res://assets/gfx/ruinborn_mImwZSNWBM.png" id="1_0amil"]
|
||||||
|
[ext_resource type="Script" uid="uid://20kfmxrtt20e" path="res://assets/scripts/ui/inventory.gd" id="1_k81k7"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://hib38y541eog" path="res://assets/gfx/items_n_shit.png" id="2_7vwhs"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://ct0rllwve2s1y" path="res://assets/gfx/ui/inventory_panel_small.png" id="2_voqm7"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://who0clhmi5cl" path="res://assets/gfx/ui/inventory_panel.png" id="4_nlhqn"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://cxend0ndnfn32" path="res://assets/gfx/ui/inventory_slot_kenny_white.png" id="4_nxmsh"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bsnfadlf1dgnw" path="res://assets/gfx/ui/inventory_slot_kenny_black_sword.png" id="6_k81k7"]
|
||||||
|
[ext_resource type="FontFile" uid="uid://cbmcfue0ek0tk" path="res://assets/fonts/dmg_numbers.png" id="7_kiwfx"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://tdivehfcj0el" path="res://assets/gfx/ui/inventory_slot_kenny_black_shield.png" id="7_vardb"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://b1l30o2ljhl2t" path="res://assets/gfx/ui/inventory_slot_kenny_black_armour.png" id="8_mnwqb"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://jgbrhnsaxvg" path="res://assets/gfx/ui/inventory_slot_kenny_black_helm.png" id="9_nbh80"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://b71gs7h2v0rdi" path="res://assets/gfx/ui/inventory_slot_kenny_black_ring.png" id="10_kiwfx"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://ckctmypotajtf" path="res://assets/gfx/ui/inventory_slot_kenny_black_shoes.png" id="11_ylqbh"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c21a60s4funrr" path="res://assets/gfx/ui/inventory_info_panel.png" id="13_vardb"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://x6lxrywls7e2" path="res://assets/audio/sfx/inventory/inventory_open.mp3" id="14_mnwqb"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://cfsubtwvpi7yn" path="res://assets/audio/sfx/inventory/inventory_open_inverted.mp3" id="14_nbh80"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://djw6c5rb4mm60" path="res://assets/audio/sfx/cloth/leather_cloth_02.wav.mp3" id="17_51fgf"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://umoxmryvbm01" path="res://assets/audio/sfx/cloth/leather_cloth_01.wav.mp3" id="18_qk47y"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_nbh80"]
|
||||||
|
texture = ExtResource("4_nxmsh")
|
||||||
|
modulate_color = Color(0.511719, 0.511719, 0.511719, 1)
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_kiwfx"]
|
||||||
|
texture = ExtResource("4_nxmsh")
|
||||||
|
modulate_color = Color(0, 0, 0, 1)
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_ylqbh"]
|
||||||
|
texture = ExtResource("4_nxmsh")
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_nbh80"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_51fgf"]
|
||||||
|
texture = ExtResource("4_nxmsh")
|
||||||
|
|
||||||
|
[node name="Inventory" type="CanvasLayer"]
|
||||||
|
layer = 21
|
||||||
|
script = ExtResource("1_k81k7")
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||||
|
visible = false
|
||||||
|
modulate = Color(0.980057, 0.975295, 1, 1)
|
||||||
|
z_index = -2
|
||||||
|
z_as_relative = false
|
||||||
|
position = Vector2(164.625, 92.75)
|
||||||
|
scale = Vector2(0.258803, 0.263268)
|
||||||
|
texture = ExtResource("1_0amil")
|
||||||
|
|
||||||
|
[node name="ControlContainer" type="Control" parent="."]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="ControlStats" type="Control" parent="ControlContainer"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 9
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Sprite2DPanelz" type="Sprite2D" parent="ControlContainer/ControlStats"]
|
||||||
|
texture = ExtResource("4_nlhqn")
|
||||||
|
centered = false
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="ControlContainer/ControlStats"]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 40.0
|
||||||
|
offset_bottom = 40.0
|
||||||
|
theme_override_constants/margin_left = 3
|
||||||
|
theme_override_constants/margin_top = 3
|
||||||
|
theme_override_constants/margin_right = 3
|
||||||
|
theme_override_constants/margin_bottom = 3
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="ControlContainer/ControlStats/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="LabelPlayerName" type="Label" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "Fronko"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="LabelBaseStats" type="Label" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/line_spacing = 0
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "Level
|
||||||
|
|
||||||
|
Hp
|
||||||
|
Mp
|
||||||
|
|
||||||
|
Str
|
||||||
|
Dex
|
||||||
|
End
|
||||||
|
Int
|
||||||
|
Wis
|
||||||
|
Lck"
|
||||||
|
|
||||||
|
[node name="LabelBaseStatsValue" type="Label" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/line_spacing = 0
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "1
|
||||||
|
|
||||||
|
30 / 30
|
||||||
|
20 / 20
|
||||||
|
|
||||||
|
10
|
||||||
|
10
|
||||||
|
10
|
||||||
|
10
|
||||||
|
10
|
||||||
|
10"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="LabelDerivedStats" type="Label" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/line_spacing = 0
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "Exp
|
||||||
|
Coin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Damage
|
||||||
|
Defense
|
||||||
|
MovSpd
|
||||||
|
AtkSpd
|
||||||
|
Sight
|
||||||
|
SpellAmp
|
||||||
|
CritChance"
|
||||||
|
|
||||||
|
[node name="LabelDerivedStatsValue" type="Label" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/line_spacing = 0
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "400 / 1000
|
||||||
|
1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
2
|
||||||
|
3
|
||||||
|
2.1
|
||||||
|
1.4
|
||||||
|
7.0
|
||||||
|
3.0
|
||||||
|
12.0%"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="ControlInventory" type="Control" parent="ControlContainer"]
|
||||||
|
custom_minimum_size = Vector2(80, 0)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 11
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Sprite2DPanel" type="Sprite2D" parent="ControlContainer/ControlInventory"]
|
||||||
|
modulate = Color(0, 0, 0, 0.862745)
|
||||||
|
texture = ExtResource("2_voqm7")
|
||||||
|
centered = false
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="ControlContainer/ControlInventory"]
|
||||||
|
custom_minimum_size = Vector2(80, 0)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_constants/margin_left = 2
|
||||||
|
theme_override_constants/margin_top = 2
|
||||||
|
theme_override_constants/margin_right = 2
|
||||||
|
theme_override_constants/margin_bottom = 2
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="ControlContainer/ControlInventory/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 0
|
||||||
|
|
||||||
|
[node name="LabelInventory" type="Label" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "Inventory"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="ControlInventory" type="Control" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(0, 72)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory"]
|
||||||
|
layout_mode = 2
|
||||||
|
offset_left = 2.0
|
||||||
|
offset_top = 2.0
|
||||||
|
offset_right = 78.0
|
||||||
|
offset_bottom = 70.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/margin_left = 2
|
||||||
|
theme_override_constants/margin_top = 2
|
||||||
|
theme_override_constants/margin_right = 2
|
||||||
|
theme_override_constants/margin_bottom = 2
|
||||||
|
|
||||||
|
[node name="ScrollContainer" type="ScrollContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
follow_focus = true
|
||||||
|
|
||||||
|
[node name="VBoxContainerInventory" type="VBoxContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = -4
|
||||||
|
|
||||||
|
[node name="HBoxControl1" type="HBoxContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/separation = 0
|
||||||
|
|
||||||
|
[node name="Button3" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1/Button3"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="LabelStack" type="Label" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1/Button3"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -26.0
|
||||||
|
offset_top = 2.0
|
||||||
|
offset_right = -2.0
|
||||||
|
offset_bottom = 26.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
theme_override_fonts/font = ExtResource("7_kiwfx")
|
||||||
|
theme_override_font_sizes/font_size = 8
|
||||||
|
text = "5"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="Button6" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1/Button6"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button5" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1/Button5"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button4" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1/Button4"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="HBoxControl2" type="HBoxContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/separation = 0
|
||||||
|
|
||||||
|
[node name="Button3" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2/Button3"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button5" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2/Button5"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button4" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2/Button4"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="HBoxControl3" type="HBoxContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/separation = 0
|
||||||
|
|
||||||
|
[node name="Button3" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3/Button3"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button5" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3/Button5"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button4" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3/Button4"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="ControlEquipment" type="Control" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(80, 64)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 5
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
offset_left = -21.5
|
||||||
|
offset_right = 21.5
|
||||||
|
offset_bottom = 23.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "Equipment"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(13, 21)
|
||||||
|
texture = ExtResource("6_k81k7")
|
||||||
|
|
||||||
|
[node name="Sprite2D2" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(67, 21)
|
||||||
|
texture = ExtResource("7_vardb")
|
||||||
|
|
||||||
|
[node name="Sprite2D3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(40, 21)
|
||||||
|
texture = ExtResource("8_mnwqb")
|
||||||
|
|
||||||
|
[node name="Sprite2D4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(13, 48)
|
||||||
|
texture = ExtResource("9_nbh80")
|
||||||
|
|
||||||
|
[node name="Sprite2D9" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(67, 48)
|
||||||
|
texture = ExtResource("10_kiwfx")
|
||||||
|
|
||||||
|
[node name="Sprite2D5" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(40, 48)
|
||||||
|
texture = ExtResource("11_ylqbh")
|
||||||
|
|
||||||
|
[node name="ControlEquipmentContainer" type="Control" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 1.0
|
||||||
|
offset_top = 9.0
|
||||||
|
offset_right = 25.0
|
||||||
|
offset_bottom = 33.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button2" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 28.0
|
||||||
|
offset_top = 9.0
|
||||||
|
offset_right = 52.0
|
||||||
|
offset_bottom = 33.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button2"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button3" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 55.0
|
||||||
|
offset_top = 9.0
|
||||||
|
offset_right = 79.0
|
||||||
|
offset_bottom = 33.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button3"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button4" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 1.0
|
||||||
|
offset_top = 36.0
|
||||||
|
offset_right = 25.0
|
||||||
|
offset_bottom = 60.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button4"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button5" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 28.0
|
||||||
|
offset_top = 36.0
|
||||||
|
offset_right = 52.0
|
||||||
|
offset_bottom = 60.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button5"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button6" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 55.0
|
||||||
|
offset_top = 36.0
|
||||||
|
offset_right = 79.0
|
||||||
|
offset_bottom = 60.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button6"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="ControlInfo" type="Control" parent="ControlContainer"]
|
||||||
|
custom_minimum_size = Vector2(0, 33)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 12
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
|
||||||
|
[node name="Control" type="Control" parent="ControlContainer/ControlInfo"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="ControlContainer/ControlInfo/Control"]
|
||||||
|
texture = ExtResource("13_vardb")
|
||||||
|
centered = false
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="ControlContainer/ControlInfo/Control"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_constants/margin_left = 5
|
||||||
|
theme_override_constants/margin_top = 5
|
||||||
|
theme_override_constants/margin_right = 5
|
||||||
|
theme_override_constants/margin_bottom = 5
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="ControlContainer/ControlInfo/Control/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Control" type="Control" parent="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 4
|
||||||
|
|
||||||
|
[node name="Sprite2DSelector" type="Sprite2D" parent="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control"]
|
||||||
|
modulate = Color(0.20704, 0.205805, 0.214844, 1)
|
||||||
|
texture = ExtResource("4_nxmsh")
|
||||||
|
centered = false
|
||||||
|
|
||||||
|
[node name="Sprite2DItem" type="Sprite2D" parent="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="LabelItemDescription" type="Label" parent="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 1
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "A small, but sturdy wooden shield. + 1 DEF"
|
||||||
|
|
||||||
|
[node name="ButtonDrop" type="Button" parent="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 8
|
||||||
|
text = "Drop"
|
||||||
|
|
||||||
|
[node name="SfxInventoryClose" type="AudioStreamPlayer2D" parent="."]
|
||||||
|
stream = ExtResource("14_nbh80")
|
||||||
|
volume_db = -10.0
|
||||||
|
|
||||||
|
[node name="SfxInventoryOpen" type="AudioStreamPlayer2D" parent="."]
|
||||||
|
stream = ExtResource("14_mnwqb")
|
||||||
|
volume_db = -10.0
|
||||||
|
|
||||||
|
[node name="SfxUnequip" type="AudioStreamPlayer2D" parent="."]
|
||||||
|
stream = ExtResource("17_51fgf")
|
||||||
|
|
||||||
|
[node name="SfxEquip" type="AudioStreamPlayer2D" parent="."]
|
||||||
|
stream = ExtResource("18_qk47y")
|
||||||
|
|
||||||
|
[connection signal="pressed" from="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/ButtonDrop" to="." method="_on_button_drop_pressed"]
|
||||||
692
src/scripts/inspiration_scripts/inventory.tscn3980824984.tmp
Normal file
692
src/scripts/inspiration_scripts/inventory.tscn3980824984.tmp
Normal file
@@ -0,0 +1,692 @@
|
|||||||
|
[gd_scene load_steps=24 format=3 uid="uid://du1cpug8yag6w"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://b7u7dbiaub8lp" path="res://assets/gfx/ruinborn_mImwZSNWBM.png" id="1_0amil"]
|
||||||
|
[ext_resource type="Script" uid="uid://20kfmxrtt20e" path="res://assets/scripts/ui/inventory.gd" id="1_k81k7"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://hib38y541eog" path="res://assets/gfx/items_n_shit.png" id="2_7vwhs"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://ct0rllwve2s1y" path="res://assets/gfx/ui/inventory_panel_small.png" id="2_voqm7"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://who0clhmi5cl" path="res://assets/gfx/ui/inventory_panel.png" id="4_nlhqn"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://cxend0ndnfn32" path="res://assets/gfx/ui/inventory_slot_kenny_white.png" id="4_nxmsh"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bsnfadlf1dgnw" path="res://assets/gfx/ui/inventory_slot_kenny_black_sword.png" id="6_k81k7"]
|
||||||
|
[ext_resource type="FontFile" uid="uid://cbmcfue0ek0tk" path="res://assets/fonts/dmg_numbers.png" id="7_kiwfx"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://tdivehfcj0el" path="res://assets/gfx/ui/inventory_slot_kenny_black_shield.png" id="7_vardb"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://b1l30o2ljhl2t" path="res://assets/gfx/ui/inventory_slot_kenny_black_armour.png" id="8_mnwqb"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://jgbrhnsaxvg" path="res://assets/gfx/ui/inventory_slot_kenny_black_helm.png" id="9_nbh80"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://b71gs7h2v0rdi" path="res://assets/gfx/ui/inventory_slot_kenny_black_ring.png" id="10_kiwfx"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://ckctmypotajtf" path="res://assets/gfx/ui/inventory_slot_kenny_black_shoes.png" id="11_ylqbh"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c21a60s4funrr" path="res://assets/gfx/ui/inventory_info_panel.png" id="13_vardb"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://x6lxrywls7e2" path="res://assets/audio/sfx/inventory/inventory_open.mp3" id="14_mnwqb"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://cfsubtwvpi7yn" path="res://assets/audio/sfx/inventory/inventory_open_inverted.mp3" id="14_nbh80"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://djw6c5rb4mm60" path="res://assets/audio/sfx/cloth/leather_cloth_02.wav.mp3" id="17_51fgf"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://umoxmryvbm01" path="res://assets/audio/sfx/cloth/leather_cloth_01.wav.mp3" id="18_qk47y"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_nbh80"]
|
||||||
|
texture = ExtResource("4_nxmsh")
|
||||||
|
modulate_color = Color(0.511719, 0.511719, 0.511719, 1)
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_kiwfx"]
|
||||||
|
texture = ExtResource("4_nxmsh")
|
||||||
|
modulate_color = Color(0, 0, 0, 1)
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_ylqbh"]
|
||||||
|
texture = ExtResource("4_nxmsh")
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_nbh80"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_51fgf"]
|
||||||
|
texture = ExtResource("4_nxmsh")
|
||||||
|
|
||||||
|
[node name="Inventory" type="CanvasLayer"]
|
||||||
|
layer = 21
|
||||||
|
script = ExtResource("1_k81k7")
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||||
|
visible = false
|
||||||
|
modulate = Color(0.980057, 0.975295, 1, 1)
|
||||||
|
z_index = -2
|
||||||
|
z_as_relative = false
|
||||||
|
position = Vector2(164.625, 92.75)
|
||||||
|
scale = Vector2(0.258803, 0.263268)
|
||||||
|
texture = ExtResource("1_0amil")
|
||||||
|
|
||||||
|
[node name="ControlContainer" type="Control" parent="."]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="ControlStats" type="Control" parent="ControlContainer"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 9
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Sprite2DPanelz" type="Sprite2D" parent="ControlContainer/ControlStats"]
|
||||||
|
texture = ExtResource("4_nlhqn")
|
||||||
|
centered = false
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="ControlContainer/ControlStats"]
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 40.0
|
||||||
|
offset_bottom = 40.0
|
||||||
|
theme_override_constants/margin_left = 3
|
||||||
|
theme_override_constants/margin_top = 3
|
||||||
|
theme_override_constants/margin_right = 3
|
||||||
|
theme_override_constants/margin_bottom = 3
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="ControlContainer/ControlStats/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="LabelPlayerName" type="Label" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "Fronko"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="LabelBaseStats" type="Label" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/line_spacing = 0
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "Level
|
||||||
|
|
||||||
|
Hp
|
||||||
|
Mp
|
||||||
|
|
||||||
|
Str
|
||||||
|
Dex
|
||||||
|
End
|
||||||
|
Int
|
||||||
|
Wis
|
||||||
|
Lck"
|
||||||
|
|
||||||
|
[node name="LabelBaseStatsValue" type="Label" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/line_spacing = 0
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "1
|
||||||
|
|
||||||
|
30 / 30
|
||||||
|
20 / 20
|
||||||
|
|
||||||
|
10
|
||||||
|
10
|
||||||
|
10
|
||||||
|
10
|
||||||
|
10
|
||||||
|
10"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="LabelDerivedStats" type="Label" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/line_spacing = 0
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "Exp
|
||||||
|
Coin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Damage
|
||||||
|
Defense
|
||||||
|
MovSpd
|
||||||
|
AtkSpd
|
||||||
|
Sight
|
||||||
|
SpellAmp
|
||||||
|
CritChance"
|
||||||
|
|
||||||
|
[node name="LabelDerivedStatsValue" type="Label" parent="ControlContainer/ControlStats/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/line_spacing = 0
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "400 / 1000
|
||||||
|
1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
2
|
||||||
|
3
|
||||||
|
2.1
|
||||||
|
1.4
|
||||||
|
7.0
|
||||||
|
3.0
|
||||||
|
12.0%"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="ControlInventory" type="Control" parent="ControlContainer"]
|
||||||
|
custom_minimum_size = Vector2(80, 0)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 11
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Sprite2DPanel" type="Sprite2D" parent="ControlContainer/ControlInventory"]
|
||||||
|
modulate = Color(0, 0, 0, 0.862745)
|
||||||
|
texture = ExtResource("2_voqm7")
|
||||||
|
centered = false
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="ControlContainer/ControlInventory"]
|
||||||
|
custom_minimum_size = Vector2(80, 0)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_constants/margin_left = 2
|
||||||
|
theme_override_constants/margin_top = 2
|
||||||
|
theme_override_constants/margin_right = 2
|
||||||
|
theme_override_constants/margin_bottom = 2
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="ControlContainer/ControlInventory/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 0
|
||||||
|
|
||||||
|
[node name="LabelInventory" type="Label" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "Inventory"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="ControlInventory" type="Control" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(0, 72)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory"]
|
||||||
|
layout_mode = 2
|
||||||
|
offset_left = 2.0
|
||||||
|
offset_top = 2.0
|
||||||
|
offset_right = 78.0
|
||||||
|
offset_bottom = 70.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/margin_left = 2
|
||||||
|
theme_override_constants/margin_top = 2
|
||||||
|
theme_override_constants/margin_right = 2
|
||||||
|
theme_override_constants/margin_bottom = 2
|
||||||
|
|
||||||
|
[node name="ScrollContainer" type="ScrollContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
follow_focus = true
|
||||||
|
|
||||||
|
[node name="VBoxContainerInventory" type="VBoxContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = -4
|
||||||
|
|
||||||
|
[node name="HBoxControl1" type="HBoxContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/separation = 0
|
||||||
|
|
||||||
|
[node name="Button3" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1/Button3"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="LabelStack" type="Label" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1/Button3"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -26.0
|
||||||
|
offset_top = 2.0
|
||||||
|
offset_right = -2.0
|
||||||
|
offset_bottom = 26.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
theme_override_fonts/font = ExtResource("7_kiwfx")
|
||||||
|
theme_override_font_sizes/font_size = 8
|
||||||
|
text = "5"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="Button6" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1/Button6"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button5" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1/Button5"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button4" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl1/Button4"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="HBoxControl2" type="HBoxContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/separation = 0
|
||||||
|
|
||||||
|
[node name="Button3" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2/Button3"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button5" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2/Button5"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button4" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl2/Button4"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="HBoxControl3" type="HBoxContainer" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/separation = 0
|
||||||
|
|
||||||
|
[node name="Button3" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3/Button3"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button5" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3/Button5"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button4" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
theme_override_styles/focus = SubResource("StyleBoxTexture_nbh80")
|
||||||
|
theme_override_styles/hover = SubResource("StyleBoxTexture_kiwfx")
|
||||||
|
theme_override_styles/pressed = SubResource("StyleBoxTexture_ylqbh")
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxEmpty_nbh80")
|
||||||
|
text = " "
|
||||||
|
|
||||||
|
[node name="Sprite2DShield4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlInventory/MarginContainer/ScrollContainer/VBoxContainerInventory/HBoxControl3/Button4"]
|
||||||
|
process_mode = 4
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="ControlEquipment" type="Control" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(80, 64)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 5
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
offset_left = -21.5
|
||||||
|
offset_right = 21.5
|
||||||
|
offset_bottom = 23.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "Equipment"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(13, 21)
|
||||||
|
texture = ExtResource("6_k81k7")
|
||||||
|
|
||||||
|
[node name="Sprite2D2" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(67, 21)
|
||||||
|
texture = ExtResource("7_vardb")
|
||||||
|
|
||||||
|
[node name="Sprite2D3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(40, 21)
|
||||||
|
texture = ExtResource("8_mnwqb")
|
||||||
|
|
||||||
|
[node name="Sprite2D4" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(13, 48)
|
||||||
|
texture = ExtResource("9_nbh80")
|
||||||
|
|
||||||
|
[node name="Sprite2D9" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(67, 48)
|
||||||
|
texture = ExtResource("10_kiwfx")
|
||||||
|
|
||||||
|
[node name="Sprite2D5" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
modulate = Color(1, 1, 1, 0.670588)
|
||||||
|
position = Vector2(40, 48)
|
||||||
|
texture = ExtResource("11_ylqbh")
|
||||||
|
|
||||||
|
[node name="ControlEquipmentContainer" type="Control" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 1.0
|
||||||
|
offset_top = 9.0
|
||||||
|
offset_right = 25.0
|
||||||
|
offset_bottom = 33.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button2" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 28.0
|
||||||
|
offset_top = 9.0
|
||||||
|
offset_right = 52.0
|
||||||
|
offset_bottom = 33.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button2"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button3" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 55.0
|
||||||
|
offset_top = 9.0
|
||||||
|
offset_right = 79.0
|
||||||
|
offset_bottom = 33.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button3"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button4" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 1.0
|
||||||
|
offset_top = 36.0
|
||||||
|
offset_right = 25.0
|
||||||
|
offset_bottom = 60.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button4"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button5" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 28.0
|
||||||
|
offset_top = 36.0
|
||||||
|
offset_right = 52.0
|
||||||
|
offset_bottom = 60.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button5"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="Button6" type="Button" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 55.0
|
||||||
|
offset_top = 36.0
|
||||||
|
offset_right = 79.0
|
||||||
|
offset_bottom = 60.0
|
||||||
|
theme_override_styles/normal = SubResource("StyleBoxTexture_51fgf")
|
||||||
|
|
||||||
|
[node name="Sprite2DShield3" type="Sprite2D" parent="ControlContainer/ControlInventory/MarginContainer/VBoxContainer/ControlEquipment/ControlEquipmentContainer/Button6"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="ControlInfo" type="Control" parent="ControlContainer"]
|
||||||
|
custom_minimum_size = Vector2(0, 33)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 12
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
|
||||||
|
[node name="Control" type="Control" parent="ControlContainer/ControlInfo"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="ControlContainer/ControlInfo/Control"]
|
||||||
|
texture = ExtResource("13_vardb")
|
||||||
|
centered = false
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="ControlContainer/ControlInfo/Control"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_constants/margin_left = 5
|
||||||
|
theme_override_constants/margin_top = 5
|
||||||
|
theme_override_constants/margin_right = 5
|
||||||
|
theme_override_constants/margin_bottom = 5
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="ControlContainer/ControlInfo/Control/MarginContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Control" type="Control" parent="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(24, 24)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 4
|
||||||
|
|
||||||
|
[node name="Sprite2DSelector" type="Sprite2D" parent="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control"]
|
||||||
|
modulate = Color(0.20704, 0.205805, 0.214844, 1)
|
||||||
|
texture = ExtResource("4_nxmsh")
|
||||||
|
centered = false
|
||||||
|
|
||||||
|
[node name="Sprite2DItem" type="Sprite2D" parent="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer/Control"]
|
||||||
|
position = Vector2(4, 4)
|
||||||
|
texture = ExtResource("2_7vwhs")
|
||||||
|
centered = false
|
||||||
|
hframes = 20
|
||||||
|
vframes = 14
|
||||||
|
frame = 8
|
||||||
|
|
||||||
|
[node name="LabelItemDescription" type="Label" parent="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 1
|
||||||
|
theme_override_font_sizes/font_size = 6
|
||||||
|
text = "A small, but sturdy wooden shield. + 1 DEF"
|
||||||
|
|
||||||
|
[node name="ButtonEquip" type="Button" parent="ControlContainer/ControlInfo/Control/MarginContainer/HBoxContainer"]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 8
|
||||||
|
text = "Equip"
|
||||||
|
|
||||||
|
[node name="SfxInventoryClose" type="AudioStreamPlayer2D" parent="."]
|
||||||
|
stream = ExtResource("14_nbh80")
|
||||||
|
volume_db = -10.0
|
||||||
|
|
||||||
|
[node name="SfxInventoryOpen" type="AudioStreamPlayer2D" parent="."]
|
||||||
|
stream = ExtResource("14_mnwqb")
|
||||||
|
volume_db = -10.0
|
||||||
|
|
||||||
|
[node name="SfxUnequip" type="AudioStreamPlayer2D" parent="."]
|
||||||
|
stream = ExtResource("17_51fgf")
|
||||||
|
|
||||||
|
[node name="SfxEquip" type="AudioStreamPlayer2D" parent="."]
|
||||||
|
stream = ExtResource("18_qk47y")
|
||||||
@@ -27,7 +27,11 @@ enum WeaponType {
|
|||||||
AMMUNITION,
|
AMMUNITION,
|
||||||
BOW,
|
BOW,
|
||||||
SWORD,
|
SWORD,
|
||||||
AXE
|
AXE,
|
||||||
|
DAGGER,
|
||||||
|
STAFF,
|
||||||
|
SPEAR,
|
||||||
|
MACE
|
||||||
}
|
}
|
||||||
|
|
||||||
var use_function = null
|
var use_function = null
|
||||||
|
|||||||
@@ -279,6 +279,19 @@ func _break_into_pieces():
|
|||||||
$Shadow.visible = false
|
$Shadow.visible = false
|
||||||
$Sprite2DAbove.visible = false
|
$Sprite2DAbove.visible = false
|
||||||
$Sprite2D.visible = false
|
$Sprite2D.visible = false
|
||||||
|
|
||||||
|
# Spawn item loot when breaking (30% chance)
|
||||||
|
if is_multiplayer_authority():
|
||||||
|
var drop_chance = randf()
|
||||||
|
if drop_chance < 0.3: # 30% chance to drop item
|
||||||
|
var item = ItemDatabase.get_random_container_item()
|
||||||
|
if item:
|
||||||
|
var entities_node = get_parent()
|
||||||
|
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||||
|
if entities_node and game_world:
|
||||||
|
ItemLootHelper.spawn_item_loot(item, global_position, entities_node, game_world)
|
||||||
|
print(name, " dropped item: ", item.item_name, " when broken")
|
||||||
|
|
||||||
if ($SfxShatter.playing):
|
if ($SfxShatter.playing):
|
||||||
await $SfxShatter.finished
|
await $SfxShatter.finished
|
||||||
if ($SfxBreakCrate.playing):
|
if ($SfxBreakCrate.playing):
|
||||||
@@ -562,13 +575,13 @@ func _open_chest(by_player: Node = null):
|
|||||||
$SfxChestOpen.play()
|
$SfxChestOpen.play()
|
||||||
|
|
||||||
print(name, " opened by ", by_player.name, "! Item given: ", selected_loot.name)
|
print(name, " opened by ", by_player.name, "! Item given: ", selected_loot.name)
|
||||||
|
|
||||||
|
# Sync chest opening visual to all clients (item already given on server)
|
||||||
|
if multiplayer.has_multiplayer_peer():
|
||||||
|
var player_peer_id = by_player.get_multiplayer_authority() if by_player else 0
|
||||||
|
_sync_chest_open.rpc(selected_loot.type if by_player else "coin", player_peer_id)
|
||||||
else:
|
else:
|
||||||
push_error("Chest: ERROR - No valid player to give item to!")
|
push_error("Chest: ERROR - No valid player to give item to!")
|
||||||
|
|
||||||
# Sync chest opening visual to all clients (item already given on server)
|
|
||||||
if multiplayer.has_multiplayer_peer():
|
|
||||||
var player_peer_id = by_player.get_multiplayer_authority() if by_player else 0
|
|
||||||
_sync_chest_open.rpc(selected_loot.type if by_player else "coin", player_peer_id)
|
|
||||||
|
|
||||||
@rpc("any_peer", "reliable")
|
@rpc("any_peer", "reliable")
|
||||||
func _request_chest_open(player_peer_id: int):
|
func _request_chest_open(player_peer_id: int):
|
||||||
|
|||||||
566
src/scripts/inventory_ui.gd
Normal file
566
src/scripts/inventory_ui.gd
Normal file
@@ -0,0 +1,566 @@
|
|||||||
|
extends CanvasLayer
|
||||||
|
|
||||||
|
# Minimalistic Inventory UI with Stats Panel
|
||||||
|
# Toggle with Tab key, shows stats, equipment slots and inventory items
|
||||||
|
|
||||||
|
var is_open: bool = false
|
||||||
|
var local_player: Node = null
|
||||||
|
|
||||||
|
# Selection tracking
|
||||||
|
var selected_item: Item = null # Selected inventory item
|
||||||
|
var selected_slot: String = "" # Selected equipment slot name
|
||||||
|
var selected_type: String = "" # "item" or "equipment"
|
||||||
|
|
||||||
|
# UI Nodes
|
||||||
|
var container: Control = null
|
||||||
|
var stats_panel: Control = null
|
||||||
|
var equipment_panel: Control = null
|
||||||
|
var inventory_grid: GridContainer = null
|
||||||
|
var scroll_container: ScrollContainer = null
|
||||||
|
|
||||||
|
# Stats labels
|
||||||
|
var label_base_stats: Label = null
|
||||||
|
var label_base_stats_value: Label = null
|
||||||
|
var label_derived_stats: Label = null
|
||||||
|
var label_derived_stats_value: Label = null
|
||||||
|
|
||||||
|
# Store button/item mappings for selection highlighting
|
||||||
|
var inventory_buttons: Dictionary = {} # item -> button
|
||||||
|
var equipment_buttons: Dictionary = {} # slot_name -> button
|
||||||
|
|
||||||
|
# Equipment slot buttons
|
||||||
|
var equipment_slots: Dictionary = {
|
||||||
|
"mainhand": null,
|
||||||
|
"offhand": null,
|
||||||
|
"headgear": null,
|
||||||
|
"armour": null,
|
||||||
|
"boots": null,
|
||||||
|
"accessory": null
|
||||||
|
}
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
# Set layer to be above game but below chat
|
||||||
|
layer = 150
|
||||||
|
|
||||||
|
# Create UI structure
|
||||||
|
_setup_ui()
|
||||||
|
|
||||||
|
# Find local player
|
||||||
|
call_deferred("_find_local_player")
|
||||||
|
|
||||||
|
func _find_local_player():
|
||||||
|
# Find the local player
|
||||||
|
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||||
|
if game_world:
|
||||||
|
var player_manager = game_world.get_node_or_null("PlayerManager")
|
||||||
|
if player_manager:
|
||||||
|
var local_players = player_manager.get_local_players()
|
||||||
|
if local_players.size() > 0:
|
||||||
|
local_player = local_players[0]
|
||||||
|
if local_player and local_player.character_stats:
|
||||||
|
# Connect to character_changed signal
|
||||||
|
if local_player.character_stats.character_changed.is_connected(_on_character_changed):
|
||||||
|
local_player.character_stats.character_changed.disconnect(_on_character_changed)
|
||||||
|
local_player.character_stats.character_changed.connect(_on_character_changed)
|
||||||
|
# Initial update
|
||||||
|
_update_ui()
|
||||||
|
_update_stats()
|
||||||
|
|
||||||
|
func _setup_ui():
|
||||||
|
# Main container (anchored to bottom-left, larger to fit stats + inventory)
|
||||||
|
container = Control.new()
|
||||||
|
container.name = "InventoryContainer"
|
||||||
|
container.set_anchors_preset(Control.PRESET_BOTTOM_LEFT)
|
||||||
|
container.offset_left = 10 # 10px padding from left
|
||||||
|
container.offset_right = 10 + 620 # Width 620px (stats 200px + inventory 400px + gap 20px)
|
||||||
|
container.offset_top = -350 # Height 340px (350 - 10)
|
||||||
|
container.offset_bottom = -10 # 10px padding from bottom
|
||||||
|
container.visible = false
|
||||||
|
add_child(container)
|
||||||
|
|
||||||
|
# Background panel
|
||||||
|
var background = ColorRect.new()
|
||||||
|
background.name = "Background"
|
||||||
|
background.color = Color(0.1, 0.1, 0.1, 0.85)
|
||||||
|
background.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
container.add_child(background)
|
||||||
|
background.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
|
||||||
|
# HBox for stats and inventory side by side
|
||||||
|
var hbox = HBoxContainer.new()
|
||||||
|
hbox.name = "HBox"
|
||||||
|
container.add_child(hbox)
|
||||||
|
hbox.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
hbox.add_theme_constant_override("separation", 10)
|
||||||
|
|
||||||
|
# Stats panel (left side)
|
||||||
|
_create_stats_panel()
|
||||||
|
hbox.add_child(stats_panel)
|
||||||
|
|
||||||
|
# Inventory/Equipment panel (right side)
|
||||||
|
var inv_panel = VBoxContainer.new()
|
||||||
|
inv_panel.name = "InventoryPanel"
|
||||||
|
inv_panel.custom_minimum_size = Vector2(400, 0)
|
||||||
|
hbox.add_child(inv_panel)
|
||||||
|
inv_panel.add_theme_constant_override("separation", 5)
|
||||||
|
|
||||||
|
# Equipment label
|
||||||
|
var eq_label = Label.new()
|
||||||
|
eq_label.text = "Equipment"
|
||||||
|
eq_label.add_theme_font_size_override("font_size", 14)
|
||||||
|
var standard_font_resource = null
|
||||||
|
if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
|
||||||
|
standard_font_resource = load("res://assets/fonts/standard_font.png")
|
||||||
|
if standard_font_resource:
|
||||||
|
eq_label.add_theme_font_override("font", standard_font_resource)
|
||||||
|
inv_panel.add_child(eq_label)
|
||||||
|
|
||||||
|
equipment_panel = GridContainer.new()
|
||||||
|
equipment_panel.name = "EquipmentPanel"
|
||||||
|
equipment_panel.columns = 3
|
||||||
|
equipment_panel.add_theme_constant_override("h_separation", 5)
|
||||||
|
equipment_panel.add_theme_constant_override("v_separation", 5)
|
||||||
|
inv_panel.add_child(equipment_panel)
|
||||||
|
|
||||||
|
# Create equipment slot buttons
|
||||||
|
_create_equipment_slots()
|
||||||
|
|
||||||
|
# Inventory label
|
||||||
|
var inv_label = Label.new()
|
||||||
|
inv_label.text = "Inventory"
|
||||||
|
inv_label.add_theme_font_size_override("font_size", 14)
|
||||||
|
if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
|
||||||
|
standard_font_resource = load("res://assets/fonts/standard_font.png")
|
||||||
|
if standard_font_resource:
|
||||||
|
inv_label.add_theme_font_override("font", standard_font_resource)
|
||||||
|
inv_panel.add_child(inv_label)
|
||||||
|
|
||||||
|
# Scroll container for inventory
|
||||||
|
scroll_container = ScrollContainer.new()
|
||||||
|
scroll_container.name = "InventoryScroll"
|
||||||
|
scroll_container.custom_minimum_size = Vector2(380, 120)
|
||||||
|
inv_panel.add_child(scroll_container)
|
||||||
|
|
||||||
|
# Inventory grid
|
||||||
|
inventory_grid = GridContainer.new()
|
||||||
|
inventory_grid.name = "InventoryGrid"
|
||||||
|
inventory_grid.columns = 6
|
||||||
|
inventory_grid.add_theme_constant_override("h_separation", 3)
|
||||||
|
inventory_grid.add_theme_constant_override("v_separation", 3)
|
||||||
|
scroll_container.add_child(inventory_grid)
|
||||||
|
|
||||||
|
func _create_stats_panel():
|
||||||
|
stats_panel = VBoxContainer.new()
|
||||||
|
stats_panel.name = "StatsPanel"
|
||||||
|
stats_panel.custom_minimum_size = Vector2(200, 0)
|
||||||
|
|
||||||
|
# Stats label
|
||||||
|
var stats_label = Label.new()
|
||||||
|
stats_label.text = "Stats"
|
||||||
|
stats_label.add_theme_font_size_override("font_size", 14)
|
||||||
|
var standard_font_resource = null
|
||||||
|
if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
|
||||||
|
standard_font_resource = load("res://assets/fonts/standard_font.png")
|
||||||
|
if standard_font_resource:
|
||||||
|
stats_label.add_theme_font_override("font", standard_font_resource)
|
||||||
|
stats_panel.add_child(stats_label)
|
||||||
|
|
||||||
|
# HBox for stats columns
|
||||||
|
var stats_hbox = HBoxContainer.new()
|
||||||
|
stats_hbox.name = "StatsHBox"
|
||||||
|
stats_panel.add_child(stats_hbox)
|
||||||
|
stats_hbox.add_theme_constant_override("separation", 5)
|
||||||
|
|
||||||
|
# Base stats label
|
||||||
|
label_base_stats = Label.new()
|
||||||
|
label_base_stats.name = "LabelBaseStats"
|
||||||
|
label_base_stats.text = "Level\n\nHP\nMP\n\nSTR\nDEX\nEND\nINT\nWIS\nLCK"
|
||||||
|
label_base_stats.add_theme_font_size_override("font_size", 10)
|
||||||
|
if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
|
||||||
|
var font_resource = load("res://assets/fonts/standard_font.png")
|
||||||
|
if font_resource:
|
||||||
|
label_base_stats.add_theme_font_override("font", font_resource)
|
||||||
|
stats_hbox.add_child(label_base_stats)
|
||||||
|
|
||||||
|
# Base stats values label
|
||||||
|
label_base_stats_value = Label.new()
|
||||||
|
label_base_stats_value.name = "LabelBaseStatsValue"
|
||||||
|
label_base_stats_value.text = "1\n\n30/30\n20/20\n\n10\n10\n10\n10\n10\n10"
|
||||||
|
label_base_stats_value.add_theme_font_size_override("font_size", 10)
|
||||||
|
label_base_stats_value.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||||
|
if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
|
||||||
|
var font_resource = load("res://assets/fonts/standard_font.png")
|
||||||
|
if font_resource:
|
||||||
|
label_base_stats_value.add_theme_font_override("font", font_resource)
|
||||||
|
stats_hbox.add_child(label_base_stats_value)
|
||||||
|
|
||||||
|
# Derived stats label
|
||||||
|
label_derived_stats = Label.new()
|
||||||
|
label_derived_stats.name = "LabelDerivedStats"
|
||||||
|
label_derived_stats.text = "XP\nCoin\n\n\n\nDMG\nDEF\nMovSpd\nAtkSpd\nSight\nSpellAmp\nCrit%"
|
||||||
|
label_derived_stats.add_theme_font_size_override("font_size", 10)
|
||||||
|
if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
|
||||||
|
var font_resource = load("res://assets/fonts/standard_font.png")
|
||||||
|
if font_resource:
|
||||||
|
label_derived_stats.add_theme_font_override("font", font_resource)
|
||||||
|
stats_hbox.add_child(label_derived_stats)
|
||||||
|
|
||||||
|
# Derived stats values label
|
||||||
|
label_derived_stats_value = Label.new()
|
||||||
|
label_derived_stats_value.name = "LabelDerivedStatsValue"
|
||||||
|
label_derived_stats_value.text = "0/100\n0\n\n\n\n2.0\n2.0\n2.1\n1.4\n7.0\n5.0\n12.0%"
|
||||||
|
label_derived_stats_value.add_theme_font_size_override("font_size", 10)
|
||||||
|
label_derived_stats_value.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||||
|
if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
|
||||||
|
var font_resource = load("res://assets/fonts/standard_font.png")
|
||||||
|
if font_resource:
|
||||||
|
label_derived_stats_value.add_theme_font_override("font", font_resource)
|
||||||
|
stats_hbox.add_child(label_derived_stats_value)
|
||||||
|
|
||||||
|
func _update_stats():
|
||||||
|
if not local_player or not local_player.character_stats:
|
||||||
|
return
|
||||||
|
|
||||||
|
var char_stats = local_player.character_stats
|
||||||
|
|
||||||
|
# Update base stats
|
||||||
|
label_base_stats_value.text = str(char_stats.level) + "\n\n" + \
|
||||||
|
str(int(char_stats.hp)) + "/" + str(int(char_stats.maxhp)) + "\n" + \
|
||||||
|
str(int(char_stats.mp)) + "/" + str(int(char_stats.maxmp)) + "\n\n" + \
|
||||||
|
str(char_stats.baseStats.str) + "\n" + \
|
||||||
|
str(char_stats.baseStats.dex) + "\n" + \
|
||||||
|
str(char_stats.baseStats.end) + "\n" + \
|
||||||
|
str(char_stats.baseStats.int) + "\n" + \
|
||||||
|
str(char_stats.baseStats.wis) + "\n" + \
|
||||||
|
str(char_stats.baseStats.lck)
|
||||||
|
|
||||||
|
# Update derived stats
|
||||||
|
label_derived_stats_value.text = str(int(char_stats.xp)) + "/" + str(int(char_stats.xp_to_next_level)) + "\n" + \
|
||||||
|
str(char_stats.coin) + "\n\n\n\n" + \
|
||||||
|
str(char_stats.damage) + "\n" + \
|
||||||
|
str(char_stats.defense) + "\n" + \
|
||||||
|
str(char_stats.move_speed) + "\n" + \
|
||||||
|
str(char_stats.attack_speed) + "\n" + \
|
||||||
|
str(char_stats.sight) + "\n" + \
|
||||||
|
str(char_stats.spell_amp) + "\n" + \
|
||||||
|
str(char_stats.crit_chance) + "%"
|
||||||
|
|
||||||
|
func _create_equipment_slots():
|
||||||
|
# Equipment slot order: mainhand, offhand, headgear, armour, boots, accessory
|
||||||
|
var slot_names = ["mainhand", "offhand", "headgear", "armour", "boots", "accessory"]
|
||||||
|
var slot_labels = ["Weapon", "Shield", "Head", "Armour", "Boots", "Accessory"]
|
||||||
|
|
||||||
|
for i in range(slot_names.size()):
|
||||||
|
var slot_name = slot_names[i]
|
||||||
|
var slot_label = slot_labels[i]
|
||||||
|
|
||||||
|
# Container for slot
|
||||||
|
var slot_container = VBoxContainer.new()
|
||||||
|
slot_container.name = slot_name + "_slot"
|
||||||
|
equipment_panel.add_child(slot_container)
|
||||||
|
|
||||||
|
# Label
|
||||||
|
var label = Label.new()
|
||||||
|
label.text = slot_label
|
||||||
|
label.add_theme_font_size_override("font_size", 10)
|
||||||
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
|
||||||
|
var font_resource = load("res://assets/fonts/standard_font.png")
|
||||||
|
if font_resource:
|
||||||
|
label.add_theme_font_override("font", font_resource)
|
||||||
|
slot_container.add_child(label)
|
||||||
|
|
||||||
|
# Button
|
||||||
|
var button = Button.new()
|
||||||
|
button.name = slot_name + "_btn"
|
||||||
|
button.custom_minimum_size = Vector2(60, 60)
|
||||||
|
button.flat = true
|
||||||
|
button.connect("pressed", _on_equipment_slot_pressed.bind(slot_name))
|
||||||
|
slot_container.add_child(button)
|
||||||
|
|
||||||
|
equipment_slots[slot_name] = button
|
||||||
|
equipment_buttons[slot_name] = button
|
||||||
|
|
||||||
|
func _on_equipment_slot_pressed(slot_name: String):
|
||||||
|
if not local_player or not local_player.character_stats:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Select this slot
|
||||||
|
selected_slot = slot_name
|
||||||
|
selected_item = local_player.character_stats.equipment[slot_name]
|
||||||
|
selected_type = "equipment" if selected_item else ""
|
||||||
|
_update_selection_highlight()
|
||||||
|
|
||||||
|
func _update_selection_highlight():
|
||||||
|
# Reset all button styles
|
||||||
|
for button in equipment_buttons.values():
|
||||||
|
if button:
|
||||||
|
var highlight = button.get_node_or_null("Highlight")
|
||||||
|
if highlight:
|
||||||
|
highlight.queue_free()
|
||||||
|
|
||||||
|
for button in inventory_buttons.values():
|
||||||
|
if button:
|
||||||
|
var highlight = button.get_node_or_null("Highlight")
|
||||||
|
if highlight:
|
||||||
|
highlight.queue_free()
|
||||||
|
|
||||||
|
# Highlight selected equipment slot
|
||||||
|
if selected_type == "equipment" and selected_slot != "":
|
||||||
|
var button = equipment_buttons.get(selected_slot)
|
||||||
|
if button:
|
||||||
|
var highlight = ColorRect.new()
|
||||||
|
highlight.name = "Highlight"
|
||||||
|
highlight.color = Color(1.0, 1.0, 0.0, 0.6)
|
||||||
|
highlight.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
highlight.z_index = 10
|
||||||
|
button.add_child(highlight)
|
||||||
|
highlight.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
|
||||||
|
# Highlight selected inventory item
|
||||||
|
if selected_type == "item" and selected_item:
|
||||||
|
var button = inventory_buttons.get(selected_item)
|
||||||
|
if button:
|
||||||
|
var highlight = ColorRect.new()
|
||||||
|
highlight.name = "Highlight"
|
||||||
|
highlight.color = Color(1.0, 1.0, 0.0, 0.6)
|
||||||
|
highlight.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
highlight.z_index = 10
|
||||||
|
button.add_child(highlight)
|
||||||
|
highlight.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
|
||||||
|
func _update_ui():
|
||||||
|
if not local_player or not local_player.character_stats:
|
||||||
|
return
|
||||||
|
|
||||||
|
var char_stats = local_player.character_stats
|
||||||
|
|
||||||
|
# Clear button mappings
|
||||||
|
inventory_buttons.clear()
|
||||||
|
|
||||||
|
# Update equipment slots
|
||||||
|
for slot_name in equipment_slots.keys():
|
||||||
|
var button = equipment_slots[slot_name]
|
||||||
|
if not button:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Clear existing children (sprite, highlight)
|
||||||
|
for child in button.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
var equipped_item = char_stats.equipment[slot_name]
|
||||||
|
if equipped_item:
|
||||||
|
var sprite = Sprite2D.new()
|
||||||
|
var texture_path = ""
|
||||||
|
var use_equipment_path = false
|
||||||
|
|
||||||
|
if slot_name == "mainhand" or slot_name == "offhand":
|
||||||
|
texture_path = equipped_item.spritePath
|
||||||
|
use_equipment_path = false
|
||||||
|
else:
|
||||||
|
texture_path = equipped_item.equipmentPath if equipped_item.equipmentPath != "" else equipped_item.spritePath
|
||||||
|
use_equipment_path = (equipped_item.equipmentPath != "")
|
||||||
|
|
||||||
|
var texture = load(texture_path)
|
||||||
|
if texture:
|
||||||
|
sprite.texture = texture
|
||||||
|
if use_equipment_path:
|
||||||
|
sprite.hframes = 35
|
||||||
|
sprite.vframes = 8
|
||||||
|
else:
|
||||||
|
sprite.hframes = equipped_item.spriteFrames.x if equipped_item.spriteFrames.x > 0 else 20
|
||||||
|
sprite.vframes = equipped_item.spriteFrames.y if equipped_item.spriteFrames.y > 0 else 14
|
||||||
|
sprite.frame = equipped_item.spriteFrame
|
||||||
|
sprite.scale = Vector2(1.2, 1.2)
|
||||||
|
button.add_child(sprite)
|
||||||
|
|
||||||
|
# Update inventory grid
|
||||||
|
for child in inventory_grid.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
for item in char_stats.inventory:
|
||||||
|
var button = Button.new()
|
||||||
|
button.custom_minimum_size = Vector2(60, 60)
|
||||||
|
button.flat = true
|
||||||
|
button.connect("pressed", _on_inventory_item_pressed.bind(item))
|
||||||
|
|
||||||
|
if item.spritePath != "":
|
||||||
|
var sprite = Sprite2D.new()
|
||||||
|
var texture = load(item.spritePath)
|
||||||
|
if texture:
|
||||||
|
sprite.texture = texture
|
||||||
|
sprite.hframes = item.spriteFrames.x if item.spriteFrames.x > 0 else 20
|
||||||
|
sprite.vframes = item.spriteFrames.y if item.spriteFrames.y > 0 else 14
|
||||||
|
sprite.frame = item.spriteFrame
|
||||||
|
sprite.scale = Vector2(1.2, 1.2)
|
||||||
|
button.add_child(sprite)
|
||||||
|
|
||||||
|
inventory_grid.add_child(button)
|
||||||
|
inventory_buttons[item] = button
|
||||||
|
|
||||||
|
# Update selection highlight
|
||||||
|
_update_selection_highlight()
|
||||||
|
|
||||||
|
# Update stats
|
||||||
|
_update_stats()
|
||||||
|
|
||||||
|
func _on_inventory_item_pressed(item: Item):
|
||||||
|
if not local_player or not local_player.character_stats:
|
||||||
|
return
|
||||||
|
|
||||||
|
selected_item = item
|
||||||
|
selected_slot = ""
|
||||||
|
selected_type = "item"
|
||||||
|
_update_selection_highlight()
|
||||||
|
|
||||||
|
func _on_character_changed(_char: CharacterStats):
|
||||||
|
_update_ui()
|
||||||
|
_update_stats()
|
||||||
|
|
||||||
|
func _input(event):
|
||||||
|
# Toggle with Tab key
|
||||||
|
if event is InputEventKey and event.keycode == KEY_TAB and event.pressed and not event.echo:
|
||||||
|
if is_open:
|
||||||
|
_close_inventory()
|
||||||
|
else:
|
||||||
|
_open_inventory()
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
|
|
||||||
|
if not is_open:
|
||||||
|
return
|
||||||
|
|
||||||
|
# F key: Unequip/equip items
|
||||||
|
if event is InputEventKey and event.keycode == KEY_F and event.pressed and not event.echo:
|
||||||
|
_handle_f_key()
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
|
|
||||||
|
# E key: Drop selected item
|
||||||
|
if event is InputEventKey and event.keycode == KEY_E and event.pressed and not event.echo:
|
||||||
|
_handle_e_key()
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
|
|
||||||
|
func _handle_f_key():
|
||||||
|
if not local_player or not local_player.character_stats:
|
||||||
|
return
|
||||||
|
|
||||||
|
var char_stats = local_player.character_stats
|
||||||
|
|
||||||
|
if selected_type == "equipment" and selected_slot != "":
|
||||||
|
var equipped_item = char_stats.equipment[selected_slot]
|
||||||
|
if equipped_item:
|
||||||
|
char_stats.unequip_item(equipped_item)
|
||||||
|
selected_item = null
|
||||||
|
selected_slot = ""
|
||||||
|
selected_type = ""
|
||||||
|
return
|
||||||
|
|
||||||
|
if selected_type == "item" and selected_item:
|
||||||
|
if selected_item.item_type == Item.ItemType.Equippable and selected_item.equipment_type != Item.EquipmentType.NONE:
|
||||||
|
char_stats.equip_item(selected_item)
|
||||||
|
selected_item = null
|
||||||
|
selected_slot = ""
|
||||||
|
selected_type = ""
|
||||||
|
elif selected_item.item_type == Item.ItemType.Restoration:
|
||||||
|
_use_consumable_item(selected_item)
|
||||||
|
selected_item = null
|
||||||
|
selected_slot = ""
|
||||||
|
selected_type = ""
|
||||||
|
|
||||||
|
func _use_consumable_item(item: Item):
|
||||||
|
if not local_player or not local_player.character_stats:
|
||||||
|
return
|
||||||
|
|
||||||
|
var char_stats = local_player.character_stats
|
||||||
|
|
||||||
|
if item.modifiers.has("hp"):
|
||||||
|
var hp_heal = item.modifiers["hp"]
|
||||||
|
if local_player.has_method("heal"):
|
||||||
|
local_player.heal(hp_heal)
|
||||||
|
if item.modifiers.has("mp"):
|
||||||
|
var mana_amount = item.modifiers["mp"]
|
||||||
|
char_stats.restore_mana(mana_amount)
|
||||||
|
|
||||||
|
var index = char_stats.inventory.find(item)
|
||||||
|
if index >= 0:
|
||||||
|
char_stats.inventory.remove_at(index)
|
||||||
|
char_stats.character_changed.emit(char_stats)
|
||||||
|
|
||||||
|
print(local_player.name, " used item: ", item.item_name)
|
||||||
|
|
||||||
|
func _handle_e_key():
|
||||||
|
if not local_player or not local_player.character_stats:
|
||||||
|
return
|
||||||
|
|
||||||
|
if selected_type != "item" or not selected_item:
|
||||||
|
return
|
||||||
|
|
||||||
|
var char_stats = local_player.character_stats
|
||||||
|
|
||||||
|
if not selected_item in char_stats.inventory:
|
||||||
|
return
|
||||||
|
|
||||||
|
var index = char_stats.inventory.find(selected_item)
|
||||||
|
if index >= 0:
|
||||||
|
char_stats.inventory.remove_at(index)
|
||||||
|
|
||||||
|
var drop_position = local_player.global_position + Vector2(randf_range(-20, 20), randf_range(-20, 20))
|
||||||
|
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||||
|
var entities_node = null
|
||||||
|
if game_world:
|
||||||
|
entities_node = game_world.get_node_or_null("Entities")
|
||||||
|
|
||||||
|
if entities_node:
|
||||||
|
var loot = ItemLootHelper.spawn_item_loot(selected_item, drop_position, entities_node, game_world)
|
||||||
|
if loot:
|
||||||
|
if local_player.has_method("get_multiplayer_authority"):
|
||||||
|
var player_peer_id = local_player.get_multiplayer_authority()
|
||||||
|
loot.set_meta("dropped_by_peer_id", player_peer_id)
|
||||||
|
loot.set_meta("drop_time", Time.get_ticks_msec())
|
||||||
|
|
||||||
|
selected_item = null
|
||||||
|
selected_slot = ""
|
||||||
|
selected_type = ""
|
||||||
|
|
||||||
|
char_stats.character_changed.emit(char_stats)
|
||||||
|
|
||||||
|
print(local_player.name, " dropped item")
|
||||||
|
|
||||||
|
func _open_inventory():
|
||||||
|
if is_open:
|
||||||
|
return
|
||||||
|
|
||||||
|
is_open = true
|
||||||
|
if container:
|
||||||
|
container.visible = true
|
||||||
|
|
||||||
|
_lock_player_controls(true)
|
||||||
|
_update_ui()
|
||||||
|
|
||||||
|
if not local_player:
|
||||||
|
_find_local_player()
|
||||||
|
|
||||||
|
func _close_inventory():
|
||||||
|
if not is_open:
|
||||||
|
return
|
||||||
|
|
||||||
|
is_open = false
|
||||||
|
if container:
|
||||||
|
container.visible = false
|
||||||
|
|
||||||
|
selected_item = null
|
||||||
|
selected_slot = ""
|
||||||
|
selected_type = ""
|
||||||
|
|
||||||
|
_lock_player_controls(false)
|
||||||
|
|
||||||
|
func _lock_player_controls(lock: bool):
|
||||||
|
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||||
|
if game_world:
|
||||||
|
var player_manager = game_world.get_node_or_null("PlayerManager")
|
||||||
|
if player_manager:
|
||||||
|
var local_players = player_manager.get_local_players()
|
||||||
|
for player in local_players:
|
||||||
|
player.controls_disabled = lock
|
||||||
1
src/scripts/inventory_ui.gd.uid
Normal file
1
src/scripts/inventory_ui.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://vm6intetgl40
|
||||||
1361
src/scripts/item_database.gd
Normal file
1361
src/scripts/item_database.gd
Normal file
File diff suppressed because it is too large
Load Diff
1
src/scripts/item_database.gd.uid
Normal file
1
src/scripts/item_database.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://drle8r6y7mwq8
|
||||||
73
src/scripts/item_loot_helper.gd
Normal file
73
src/scripts/item_loot_helper.gd
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
class_name ItemLootHelper
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
# Helper class for spawning Item instances as loot
|
||||||
|
|
||||||
|
# Spawn an Item instance as loot at a position
|
||||||
|
static func spawn_item_loot(item: Item, position: Vector2, entities_node: Node, game_world: Node = null) -> Node:
|
||||||
|
if not item:
|
||||||
|
push_error("ItemLootHelper: Cannot spawn null item!")
|
||||||
|
return null
|
||||||
|
|
||||||
|
var loot_scene = preload("res://scenes/loot.tscn")
|
||||||
|
if not loot_scene:
|
||||||
|
push_error("ItemLootHelper: Could not load loot.tscn scene!")
|
||||||
|
return null
|
||||||
|
|
||||||
|
# Generate random velocity for physics
|
||||||
|
var random_angle = randf() * PI * 2
|
||||||
|
var random_force = randf_range(50.0, 100.0)
|
||||||
|
var random_velocity_z = randf_range(80.0, 120.0)
|
||||||
|
var initial_velocity = Vector2(cos(random_angle), sin(random_angle)) * random_force
|
||||||
|
|
||||||
|
# Find safe spawn position if game_world is provided
|
||||||
|
var safe_spawn_pos = position
|
||||||
|
if game_world and game_world.has_method("_find_nearby_safe_spawn_position"):
|
||||||
|
safe_spawn_pos = game_world._find_nearby_safe_spawn_position(position, 64.0)
|
||||||
|
|
||||||
|
# Spawn loot instance
|
||||||
|
var loot = loot_scene.instantiate()
|
||||||
|
if not entities_node:
|
||||||
|
push_error("ItemLootHelper: entities_node is null!")
|
||||||
|
return null
|
||||||
|
|
||||||
|
# Set properties before adding to scene tree (to avoid physics state change errors)
|
||||||
|
loot.global_position = safe_spawn_pos
|
||||||
|
loot.loot_type = loot.LootType.ITEM
|
||||||
|
loot.item = item # Set the item instance
|
||||||
|
# Set initial velocity before _ready() processes
|
||||||
|
loot.velocity = initial_velocity
|
||||||
|
loot.velocity_z = random_velocity_z
|
||||||
|
loot.velocity_set_by_spawner = true
|
||||||
|
loot.is_airborne = true
|
||||||
|
|
||||||
|
# Add to scene tree using call_deferred to avoid physics state change errors
|
||||||
|
entities_node.call_deferred("add_child", loot)
|
||||||
|
|
||||||
|
# Set multiplayer authority to server if in multiplayer
|
||||||
|
# Since this is a static helper function (RefCounted), we can't access the global multiplayer directly
|
||||||
|
# Instead, check if game_world is provided and has the sync method (which implies multiplayer)
|
||||||
|
# We'll also check if entities_node is in a scene tree with multiplayer
|
||||||
|
var is_multiplayer = false
|
||||||
|
if game_world and game_world.has_method("_sync_item_loot_spawn"):
|
||||||
|
# If game_world has the sync method, we're likely in multiplayer
|
||||||
|
# Use a script call to check multiplayer (game_world is a Node and has access to multiplayer)
|
||||||
|
# We'll rely on the sync method existence as an indicator
|
||||||
|
is_multiplayer = true
|
||||||
|
|
||||||
|
if is_multiplayer:
|
||||||
|
loot.set_multiplayer_authority(1)
|
||||||
|
|
||||||
|
# Sync to clients if game_world is provided
|
||||||
|
if game_world and game_world.has_method("_sync_item_loot_spawn"):
|
||||||
|
# Generate unique loot ID
|
||||||
|
# loot_id_counter is declared as a variable in game_world.gd, so it always exists
|
||||||
|
var loot_id = game_world.loot_id_counter
|
||||||
|
game_world.loot_id_counter += 1
|
||||||
|
loot.set_meta("loot_id", loot_id)
|
||||||
|
|
||||||
|
# Sync item data to clients
|
||||||
|
game_world._sync_item_loot_spawn.rpc(safe_spawn_pos, item.save(), initial_velocity, random_velocity_z, loot_id)
|
||||||
|
|
||||||
|
print("ItemLootHelper: Spawned item loot: ", item.item_name, " at ", safe_spawn_pos)
|
||||||
|
return loot
|
||||||
1
src/scripts/item_loot_helper.gd.uid
Normal file
1
src/scripts/item_loot_helper.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://2jn53es04orw
|
||||||
@@ -7,7 +7,8 @@ enum LootType {
|
|||||||
APPLE,
|
APPLE,
|
||||||
BANANA,
|
BANANA,
|
||||||
CHERRY,
|
CHERRY,
|
||||||
KEY
|
KEY,
|
||||||
|
ITEM # Item instance (equipment, consumables, etc.)
|
||||||
}
|
}
|
||||||
|
|
||||||
@export var loot_type: LootType = LootType.COIN
|
@export var loot_type: LootType = LootType.COIN
|
||||||
@@ -29,6 +30,7 @@ var bounce_timer: float = 0.0 # Prevent rapid bounce sounds
|
|||||||
var coin_value: int = 1
|
var coin_value: int = 1
|
||||||
var heal_amount: float = 20.0
|
var heal_amount: float = 20.0
|
||||||
var collected: bool = false
|
var collected: bool = false
|
||||||
|
var item: Item = null # Item instance (for LootType.ITEM)
|
||||||
|
|
||||||
@onready var sprite = $Sprite2D
|
@onready var sprite = $Sprite2D
|
||||||
@onready var shadow = $Shadow
|
@onready var shadow = $Shadow
|
||||||
@@ -125,6 +127,20 @@ func _setup_sprite():
|
|||||||
sprite.hframes = 20
|
sprite.hframes = 20
|
||||||
sprite.vframes = 14
|
sprite.vframes = 14
|
||||||
sprite.frame = (13 * 20) + 10
|
sprite.frame = (13 * 20) + 10
|
||||||
|
LootType.ITEM:
|
||||||
|
# Item instance - use item's spritePath and spriteFrame
|
||||||
|
if item and item.spritePath != "":
|
||||||
|
var items_texture = load(item.spritePath)
|
||||||
|
if items_texture:
|
||||||
|
sprite.texture = items_texture
|
||||||
|
sprite.hframes = item.spriteFrames.x if item.spriteFrames.x > 0 else 20
|
||||||
|
sprite.vframes = item.spriteFrames.y if item.spriteFrames.y > 0 else 14
|
||||||
|
sprite.frame = item.spriteFrame
|
||||||
|
print("Loot: Set up item sprite for ", item.item_name, " frame=", sprite.frame)
|
||||||
|
else:
|
||||||
|
print("Loot: ERROR - Could not load texture from spritePath: ", item.spritePath)
|
||||||
|
else:
|
||||||
|
print("Loot: ERROR - Item loot has no item instance or spritePath! item=", item)
|
||||||
|
|
||||||
func _setup_collision_shape():
|
func _setup_collision_shape():
|
||||||
if not collision_shape:
|
if not collision_shape:
|
||||||
@@ -278,6 +294,21 @@ func _animate_coin(delta):
|
|||||||
|
|
||||||
func _on_pickup_area_body_entered(body):
|
func _on_pickup_area_body_entered(body):
|
||||||
if body and body.is_in_group("player") and not body.is_dead:
|
if body and body.is_in_group("player") and not body.is_dead:
|
||||||
|
# Check if this item was dropped by this player recently (5 second cooldown)
|
||||||
|
if has_meta("dropped_by_peer_id") and has_meta("drop_time"):
|
||||||
|
var dropped_by_peer_id = get_meta("dropped_by_peer_id")
|
||||||
|
var drop_time = get_meta("drop_time")
|
||||||
|
var current_time = Time.get_ticks_msec()
|
||||||
|
var time_since_drop = (current_time - drop_time) / 1000.0 # Convert to seconds
|
||||||
|
|
||||||
|
# Check if this player dropped the item and cooldown hasn't expired
|
||||||
|
if body.has_method("get_multiplayer_authority"):
|
||||||
|
var player_peer_id = body.get_multiplayer_authority()
|
||||||
|
if player_peer_id == dropped_by_peer_id and time_since_drop < 5.0:
|
||||||
|
# Player can't pick up their own dropped item for 5 seconds
|
||||||
|
print("Loot: Player ", body.name, " cannot pick up item they dropped (", time_since_drop, "s ago, need 5s cooldown)")
|
||||||
|
return
|
||||||
|
|
||||||
print("Loot: Pickup area entered by player: ", body.name, " is_local: ", body.is_local_player if "is_local_player" in body else "unknown", " is_server: ", multiplayer.is_server())
|
print("Loot: Pickup area entered by player: ", body.name, " is_local: ", body.is_local_player if "is_local_player" in body else "unknown", " is_server: ", multiplayer.is_server())
|
||||||
_pickup(body)
|
_pickup(body)
|
||||||
|
|
||||||
@@ -456,6 +487,61 @@ func _process_pickup_on_server(player: Node):
|
|||||||
if sfx_key_collect and sfx_key_collect.playing:
|
if sfx_key_collect and sfx_key_collect.playing:
|
||||||
await $SfxKeyCollect.finished
|
await $SfxKeyCollect.finished
|
||||||
queue_free()
|
queue_free()
|
||||||
|
LootType.ITEM:
|
||||||
|
# Item instance pickup
|
||||||
|
if not item:
|
||||||
|
print("Loot: ERROR - Item loot has no item instance!")
|
||||||
|
queue_free()
|
||||||
|
return
|
||||||
|
|
||||||
|
if sfx_loot_collect:
|
||||||
|
sfx_loot_collect.play()
|
||||||
|
|
||||||
|
# Handle item pickup based on type
|
||||||
|
if item.item_type == Item.ItemType.Equippable:
|
||||||
|
# Equippable item - add to inventory
|
||||||
|
if player.character_stats:
|
||||||
|
player.character_stats.add_item(item)
|
||||||
|
print(name, " picked up item: ", item.item_name, " (added to inventory)")
|
||||||
|
elif item.item_type == Item.ItemType.Restoration:
|
||||||
|
# Consumable item - use immediately
|
||||||
|
if player.character_stats:
|
||||||
|
# Apply modifiers (hp, mp, etc.)
|
||||||
|
if item.modifiers.has("hp"):
|
||||||
|
var hp_heal = item.modifiers["hp"]
|
||||||
|
if player.has_method("heal"):
|
||||||
|
player.heal(hp_heal)
|
||||||
|
if item.modifiers.has("mp"):
|
||||||
|
var mana_amount = item.modifiers["mp"]
|
||||||
|
player.character_stats.restore_mana(mana_amount)
|
||||||
|
|
||||||
|
# TODO: Handle other modifiers (dodge_chance, res_all, etc.) - these would need duration tracking
|
||||||
|
|
||||||
|
print(name, " used item: ", item.item_name)
|
||||||
|
|
||||||
|
# Show floating text with item name
|
||||||
|
var items_texture = load(item.spritePath)
|
||||||
|
var display_text = item.item_name
|
||||||
|
var text_color = Color.WHITE
|
||||||
|
|
||||||
|
# Color code based on item type
|
||||||
|
if item.item_type == Item.ItemType.Equippable:
|
||||||
|
text_color = Color.CYAN # Cyan for equipment
|
||||||
|
elif item.item_type == Item.ItemType.Restoration:
|
||||||
|
text_color = Color.GREEN # Green for consumables
|
||||||
|
|
||||||
|
_show_floating_text(player, display_text, text_color, 0.5, 0.5, items_texture, item.spriteFrames.x, item.spriteFrames.y, item.spriteFrame)
|
||||||
|
|
||||||
|
# Sync floating text to client
|
||||||
|
if multiplayer.has_multiplayer_peer() and player.get_multiplayer_authority() != 1:
|
||||||
|
_sync_show_floating_text.rpc_id(player.get_multiplayer_authority(), loot_type, display_text, text_color, 0, item.spriteFrame, player.get_multiplayer_authority())
|
||||||
|
|
||||||
|
self.visible = false
|
||||||
|
|
||||||
|
# Wait for sound to finish before removing
|
||||||
|
if sfx_loot_collect and sfx_loot_collect.playing:
|
||||||
|
await sfx_loot_collect.finished
|
||||||
|
queue_free()
|
||||||
|
|
||||||
var processing_pickup: bool = false # Mutex to prevent concurrent pickup processing
|
var processing_pickup: bool = false # Mutex to prevent concurrent pickup processing
|
||||||
|
|
||||||
@@ -526,7 +612,7 @@ func _sync_remove():
|
|||||||
LootType.COIN:
|
LootType.COIN:
|
||||||
if sfx_coin_collect:
|
if sfx_coin_collect:
|
||||||
sfx_coin_collect.play()
|
sfx_coin_collect.play()
|
||||||
LootType.APPLE, LootType.BANANA, LootType.CHERRY:
|
LootType.APPLE, LootType.BANANA, LootType.CHERRY, LootType.ITEM:
|
||||||
if sfx_loot_collect:
|
if sfx_loot_collect:
|
||||||
sfx_loot_collect.play()
|
sfx_loot_collect.play()
|
||||||
|
|
||||||
@@ -543,7 +629,7 @@ func _sync_remove():
|
|||||||
if loot_type == LootType.COIN and sfx_coin_collect and sfx_coin_collect.playing:
|
if loot_type == LootType.COIN and sfx_coin_collect and sfx_coin_collect.playing:
|
||||||
_sound_playing = true
|
_sound_playing = true
|
||||||
await sfx_coin_collect.finished
|
await sfx_coin_collect.finished
|
||||||
elif loot_type in [LootType.APPLE, LootType.BANANA, LootType.CHERRY] and sfx_loot_collect and sfx_loot_collect.playing:
|
elif loot_type in [LootType.APPLE, LootType.BANANA, LootType.CHERRY, LootType.ITEM] and sfx_loot_collect and sfx_loot_collect.playing:
|
||||||
_sound_playing = true
|
_sound_playing = true
|
||||||
await sfx_loot_collect.finished
|
await sfx_loot_collect.finished
|
||||||
|
|
||||||
@@ -582,6 +668,13 @@ func _sync_show_floating_text(loot_type_value: int, text: String, color_value: C
|
|||||||
item_texture = load("res://assets/gfx/pickups/items_n_shit.png")
|
item_texture = load("res://assets/gfx/pickups/items_n_shit.png")
|
||||||
sprite_hframes = 20
|
sprite_hframes = 20
|
||||||
sprite_vframes = 14
|
sprite_vframes = 14
|
||||||
|
LootType.ITEM:
|
||||||
|
# Item instance - use item's sprite path
|
||||||
|
# Note: item data is not available on client in this sync, so we use default
|
||||||
|
# The actual item sprite is set when the loot is created
|
||||||
|
item_texture = load("res://assets/gfx/pickups/items_n_shit.png")
|
||||||
|
sprite_hframes = 20
|
||||||
|
sprite_vframes = 14
|
||||||
|
|
||||||
# Show floating text on client
|
# Show floating text on client
|
||||||
_show_floating_text(player, text, color_value, 0.5, 0.5, item_texture, sprite_hframes, sprite_vframes, sprite_frame_value)
|
_show_floating_text(player, text, color_value, 0.5, 0.5, item_texture, sprite_hframes, sprite_vframes, sprite_frame_value)
|
||||||
|
|||||||
@@ -389,6 +389,11 @@ func _initialize_character_stats():
|
|||||||
# Initialize health/mana from stats
|
# Initialize health/mana from stats
|
||||||
character_stats.hp = character_stats.maxhp
|
character_stats.hp = character_stats.maxhp
|
||||||
character_stats.mp = character_stats.maxmp
|
character_stats.mp = character_stats.maxmp
|
||||||
|
|
||||||
|
# Connect signals
|
||||||
|
if character_stats:
|
||||||
|
character_stats.level_up_stats.connect(_on_level_up_stats)
|
||||||
|
character_stats.character_changed.connect(_on_character_changed)
|
||||||
|
|
||||||
func _randomize_stats():
|
func _randomize_stats():
|
||||||
# Randomize base stats within reasonable ranges
|
# Randomize base stats within reasonable ranges
|
||||||
@@ -471,13 +476,33 @@ func _apply_appearance_to_sprites():
|
|||||||
sprite_body.vframes = 8
|
sprite_body.vframes = 8
|
||||||
sprite_body.modulate = Color.WHITE # Remove old color tint
|
sprite_body.modulate = Color.WHITE # Remove old color tint
|
||||||
|
|
||||||
# Boots - empty (bare)
|
# Boots
|
||||||
if sprite_boots:
|
if sprite_boots:
|
||||||
sprite_boots.texture = null
|
var equipped_boots = character_stats.equipment["boots"]
|
||||||
|
if equipped_boots and equipped_boots.equipmentPath != "":
|
||||||
|
var boots_texture = load(equipped_boots.equipmentPath)
|
||||||
|
if boots_texture:
|
||||||
|
sprite_boots.texture = boots_texture
|
||||||
|
sprite_boots.hframes = 35
|
||||||
|
sprite_boots.vframes = 8
|
||||||
|
else:
|
||||||
|
sprite_boots.texture = null
|
||||||
|
else:
|
||||||
|
sprite_boots.texture = null
|
||||||
|
|
||||||
# Armour - empty (bare)
|
# Armour
|
||||||
if sprite_armour:
|
if sprite_armour:
|
||||||
sprite_armour.texture = null
|
var equipped_armour = character_stats.equipment["armour"]
|
||||||
|
if equipped_armour and equipped_armour.equipmentPath != "":
|
||||||
|
var armour_texture = load(equipped_armour.equipmentPath)
|
||||||
|
if armour_texture:
|
||||||
|
sprite_armour.texture = armour_texture
|
||||||
|
sprite_armour.hframes = 35
|
||||||
|
sprite_armour.vframes = 8
|
||||||
|
else:
|
||||||
|
sprite_armour.texture = null
|
||||||
|
else:
|
||||||
|
sprite_armour.texture = null
|
||||||
|
|
||||||
# Facial Hair
|
# Facial Hair
|
||||||
if sprite_facial_hair:
|
if sprite_facial_hair:
|
||||||
@@ -546,19 +571,35 @@ func _apply_appearance_to_sprites():
|
|||||||
else:
|
else:
|
||||||
sprite_addons.texture = null
|
sprite_addons.texture = null
|
||||||
|
|
||||||
# Headgear - empty (bare)
|
# Headgear
|
||||||
if sprite_headgear:
|
if sprite_headgear:
|
||||||
sprite_headgear.texture = null
|
var equipped_headgear = character_stats.equipment["headgear"]
|
||||||
|
if equipped_headgear and equipped_headgear.equipmentPath != "":
|
||||||
|
var headgear_texture = load(equipped_headgear.equipmentPath)
|
||||||
|
if headgear_texture:
|
||||||
|
sprite_headgear.texture = headgear_texture
|
||||||
|
sprite_headgear.hframes = 35
|
||||||
|
sprite_headgear.vframes = 8
|
||||||
|
else:
|
||||||
|
sprite_headgear.texture = null
|
||||||
|
else:
|
||||||
|
sprite_headgear.texture = null
|
||||||
|
|
||||||
# Weapon - empty (bare)
|
# Weapon (Mainhand)
|
||||||
|
# NOTE: Weapons should NEVER use equipmentPath - they don't have character sprite sheets
|
||||||
|
# Weapons are only displayed as inventory icons (spritePath), not as character sprite layers
|
||||||
if sprite_weapon:
|
if sprite_weapon:
|
||||||
sprite_weapon.texture = null
|
sprite_weapon.texture = null # Weapons don't use character sprite layers
|
||||||
|
|
||||||
print(name, " appearance applied: skin=", character_stats.skin,
|
print(name, " appearance applied: skin=", character_stats.skin,
|
||||||
" hair=", character_stats.hairstyle,
|
" hair=", character_stats.hairstyle,
|
||||||
" facial_hair=", character_stats.facial_hair,
|
" facial_hair=", character_stats.facial_hair,
|
||||||
" eyes=", character_stats.eyes)
|
" eyes=", character_stats.eyes)
|
||||||
|
|
||||||
|
func _on_character_changed(_char: CharacterStats):
|
||||||
|
# Update appearance when character stats change (e.g., equipment)
|
||||||
|
_apply_appearance_to_sprites()
|
||||||
|
|
||||||
func _get_player_color() -> Color:
|
func _get_player_color() -> Color:
|
||||||
# Legacy function - now returns white (no color tint)
|
# Legacy function - now returns white (no color tint)
|
||||||
return Color.WHITE
|
return Color.WHITE
|
||||||
@@ -1582,15 +1623,40 @@ func _perform_attack():
|
|||||||
# Delay before spawning sword slash
|
# Delay before spawning sword slash
|
||||||
await get_tree().create_timer(0.15).timeout
|
await get_tree().create_timer(0.15).timeout
|
||||||
|
|
||||||
|
# Calculate damage from character_stats with randomization
|
||||||
|
var base_damage = 20.0 # Default damage
|
||||||
|
if character_stats:
|
||||||
|
base_damage = character_stats.damage
|
||||||
|
|
||||||
|
# D&D style randomization: ±20% variance
|
||||||
|
var damage_variance = 0.2
|
||||||
|
var damage_multiplier = 1.0 + randf_range(-damage_variance, damage_variance)
|
||||||
|
var final_damage = base_damage * damage_multiplier
|
||||||
|
|
||||||
|
# Critical strike chance (based on LCK stat)
|
||||||
|
var crit_chance = 0.0
|
||||||
|
if character_stats:
|
||||||
|
crit_chance = (character_stats.baseStats.lck + character_stats.get_pass("lck")) * 0.01 # 1% per LCK point
|
||||||
|
var is_crit = randf() < crit_chance
|
||||||
|
if is_crit:
|
||||||
|
final_damage *= 2.0 # Critical strikes deal 2x damage
|
||||||
|
print(name, " CRITICAL STRIKE! (LCK: ", character_stats.baseStats.lck + character_stats.get_pass("lck"), ")")
|
||||||
|
|
||||||
|
# Round to 1 decimal place
|
||||||
|
final_damage = round(final_damage * 10.0) / 10.0
|
||||||
|
|
||||||
# Spawn sword projectile
|
# Spawn sword projectile
|
||||||
if sword_projectile_scene:
|
if sword_projectile_scene:
|
||||||
var projectile = sword_projectile_scene.instantiate()
|
var projectile = sword_projectile_scene.instantiate()
|
||||||
get_parent().add_child(projectile)
|
get_parent().add_child(projectile)
|
||||||
projectile.setup(attack_direction, self)
|
projectile.setup(attack_direction, self, final_damage)
|
||||||
|
# Store crit status for visual feedback
|
||||||
|
if is_crit:
|
||||||
|
projectile.set_meta("is_crit", true)
|
||||||
# Spawn projectile a bit in front of the player
|
# Spawn projectile a bit in front of the player
|
||||||
var spawn_offset = attack_direction * 10.0 # 10 pixels in front
|
var spawn_offset = attack_direction * 10.0 # 10 pixels in front
|
||||||
projectile.global_position = global_position + spawn_offset
|
projectile.global_position = global_position + spawn_offset
|
||||||
print(name, " attacked with sword projectile!")
|
print(name, " attacked with sword projectile! Damage: ", final_damage, " (base: ", base_damage, ", crit: ", is_crit, ")")
|
||||||
|
|
||||||
# Sync attack over network
|
# Sync attack over network
|
||||||
if multiplayer.has_multiplayer_peer() and is_multiplayer_authority() and can_send_rpcs and is_inside_tree():
|
if multiplayer.has_multiplayer_peer() and is_multiplayer_authority() and can_send_rpcs and is_inside_tree():
|
||||||
@@ -2093,13 +2159,37 @@ func take_damage(amount: float, attacker_position: Vector2):
|
|||||||
if is_dead:
|
if is_dead:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Take damage using character_stats
|
# Check for dodge chance (based on DEX)
|
||||||
|
var _was_dodged = false
|
||||||
if character_stats:
|
if character_stats:
|
||||||
character_stats.take_damage(amount, false) # false = not magical damage
|
var dodge_roll = randf()
|
||||||
print(name, " took ", amount, " damage! Health: ", character_stats.hp, "/", character_stats.maxhp)
|
var dodge_chance = character_stats.dodge_chance
|
||||||
|
if dodge_roll < dodge_chance:
|
||||||
|
_was_dodged = true
|
||||||
|
print(name, " DODGED the attack! (DEX: ", character_stats.baseStats.dex + character_stats.get_pass("dex"), ", dodge chance: ", dodge_chance * 100.0, "%)")
|
||||||
|
# Show "DODGED" text
|
||||||
|
_show_damage_number(0.0, attacker_position, false, false, true) # is_dodged = true
|
||||||
|
# Sync dodge visual to other clients
|
||||||
|
if multiplayer.has_multiplayer_peer() and can_send_rpcs and is_inside_tree():
|
||||||
|
_sync_damage.rpc(0.0, attacker_position, false, false, true) # is_dodged = true
|
||||||
|
return # No damage taken, exit early
|
||||||
|
|
||||||
|
# If not dodged, apply damage with DEF reduction
|
||||||
|
var actual_damage = amount
|
||||||
|
if character_stats:
|
||||||
|
# Calculate damage after DEF reduction (critical hits pierce 80% of DEF)
|
||||||
|
actual_damage = character_stats.calculate_damage(amount, false, false) # false = not magical, false = not critical (enemy attacks don't crit yet)
|
||||||
|
# Apply the reduced damage using take_damage (which handles health modification and signals)
|
||||||
|
var _old_hp = character_stats.hp
|
||||||
|
character_stats.modify_health(-actual_damage)
|
||||||
|
if character_stats.hp <= 0:
|
||||||
|
character_stats.no_health.emit()
|
||||||
|
character_stats.character_changed.emit(character_stats)
|
||||||
|
print(name, " took ", actual_damage, " damage (", amount, " base - ", character_stats.defense, " DEF = ", actual_damage, ")! Health: ", character_stats.hp, "/", character_stats.maxhp)
|
||||||
else:
|
else:
|
||||||
# Fallback for legacy
|
# Fallback for legacy
|
||||||
current_health -= amount
|
current_health -= amount
|
||||||
|
actual_damage = amount
|
||||||
print(name, " took ", amount, " damage! Health: ", current_health)
|
print(name, " took ", amount, " damage! Health: ", current_health)
|
||||||
|
|
||||||
# Play damage sound effect
|
# Play damage sound effect
|
||||||
@@ -2129,11 +2219,11 @@ func take_damage(amount: float, attacker_position: Vector2):
|
|||||||
tween.tween_property(sprite_body, "modulate", Color.WHITE, 0.1)
|
tween.tween_property(sprite_body, "modulate", Color.WHITE, 0.1)
|
||||||
|
|
||||||
# Show damage number (red, using dmg_numbers.png font)
|
# Show damage number (red, using dmg_numbers.png font)
|
||||||
_show_damage_number(amount, attacker_position)
|
_show_damage_number(actual_damage, attacker_position)
|
||||||
|
|
||||||
# Sync damage visual effects to other clients (including damage numbers)
|
# Sync damage visual effects to other clients (including damage numbers)
|
||||||
if multiplayer.has_multiplayer_peer() and can_send_rpcs and is_inside_tree():
|
if multiplayer.has_multiplayer_peer() and can_send_rpcs and is_inside_tree():
|
||||||
_sync_damage.rpc(amount, attacker_position)
|
_sync_damage.rpc(actual_damage, attacker_position)
|
||||||
|
|
||||||
# Check if dead - but wait for damage animation to play first
|
# Check if dead - but wait for damage animation to play first
|
||||||
var health = character_stats.hp if character_stats else current_health
|
var health = character_stats.hp if character_stats else current_health
|
||||||
@@ -2493,11 +2583,9 @@ func use_key():
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
@rpc("authority", "reliable")
|
@rpc("authority", "reliable")
|
||||||
func _show_damage_number(amount: float, from_position: Vector2):
|
func _show_damage_number(amount: float, from_position: Vector2, is_crit: bool = false, is_miss: bool = false, is_dodged: bool = false):
|
||||||
# Show damage number (red, using dmg_numbers.png font) above player
|
# Show damage number (red, using dmg_numbers.png font) above player
|
||||||
# Only show if damage > 0
|
# Show even if amount is 0 for MISS/DODGED
|
||||||
if amount <= 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
var damage_number_scene = preload("res://scenes/damage_number.tscn")
|
var damage_number_scene = preload("res://scenes/damage_number.tscn")
|
||||||
if not damage_number_scene:
|
if not damage_number_scene:
|
||||||
@@ -2507,9 +2595,16 @@ func _show_damage_number(amount: float, from_position: Vector2):
|
|||||||
if not damage_label:
|
if not damage_label:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set damage text and red color
|
# Set text and color based on type
|
||||||
damage_label.label = str(int(amount))
|
if is_dodged:
|
||||||
damage_label.color = Color.RED
|
damage_label.label = "DODGED"
|
||||||
|
damage_label.color = Color.CYAN
|
||||||
|
elif is_miss:
|
||||||
|
damage_label.label = "MISS"
|
||||||
|
damage_label.color = Color.GRAY
|
||||||
|
else:
|
||||||
|
damage_label.label = str(int(amount))
|
||||||
|
damage_label.color = Color.ORANGE if is_crit else Color.RED
|
||||||
|
|
||||||
# Calculate direction from attacker (slight upward variation)
|
# Calculate direction from attacker (slight upward variation)
|
||||||
var direction_from_attacker = (global_position - from_position).normalized()
|
var direction_from_attacker = (global_position - from_position).normalized()
|
||||||
@@ -2531,12 +2626,81 @@ func _show_damage_number(amount: float, from_position: Vector2):
|
|||||||
get_tree().current_scene.add_child(damage_label)
|
get_tree().current_scene.add_child(damage_label)
|
||||||
damage_label.global_position = global_position + Vector2(0, -16)
|
damage_label.global_position = global_position + Vector2(0, -16)
|
||||||
|
|
||||||
|
func _on_level_up_stats(stats_increased: Array):
|
||||||
|
# Show floating text for level up - "LEVEL UP!" and stat increases
|
||||||
|
# Use damage_number scene with damage_numbers font
|
||||||
|
if not character_stats:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Stat name to display name mapping
|
||||||
|
var stat_display_names = {
|
||||||
|
"str": "STR",
|
||||||
|
"dex": "DEX",
|
||||||
|
"int": "INT",
|
||||||
|
"end": "END",
|
||||||
|
"wis": "WIS",
|
||||||
|
"lck": "LCK"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Stat name to color mapping
|
||||||
|
var stat_colors = {
|
||||||
|
"str": Color.RED,
|
||||||
|
"dex": Color.GREEN,
|
||||||
|
"int": Color.BLUE,
|
||||||
|
"end": Color.WHITE,
|
||||||
|
"wis": Color(0.5, 0.0, 0.5), # Purple
|
||||||
|
"lck": Color.YELLOW
|
||||||
|
}
|
||||||
|
|
||||||
|
var damage_number_scene = preload("res://scenes/damage_number.tscn")
|
||||||
|
if not damage_number_scene:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get entities node for adding text
|
||||||
|
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||||
|
var entities_node = null
|
||||||
|
if game_world:
|
||||||
|
entities_node = game_world.get_node_or_null("Entities")
|
||||||
|
if not entities_node:
|
||||||
|
entities_node = get_tree().current_scene
|
||||||
|
|
||||||
|
var base_y_offset = -32.0 # Start above player head
|
||||||
|
var y_spacing = 12.0 # Space between each text
|
||||||
|
|
||||||
|
# Show "LEVEL UP!" first (in white)
|
||||||
|
var level_up_text = damage_number_scene.instantiate()
|
||||||
|
if level_up_text:
|
||||||
|
level_up_text.label = "LEVEL UP!"
|
||||||
|
level_up_text.color = Color.WHITE
|
||||||
|
level_up_text.direction = Vector2(0, -1) # Straight up
|
||||||
|
entities_node.add_child(level_up_text)
|
||||||
|
level_up_text.global_position = global_position + Vector2(0, base_y_offset)
|
||||||
|
base_y_offset -= y_spacing
|
||||||
|
|
||||||
|
# Show each stat increase
|
||||||
|
for i in range(stats_increased.size()):
|
||||||
|
var stat_name = stats_increased[i]
|
||||||
|
var stat_text = damage_number_scene.instantiate()
|
||||||
|
if stat_text:
|
||||||
|
var display_name = stat_display_names.get(stat_name, stat_name.to_upper())
|
||||||
|
stat_text.label = "+1 " + display_name
|
||||||
|
stat_text.color = stat_colors.get(stat_name, Color.WHITE)
|
||||||
|
stat_text.direction = Vector2(randf_range(-0.2, 0.2), -1.0).normalized() # Slight random spread
|
||||||
|
entities_node.add_child(stat_text)
|
||||||
|
stat_text.global_position = global_position + Vector2(0, base_y_offset)
|
||||||
|
base_y_offset -= y_spacing
|
||||||
|
|
||||||
@rpc("any_peer", "reliable")
|
@rpc("any_peer", "reliable")
|
||||||
func _sync_damage(_amount: float, attacker_position: Vector2):
|
func _sync_damage(_amount: float, attacker_position: Vector2, is_crit: bool = false, is_miss: bool = false, is_dodged: bool = false):
|
||||||
# This RPC only syncs visual effects, not damage application
|
# This RPC only syncs visual effects, not damage application
|
||||||
# (damage is already applied via rpc_take_damage)
|
# (damage is already applied via rpc_take_damage)
|
||||||
if not is_multiplayer_authority():
|
if not is_multiplayer_authority():
|
||||||
# Play damage sound effect on clients
|
# If dodged, only show dodge text, no other effects
|
||||||
|
if is_dodged:
|
||||||
|
_show_damage_number(0.0, attacker_position, false, false, true)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Play damage sound and effects
|
||||||
if sfx_take_damage:
|
if sfx_take_damage:
|
||||||
sfx_take_damage.play()
|
sfx_take_damage.play()
|
||||||
|
|
||||||
@@ -2561,6 +2725,9 @@ func _sync_damage(_amount: float, attacker_position: Vector2):
|
|||||||
var tween = create_tween()
|
var tween = create_tween()
|
||||||
tween.tween_property(sprite_body, "modulate", Color.RED, 0.1)
|
tween.tween_property(sprite_body, "modulate", Color.RED, 0.1)
|
||||||
tween.tween_property(sprite_body, "modulate", Color.WHITE, 0.1)
|
tween.tween_property(sprite_body, "modulate", Color.WHITE, 0.1)
|
||||||
|
|
||||||
|
# Show damage number
|
||||||
|
_show_damage_number(_amount, attacker_position, is_crit, is_miss, false)
|
||||||
|
|
||||||
func on_grabbed(by_player):
|
func on_grabbed(by_player):
|
||||||
print(name, " grabbed by ", by_player.name)
|
print(name, " grabbed by ", by_player.name)
|
||||||
|
|||||||
@@ -25,9 +25,10 @@ func _ready():
|
|||||||
if hit_area:
|
if hit_area:
|
||||||
hit_area.body_entered.connect(_on_body_entered)
|
hit_area.body_entered.connect(_on_body_entered)
|
||||||
|
|
||||||
func setup(direction: Vector2, owner_player: Node):
|
func setup(direction: Vector2, owner_player: Node, damage_value: float = 20.0):
|
||||||
travel_direction = direction.normalized()
|
travel_direction = direction.normalized()
|
||||||
player_owner = owner_player
|
player_owner = owner_player
|
||||||
|
damage = damage_value # Set damage from player
|
||||||
current_speed = initial_speed
|
current_speed = initial_speed
|
||||||
|
|
||||||
# Rotate sprite to face travel direction
|
# Rotate sprite to face travel direction
|
||||||
@@ -102,19 +103,37 @@ func _on_body_entered(body):
|
|||||||
|
|
||||||
# Deal damage to enemies - only authority (creator) deals damage
|
# Deal damage to enemies - only authority (creator) deals damage
|
||||||
elif body.is_in_group("enemy") and body.has_method("rpc_take_damage"):
|
elif body.is_in_group("enemy") and body.has_method("rpc_take_damage"):
|
||||||
$SfxImpact.play()
|
|
||||||
var attacker_pos = player_owner.global_position if player_owner else global_position
|
var attacker_pos = player_owner.global_position if player_owner else global_position
|
||||||
|
var is_crit = get_meta("is_crit") if has_meta("is_crit") else false
|
||||||
|
|
||||||
|
# Check hit chance (based on player's DEX stat)
|
||||||
|
var hit_roll = randf()
|
||||||
|
var hit_chance = 0.95 # Base hit chance
|
||||||
|
if player_owner and player_owner.character_stats:
|
||||||
|
hit_chance = player_owner.character_stats.hit_chance
|
||||||
|
var is_miss = hit_roll >= hit_chance
|
||||||
|
|
||||||
|
if is_miss:
|
||||||
|
# Attack missed - show MISS text, don't deal damage, don't play impact sound
|
||||||
|
print("Player MISSED enemy: ", body.name, "! (hit chance: ", hit_chance * 100.0, "%)")
|
||||||
|
# Show MISS text on the enemy
|
||||||
|
if body.has_method("_show_damage_number"):
|
||||||
|
body._show_damage_number(0.0, attacker_pos, false, true, false) # is_miss = true
|
||||||
|
return # Don't deal damage, don't play impact sound, don't cause knockback
|
||||||
|
|
||||||
|
# Hit successful - play impact sound and deal damage
|
||||||
|
$SfxImpact.play()
|
||||||
var enemy_peer_id = body.get_multiplayer_authority()
|
var enemy_peer_id = body.get_multiplayer_authority()
|
||||||
if enemy_peer_id != 0:
|
if enemy_peer_id != 0:
|
||||||
# If enemy is on the same peer (server), call directly
|
# If enemy is on the same peer (server), call directly
|
||||||
if multiplayer.is_server() and enemy_peer_id == multiplayer.get_unique_id():
|
if multiplayer.is_server() and enemy_peer_id == multiplayer.get_unique_id():
|
||||||
body.rpc_take_damage(damage, attacker_pos)
|
body.rpc_take_damage(damage, attacker_pos, is_crit)
|
||||||
else:
|
else:
|
||||||
# Send RPC to enemy's authority (server) - clients can do this!
|
# Send RPC to enemy's authority (server) - clients can do this!
|
||||||
body.rpc_take_damage.rpc_id(enemy_peer_id, damage, attacker_pos)
|
body.rpc_take_damage.rpc_id(enemy_peer_id, damage, attacker_pos, is_crit)
|
||||||
else:
|
else:
|
||||||
# Fallback: broadcast if we can't get peer_id
|
# Fallback: broadcast if we can't get peer_id
|
||||||
body.rpc_take_damage.rpc(damage, attacker_pos)
|
body.rpc_take_damage.rpc(damage, attacker_pos, is_crit)
|
||||||
# Debug print - handle null player_owner safely
|
# Debug print - handle null player_owner safely
|
||||||
var owner_name: String = "none"
|
var owner_name: String = "none"
|
||||||
var is_authority: bool = false
|
var is_authority: bool = false
|
||||||
|
|||||||
Reference in New Issue
Block a user