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)