class_name Mortar extends Gun func calculate_projectile_velocity(source: Vector3, target: Vector3, max_height: float) -> Vector3: assert(is_equal_approx(source.y, target.y), "Source and target must be at the same height.") assert(max_height > 0.0, "Max height must be positive.") # Flatten to the horizontal plane and get range + direction var horizontal_offset := Vector3(target.x - source.x, 0.0, target.z - source.z) var R := horizontal_offset.length() assert(R > 0.0, "Source and target must not be the same point.") var direction := horizontal_offset / R var g := MortarProjectile.GRAVITY # Angle whose tangent is 4H/R — peaks exactly at max_height var angle := atan(4.0 * max_height / R) # Speed derived from v = sqrt(2gH + gR²/8H) var speed := sqrt(2.0 * g * max_height + (g * R * R) / (8.0 * max_height)) var horizontal_speed := speed * cos(angle) var vertical_speed := speed * sin(angle) return direction * horizontal_speed + Vector3.UP * vertical_speed func fire(_aim_angle: float) -> void: var pos: Vector3 = Player.instance.get_node("%Reticle").global_position pos.y = global_position.y var vel := calculate_projectile_velocity(global_position, pos, 10.) var proj := preload("res://player/projectile/mortar_projectile.tscn").instantiate() proj.velocity = vel proj.global_position = global_position get_tree().current_scene.add_child(proj) fire_clock = 60. / fire_rate %MortarFire.play()