2025-10-03 16:36:28 +02:00
|
|
|
class_name Vine extends Node2D
|
2025-10-21 16:06:16 +02:00
|
|
|
# Grid position data of the petal
|
|
|
|
|
@export var petal : Petal
|
|
|
|
|
|
|
|
|
|
# Locations and offsets of all vine segments
|
2025-10-03 16:36:28 +02:00
|
|
|
@export var vine_locations : Array[Vector2]
|
|
|
|
|
@export var bud_resource : PackedScene
|
2025-10-21 16:06:16 +02:00
|
|
|
|
|
|
|
|
# Paths for vine segment sprites while active/inactive
|
2025-10-05 15:01:51 +02:00
|
|
|
var img_path_inactive = "res://vines_petals/vine_inactive.png"
|
2025-10-11 19:06:46 +02:00
|
|
|
@export var img_path_active : String
|
|
|
|
|
|
2025-10-21 16:06:16 +02:00
|
|
|
# The table from which to choose a status effect at random, including the corresponding vine color
|
2025-10-11 19:06:46 +02:00
|
|
|
const status_data = [
|
|
|
|
|
{
|
|
|
|
|
"name": "Slow",
|
|
|
|
|
"params" : {},
|
|
|
|
|
"img_path": "res://vines_petals/vine_active_green.png"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "Vulnerable",
|
|
|
|
|
"params" : {},
|
|
|
|
|
"img_path": "res://vines_petals/vine_active_purple.png"
|
|
|
|
|
},]
|
2025-10-21 16:06:16 +02:00
|
|
|
|
|
|
|
|
# The chosen status and its data
|
2025-10-11 19:06:46 +02:00
|
|
|
var status_name : String
|
|
|
|
|
var status_params : Dictionary = {}
|
2025-10-21 16:06:16 +02:00
|
|
|
|
|
|
|
|
# To which depths vine segments and nodes are active.
|
|
|
|
|
# When max depth is reached, everything is active from then on.
|
2025-10-05 15:01:51 +02:00
|
|
|
var active_depth = -1
|
|
|
|
|
var fully_active = false
|
2025-10-21 16:06:16 +02:00
|
|
|
var max_depth = 50
|
|
|
|
|
|
|
|
|
|
# Array containings lists of sprites, using their depths as index
|
2025-10-05 15:01:51 +02:00
|
|
|
var vine_data = []
|
2025-10-03 16:36:28 +02:00
|
|
|
|
2025-10-21 16:06:16 +02:00
|
|
|
# List of "leaf locations" of the vine tree
|
|
|
|
|
var vine_end_data = []
|
2025-10-11 19:06:46 +02:00
|
|
|
|
2025-10-21 16:06:16 +02:00
|
|
|
# Places a sprite for a vine segment from pos1 to pos2 at a given depth,
|
|
|
|
|
# which might be activated already, depending on the active depth.
|
2025-10-05 15:01:51 +02:00
|
|
|
func draw_vine(pos1 : Vector2, pos2 : Vector2, depth : int):
|
2025-10-03 16:36:28 +02:00
|
|
|
var sprite = Sprite2D.new()
|
|
|
|
|
get_tree().get_root().get_node("main").add_child(sprite)
|
2025-10-05 15:01:51 +02:00
|
|
|
if active_depth >= depth:
|
|
|
|
|
sprite.texture = ResourceLoader.load(img_path_active)
|
|
|
|
|
else:
|
|
|
|
|
sprite.texture = ResourceLoader.load(img_path_inactive)
|
2025-10-21 16:06:16 +02:00
|
|
|
# If neccessary, extend vine_data to the current depth
|
2025-10-05 15:01:51 +02:00
|
|
|
while depth + 1 > vine_data.size():
|
|
|
|
|
vine_data.append([])
|
|
|
|
|
vine_data[depth].append(sprite)
|
2025-10-03 16:36:28 +02:00
|
|
|
sprite.position = (pos1 + pos2)/2.0
|
|
|
|
|
sprite.rotation = (pos1 - pos2).angle() + PI/2
|
|
|
|
|
sprite.scale *= (pos1 - pos2).length() * 1 / sprite.texture.get_width()
|
|
|
|
|
sprite.scale.x = 3
|
2025-10-03 17:09:51 +02:00
|
|
|
sprite.z_index = -1
|
2025-10-03 16:36:28 +02:00
|
|
|
|
2025-10-21 16:06:16 +02:00
|
|
|
# Grows a sequence of vine segments from grid position 1 to grid position 2, starting at given depth.
|
|
|
|
|
func grow_vine_sequence(start : VineNode, target: VineNode, grow_bud = false, quick_spawn = false):
|
|
|
|
|
var depth = min(start.depth, max_depth)
|
|
|
|
|
|
|
|
|
|
# Calculate the number and length of segments from the distance of source and target
|
|
|
|
|
var dist = (start.position - target.position).length()
|
|
|
|
|
var num_segments = floor(dist / 96)
|
|
|
|
|
var segment_length = dist / num_segments
|
2025-10-03 16:36:28 +02:00
|
|
|
|
2025-10-21 16:06:16 +02:00
|
|
|
# Build a list of vine segment positions placed horizontally equidistant along the line from the source
|
|
|
|
|
# to the target, with vertical offset having controlled 2nd derivative to prevent sharp edges.
|
|
|
|
|
var positions = []
|
|
|
|
|
positions.append(start.position)
|
2025-10-03 16:36:28 +02:00
|
|
|
var offsets = generate_random_offsets(num_segments, 0.6 * segment_length)
|
|
|
|
|
for i in range(num_segments - 1):
|
|
|
|
|
var t = (i + 1) * 1.0/num_segments
|
2025-10-21 16:06:16 +02:00
|
|
|
var center = t * target.position + (1 - t) * start.position
|
|
|
|
|
var offset = offsets[i + 1] * (target.position - start.position).normalized().rotated(PI/2)
|
2025-10-03 16:36:28 +02:00
|
|
|
positions.append(center + offset)
|
2025-10-21 16:06:16 +02:00
|
|
|
positions.append(target.position)
|
|
|
|
|
|
|
|
|
|
# Draw vine segments along the positions determined previously.
|
|
|
|
|
# Do so slowly unless quick_spawn is specified.
|
2025-10-03 16:36:28 +02:00
|
|
|
for i in range(num_segments):
|
2025-10-05 15:01:51 +02:00
|
|
|
draw_vine(positions[i], positions[i+1], depth + i + 1)
|
|
|
|
|
if not quick_spawn:
|
|
|
|
|
await get_tree().create_timer(0.2).timeout
|
2025-10-21 16:06:16 +02:00
|
|
|
|
2025-10-05 15:01:51 +02:00
|
|
|
if active_depth >= depth + num_segments:
|
2025-10-21 16:06:16 +02:00
|
|
|
if grow_bud and target.location.y > 0 and target.location.y <= Grid.max_bud_height:
|
|
|
|
|
spawn_bud(target.location, target.offset, depth + num_segments)
|
2025-10-05 15:01:51 +02:00
|
|
|
else:
|
2025-10-21 16:06:16 +02:00
|
|
|
if target.location.y > 0 and target.location.y <= Grid.max_bud_height:
|
2025-10-05 15:01:51 +02:00
|
|
|
for i in range(vine_end_data.size()):
|
2025-10-21 16:06:16 +02:00
|
|
|
if vine_end_data[i].location == start.location:
|
2025-10-05 15:01:51 +02:00
|
|
|
vine_end_data.remove_at(i)
|
|
|
|
|
break
|
2025-10-21 16:06:16 +02:00
|
|
|
target.depth = depth + num_segments
|
|
|
|
|
vine_end_data.append(target)
|
|
|
|
|
Grid.add_vine_to(self, target.location)
|
|
|
|
|
vine_locations.append(Global.vec_mod(target.location, Grid.num_collumns, true))
|
2025-10-03 16:36:28 +02:00
|
|
|
|
|
|
|
|
func generate_random_offsets(segment_count, max_second_derivative):
|
|
|
|
|
var differences = []
|
|
|
|
|
var last_diff = 0
|
|
|
|
|
for i in range(segment_count):
|
|
|
|
|
var new_diff = last_diff + randf_range(-max_second_derivative, max_second_derivative)
|
|
|
|
|
differences.append(new_diff)
|
|
|
|
|
last_diff = new_diff
|
|
|
|
|
var sum = 0.0
|
|
|
|
|
for i in range(segment_count):
|
|
|
|
|
sum += differences[i]
|
|
|
|
|
var correction = - sum / segment_count
|
|
|
|
|
for i in range(segment_count):
|
|
|
|
|
differences[i] += correction
|
|
|
|
|
var ret = []
|
|
|
|
|
var next_val = 0
|
|
|
|
|
for i in range(segment_count):
|
|
|
|
|
ret.append(next_val)
|
|
|
|
|
next_val += differences[i]
|
|
|
|
|
return ret
|
|
|
|
|
|
2025-10-05 15:01:51 +02:00
|
|
|
func spawn_bud(location, offset, depth):
|
2025-10-03 16:36:28 +02:00
|
|
|
var bud = bud_resource.instantiate()
|
|
|
|
|
bud.location = location
|
|
|
|
|
bud.offset = offset
|
|
|
|
|
bud.vine = self
|
2025-10-05 15:01:51 +02:00
|
|
|
bud.depth = depth
|
2025-10-03 16:36:28 +02:00
|
|
|
get_tree().get_root().get_node("main").add_child(bud)
|
2025-10-05 15:01:51 +02:00
|
|
|
|
|
|
|
|
func activate():
|
|
|
|
|
update_active_depth()
|
|
|
|
|
|
|
|
|
|
func update_active_depth():
|
|
|
|
|
if active_depth < max_depth:
|
|
|
|
|
await get_tree().create_timer(0.15).timeout
|
|
|
|
|
active_depth += 1
|
|
|
|
|
if vine_data.size() > active_depth:
|
|
|
|
|
for sprite in vine_data[active_depth]:
|
|
|
|
|
sprite.texture = ResourceLoader.load(img_path_active)
|
|
|
|
|
for data in vine_end_data:
|
|
|
|
|
if data.depth == active_depth:
|
|
|
|
|
spawn_bud(data.location, data.offset, data.depth)
|
|
|
|
|
update_active_depth()
|
|
|
|
|
|
2025-10-21 16:06:16 +02:00
|
|
|
func _enter_tree() -> void:
|
|
|
|
|
var data : Dictionary = status_data.pick_random()
|
|
|
|
|
status_name = data.name
|
|
|
|
|
status_params = data.params if data.has("params") else {}
|
|
|
|
|
img_path_active = data.img_path
|
|
|
|
|
init_random()
|
|
|
|
|
|
|
|
|
|
func random_vine_node_at(location):
|
|
|
|
|
var offset = random_offset()
|
|
|
|
|
return VineNode.new(self, location, offset)
|
|
|
|
|
|
2025-10-05 15:01:51 +02:00
|
|
|
func init_random():
|
2025-10-21 16:06:16 +02:00
|
|
|
Grid.add_vine_to(self, petal.location)
|
|
|
|
|
vine_locations.append(petal.location)
|
|
|
|
|
vine_end_data.append(petal)
|
2025-10-05 15:01:51 +02:00
|
|
|
for i in range(randi_range(2,2)):
|
|
|
|
|
var end = vine_end_data.pick_random()
|
|
|
|
|
var branches_count = 0
|
|
|
|
|
for branch in range(ceil(randf() * 4)):
|
|
|
|
|
var dir = [Vector2.UP, Vector2.DOWN, Vector2.RIGHT, Vector2.LEFT].pick_random()
|
2025-10-21 16:06:16 +02:00
|
|
|
var target_location = Global.vec_mod(end.location + dir, Grid.num_collumns, true)
|
|
|
|
|
if target_location.y <= Grid.max_bud_height + 1 and Grid.get_vines_at(target_location).is_empty():
|
|
|
|
|
if not (target_location.y <= 0 or target_location.y > Grid.max_bud_height):
|
2025-10-05 15:01:51 +02:00
|
|
|
branches_count += 1
|
2025-10-21 16:06:16 +02:00
|
|
|
var target = random_vine_node_at(target_location)
|
|
|
|
|
grow_vine_sequence(end, target, true, true)
|
2025-10-05 15:01:51 +02:00
|
|
|
if i==0 and branches_count == 1:
|
2025-10-21 16:06:16 +02:00
|
|
|
vine_end_data.append(petal)
|
2025-10-05 15:01:51 +02:00
|
|
|
|
|
|
|
|
func random_offset():
|
2025-10-11 12:47:24 +02:00
|
|
|
return Vector2(randf_range(60, 240), randf_range(90, 270))
|