diff --git a/scenes/IronShield.tscn b/scenes/IronShield.tscn new file mode 100644 index 0000000..9197e12 --- /dev/null +++ b/scenes/IronShield.tscn @@ -0,0 +1,26 @@ +[gd_scene format=3 uid="uid://1uwmdnvgyaii"] + +[ext_resource type="Script" path="res://scripts/IronShield.gd" id="1_ishield"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1"] +albedo_color = Color(0.55, 0.58, 0.62, 1) +roughness = 0.4 +metallic = 0.7 + +[sub_resource type="BoxMesh" id="BoxMesh_1"] +size = Vector3(0.55, 0.7, 0.1) + +[node name="IronShield" type="Node3D"] +script = ExtResource("1_ishield") + +[node name="ShieldMesh" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.35, 0) +material_override = SubResource("StandardMaterial3D_1") +mesh = SubResource("BoxMesh_1") + +[node name="Tooltip" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.3, 0) +billboard = 1 +text = "[E] Iron Shield +Tier 2 | Hold Shift to block" +outline_size = 6 diff --git a/scenes/WoodenShield.tscn b/scenes/WoodenShield.tscn new file mode 100644 index 0000000..c76e4d6 --- /dev/null +++ b/scenes/WoodenShield.tscn @@ -0,0 +1,25 @@ +[gd_scene format=3 uid="uid://bayxgoeu4qxqe"] + +[ext_resource type="Script" path="res://scripts/WoodenShield.gd" id="1_wshield"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1"] +albedo_color = Color(0.55, 0.38, 0.18, 1) +roughness = 0.85 + +[sub_resource type="BoxMesh" id="BoxMesh_1"] +size = Vector3(0.5, 0.65, 0.12) + +[node name="WoodenShield" type="Node3D"] +script = ExtResource("1_wshield") + +[node name="ShieldMesh" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.35, 0) +material_override = SubResource("StandardMaterial3D_1") +mesh = SubResource("BoxMesh_1") + +[node name="Tooltip" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.3, 0) +billboard = 1 +text = "[E] Wooden Shield +Tier 1 | Hold Shift to block" +outline_size = 6 diff --git a/scripts/Boulder.gd b/scripts/Boulder.gd index bd8b74f..2fc3b01 100644 --- a/scripts/Boulder.gd +++ b/scripts/Boulder.gd @@ -65,7 +65,7 @@ func _fly(delta: float) -> void: handled = true break elif col3d.is_in_group("player"): - col3d.call("take_damage", int(speed_now * damage_modifier)) + col3d.call("take_damage", int(speed_now * damage_modifier), toughness_tier) fly_vel *= 0.3 handled = true break diff --git a/scripts/Iron.gd b/scripts/Iron.gd index 492abc1..b0dba2a 100644 --- a/scripts/Iron.gd +++ b/scripts/Iron.gd @@ -70,7 +70,7 @@ func _fly(delta: float) -> void: handled = true break elif col3d.is_in_group("player"): - col3d.call("take_damage", int(speed_now * damage_modifier)) + col3d.call("take_damage", int(speed_now * damage_modifier), toughness_tier) fly_vel *= 0.3 handled = true break diff --git a/scripts/IronShield.gd b/scripts/IronShield.gd new file mode 100644 index 0000000..e5a2f4e --- /dev/null +++ b/scripts/IronShield.gd @@ -0,0 +1,23 @@ +extends Node3D + +@onready var tooltip: Label3D = $Tooltip + +func _ready() -> void: + add_to_group("interactable") + tooltip.visible = false + var tw := create_tween().set_loops() + tw.tween_property(self, "position:y", 0.45, 0.65) + tw.tween_property(self, "position:y", 0.2, 0.65) + +func _process(delta: float) -> void: + rotation.y += delta * 1.1 + var players := get_tree().get_nodes_in_group("player") + if players.is_empty(): + tooltip.visible = false + return + var p := players[0] as Node3D + tooltip.visible = p != null and global_position.distance_to(p.global_position) < 2.5 + +func interact(player: Node) -> void: + if player.call("apply_iron_shield"): + queue_free() diff --git a/scripts/IronShield.gd.uid b/scripts/IronShield.gd.uid new file mode 100644 index 0000000..8a66bf1 --- /dev/null +++ b/scripts/IronShield.gd.uid @@ -0,0 +1 @@ +uid://bcvyiisb5ugtn diff --git a/scripts/Main.gd b/scripts/Main.gd index 0803414..e2aa31e 100644 --- a/scripts/Main.gd +++ b/scripts/Main.gd @@ -31,6 +31,7 @@ var upgrading: bool = false var canvas: CanvasLayer var score_label: Label var wave_label: Label +var tier_label: Label var hp_bar: ColorRect var hp_bar_bg: ColorRect var progress_bar: ColorRect @@ -92,6 +93,7 @@ func _process(delta: float) -> void: camera.look_at(look_at_pos, Vector3.UP) if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT): player.set_aim_direction(deg_to_rad(cam_yaw)) + _update_tier_label() # ─── Rocks ──────────────────────────────────────────────────────────────────── @@ -339,6 +341,9 @@ func _make_hud() -> void: progress_bg = _crect(Vector2(12, 130), Vector2(200, 10), Color(0.1, 0.1, 0.25)) progress_bar = _crect(Vector2(12, 130), Vector2(0, 10), Color(0.4, 0.8, 1.0)) + # Tier display + tier_label = _label(Vector2(12, 148), "", 17) + func _make_upgrade_panel() -> void: upgrade_panel = Panel.new() upgrade_panel.process_mode = Node.PROCESS_MODE_ALWAYS @@ -447,3 +452,12 @@ func _update_labels() -> void: func _update_progress() -> void: var t := float(kills) / float(kills_for_next) progress_bar.size.x = 200.0 * t + +func _update_tier_label() -> void: + if not is_instance_valid(player): + return + var kt: int = player.get("kick_tier") + var tt: int = player.get("toughness_tier") + var st: int = player.get("shield_tier") + var shield_str := "-" if st == 0 else str(st) + tier_label.text = "Kick: %d Tough: %d Shield: %s" % [kt, tt, shield_str] diff --git a/scripts/MergeRecipes.gd b/scripts/MergeRecipes.gd index adefd90..1df335f 100644 --- a/scripts/MergeRecipes.gd +++ b/scripts/MergeRecipes.gd @@ -28,6 +28,16 @@ static var _list: Array[Dictionary] = [ "result_scene": "res://scenes/PlateArmor.tscn", "speed_threshold": 18.0, }, + { + "ingredients": ["rock", "stick"], + "result_scene": "res://scenes/WoodenShield.tscn", + "speed_threshold": 18.0, + }, + { + "ingredients": ["iron", "leather"], + "result_scene": "res://scenes/IronShield.tscn", + "speed_threshold": 18.0, + }, ] static func find(type_a: String, type_b: String, speed: float) -> Dictionary: diff --git a/scripts/MetalPlate.gd b/scripts/MetalPlate.gd index 8e3b76d..5f841fc 100644 --- a/scripts/MetalPlate.gd +++ b/scripts/MetalPlate.gd @@ -65,7 +65,7 @@ func _fly(delta: float) -> void: handled = true break elif col3d.is_in_group("player"): - col3d.call("take_damage", int(speed_now * damage_modifier)) + col3d.call("take_damage", int(speed_now * damage_modifier), toughness_tier) fly_vel *= 0.3 handled = true break diff --git a/scripts/Player.gd b/scripts/Player.gd index c7a1a2b..9a11767 100644 --- a/scripts/Player.gd +++ b/scripts/Player.gd @@ -16,6 +16,10 @@ var toughness_tier: int = 0 var has_stick_armor: bool = false var has_leather_armor: bool = false var has_plate_armor: bool = false +var has_wooden_shield: bool = false +var has_iron_shield: bool = false +var shield_tier: int = 0 +var is_shielding: bool = false var kick_timer: float = 0.0 var invincible_timer: float = 0.0 var is_alive: bool = true @@ -81,6 +85,7 @@ func _input(event: InputEvent) -> void: func _physics_process(delta: float) -> void: if not is_alive: return + is_shielding = shield_tier > 0 and Input.is_key_pressed(KEY_SHIFT) _handle_movement(delta) _handle_kick(delta) _handle_iframes(delta) @@ -88,6 +93,7 @@ func _physics_process(delta: float) -> void: _do_kick() func _handle_movement(delta: float) -> void: + var effective_speed := move_speed * (0.2 if is_shielding else 1.0) var input_x: float = ( float(Input.is_key_pressed(KEY_D) or Input.is_key_pressed(KEY_RIGHT)) - float(Input.is_key_pressed(KEY_A) or Input.is_key_pressed(KEY_LEFT)) @@ -107,15 +113,15 @@ func _handle_movement(delta: float) -> void: var move := cam_fwd * (-input_z) + cam_right * input_x if move.length() > 0.01: move = move.normalized() - velocity.x = move.x * move_speed - velocity.z = move.z * move_speed + velocity.x = move.x * effective_speed + velocity.z = move.z * effective_speed last_move_dir = move if not _is_aiming: var target_y: float = atan2(-move.x, -move.z) rotation.y = lerp_angle(rotation.y, target_y, 16.0 * delta) else: - velocity.x = move_toward(velocity.x, 0.0, move_speed * 12.0 * delta) - velocity.z = move_toward(velocity.z, 0.0, move_speed * 12.0 * delta) + velocity.x = move_toward(velocity.x, 0.0, effective_speed * 12.0 * delta) + velocity.z = move_toward(velocity.z, 0.0, effective_speed * 12.0 * delta) if _is_aiming: rotation.y = lerp_angle(rotation.y, _aim_yaw, 14.0 * delta) _is_aiming = false @@ -221,10 +227,14 @@ func receive_kick(direction: Vector3, force: float) -> void: invincible_timer = IFRAMES_DURATION * 0.5 _squish_effect() -func take_damage(amount: int) -> void: +func take_damage(amount: int, attacker_toughness: int = 0) -> void: if not is_alive or invincible_timer > 0.0: return invincible_timer = IFRAMES_DURATION + if is_shielding and shield_tier > 0: + var diff := shield_tier - attacker_toughness + var mod: float = 0.15 if diff >= 2 else (0.30 if diff == 1 else 0.50) + amount = int(amount * mod) _squish_effect() func heal(amount: int) -> void: @@ -303,3 +313,23 @@ func apply_plate_armor() -> bool: tw.tween_property(player_mat, "albedo_color", Color(0.6, 0.7, 1.0), 0.1) tw.tween_property(player_mat, "albedo_color", BASE_COLOR, 0.5) return true + +func apply_wooden_shield() -> bool: + if has_wooden_shield: + return false + has_wooden_shield = true + shield_tier = max(shield_tier, 1) + var tw := create_tween() + tw.tween_property(player_mat, "albedo_color", Color(0.55, 0.38, 0.18), 0.1) + tw.tween_property(player_mat, "albedo_color", BASE_COLOR, 0.4) + return true + +func apply_iron_shield() -> bool: + if has_iron_shield: + return false + has_iron_shield = true + shield_tier = max(shield_tier, 2) + var tw := create_tween() + tw.tween_property(player_mat, "albedo_color", Color(0.55, 0.58, 0.62), 0.1) + tw.tween_property(player_mat, "albedo_color", BASE_COLOR, 0.5) + return true diff --git a/scripts/Rock.gd b/scripts/Rock.gd index 48112f5..b41ae02 100644 --- a/scripts/Rock.gd +++ b/scripts/Rock.gd @@ -68,7 +68,7 @@ func _fly(delta: float) -> void: handled = true break elif col3d.is_in_group("player"): - col3d.call("take_damage", int(speed_now * damage_modifier)) + col3d.call("take_damage", int(speed_now * damage_modifier), toughness_tier) fly_vel *= 0.3 handled = true break diff --git a/scripts/Stick.gd b/scripts/Stick.gd index 6263fe1..0189e50 100644 --- a/scripts/Stick.gd +++ b/scripts/Stick.gd @@ -65,7 +65,7 @@ func _fly(delta: float) -> void: handled = true break elif col3d.is_in_group("player"): - col3d.call("take_damage", int(speed_now * damage_modifier)) + col3d.call("take_damage", int(speed_now * damage_modifier), toughness_tier) fly_vel *= 0.3 handled = true break diff --git a/scripts/WoodenShield.gd b/scripts/WoodenShield.gd new file mode 100644 index 0000000..3b76edc --- /dev/null +++ b/scripts/WoodenShield.gd @@ -0,0 +1,23 @@ +extends Node3D + +@onready var tooltip: Label3D = $Tooltip + +func _ready() -> void: + add_to_group("interactable") + tooltip.visible = false + var tw := create_tween().set_loops() + tw.tween_property(self, "position:y", 0.45, 0.65) + tw.tween_property(self, "position:y", 0.2, 0.65) + +func _process(delta: float) -> void: + rotation.y += delta * 1.1 + var players := get_tree().get_nodes_in_group("player") + if players.is_empty(): + tooltip.visible = false + return + var p := players[0] as Node3D + tooltip.visible = p != null and global_position.distance_to(p.global_position) < 2.5 + +func interact(player: Node) -> void: + if player.call("apply_wooden_shield"): + queue_free() diff --git a/scripts/WoodenShield.gd.uid b/scripts/WoodenShield.gd.uid new file mode 100644 index 0000000..a3ab07c --- /dev/null +++ b/scripts/WoodenShield.gd.uid @@ -0,0 +1 @@ +uid://djbxvch3r5py5