From d37ef6c0b9ae35d1483ec840de041eb3e3c1df5d Mon Sep 17 00:00:00 2001 From: hz Date: Sat, 8 Nov 2025 11:48:30 -0600 Subject: [PATCH] Not sure why these files werent comitted --- scenes/Grid/day_night_controller.gd | 61 ++++++++++ scenes/Grid/day_night_controller.gd.uid | 1 + scenes/Grid/grid_camera_controller.gd | 117 +++++++++++++++++++ scenes/Grid/grid_camera_controller.gd.uid | 1 + scenes/Grid/grid_character_controller.gd | 99 ++++++++++++++++ scenes/Grid/grid_character_controller.gd.uid | 1 + scenes/Grid/grid_highlight_controller.gd | 80 +++++++++++++ scenes/Grid/grid_highlight_controller.gd.uid | 1 + 8 files changed, 361 insertions(+) create mode 100644 scenes/Grid/day_night_controller.gd create mode 100644 scenes/Grid/day_night_controller.gd.uid create mode 100644 scenes/Grid/grid_camera_controller.gd create mode 100644 scenes/Grid/grid_camera_controller.gd.uid create mode 100644 scenes/Grid/grid_character_controller.gd create mode 100644 scenes/Grid/grid_character_controller.gd.uid create mode 100644 scenes/Grid/grid_highlight_controller.gd create mode 100644 scenes/Grid/grid_highlight_controller.gd.uid diff --git a/scenes/Grid/day_night_controller.gd b/scenes/Grid/day_night_controller.gd new file mode 100644 index 0000000..1843312 --- /dev/null +++ b/scenes/Grid/day_night_controller.gd @@ -0,0 +1,61 @@ +@tool +extends DirectionalLight3D +class_name DayNightController + +@export var grid_manager_path: NodePath +@export_range(1.0, 48.0, 0.5) var day_length_hours: float = 12.0 +@export_range(1.0, 600.0, 1.0) var real_seconds_per_game_hour: float = 60.0 +@export_range(0.0, 48.0, 0.25) var start_time_of_day_hours: float = 6.0 +@export var orbit_axis: Vector3 = Vector3(1.0, 0.0, -1.0) +@export_range(0.0, 90.0, 0.5) var inclination_degrees: float = 35.0 + +var _grid_manager: Node3D = null +var _time_of_day_hours: float = 0.0 + +func _ready() -> void: + set_process(true) + _resolve_references() + _time_of_day_hours = clamp(start_time_of_day_hours, 0.0, day_length_hours) + _apply_sun_lighting() + +func _process(delta: float) -> void: + if Engine.is_editor_hint(): + return + if day_length_hours <= 0.0 or real_seconds_per_game_hour <= 0.0: + return + _time_of_day_hours = fposmod(_time_of_day_hours + delta / real_seconds_per_game_hour, day_length_hours) + _apply_sun_lighting() + +func _resolve_references() -> void: + if grid_manager_path != NodePath(): + _grid_manager = get_node_or_null(grid_manager_path) + else: + _grid_manager = get_parent() as Node3D + +func _apply_sun_lighting() -> void: + if day_length_hours <= 0.0: + return + var cycle: float = 0.0 + if day_length_hours > 0.0: + cycle = fposmod(_time_of_day_hours / day_length_hours, 1.0) + var sun_angle: float = cycle * TAU + var axis: Vector3 = orbit_axis.normalized() + if axis.length() == 0.0: + axis = Vector3.RIGHT + var direction: Vector3 = Vector3.UP.rotated(axis, sun_angle) + direction = direction.rotated(Vector3.UP, deg_to_rad(inclination_degrees)).normalized() + var target: Vector3 = _grid_manager.global_transform.origin if _grid_manager else global_transform.origin + var sun_distance: float = 60.0 + var sun_position: Vector3 = target - direction * sun_distance + var up_vector: Vector3 = Vector3.UP + if direction.is_equal_approx(up_vector) or direction.is_equal_approx(-up_vector): + up_vector = Vector3.FORWARD + look_at_from_position(sun_position, target, up_vector) + var daylight: float = clamp(direction.y * 0.5 + 0.5, 0.0, 1.0) + var dawn_color: Color = Color(0.95, 0.58, 0.32) + var night_color: Color = Color(0.12, 0.18, 0.35) + var day_color: Color = Color(1.0, 0.96, 0.88) + var warm_mix: Color = dawn_color.lerp(day_color, clamp((daylight - 0.25) / 0.5, 0.0, 1.0)) + light_color = night_color.lerp(warm_mix, daylight) + light_energy = lerp(0.05, 1.25, daylight) + shadow_enabled = daylight > 0.1 diff --git a/scenes/Grid/day_night_controller.gd.uid b/scenes/Grid/day_night_controller.gd.uid new file mode 100644 index 0000000..4034624 --- /dev/null +++ b/scenes/Grid/day_night_controller.gd.uid @@ -0,0 +1 @@ +uid://oy8cl8dscrh2 diff --git a/scenes/Grid/grid_camera_controller.gd b/scenes/Grid/grid_camera_controller.gd new file mode 100644 index 0000000..7a1fea2 --- /dev/null +++ b/scenes/Grid/grid_camera_controller.gd @@ -0,0 +1,117 @@ +@tool +extends Camera3D +class_name GridCameraController + +@export var grid_manager_path: NodePath +@export var ground_mesh_path: NodePath +@export var ground_collision_path: NodePath +@export_range(0.1, 10.0, 0.1) var zoom_min: float = 0.5 +@export_range(0.1, 10.0, 0.1) var zoom_max: float = 3.0 +@export_range(0.01, 1.0, 0.01) var zoom_step: float = 0.1 + +var _grid_manager: GridPlaneManager = null +var _ground_mesh: MeshInstance3D = null +var _ground_collision: CollisionShape3D = null +var _zoom_factor: float = 1.0 + +func _ready() -> void: + _resolve_references() + if _grid_manager: + _grid_manager.grid_layout_changed.connect(_on_grid_layout_changed) + _update_camera_view() + _update_ground_assets() + +func _on_grid_layout_changed(_cells: Dictionary) -> void: + _update_camera_view() + _update_ground_assets() + +func _unhandled_input(event: InputEvent) -> void: + if Engine.is_editor_hint(): + return + if event is InputEventMouseButton and event.pressed: + var button_event := event as InputEventMouseButton + if button_event.button_index == MOUSE_BUTTON_WHEEL_UP: + _adjust_zoom(-zoom_step) + elif button_event.button_index == MOUSE_BUTTON_WHEEL_DOWN: + _adjust_zoom(zoom_step) + +func _adjust_zoom(delta: float) -> void: + var new_zoom: float = clamp(_zoom_factor + delta, zoom_min, zoom_max) + if is_equal_approx(new_zoom, _zoom_factor): + return + _zoom_factor = new_zoom + _update_camera_view() + _update_ground_assets() + +func _resolve_references() -> void: + if grid_manager_path != NodePath(): + _grid_manager = get_node_or_null(grid_manager_path) as GridPlaneManager + else: + _grid_manager = get_parent() as GridPlaneManager + if ground_mesh_path != NodePath(): + _ground_mesh = get_node_or_null(ground_mesh_path) as MeshInstance3D + if ground_collision_path != NodePath(): + _ground_collision = get_node_or_null(ground_collision_path) as CollisionShape3D + +func _update_camera_view() -> void: + if not is_inside_tree(): + return + var grid_size: Vector2 = Vector2(20, 20) + var target: Vector3 = global_transform.origin + if _grid_manager: + grid_size = _grid_manager.get_grid_size() + target = _grid_manager.global_transform.origin + var extent: float = max(grid_size.x, grid_size.y) + var half_extent: float = max(extent * 0.5, 1.0) + var distance: float = max(half_extent * 1.1, 6.0) * _zoom_factor + var height: float = max(half_extent * 0.8, 6.0) * _zoom_factor + var offset: Vector3 = Vector3(distance, height, distance) + var desired_position: Vector3 = target + offset + look_at_from_position(desired_position, target, Vector3.UP) + +func _update_ground_assets() -> void: + if not is_inside_tree(): + return + var required_size: Vector2 = _compute_required_ground_size() + if _ground_mesh and _ground_mesh.mesh is PlaneMesh: + var plane_mesh: PlaneMesh = _ground_mesh.mesh as PlaneMesh + plane_mesh.size = required_size + if _ground_collision and _ground_collision.shape is BoxShape3D: + var box: BoxShape3D = _ground_collision.shape as BoxShape3D + var thickness: float = _grid_manager.get_ground_thickness() if _grid_manager else 0.4 + box.size = Vector3(required_size.x, thickness, required_size.y) + +func _compute_required_ground_size() -> Vector2: + var grid_size: Vector2 = Vector2(20, 20) + var padding: float = 20.0 + if _grid_manager: + grid_size = _grid_manager.get_grid_size() + padding = _grid_manager.get_ground_padding() + var base_half: Vector2 = Vector2(max(grid_size.x * 0.5, 1.0), max(grid_size.y * 0.5, 1.0)) + var half_extents: Vector2 = base_half + var viewport: Viewport = get_viewport() + if viewport: + var view_size: Vector2 = viewport.get_visible_rect().size + var corners: Array = [ + Vector2.ZERO, + Vector2(view_size.x, 0), + Vector2(0, view_size.y), + view_size + ] + var plane_normal: Vector3 = (_grid_manager.global_transform.basis.y if _grid_manager else global_transform.basis.y).normalized() + var plane_point: Vector3 = (_grid_manager.global_transform.origin if _grid_manager else global_transform.origin) + for corner in corners: + var origin: Vector3 = project_ray_origin(corner) + var direction: Vector3 = project_ray_normal(corner) + var denominator: float = plane_normal.dot(direction) + if abs(denominator) < 0.0001: + continue + var t: float = plane_normal.dot(plane_point - origin) / denominator + if t <= 0.0: + continue + var world_point: Vector3 = origin + direction * t + var local_point: Vector3 = _grid_manager.to_local(world_point) if _grid_manager else to_local(world_point) + half_extents.x = max(half_extents.x, abs(local_point.x)) + half_extents.y = max(half_extents.y, abs(local_point.z)) + var padding_vec: Vector2 = Vector2.ONE * padding + return (half_extents + padding_vec) * 2.0 diff --git a/scenes/Grid/grid_camera_controller.gd.uid b/scenes/Grid/grid_camera_controller.gd.uid new file mode 100644 index 0000000..3614b90 --- /dev/null +++ b/scenes/Grid/grid_camera_controller.gd.uid @@ -0,0 +1 @@ +uid://5p4mcxw7wann diff --git a/scenes/Grid/grid_character_controller.gd b/scenes/Grid/grid_character_controller.gd new file mode 100644 index 0000000..5ff3126 --- /dev/null +++ b/scenes/Grid/grid_character_controller.gd @@ -0,0 +1,99 @@ +@tool +extends Node3D +class_name GridCharacterController + +@export var grid_manager_path: NodePath +@export var camera_path: NodePath +@export var start_cell: Vector2i = Vector2i.ZERO +@export_range(0.1, 20.0, 0.1) var move_speed: float = 4.0 +@export_range(0.0, 2.0, 0.01) var height_offset: float = 0.3 + +var _grid_manager: GridPlaneManager = null +var _camera: Camera3D = null +var _current_cell: Vector2i = Vector2i(-1, -1) +var _target_position: Vector3 +var _is_moving: bool = false + +func _ready() -> void: + set_physics_process(true) + set_process_unhandled_input(true) + _resolve_references() + if _grid_manager: + _grid_manager.grid_layout_changed.connect(_on_grid_layout_changed) + _move_to_cell(start_cell, true) + +func _physics_process(delta: float) -> void: + if not _is_moving: + return + var direction: Vector3 = _target_position - global_transform.origin + var distance: float = direction.length() + if distance <= 0.01: + global_position = _target_position + _is_moving = false + return + var step: float = move_speed * delta + var move_vector: Vector3 = direction.normalized() * min(step, distance) + global_position += move_vector + +func _unhandled_input(event: InputEvent) -> void: + if Engine.is_editor_hint(): + return + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + _move_to_mouse_click() + +func _move_to_mouse_click() -> void: + if not _grid_manager or not _camera: + return + var viewport: Viewport = get_viewport() + if viewport == null: + return + var mouse_position: Vector2 = viewport.get_mouse_position() + var target_cell: Vector2i = _pick_cell(mouse_position) + if target_cell.x < 0 or target_cell.y < 0: + return + _move_to_cell(target_cell) + +func _move_to_cell(cell: Vector2i, instant: bool = false) -> void: + if not _grid_manager or not _grid_manager.has_cell(cell): + return + _current_cell = cell + var local_center: Vector3 = _grid_manager.cell_to_world(cell) + var world_center: Vector3 = _grid_manager.to_global(local_center) + _target_position = world_center + Vector3.UP * height_offset + if instant: + global_position = _target_position + _is_moving = false + else: + _is_moving = true + +func _pick_cell(mouse_position: Vector2) -> Vector2i: + var ray_origin: Vector3 = _camera.project_ray_origin(mouse_position) + var ray_direction: Vector3 = _camera.project_ray_normal(mouse_position) + var plane_normal: Vector3 = _grid_manager.global_transform.basis.y.normalized() + var plane_point: Vector3 = _grid_manager.global_transform.origin + var denominator: float = plane_normal.dot(ray_direction) + if abs(denominator) < 0.0001: + return Vector2i(-1, -1) + var t: float = plane_normal.dot(plane_point - ray_origin) / denominator + if t <= 0.0: + return Vector2i(-1, -1) + var world_point: Vector3 = ray_origin + ray_direction * t + return _grid_manager.world_to_cell(world_point, true) + +func _on_grid_layout_changed(_cells: Dictionary) -> void: + if not _grid_manager: + return + if not _grid_manager.has_cell(_current_cell): + _current_cell = Vector2i( + clamp(_current_cell.x, 0, _grid_manager.columns - 1), + clamp(_current_cell.y, 0, _grid_manager.rows - 1) + ) + _move_to_cell(_current_cell, true) + +func _resolve_references() -> void: + if grid_manager_path != NodePath(): + _grid_manager = get_node_or_null(grid_manager_path) as GridPlaneManager + else: + _grid_manager = get_parent() as GridPlaneManager + if camera_path != NodePath(): + _camera = get_node_or_null(camera_path) as Camera3D diff --git a/scenes/Grid/grid_character_controller.gd.uid b/scenes/Grid/grid_character_controller.gd.uid new file mode 100644 index 0000000..8649afd --- /dev/null +++ b/scenes/Grid/grid_character_controller.gd.uid @@ -0,0 +1 @@ +uid://6asq70b60kif diff --git a/scenes/Grid/grid_highlight_controller.gd b/scenes/Grid/grid_highlight_controller.gd new file mode 100644 index 0000000..54e2c39 --- /dev/null +++ b/scenes/Grid/grid_highlight_controller.gd @@ -0,0 +1,80 @@ +@tool +extends MeshInstance3D +class_name GridHighlightController + +@export var grid_manager_path: NodePath +@export var camera_path: NodePath + +var _grid_manager: GridPlaneManager = null +var _camera: Camera3D = null +var _hovered_cell: Vector2i = Vector2i(-1, -1) + +func _ready() -> void: + set_process(true) + _resolve_references() + _refresh_mesh() + if _grid_manager: + _grid_manager.grid_layout_changed.connect(_on_grid_layout_changed) + +func _on_grid_layout_changed(_cells: Dictionary) -> void: + _refresh_mesh() + _hovered_cell = Vector2i(-1, -1) + visible = false + +func _process(_delta: float) -> void: + if Engine.is_editor_hint(): + return + if not _grid_manager or not _camera: + return + var viewport: Viewport = get_viewport() + if viewport == null: + return + var mouse_position: Vector2 = viewport.get_mouse_position() + _update_hovered_cell(mouse_position) + +func _resolve_references() -> void: + if grid_manager_path != NodePath(): + _grid_manager = get_node_or_null(grid_manager_path) as GridPlaneManager + else: + _grid_manager = get_parent() as GridPlaneManager + if camera_path != NodePath(): + _camera = get_node_or_null(camera_path) as Camera3D + +func _refresh_mesh() -> void: + if not _grid_manager: + return + mesh = _grid_manager.make_highlight_mesh() + visible = false + +func _update_hovered_cell(mouse_position: Vector2) -> void: + var ray_origin: Vector3 = _camera.project_ray_origin(mouse_position) + var ray_direction: Vector3 = _camera.project_ray_normal(mouse_position) + var plane_normal: Vector3 = _grid_manager.global_transform.basis.y.normalized() + var plane_point: Vector3 = _grid_manager.global_transform.origin + var denominator: float = plane_normal.dot(ray_direction) + if abs(denominator) < 0.0001: + _set_hover_visible(false) + return + var t: float = plane_normal.dot(plane_point - ray_origin) / denominator + if t <= 0.0: + _set_hover_visible(false) + return + var world_point: Vector3 = ray_origin + ray_direction * t + var cell_index: Vector2i = _grid_manager.world_to_cell(world_point, false) + if cell_index.x < 0 or cell_index.y < 0 or not _grid_manager.has_cell(cell_index): + _set_hover_visible(false) + return + if cell_index == _hovered_cell: + return + _hovered_cell = cell_index + var local_center: Vector3 = _grid_manager.cell_to_world(cell_index) + var target_position: Vector3 = _grid_manager.to_global(local_center) + global_position = target_position + plane_normal * 0.02 + _set_hover_visible(true) + +func _set_hover_visible(state: bool) -> void: + if not state: + visible = false + _hovered_cell = Vector2i(-1, -1) + else: + visible = true diff --git a/scenes/Grid/grid_highlight_controller.gd.uid b/scenes/Grid/grid_highlight_controller.gd.uid new file mode 100644 index 0000000..4a86efe --- /dev/null +++ b/scenes/Grid/grid_highlight_controller.gd.uid @@ -0,0 +1 @@ +uid://ehef5rrel4d6