diff --git a/scripts/dolphin/HUD.gd b/scripts/dolphin/HUD.gd index f597b6e..5313a72 100644 --- a/scripts/dolphin/HUD.gd +++ b/scripts/dolphin/HUD.gd @@ -41,6 +41,9 @@ var _speed_lines: Control = null var _speed_line_data: Array = [] const SPEED_LINE_COUNT: int = 16 +# Mini-map +var _mini_map: Control = null + func _ready() -> void: _style_bar(_oxygen_bar, Color(0.31, 0.76, 0.97)) @@ -66,6 +69,7 @@ func _ready() -> void: _build_combo_panel() _build_boost_bar() _build_speed_lines() + _build_mini_map() var am: Node = get_node_or_null("/root/AchievementManager") if am != null: am.achievement_unlocked.connect(_on_achievement_unlocked) @@ -603,11 +607,22 @@ func _draw_speed_lines() -> void: _speed_lines.draw_line(start, end, col, 1.5) +func _build_mini_map() -> void: + var mm_script: Script = load("res://scripts/dolphin/MiniMap.gd") + if mm_script == null: + return + _mini_map = Control.new() + _mini_map.set_script(mm_script) + add_child(_mini_map) + + func connect_to_dolphin(dolphin: CharacterBody3D) -> void: dolphin.stats_changed.connect(_on_stats_changed) if dolphin.has_signal("boost_changed"): dolphin.boost_changed.connect(_on_boost_changed) _dolphin = dolphin + if _mini_map != null and _mini_map.has_method("setup"): + _mini_map.call("setup", dolphin) var main: Node = get_tree().get_first_node_in_group("main") if main != null: diff --git a/scripts/dolphin/MiniMap.gd b/scripts/dolphin/MiniMap.gd new file mode 100644 index 0000000..a662d0a --- /dev/null +++ b/scripts/dolphin/MiniMap.gd @@ -0,0 +1,183 @@ +extends Control +## MiniMap — radar circulaire 2D dans le coin HUD. +## Rayon 32 blocs. Montre : joueur (blanc), mobs (rouge), perles (jaune), +## blocs spéciaux via couleur biome (dessin manuel sur CanvasItem). + +const MAP_RADIUS_PX: float = 52.0 # rayon en pixels du cercle radar +const MAP_WORLD_RADIUS: float = 32.0 # rayon en unités monde représenté + +var _dolphin: Node3D = null + +# Pulsation des icônes mobs +var _time: float = 0.0 + + +func _ready() -> void: + # Positionnement: coin bas-gauche au-dessus de la barre boost + anchor_left = 0.0 + anchor_right = 0.0 + anchor_top = 1.0 + anchor_bottom = 1.0 + var size: float = MAP_RADIUS_PX * 2.0 + 8.0 + offset_left = 16.0 + offset_right = 16.0 + size + offset_top = -(78.0 + size + 12.0) + offset_bottom = -(78.0 + 12.0) + custom_minimum_size = Vector2(size, size) + mouse_filter = MOUSE_FILTER_IGNORE + + +func setup(dolphin: Node3D) -> void: + _dolphin = dolphin + + +func _process(delta: float) -> void: + _time += delta + queue_redraw() + + +func _draw() -> void: + if not is_instance_valid(_dolphin): + return + + var center: Vector2 = Vector2(MAP_RADIUS_PX + 4.0, MAP_RADIUS_PX + 4.0) + var r: float = MAP_RADIUS_PX + + # --- Background circle --- + draw_circle(center, r + 2.0, Color(0.0, 0.0, 0.0, 0.45)) + draw_arc(center, r + 2.0, 0.0, TAU, 48, Color(0.3, 0.7, 1.0, 0.55), 1.5) + + # --- Clip mask simulation: draw a dark ring outside the circle to fake clipping --- + # (Godot 4 CanvasItem draw doesn't natively clip circles — we use stencil trick with overdraw) + + var player_pos: Vector3 = _dolphin.global_position + var player_yaw: float = _dolphin.rotation.y # for oriented radar + + # --- Draw world blip grid (simplified biome color dots) --- + _draw_biome_blips(center, r, player_pos, player_yaw) + + # --- Draw Pearls --- + var pearls := get_tree().get_nodes_in_group("pearl") + for pearl: Node in pearls: + if not is_instance_valid(pearl): + continue + var wp: Vector3 = pearl.global_position + var blip: Vector2 = _world_to_radar(player_pos, player_yaw, wp, r) + if blip.length() <= r: + draw_circle(center + blip, 3.5, Color(1.0, 0.92, 0.2, 0.9)) + + # --- Draw Mobs --- + var mobs: Array = [] + mobs.append_array(get_tree().get_nodes_in_group("shark")) + mobs.append_array(get_tree().get_nodes_in_group("mob")) + # Also scan MobSpawner children directly + var spawner: Node = _find_mob_spawner() + if spawner != null: + _collect_mob_children(spawner, mobs) + + for mob: Node in mobs: + if not is_instance_valid(mob): + continue + var mp: Vector3 = mob.global_position + var blip: Vector2 = _world_to_radar(player_pos, player_yaw, mp, r) + if blip.length() <= r: + var pulse: float = 0.75 + sin(_time * 4.0) * 0.25 + draw_circle(center + blip, 3.5, Color(1.0, 0.2, 0.2, pulse)) + + # --- Draw Player dot (center, white with ring) --- + draw_circle(center, 4.5, Color(1.0, 1.0, 1.0, 1.0)) + draw_arc(center, 5.5, 0.0, TAU, 16, Color(0.5, 0.8, 1.0, 0.8), 1.0) + + # --- Direction tick (north indicator) --- + var north_angle: float = -player_yaw - PI * 0.5 + var north_dir: Vector2 = Vector2(cos(north_angle), sin(north_angle)) * (r - 4.0) + draw_line(center + north_dir * 0.85, center + north_dir, Color(1.0, 0.3, 0.3, 0.9), 2.0) + + # --- Border ring (final, on top) --- + draw_arc(center, r, 0.0, TAU, 64, Color(0.25, 0.6, 0.9, 0.75), 2.0) + + +func _world_to_radar(player_pos: Vector3, player_yaw: float, world_pos: Vector3, r: float) -> Vector2: + var dx: float = world_pos.x - player_pos.x + var dz: float = world_pos.z - player_pos.z + # Rotate by -player_yaw so the map is oriented (player forward = up) + var cos_y: float = cos(-player_yaw) + var sin_y: float = sin(-player_yaw) + var rx: float = dx * cos_y - dz * sin_y + var rz: float = dx * sin_y + dz * cos_y + # Scale to radar pixels + var scale: float = r / MAP_WORLD_RADIUS + return Vector2(rx * scale, rz * scale) + + +func _draw_biome_blips(center: Vector2, r: float, player_pos: Vector3, player_yaw: float) -> void: + # Sample a coarse grid around the player and draw colored pixels by block type + # We do a 5x5 sparse grid to keep it performant + var chunk_mgr: Node = _find_chunk_manager() + if chunk_mgr == null or not chunk_mgr.has_method("get_block"): + return + + var step: float = MAP_WORLD_RADIUS / 5.0 + for ix: int in range(-5, 6): + for iz: int in range(-5, 6): + var wx: float = player_pos.x + ix * step + var wz: float = player_pos.z + iz * step + var wy: float = player_pos.y + var block_id: int = chunk_mgr.call("get_block", Vector3(wx, wy, wz)) + if block_id <= 1: + continue + var col: Color = _block_color(block_id) + col.a = 0.3 + var blip: Vector2 = _world_to_radar(player_pos, player_yaw, Vector3(wx, wy, wz), r) + if blip.length() < r - 4.0: + draw_rect(Rect2(center + blip - Vector2(1.5, 1.5), Vector2(3.0, 3.0)), col) + + +func _block_color(block_id: int) -> Color: + match block_id: + 2: return Color(0.76, 0.70, 0.50) # sand + 3: return Color(0.45, 0.45, 0.45) # rock + 4: return Color(0.9, 0.2, 0.2) # coral rouge + 5: return Color(0.2, 0.4, 0.9) # coral bleu + 6: return Color(0.1, 0.6, 0.1) # kelp + 7: return Color(0.55, 0.35, 0.15) # epave + 8: return Color(0.7, 0.9, 1.0) # glace + _: return Color(0.5, 0.5, 0.5) + + +func _find_chunk_manager() -> Node: + var candidates := get_tree().get_nodes_in_group("chunk_manager") + if candidates.size() > 0: + return candidates[0] + return get_node_or_null("/root/Main/World/ChunkManager") + + +func _find_mob_spawner() -> Node: + var root: Node = get_tree().current_scene + if root == null: + return null + return _search_for_mob_spawner(root) + + +func _search_for_mob_spawner(node: Node) -> Node: + if node.get_script() != null: + var path: String = node.get_script().resource_path + if "MobSpawner" in path: + return node + for child: Node in node.get_children(): + var found: Node = _search_for_mob_spawner(child) + if found != null: + return found + return null + + +func _collect_mob_children(mob_spawner: Node, out: Array) -> void: + # MobSpawner spawns mobs as children of current_scene, tracked in _sharks/_jellyfish_list + # We can read them directly from scene tree by checking script type + var scene_root: Node = get_tree().current_scene + if scene_root == null: + return + for child: Node in scene_root.get_children(): + if child is CharacterBody3D and child != _dolphin: + if not out.has(child): + out.append(child) diff --git a/scripts/mobs/Shark.gd b/scripts/mobs/Shark.gd index 0dac0ed..31801c3 100644 --- a/scripts/mobs/Shark.gd +++ b/scripts/mobs/Shark.gd @@ -31,6 +31,7 @@ func _ready() -> void: _build_visuals() _find_player() _generate_patrol_points() + add_to_group("shark") func _find_player() -> void: diff --git a/scripts/world/Pearl.gd b/scripts/world/Pearl.gd index 706daa3..41835ba 100644 --- a/scripts/world/Pearl.gd +++ b/scripts/world/Pearl.gd @@ -15,6 +15,7 @@ func _ready() -> void: body_entered.connect(_on_body_entered) _build_visual() _build_shape() + add_to_group("pearl") func _build_visual() -> void: