feat(biome): abyssal bioluminescent + glow blocks + abyssal jellyfish + particles

- BlockDatabase: GLOW_CORAL_CYAN(10), GLOW_CORAL_VIOLET(11), LAVA_VENT(12) with emission data
- WorldGenerator: abyssal blocks at y<-40 (cyan 4%, violet 1%, vent 0.5% via 3D noise)
- Chunk.gd: multi-surface mesh (solid + 3 emissive surfaces with StandardMaterial3D emission)
- MobSpawner: abyssal jellyfish spawn when player y<-30, max 8
- AbyssalJellyfish: cyan fluo, slower, double damage (10), bioluminescent flash on attack
- BioluminescentParticles: 200 cyan-green particles follow player, only emit in abyss

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Floppyrj45
2026-04-19 18:12:37 +02:00
parent e1fda4d393
commit d8e19932cc
6 changed files with 324 additions and 28 deletions

View File

@@ -6,10 +6,12 @@ extends Node3D
@export var max_fish_schools: int = 3
@export var max_jellyfish: int = 5
@export var max_sharks: int = 1
@export var abyssal_jellyfish_max: int = 8
const FISH_SCHOOL_SCENE := "res://scenes/mobs/FishSchool.tscn"
const JELLYFISH_SCENE := "res://scenes/mobs/Jellyfish.tscn"
const SHARK_SCENE := "res://scenes/mobs/Shark.tscn"
const ABYSSAL_JELLYFISH_SCENE := "res://scenes/mobs/AbyssalJellyfish.tscn"
var _player_node: CharacterBody3D = null
var _chunk_manager: Node = null
@@ -19,6 +21,7 @@ const SPAWN_INTERVAL: float = 5.0
var _fish_schools: Array[Node3D] = []
var _jellyfish_list: Array[Node3D] = []
var _sharks: Array[Node3D] = []
var _abyssal_jellyfish_list: Array[Node3D] = []
func _ready() -> void:
@@ -67,6 +70,7 @@ func _tick_spawner() -> void:
_cull_array(_fish_schools, player_pos)
_cull_array(_jellyfish_list, player_pos)
_cull_array(_sharks, player_pos)
_cull_array(_abyssal_jellyfish_list, player_pos)
# --- Spawn fish schools ---
while _fish_schools.size() < max_fish_schools:
@@ -100,6 +104,18 @@ func _tick_spawner() -> void:
else:
break
# --- Spawn abyssal jellyfish only in deep zone ---
if player_pos.y < -30.0:
while _abyssal_jellyfish_list.size() < abyssal_jellyfish_max:
var pos: Vector3 = _random_abyss_spawn_pos(player_pos)
if _is_valid_spawn(pos):
var inst: Node3D = _load_and_instantiate(ABYSSAL_JELLYFISH_SCENE, pos)
if inst != null:
_connect_mob_signal(inst)
_abyssal_jellyfish_list.append(inst)
else:
break
func _cull_array(arr: Array[Node3D], player_pos: Vector3) -> void:
var i: int = arr.size() - 1
@@ -123,6 +139,18 @@ func _random_spawn_pos(player_pos: Vector3) -> Vector3:
)
func _random_abyss_spawn_pos(player_pos: Vector3) -> Vector3:
var angle: float = randf_range(0.0, TAU)
var dist: float = randf_range(10.0, spawn_radius)
var depth: float = randf_range(player_pos.y - 15.0, player_pos.y + 5.0)
depth = min(depth, -30.0)
return Vector3(
player_pos.x + cos(angle) * dist,
depth,
player_pos.z + sin(angle) * dist
)
func _is_valid_spawn(pos: Vector3) -> bool:
if _chunk_manager == null:
return true