diff --git a/assets/Pause_Controls.jpeg b/assets/Pause_Controls.jpeg new file mode 100644 index 0000000..1d4fd39 Binary files /dev/null and b/assets/Pause_Controls.jpeg differ diff --git a/assets/Pause_Craft.jpeg b/assets/Pause_Craft.jpeg new file mode 100644 index 0000000..6991ad9 Binary files /dev/null and b/assets/Pause_Craft.jpeg differ diff --git a/scripts/KickSystem.gd b/scripts/KickSystem.gd index 095862a..8bd4cbe 100644 --- a/scripts/KickSystem.gd +++ b/scripts/KickSystem.gd @@ -67,3 +67,4 @@ static func _execute_recipe(a: Node3D, b: Node3D, recipe: Dictionary) -> void: result.global_position = pos FX.merge_smoke(pos + Vector3(0, 0.3, 0), parent) SFX.merge(parent) + parent.get_tree().create_timer(30.0).connect("timeout", result.queue_free) diff --git a/scripts/Main.gd b/scripts/Main.gd index ccc41cf..e68e6c0 100644 --- a/scripts/Main.gd +++ b/scripts/Main.gd @@ -47,6 +47,10 @@ var progress_bar: ColorRect var progress_bg: ColorRect var upgrade_panel: Panel var gameover_panel: Panel +var pause_panel: Panel +var pause_image: TextureRect +var pause_toggle_btn: Button +var _pause_showing_craft: bool = true # Equipment slots var equip_fills: Array[ColorRect] = [] @@ -85,10 +89,13 @@ func _input(event: InputEvent) -> void: cam_pitch = clampf(cam_pitch, PITCH_MIN, PITCH_MAX) if event.is_action_pressed("ui_cancel"): - if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: - Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + if game_active and not upgrading: + _toggle_pause_menu() else: - Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + else: + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED if OS.is_debug_build(): var key := event as InputEventKey @@ -127,8 +134,7 @@ func _process(delta: float) -> void: var look_at_pos := player.global_position + Vector3(0, 0.8, 0) camera.global_position = camera.global_position.lerp(look_at_pos + offset, 14.0 * delta) 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)) + player.set_aim_direction(deg_to_rad(cam_yaw)) _update_tier_label() if boss_active: @@ -637,6 +643,7 @@ func _create_ui() -> void: _make_gameover_panel() _make_boss_ui() _make_win_panel() + _make_pause_panel() func _make_hud() -> void: score_label = _label(Vector2(16, 12), "Score: 0", 30) @@ -1030,3 +1037,94 @@ func _update_tier_label() -> void: var shield_str := "-" if st == 0 else str(st) tier_label.text = "Kick: %d Tough: %d Shield: %s" % [kt, tt, shield_str] _update_equipment_slots() + +# ─── Pause menu ─────────────────────────────────────────────────────────────── + +const _PAUSE_CRAFT_IMG := "res://assets/Pause_Craft.jpeg" +const _PAUSE_CONTROLS_IMG := "res://assets/Pause_Controls.jpeg" + +func _make_pause_panel() -> void: + pause_panel = Panel.new() + pause_panel.process_mode = Node.PROCESS_MODE_ALWAYS + pause_panel.visible = false + var sb := StyleBoxFlat.new() + sb.bg_color = Color(0.05, 0.04, 0.10, 0.96) + sb.border_width_left = 2; sb.border_width_right = 2 + sb.border_width_top = 2; sb.border_width_bottom = 2 + sb.border_color = Color(0.35, 0.28, 0.55) + sb.corner_radius_top_left = 10; sb.corner_radius_top_right = 10 + sb.corner_radius_bottom_left = 10; sb.corner_radius_bottom_right = 10 + pause_panel.add_theme_stylebox_override("panel", sb) + pause_panel.anchor_left = 0.5; pause_panel.anchor_right = 0.5 + pause_panel.anchor_top = 0.5; pause_panel.anchor_bottom = 0.5 + pause_panel.offset_left = -380; pause_panel.offset_right = 380 + pause_panel.offset_top = -300; pause_panel.offset_bottom = 300 + canvas.add_child(pause_panel) + + var vbox := VBoxContainer.new() + vbox.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + vbox.add_theme_constant_override("separation", 12) + var margin := MarginContainer.new() + margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + margin.add_theme_constant_override("margin_left", 20) + margin.add_theme_constant_override("margin_right", 20) + margin.add_theme_constant_override("margin_top", 16) + margin.add_theme_constant_override("margin_bottom", 16) + pause_panel.add_child(margin) + margin.add_child(vbox) + + var title := Label.new() + title.text = "ПАУЗА" + title.add_theme_font_size_override("font_size", 28) + title.add_theme_color_override("font_color", Color(1.0, 0.9, 0.5)) + title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + vbox.add_child(title) + + pause_image = TextureRect.new() + pause_image.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + pause_image.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + pause_image.custom_minimum_size = Vector2(0, 380) + pause_image.size_flags_vertical = Control.SIZE_EXPAND_FILL + vbox.add_child(pause_image) + _pause_load_image() + + var hbox := HBoxContainer.new() + hbox.alignment = BoxContainer.ALIGNMENT_CENTER + hbox.add_theme_constant_override("separation", 16) + vbox.add_child(hbox) + + pause_toggle_btn = _pause_btn("Управление", _on_pause_toggle) + hbox.add_child(pause_toggle_btn) + hbox.add_child(_pause_btn("Продолжить", _toggle_pause_menu)) + hbox.add_child(_pause_btn("Главное меню", func() -> void: + get_tree().paused = false + get_tree().change_scene_to_file("res://scenes/MainMenu.tscn") + )) + +func _pause_btn(text: String, cb: Callable) -> Button: + var b := Button.new() + b.text = text + b.process_mode = Node.PROCESS_MODE_ALWAYS + b.custom_minimum_size = Vector2(160, 48) + b.add_theme_font_size_override("font_size", 17) + b.connect("pressed", cb) + return b + +func _pause_load_image() -> void: + var path := _PAUSE_CRAFT_IMG if _pause_showing_craft else _PAUSE_CONTROLS_IMG + pause_image.texture = load(path) as Texture2D if ResourceLoader.exists(path) else null + +func _on_pause_toggle() -> void: + _pause_showing_craft = not _pause_showing_craft + pause_toggle_btn.text = "Управление" if _pause_showing_craft else "Рецепты Крафта" + _pause_load_image() + +func _toggle_pause_menu() -> void: + var opening := not pause_panel.visible + pause_panel.visible = opening + get_tree().paused = opening + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE if opening else Input.MOUSE_MODE_CAPTURED + if opening: + _pause_showing_craft = true + pause_toggle_btn.text = "Управление" + _pause_load_image() diff --git a/scripts/Player.gd b/scripts/Player.gd index 84ec083..51b75ef 100644 --- a/scripts/Player.gd +++ b/scripts/Player.gd @@ -21,6 +21,7 @@ var has_iron_shield: bool = false var shield_tier: int = 0 var is_shielding: bool = false var kick_timer: float = 0.0 +var interact_timer: float = 0.0 var invincible_timer: float = 0.0 var is_alive: bool = true var last_move_dir: Vector3 = Vector3.FORWARD @@ -93,11 +94,6 @@ func _make_kick_arc_mesh() -> ArrayMesh: mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays) return mesh -func _input(event: InputEvent) -> void: - if event is InputEventKey and event.pressed and not event.echo: - if event.keycode == KEY_E: - _try_interact() - func _physics_process(delta: float) -> void: if not is_alive: return @@ -105,6 +101,9 @@ func _physics_process(delta: float) -> void: _handle_movement(delta) _handle_kick(delta) _handle_iframes(delta) + interact_timer = max(0.0, interact_timer - delta) + if interact_timer <= 0.0: + _try_interact() if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) and kick_timer <= 0.0: _do_kick() @@ -273,15 +272,14 @@ func take_damage(amount: int, attacker_toughness: int = 0) -> void: emit_signal("health_changed", health, max_health) SFX.damage(get_parent()) _squish_effect() + if health <= 0: + _die() func heal(amount: int) -> void: if not is_alive: return health = min(health + amount, max_health) emit_signal("health_changed", health, max_health) - var tw := create_tween() - tw.tween_property(player_mat, "albedo_color", Color(0.1, 1.0, 0.35), 0.08) - tw.tween_property(player_mat, "albedo_color", BASE_COLOR, 0.3) func _die() -> void: is_alive = false