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:
11
scenes/mobs/AbyssalJellyfish.tscn
Normal file
11
scenes/mobs/AbyssalJellyfish.tscn
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://abyssaljellyfish001"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scripts/mobs/AbyssalJellyfish.gd" id="1_abyssaljelly"]
|
||||||
|
|
||||||
|
[node name="AbyssalJellyfish" type="CharacterBody3D"]
|
||||||
|
script = ExtResource("1_abyssaljelly")
|
||||||
|
max_health = 25.0
|
||||||
|
damage = 10.0
|
||||||
|
detection_radius = 8.0
|
||||||
|
attack_radius = 1.5
|
||||||
|
move_speed = 0.6
|
||||||
88
scripts/ambience/BioluminescentParticles.gd
Normal file
88
scripts/ambience/BioluminescentParticles.gd
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
extends GPUParticles3D
|
||||||
|
|
||||||
|
# Bioluminescent floating particles — only active in abyss zone (player y < -30)
|
||||||
|
|
||||||
|
const ABYSS_DEPTH_THRESHOLD: float = -30.0
|
||||||
|
|
||||||
|
var _player: Node3D = null
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
amount = 200
|
||||||
|
lifetime = 12.0
|
||||||
|
one_shot = false
|
||||||
|
explosiveness = 0.0
|
||||||
|
randomness = 1.0
|
||||||
|
visibility_aabb = AABB(Vector3(-30, -25, -30), Vector3(60, 50, 60))
|
||||||
|
|
||||||
|
var process_mat := ParticleProcessMaterial.new()
|
||||||
|
process_mat.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX
|
||||||
|
process_mat.emission_box_extents = Vector3(28.0, 20.0, 28.0)
|
||||||
|
|
||||||
|
process_mat.direction = Vector3(0.0, 1.0, 0.0)
|
||||||
|
process_mat.spread = 120.0
|
||||||
|
process_mat.initial_velocity_min = 0.0
|
||||||
|
process_mat.initial_velocity_max = 0.08
|
||||||
|
|
||||||
|
process_mat.linear_accel_min = -0.03
|
||||||
|
process_mat.linear_accel_max = 0.03
|
||||||
|
process_mat.radial_accel_min = -0.02
|
||||||
|
process_mat.radial_accel_max = 0.02
|
||||||
|
|
||||||
|
process_mat.damping_min = 0.4
|
||||||
|
process_mat.damping_max = 0.9
|
||||||
|
|
||||||
|
process_mat.scale_min = 0.03
|
||||||
|
process_mat.scale_max = 0.12
|
||||||
|
|
||||||
|
# Cyan-green bioluminescent color
|
||||||
|
var grad := Gradient.new()
|
||||||
|
grad.set_color(0, Color(0.0, 0.8, 0.9, 0.0))
|
||||||
|
grad.set_color(1, Color(0.0, 0.8, 0.9, 0.0))
|
||||||
|
grad.add_point(0.1, Color(0.1, 0.9, 0.8, 0.7))
|
||||||
|
grad.add_point(0.85, Color(0.2, 0.7, 1.0, 0.6))
|
||||||
|
var grad_tex := GradientTexture1D.new()
|
||||||
|
grad_tex.gradient = grad
|
||||||
|
process_mat.color_ramp = grad_tex
|
||||||
|
process_mat.color = Color(0.1, 0.9, 0.8, 0.7)
|
||||||
|
|
||||||
|
process_material = process_mat
|
||||||
|
|
||||||
|
var quad := QuadMesh.new()
|
||||||
|
quad.size = Vector2(0.08, 0.08)
|
||||||
|
|
||||||
|
var mat := StandardMaterial3D.new()
|
||||||
|
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||||
|
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
mat.billboard_mode = BaseMaterial3D.BILLBOARD_ENABLED
|
||||||
|
mat.emission_enabled = true
|
||||||
|
mat.emission = Color(0.2, 0.8, 1.0)
|
||||||
|
mat.emission_energy_multiplier = 3.0
|
||||||
|
mat.albedo_color = Color(0.1, 0.9, 0.8, 0.7)
|
||||||
|
|
||||||
|
quad.surface_set_material(0, mat)
|
||||||
|
draw_pass_1 = quad
|
||||||
|
|
||||||
|
# Start inactive until we detect depth
|
||||||
|
emitting = false
|
||||||
|
_find_player()
|
||||||
|
|
||||||
|
|
||||||
|
func _find_player() -> void:
|
||||||
|
var players := get_tree().get_nodes_in_group("player")
|
||||||
|
if players.size() > 0:
|
||||||
|
_player = players[0] as Node3D
|
||||||
|
|
||||||
|
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
if _player == null:
|
||||||
|
_find_player()
|
||||||
|
return
|
||||||
|
|
||||||
|
var in_abyss: bool = _player.global_position.y < ABYSS_DEPTH_THRESHOLD
|
||||||
|
if in_abyss != emitting:
|
||||||
|
emitting = in_abyss
|
||||||
|
|
||||||
|
# Follow player horizontally, float at player depth
|
||||||
|
if in_abyss:
|
||||||
|
global_position = _player.global_position
|
||||||
81
scripts/mobs/AbyssalJellyfish.gd
Normal file
81
scripts/mobs/AbyssalJellyfish.gd
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
extends Jellyfish
|
||||||
|
|
||||||
|
# Abyssal variant: slower, packs double damage, bioluminescent cyan flash on contact
|
||||||
|
|
||||||
|
func _build_visuals() -> void:
|
||||||
|
# Cyan emissive dome material
|
||||||
|
var mat := StandardMaterial3D.new()
|
||||||
|
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
mat.albedo_color = Color(0.1, 0.9, 1.0, 0.6)
|
||||||
|
mat.emission_enabled = true
|
||||||
|
mat.emission = Color(0.2, 0.8, 1.0)
|
||||||
|
mat.emission_energy_multiplier = 2.5
|
||||||
|
mat.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||||
|
|
||||||
|
_dome = MeshInstance3D.new()
|
||||||
|
var sphere := SphereMesh.new()
|
||||||
|
sphere.radius = 0.55
|
||||||
|
sphere.height = 0.75
|
||||||
|
_dome.mesh = sphere
|
||||||
|
_dome.material_override = mat
|
||||||
|
_dome.scale = Vector3(1.0, 0.7, 1.0)
|
||||||
|
add_child(_dome)
|
||||||
|
|
||||||
|
# Thin luminous tentacles
|
||||||
|
var tent_mat := StandardMaterial3D.new()
|
||||||
|
tent_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
tent_mat.albedo_color = Color(0.1, 0.9, 1.0, 0.4)
|
||||||
|
tent_mat.emission_enabled = true
|
||||||
|
tent_mat.emission = Color(0.2, 0.8, 1.0)
|
||||||
|
tent_mat.emission_energy_multiplier = 2.0
|
||||||
|
tent_mat.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||||
|
|
||||||
|
var offsets: Array[Vector2] = [
|
||||||
|
Vector2(0.25, 0.0), Vector2(-0.25, 0.0),
|
||||||
|
Vector2(0.0, 0.25), Vector2(0.0, -0.25),
|
||||||
|
Vector2(0.18, 0.18), Vector2(-0.18, -0.18)
|
||||||
|
]
|
||||||
|
for off: Vector2 in offsets:
|
||||||
|
var tent := MeshInstance3D.new()
|
||||||
|
var cap := CapsuleMesh.new()
|
||||||
|
cap.radius = 0.02
|
||||||
|
cap.height = 0.9
|
||||||
|
tent.mesh = cap
|
||||||
|
tent.material_override = tent_mat
|
||||||
|
tent.position = Vector3(off.x, -0.65, off.y)
|
||||||
|
add_child(tent)
|
||||||
|
_tentacles.append(tent)
|
||||||
|
|
||||||
|
# Collision
|
||||||
|
var col := CollisionShape3D.new()
|
||||||
|
var cshape := SphereShape3D.new()
|
||||||
|
cshape.radius = 0.6
|
||||||
|
col.shape = cshape
|
||||||
|
add_child(col)
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
# Override stats before parent _ready to ensure double damage and slower speed
|
||||||
|
damage = 10.0 # double base jellyfish damage
|
||||||
|
move_speed = 0.6 # slower
|
||||||
|
detection_radius = 8.0 # wider detection in dark abyss
|
||||||
|
super._ready()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_attack() -> void:
|
||||||
|
# Bioluminescent flash: briefly boost emission on dome
|
||||||
|
if _dome != null and _dome.material_override != null:
|
||||||
|
var mat := _dome.material_override as StandardMaterial3D
|
||||||
|
if mat != null:
|
||||||
|
mat.emission_energy_multiplier = 8.0
|
||||||
|
await get_tree().create_timer(0.15).timeout
|
||||||
|
if is_instance_valid(self) and _dome != null and _dome.material_override != null:
|
||||||
|
mat.emission_energy_multiplier = 2.5
|
||||||
|
|
||||||
|
|
||||||
|
func _physics_process(delta: float) -> void:
|
||||||
|
var was_attacking: bool = (_state == State.ATTACK)
|
||||||
|
super._physics_process(delta)
|
||||||
|
# Trigger flash when we just entered attack
|
||||||
|
if not was_attacking and _state == State.ATTACK:
|
||||||
|
_on_attack()
|
||||||
@@ -6,10 +6,12 @@ extends Node3D
|
|||||||
@export var max_fish_schools: int = 3
|
@export var max_fish_schools: int = 3
|
||||||
@export var max_jellyfish: int = 5
|
@export var max_jellyfish: int = 5
|
||||||
@export var max_sharks: int = 1
|
@export var max_sharks: int = 1
|
||||||
|
@export var abyssal_jellyfish_max: int = 8
|
||||||
|
|
||||||
const FISH_SCHOOL_SCENE := "res://scenes/mobs/FishSchool.tscn"
|
const FISH_SCHOOL_SCENE := "res://scenes/mobs/FishSchool.tscn"
|
||||||
const JELLYFISH_SCENE := "res://scenes/mobs/Jellyfish.tscn"
|
const JELLYFISH_SCENE := "res://scenes/mobs/Jellyfish.tscn"
|
||||||
const SHARK_SCENE := "res://scenes/mobs/Shark.tscn"
|
const SHARK_SCENE := "res://scenes/mobs/Shark.tscn"
|
||||||
|
const ABYSSAL_JELLYFISH_SCENE := "res://scenes/mobs/AbyssalJellyfish.tscn"
|
||||||
|
|
||||||
var _player_node: CharacterBody3D = null
|
var _player_node: CharacterBody3D = null
|
||||||
var _chunk_manager: Node = null
|
var _chunk_manager: Node = null
|
||||||
@@ -19,6 +21,7 @@ const SPAWN_INTERVAL: float = 5.0
|
|||||||
var _fish_schools: Array[Node3D] = []
|
var _fish_schools: Array[Node3D] = []
|
||||||
var _jellyfish_list: Array[Node3D] = []
|
var _jellyfish_list: Array[Node3D] = []
|
||||||
var _sharks: Array[Node3D] = []
|
var _sharks: Array[Node3D] = []
|
||||||
|
var _abyssal_jellyfish_list: Array[Node3D] = []
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
@@ -67,6 +70,7 @@ func _tick_spawner() -> void:
|
|||||||
_cull_array(_fish_schools, player_pos)
|
_cull_array(_fish_schools, player_pos)
|
||||||
_cull_array(_jellyfish_list, player_pos)
|
_cull_array(_jellyfish_list, player_pos)
|
||||||
_cull_array(_sharks, player_pos)
|
_cull_array(_sharks, player_pos)
|
||||||
|
_cull_array(_abyssal_jellyfish_list, player_pos)
|
||||||
|
|
||||||
# --- Spawn fish schools ---
|
# --- Spawn fish schools ---
|
||||||
while _fish_schools.size() < max_fish_schools:
|
while _fish_schools.size() < max_fish_schools:
|
||||||
@@ -100,6 +104,18 @@ func _tick_spawner() -> void:
|
|||||||
else:
|
else:
|
||||||
break
|
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:
|
func _cull_array(arr: Array[Node3D], player_pos: Vector3) -> void:
|
||||||
var i: int = arr.size() - 1
|
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:
|
func _is_valid_spawn(pos: Vector3) -> bool:
|
||||||
if _chunk_manager == null:
|
if _chunk_manager == null:
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -44,13 +44,25 @@ func generate_mesh() -> void:
|
|||||||
|
|
||||||
_init_material()
|
_init_material()
|
||||||
|
|
||||||
|
var tile_w: float = BlockDatabase.ATLAS_TILE_SIZE.x
|
||||||
|
var tile_h: float = BlockDatabase.ATLAS_TILE_SIZE.y
|
||||||
|
|
||||||
|
# Surface 0: solid blocks (atlas texture)
|
||||||
var st: SurfaceTool = SurfaceTool.new()
|
var st: SurfaceTool = SurfaceTool.new()
|
||||||
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||||
|
|
||||||
var has_faces: bool = false
|
# Emissive surfaces: cyan (1), violet (2), vent (3)
|
||||||
|
var st_cyan: SurfaceTool = SurfaceTool.new()
|
||||||
|
st_cyan.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||||
|
var st_violet: SurfaceTool = SurfaceTool.new()
|
||||||
|
st_violet.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||||
|
var st_vent: SurfaceTool = SurfaceTool.new()
|
||||||
|
st_vent.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||||
|
|
||||||
var tile_w: float = BlockDatabase.ATLAS_TILE_SIZE.x
|
var has_faces: bool = false
|
||||||
var tile_h: float = BlockDatabase.ATLAS_TILE_SIZE.y
|
var has_cyan: bool = false
|
||||||
|
var has_violet: bool = false
|
||||||
|
var has_vent: bool = false
|
||||||
|
|
||||||
for x: int in range(CHUNK_SIZE):
|
for x: int in range(CHUNK_SIZE):
|
||||||
for y: int in range(CHUNK_SIZE):
|
for y: int in range(CHUNK_SIZE):
|
||||||
@@ -61,35 +73,80 @@ func generate_mesh() -> void:
|
|||||||
|
|
||||||
var uv_tl: Vector2 = BlockDatabase.get_atlas_uv(block_id)
|
var uv_tl: Vector2 = BlockDatabase.get_atlas_uv(block_id)
|
||||||
|
|
||||||
if not _is_opaque_at(x, y + 1, z):
|
var target_st: SurfaceTool = st
|
||||||
_add_face_top(st, x, y, z, uv_tl, tile_w, tile_h)
|
var emit_type: int = 0 # 0=none, 1=cyan, 2=violet, 3=vent
|
||||||
has_faces = true
|
if block_id == BlockDatabase.BlockType.GLOW_CORAL_CYAN:
|
||||||
if not _is_opaque_at(x, y - 1, z):
|
target_st = st_cyan
|
||||||
_add_face_bottom(st, x, y, z, uv_tl, tile_w, tile_h)
|
emit_type = 1
|
||||||
has_faces = true
|
elif block_id == BlockDatabase.BlockType.GLOW_CORAL_VIOLET:
|
||||||
if not _is_opaque_at(x + 1, y, z):
|
target_st = st_violet
|
||||||
_add_face_right(st, x, y, z, uv_tl, tile_w, tile_h)
|
emit_type = 2
|
||||||
has_faces = true
|
elif block_id == BlockDatabase.BlockType.LAVA_VENT:
|
||||||
if not _is_opaque_at(x - 1, y, z):
|
target_st = st_vent
|
||||||
_add_face_left(st, x, y, z, uv_tl, tile_w, tile_h)
|
emit_type = 3
|
||||||
has_faces = true
|
|
||||||
if not _is_opaque_at(x, y, z + 1):
|
|
||||||
_add_face_front(st, x, y, z, uv_tl, tile_w, tile_h)
|
|
||||||
has_faces = true
|
|
||||||
if not _is_opaque_at(x, y, z - 1):
|
|
||||||
_add_face_back(st, x, y, z, uv_tl, tile_w, tile_h)
|
|
||||||
has_faces = true
|
|
||||||
|
|
||||||
if not has_faces:
|
var added: bool = false
|
||||||
|
if not _is_opaque_at(x, y + 1, z):
|
||||||
|
_add_face_top(target_st, x, y, z, uv_tl, tile_w, tile_h)
|
||||||
|
added = true
|
||||||
|
if not _is_opaque_at(x, y - 1, z):
|
||||||
|
_add_face_bottom(target_st, x, y, z, uv_tl, tile_w, tile_h)
|
||||||
|
added = true
|
||||||
|
if not _is_opaque_at(x + 1, y, z):
|
||||||
|
_add_face_right(target_st, x, y, z, uv_tl, tile_w, tile_h)
|
||||||
|
added = true
|
||||||
|
if not _is_opaque_at(x - 1, y, z):
|
||||||
|
_add_face_left(target_st, x, y, z, uv_tl, tile_w, tile_h)
|
||||||
|
added = true
|
||||||
|
if not _is_opaque_at(x, y, z + 1):
|
||||||
|
_add_face_front(target_st, x, y, z, uv_tl, tile_w, tile_h)
|
||||||
|
added = true
|
||||||
|
if not _is_opaque_at(x, y, z - 1):
|
||||||
|
_add_face_back(target_st, x, y, z, uv_tl, tile_w, tile_h)
|
||||||
|
added = true
|
||||||
|
|
||||||
|
if added:
|
||||||
|
match emit_type:
|
||||||
|
0: has_faces = true
|
||||||
|
1: has_cyan = true
|
||||||
|
2: has_violet = true
|
||||||
|
3: has_vent = true
|
||||||
|
|
||||||
|
var mesh: ArrayMesh = ArrayMesh.new()
|
||||||
|
|
||||||
|
if has_faces:
|
||||||
|
st.generate_normals()
|
||||||
|
mesh = st.commit() # surface 0 — will apply material via surface_set_material below
|
||||||
|
|
||||||
|
if has_cyan:
|
||||||
|
st_cyan.generate_normals()
|
||||||
|
st_cyan.commit(mesh)
|
||||||
|
if has_violet:
|
||||||
|
st_violet.generate_normals()
|
||||||
|
st_violet.commit(mesh)
|
||||||
|
if has_vent:
|
||||||
|
st_vent.generate_normals()
|
||||||
|
st_vent.commit(mesh)
|
||||||
|
|
||||||
|
if mesh.get_surface_count() == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
st.generate_normals()
|
# Assign materials per surface
|
||||||
|
var surf_idx: int = 0
|
||||||
var mesh: ArrayMesh = st.commit()
|
if has_faces:
|
||||||
|
mesh.surface_set_material(surf_idx, shared_material)
|
||||||
|
surf_idx += 1
|
||||||
|
if has_cyan:
|
||||||
|
mesh.surface_set_material(surf_idx, _make_emissive_mat(Color(0.2, 0.8, 1.0), 3.0))
|
||||||
|
surf_idx += 1
|
||||||
|
if has_violet:
|
||||||
|
mesh.surface_set_material(surf_idx, _make_emissive_mat(Color(0.7, 0.3, 0.9), 3.0))
|
||||||
|
surf_idx += 1
|
||||||
|
if has_vent:
|
||||||
|
mesh.surface_set_material(surf_idx, _make_emissive_mat(Color(1.0, 0.5, 0.1), 3.0))
|
||||||
|
|
||||||
_mesh_instance = MeshInstance3D.new()
|
_mesh_instance = MeshInstance3D.new()
|
||||||
_mesh_instance.mesh = mesh
|
_mesh_instance.mesh = mesh
|
||||||
_mesh_instance.material_override = shared_material
|
|
||||||
add_child(_mesh_instance)
|
add_child(_mesh_instance)
|
||||||
|
|
||||||
var shape: ConcavePolygonShape3D = ConcavePolygonShape3D.new()
|
var shape: ConcavePolygonShape3D = ConcavePolygonShape3D.new()
|
||||||
@@ -102,6 +159,15 @@ func generate_mesh() -> void:
|
|||||||
_static_body.add_child(col_shape)
|
_static_body.add_child(col_shape)
|
||||||
add_child(_static_body)
|
add_child(_static_body)
|
||||||
|
|
||||||
|
|
||||||
|
func _make_emissive_mat(emission_color: Color, energy: float) -> StandardMaterial3D:
|
||||||
|
var mat: StandardMaterial3D = StandardMaterial3D.new()
|
||||||
|
mat.albedo_color = emission_color
|
||||||
|
mat.emission_enabled = true
|
||||||
|
mat.emission = emission_color
|
||||||
|
mat.emission_energy_multiplier = energy
|
||||||
|
return mat
|
||||||
|
|
||||||
func _is_opaque_at(x: int, y: int, z: int) -> bool:
|
func _is_opaque_at(x: int, y: int, z: int) -> bool:
|
||||||
if x < 0 or x >= CHUNK_SIZE or y < 0 or y >= CHUNK_SIZE or z < 0 or z >= CHUNK_SIZE:
|
if x < 0 or x >= CHUNK_SIZE or y < 0 or y >= CHUNK_SIZE or z < 0 or z >= CHUNK_SIZE:
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ static func generate_chunk(chunk_x: int, chunk_y: int, chunk_z: int, seed: int)
|
|||||||
noise_wreck.noise_type = FastNoiseLite.TYPE_CELLULAR
|
noise_wreck.noise_type = FastNoiseLite.TYPE_CELLULAR
|
||||||
noise_wreck.frequency = 0.03
|
noise_wreck.frequency = 0.03
|
||||||
|
|
||||||
|
var noise_glow: FastNoiseLite = FastNoiseLite.new()
|
||||||
|
noise_glow.seed = seed + 7777
|
||||||
|
noise_glow.noise_type = FastNoiseLite.TYPE_SIMPLEX_SMOOTH
|
||||||
|
noise_glow.frequency = 0.08
|
||||||
|
|
||||||
for lx: int in range(CHUNK_SIZE):
|
for lx: int in range(CHUNK_SIZE):
|
||||||
for lz: int in range(CHUNK_SIZE):
|
for lz: int in range(CHUNK_SIZE):
|
||||||
var wx: int = chunk_x * CHUNK_SIZE + lx
|
var wx: int = chunk_x * CHUNK_SIZE + lx
|
||||||
@@ -45,7 +50,7 @@ static func generate_chunk(chunk_x: int, chunk_y: int, chunk_z: int, seed: int)
|
|||||||
var idx: int = lx + ly * CHUNK_SIZE + lz * 256
|
var idx: int = lx + ly * CHUNK_SIZE + lz * 256
|
||||||
|
|
||||||
data[idx] = _get_block_at(wy, ground_height, biome, wx, wz, lx, lz,
|
data[idx] = _get_block_at(wy, ground_height, biome, wx, wz, lx, lz,
|
||||||
is_wreck_center, noise_detail, seed)
|
is_wreck_center, noise_detail, noise_glow, seed)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@@ -61,11 +66,28 @@ static func _get_biome(biome_val: float, ground_height: int) -> int:
|
|||||||
|
|
||||||
static func _get_block_at(wy: int, ground_height: int, biome: int,
|
static func _get_block_at(wy: int, ground_height: int, biome: int,
|
||||||
wx: int, wz: int, lx: int, lz: int,
|
wx: int, wz: int, lx: int, lz: int,
|
||||||
is_wreck_center: bool, noise_detail: FastNoiseLite, seed: int) -> int:
|
is_wreck_center: bool, noise_detail: FastNoiseLite, noise_glow: FastNoiseLite, seed: int) -> int:
|
||||||
|
|
||||||
if wy <= -60:
|
if wy <= -60:
|
||||||
return BlockDatabase.BlockType.BEDROCK
|
return BlockDatabase.BlockType.BEDROCK
|
||||||
|
|
||||||
|
# --- Abyssal bioluminescent decorations (deep zone) ---
|
||||||
|
if ground_height < -30 and wy >= ground_height and wy < ground_height + 3:
|
||||||
|
if wy < -40:
|
||||||
|
var glow_val: float = noise_glow.get_noise_3d(float(wx), float(wy), float(wz))
|
||||||
|
# GLOW_CORAL_CYAN patches — 4% density on surfaces
|
||||||
|
if glow_val > 0.65:
|
||||||
|
return BlockDatabase.BlockType.GLOW_CORAL_CYAN
|
||||||
|
# GLOW_CORAL_VIOLET rare columns — 1%
|
||||||
|
var violet_val: float = noise_glow.get_noise_3d(float(wx) * 1.3, float(wy) * 0.5, float(wz) * 1.3)
|
||||||
|
if violet_val > 0.82:
|
||||||
|
return BlockDatabase.BlockType.GLOW_CORAL_VIOLET
|
||||||
|
# LAVA_VENT columns — 0.5% density, very deep
|
||||||
|
if wy < -50 and wy < ground_height + 3:
|
||||||
|
var vent_val: float = noise_glow.get_noise_3d(float(wx) * 0.5, float(wy) * 2.0, float(wz) * 0.5)
|
||||||
|
if vent_val > 0.88:
|
||||||
|
return BlockDatabase.BlockType.LAVA_VENT
|
||||||
|
|
||||||
if wy < ground_height - 5:
|
if wy < ground_height - 5:
|
||||||
return BlockDatabase.BlockType.ROCK
|
return BlockDatabase.BlockType.ROCK
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user