Compare commits
6 commits
42ea2d3d75
...
b75e6be470
| Author | SHA1 | Date | |
|---|---|---|---|
| b75e6be470 | |||
| 8ca085192d | |||
| 808ffb337e | |||
| 930d9e6d8e | |||
| 248b1915b6 | |||
| 1ea01c30d6 |
16 changed files with 131 additions and 113 deletions
|
|
@ -2,6 +2,8 @@ extends TextureRect
|
|||
|
||||
@export var colors : Array[Color] = [Color(0.3, 1, 1) * 0.7, Color(1, 0.6, 0.6) * 0.7, Color(1, 1, 1) * 0.7]
|
||||
|
||||
# Modulate the background with an interpolation of the colors in the list,
|
||||
# depending on the players position on the earth.
|
||||
func _process(_delta: float) -> void:
|
||||
var index : int = floor((%Player.position.angle() + PI)/ TAU * colors.size())
|
||||
var diff = (%Player.position.angle() + PI)/ TAU * colors.size() - index
|
||||
|
|
@ -15,31 +15,9 @@ func _ready() -> void:
|
|||
if blocks_area:
|
||||
Grid.buildings.append(self)
|
||||
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
if get_node_or_null("ObjectList") != null:
|
||||
var obj_list = $ObjectList
|
||||
obj_list.reparent(get_tree().get_root().get_node("main"), false)
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
var objects_to_be_placed = obj_list.get_children()
|
||||
for object in objects_to_be_placed:
|
||||
var offset = object.global_position;
|
||||
object.global_position = Grid.get_world_position(location, offset)
|
||||
|
||||
# The building remembers these objects: If it is destroyed, so are they.
|
||||
if object is Platform or object is Trap or object is Item:
|
||||
objects.append(object)
|
||||
if "building" in object: object.building = self
|
||||
# This scales platforms hoizontally to make sure they still form a floor without gaps.
|
||||
if(object.has_method("init_at_horizontal_distortion")):
|
||||
object.init_at_horizontal_distortion(object.position.length() / Grid.ground_radius)
|
||||
grid_entrance_callback(object)
|
||||
|
||||
func grid_entrance_callback(object : Node):
|
||||
# Objects receive a callback when placed, starting from the deepest children
|
||||
for child in object.get_children():
|
||||
grid_entrance_callback(child)
|
||||
if object.has_method("_enter_grid"):
|
||||
object.call_deferred("_enter_grid")
|
||||
Grid.call_deferred("place_object_list", obj_list, self)
|
||||
|
||||
func overlaps(other : Building):
|
||||
# heights don't overlap
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ func destroy_below():
|
|||
|
||||
func wave():
|
||||
# Raise a wave from the water at the boundary of the screen and move it towards the center.
|
||||
var angle = player.position.angle
|
||||
var angle = player.position.angle()
|
||||
var dir = randi_range(0, 1) * 2 - 1
|
||||
var speed = 3000 / water.radius_base
|
||||
water.create_tsunami(angle - speed * dir * TAU/30, dir, speed)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
[ext_resource type="Script" uid="uid://cpaskpj67pnaj" path="res://enemies/boss/boss_spawner.gd" id="10_efxa6"]
|
||||
[ext_resource type="PackedScene" uid="uid://cqn67nwyrtq3k" path="res://ui/journal/journal.tscn" id="10_w48qg"]
|
||||
[ext_resource type="PackedScene" uid="uid://cpe4s6vsn0ujd" path="res://enemies/boss/boss.tscn" id="11_efxa6"]
|
||||
[ext_resource type="Script" uid="uid://gul4u5tw1vxk" path="res://bg_image.gd" id="13_vivmo"]
|
||||
[ext_resource type="Script" uid="uid://gul4u5tw1vxk" path="res://background/bg_image.gd" id="13_vivmo"]
|
||||
|
||||
[node name="main" type="Node2D"]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,24 @@
|
|||
extends Control
|
||||
|
||||
@onready var item_list : ItemList = $ItemList
|
||||
var item_list_no_dupes = []
|
||||
|
||||
# Add items from each pool to journal. TODO: Deal with multiplicities.
|
||||
# Add items from each pool to journal.
|
||||
func _ready() -> void:
|
||||
await get_tree().create_timer(0.3).timeout
|
||||
for item_scene in ItemSpawn.item_pool.common:
|
||||
add_item_to_journal(item_scene.instantiate())
|
||||
add_item_to_journal(item_scene)
|
||||
for item_scene in ItemSpawn.item_pool.rare:
|
||||
add_item_to_journal(item_scene.instantiate())
|
||||
add_item_to_journal(item_scene)
|
||||
for item_scene in ItemSpawn.item_pool.unique:
|
||||
add_item_to_journal(item_scene.instantiate())
|
||||
add_item_to_journal(item_scene)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Input.is_action_just_pressed("journal"):
|
||||
visible = not visible
|
||||
|
||||
func add_item_to_journal(item: Item):
|
||||
item_list.add_item(item.item_name, item.icon)
|
||||
# Adds an item to the journal if it was not yet added
|
||||
func add_item_to_journal(item_scene):
|
||||
if not item_list_no_dupes.has(item_scene):
|
||||
item_list_no_dupes.append(item_scene)
|
||||
var item = item_scene.instantiate()
|
||||
item_list.add_item(item.item_name, item.icon)
|
||||
|
|
|
|||
|
|
@ -1,40 +1,37 @@
|
|||
class_name VineNode extends Node2D
|
||||
|
||||
@export var vine : Vine
|
||||
class_name GridNode extends Node2D
|
||||
|
||||
# Setting location and offset automatically adjusts position
|
||||
@export var location : Vector2 :
|
||||
set(new_loc):
|
||||
location = Global.vec_mod(new_loc, Grid.num_collumns, true)
|
||||
@export var offset : Vector2
|
||||
update_position()
|
||||
@export var offset : Vector2 :
|
||||
set(new_offset):
|
||||
offset = new_offset
|
||||
update_position()
|
||||
@export var depth : int
|
||||
|
||||
func _get(property: StringName) -> Variant:
|
||||
if property == "position":
|
||||
update_position()
|
||||
return position
|
||||
if property == "global_position":
|
||||
update_position()
|
||||
return global_position
|
||||
return null
|
||||
|
||||
# Setting the global position automatically adjusts location and offset
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
if property == "global_position":
|
||||
location = Grid.get_location_from_world_pos(value)
|
||||
offset = Grid.get_offset_from_world_pos(value)
|
||||
update_position()
|
||||
return true
|
||||
if property == "position":
|
||||
update_position()
|
||||
return false
|
||||
return false
|
||||
|
||||
# Generates position from location and offset
|
||||
func update_position():
|
||||
global_position = Grid.get_world_position(location, offset)
|
||||
|
||||
func _enter_grid() -> void:
|
||||
update_position()
|
||||
|
||||
func _init(_vine = null, _location = Vector2.ZERO, _offset = Vector2.ZERO, _depth = 0):
|
||||
vine = _vine
|
||||
# Constructor for Grid Nodes
|
||||
func _init(_location = Vector2.ZERO, _offset = Vector2.ZERO):
|
||||
location = _location
|
||||
offset = _offset
|
||||
depth = _depth
|
||||
|
||||
static func random_at(_location):
|
||||
var rand_offset = Vector2(randf_range(60, 240), randf_range(90, 270))
|
||||
return GridNode.new(_location, rand_offset)
|
||||
|
|
@ -1,14 +1,18 @@
|
|||
class_name Bud extends VineNode
|
||||
class_name Bud extends GridNode
|
||||
signal opened
|
||||
var img_path
|
||||
@export var vine : Vine
|
||||
|
||||
# Triggers when a bud is hit. Spreads the vine, then removes the bud
|
||||
func _on_opened():
|
||||
$EnemyHurtbox.monitorable = false
|
||||
$EnemyHurtbox.monitoring = false
|
||||
$AnimatedSprite2D.play("open")
|
||||
opened.emit()
|
||||
spread()
|
||||
await $AnimatedSprite2D.animation_finished
|
||||
queue_free()
|
||||
for child in get_children():
|
||||
queue_free()
|
||||
|
||||
# Spread in all directions where the given vine is not yet present
|
||||
func spread():
|
||||
|
|
@ -19,5 +23,5 @@ func spread():
|
|||
# Grow a vine
|
||||
func grow_to_next_bud(dir):
|
||||
var target_location = Global.vec_mod(location + dir, Grid.num_collumns, true)
|
||||
var target = vine.random_vine_node_at(target_location)
|
||||
var target = GridNode.random_at(target_location)
|
||||
await vine.grow_vine_sequence(self, target, true, false)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
class_name Petal extends VineNode
|
||||
class_name Petal extends GridNode
|
||||
@export var vine : Vine
|
||||
@export var vine_resource : PackedScene
|
||||
var activated = false
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ var vine_end_data = []
|
|||
func draw_vine(pos1 : Vector2, pos2 : Vector2, depth : int):
|
||||
var sprite = Sprite2D.new()
|
||||
get_tree().get_root().get_node("main").add_child(sprite)
|
||||
if active_depth >= depth:
|
||||
if active_depth >= depth or fully_active:
|
||||
sprite.texture = ResourceLoader.load(img_path_active)
|
||||
else:
|
||||
sprite.texture = ResourceLoader.load(img_path_inactive)
|
||||
|
|
@ -59,7 +59,7 @@ func draw_vine(pos1 : Vector2, pos2 : Vector2, depth : int):
|
|||
sprite.z_index = -1
|
||||
|
||||
# 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):
|
||||
func grow_vine_sequence(start : GridNode, target: GridNode, 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
|
||||
|
|
@ -86,21 +86,29 @@ func grow_vine_sequence(start : VineNode, target: VineNode, grow_bud = false, qu
|
|||
if not quick_spawn:
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
|
||||
if active_depth >= depth + num_segments:
|
||||
# If growing while active, place buds
|
||||
if active_depth >= depth + num_segments or fully_active:
|
||||
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)
|
||||
else:
|
||||
# Otherwise, remember the spot to spawn a bud later.
|
||||
# Further, note that the previous location is no longer a leaf unless it is the petal.
|
||||
if target.location.y > 0 and target.location.y <= Grid.max_bud_height:
|
||||
for i in range(vine_end_data.size()):
|
||||
if vine_end_data[i].location == start.location:
|
||||
if vine_end_data[i].location == start.location and not vine_end_data[i] is Petal:
|
||||
vine_end_data.remove_at(i)
|
||||
break
|
||||
target.depth = depth + num_segments
|
||||
vine_end_data.append(target)
|
||||
|
||||
# Register the new vine segment in the grid
|
||||
Grid.add_vine_to(self, target.location)
|
||||
vine_locations.append(Global.vec_mod(target.location, Grid.num_collumns, true))
|
||||
|
||||
# Generates a random function on (segment_count - 1)
|
||||
# many grid points with bounded second derivative
|
||||
func generate_random_offsets(segment_count, max_second_derivative):
|
||||
# First, randomize the derivative of the desired function
|
||||
var differences = []
|
||||
var last_diff = 0
|
||||
for i in range(segment_count):
|
||||
|
|
@ -108,11 +116,15 @@ func generate_random_offsets(segment_count, max_second_derivative):
|
|||
differences.append(new_diff)
|
||||
last_diff = new_diff
|
||||
var sum = 0.0
|
||||
|
||||
# Shift that derivative by a constant to add up to 0
|
||||
for i in range(segment_count):
|
||||
sum += differences[i]
|
||||
var correction = - sum / segment_count
|
||||
for i in range(segment_count):
|
||||
differences[i] += correction
|
||||
|
||||
# Return the partial sums over the derivative constructed above
|
||||
var ret = []
|
||||
var next_val = 0
|
||||
for i in range(segment_count):
|
||||
|
|
@ -120,6 +132,7 @@ func generate_random_offsets(segment_count, max_second_derivative):
|
|||
next_val += differences[i]
|
||||
return ret
|
||||
|
||||
# Instantiates a bud
|
||||
func spawn_bud(location, offset, depth):
|
||||
var bud = bud_resource.instantiate()
|
||||
bud.location = location
|
||||
|
|
@ -128,9 +141,13 @@ func spawn_bud(location, offset, depth):
|
|||
bud.depth = depth
|
||||
get_tree().get_root().get_node("main").add_child(bud)
|
||||
|
||||
# Upon activation start the process of slowly increasing the active depth
|
||||
func activate():
|
||||
update_active_depth()
|
||||
if active_depth < 0:
|
||||
update_active_depth()
|
||||
|
||||
# Progressively activate the vine by retexturing its sprites and spawning buds if neccessary
|
||||
# Once max_depth is reached, fully activate the vine
|
||||
func update_active_depth():
|
||||
if active_depth < max_depth:
|
||||
await get_tree().create_timer(0.15).timeout
|
||||
|
|
@ -138,11 +155,13 @@ func update_active_depth():
|
|||
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)
|
||||
for node in vine_end_data:
|
||||
if node.depth == active_depth and not node is Petal:
|
||||
spawn_bud(node.location, node.offset, node.depth)
|
||||
update_active_depth()
|
||||
else: fully_active = true
|
||||
|
||||
# Upon entering the scene, select the applied status, then spawn the inactive vine
|
||||
func _enter_tree() -> void:
|
||||
var data : Dictionary = status_data.pick_random()
|
||||
status_name = data.name
|
||||
|
|
@ -150,27 +169,21 @@ func _enter_tree() -> void:
|
|||
img_path_active = data.img_path
|
||||
init_random()
|
||||
|
||||
func random_vine_node_at(location):
|
||||
var offset = random_offset()
|
||||
return VineNode.new(self, location, offset)
|
||||
|
||||
# Initializes a random vine
|
||||
func init_random():
|
||||
# First, include the petal
|
||||
Grid.add_vine_to(self, petal.location)
|
||||
vine_locations.append(petal.location)
|
||||
vine_end_data.append(petal)
|
||||
for i in range(randi_range(2,2)):
|
||||
# Attempt to grow a new vine for a total 6 - 12 times
|
||||
var grow_attempts = randi_range(6,12)
|
||||
while grow_attempts > 0:
|
||||
# Attempt to grow from a random end to 1 - 4 random directions
|
||||
var end = vine_end_data.pick_random()
|
||||
var branches_count = 0
|
||||
for branch in range(ceil(randf() * 4)):
|
||||
for branch in range(min(ceil(randf() * 4), grow_attempts)):
|
||||
grow_attempts -= 1
|
||||
var dir = [Vector2.UP, Vector2.DOWN, Vector2.RIGHT, Vector2.LEFT].pick_random()
|
||||
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):
|
||||
branches_count += 1
|
||||
var target = random_vine_node_at(target_location)
|
||||
var target = GridNode.random_at(target_location)
|
||||
grow_vine_sequence(end, target, true, true)
|
||||
if i==0 and branches_count == 1:
|
||||
vine_end_data.append(petal)
|
||||
|
||||
func random_offset():
|
||||
return Vector2(randf_range(60, 240), randf_range(90, 270))
|
||||
|
|
|
|||
|
|
@ -6,40 +6,43 @@ class_name BuildingGenerator extends Node
|
|||
@export var only_on_first_load = false
|
||||
static var first_load = true
|
||||
|
||||
func random_oppostite_collumn() -> int:
|
||||
var playerpos = %Player.position
|
||||
var player_angle = atan2(playerpos.y, playerpos.x)
|
||||
|
||||
var offset = randf_range(TAU/3, 2*TAU/3)
|
||||
var spawn_angle = player_angle + offset
|
||||
var collumn = int(spawn_angle / TAU * Grid.num_collumns + Grid.num_collumns) % Grid.num_collumns
|
||||
|
||||
# Determine the player's column, then choose one in the opposite third of the circle
|
||||
func random_opposite_collumn() -> int:
|
||||
var player_location = Grid.get_location_from_world_pos(%Player.position)
|
||||
var offset = randi_range(int(Grid.num_collumns / 3.0), int(2 * Grid.num_collumns / 3.0))
|
||||
var collumn = player_location.x + offset
|
||||
return collumn
|
||||
|
||||
|
||||
func random_collumn() -> int:
|
||||
return randi_range(0, Grid.num_collumns - 1)
|
||||
|
||||
|
||||
func _ready():
|
||||
# The initial buildings in the main menu are only to be spawned upon first loading
|
||||
if not (only_on_first_load and not first_load):
|
||||
first_load = false
|
||||
for i in range(initial_buildings):
|
||||
# For each building, attempt spawns a few times.
|
||||
# Spawns are not viable if they are within the player spawn protection
|
||||
# or overlap with existing buildings
|
||||
for j in range(spawn_attempts):
|
||||
var collumn = random_collumn()
|
||||
if initial_spawn_protection and 43 <= collumn and collumn <= 49:
|
||||
continue
|
||||
var building = randomize_building()
|
||||
building.z_index = -2
|
||||
var building = random_building()
|
||||
if Grid.add_building_to_collumn(building, collumn):
|
||||
break
|
||||
|
||||
# Each time the building generator's timer runs out, generate a new building
|
||||
func _on_timer_timeout() -> void:
|
||||
for i in range(spawn_attempts):
|
||||
var collumn = random_oppostite_collumn()
|
||||
var building : Building = randomize_building()
|
||||
building.z_index = -2
|
||||
var collumn = random_opposite_collumn()
|
||||
var building : Building = random_building()
|
||||
if Grid.add_building_to_collumn(building, collumn):
|
||||
break
|
||||
|
||||
func randomize_building() -> Building:
|
||||
|
||||
# Picks a random building from the list. Sets the z_index.
|
||||
func random_building() -> Building:
|
||||
var index = randi() % Grid.packed_buildings.size()
|
||||
return Grid.packed_buildings[index].instantiate()
|
||||
var ret = Grid.packed_buildings[index].instantiate()
|
||||
ret.z_index = -2
|
||||
return ret
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
extends Node2D
|
||||
@export var radius : float;
|
||||
|
||||
func _draw():
|
||||
draw_circle(Vector2.ZERO, radius, Color.BLACK, true, -1.0, true)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://b5fhsy1xlreco
|
||||
|
|
@ -3,7 +3,7 @@ extends Node2D
|
|||
@export var grass : PackedScene
|
||||
|
||||
func _ready() -> void:
|
||||
ItemSpawn.item_pool = ResourceLoader.load("res://items/generic/item_pool.tres","",ResourceLoader.CACHE_MODE_IGNORE)
|
||||
# Place grass
|
||||
for column in range(Grid.num_collumns):
|
||||
var grass_placed : Building = grass.instantiate()
|
||||
grass_placed.location = Vector2(column, 0)
|
||||
|
|
|
|||
|
|
@ -42,15 +42,33 @@ func add_building_to_collumn(building : Building, collumn : int):
|
|||
building.free()
|
||||
return false
|
||||
|
||||
|
||||
add_child(building)
|
||||
return true
|
||||
|
||||
# Adds children of given node to the grid and their building.
|
||||
func place_object_list(obj_list : Node2D, building):
|
||||
obj_list.reparent(self, false)
|
||||
var objects_to_be_placed = obj_list.get_children()
|
||||
for object in objects_to_be_placed:
|
||||
var offset = object.global_position;
|
||||
object.global_position = Grid.get_world_position(building.location, offset)
|
||||
|
||||
# The building remembers these objects: If it is destroyed, so are they.
|
||||
if object is Platform or object is Trap or object is Item:
|
||||
building.objects.append(object)
|
||||
if "building" in object: object.building = self
|
||||
# This scales platforms hoizontally to make sure they still form a floor without gaps.
|
||||
if(object.has_method("init_at_horizontal_distortion")):
|
||||
object.init_at_horizontal_distortion(object.position.length() / Grid.ground_radius)
|
||||
grid_entrance_callback(object)
|
||||
|
||||
# Returns the global position for a given grid location and offset
|
||||
func get_world_position (location: Vector2, offset: Vector2 = Vector2.ZERO) -> Vector2:
|
||||
var height = ground_radius + location.y * cell_height - offset.y # currently assumes anchor is bottom left
|
||||
var angle = (location.x + offset.x / cell_height) * TAU / num_collumns
|
||||
return height * Vector2.from_angle(angle)
|
||||
|
||||
# Returns the grid location for a given global position
|
||||
func get_location_from_world_pos(pos : Vector2):
|
||||
var angle = fposmod(pos.angle(), TAU)
|
||||
var x = floor(num_collumns * angle / TAU)
|
||||
|
|
@ -58,6 +76,7 @@ func get_location_from_world_pos(pos : Vector2):
|
|||
var y = ceil((height - ground_radius)/cell_height)
|
||||
return Vector2(x, y)
|
||||
|
||||
# Returns the offset with respect to the grid location for a given global position
|
||||
func get_offset_from_world_pos(pos : Vector2):
|
||||
var angle = pos.angle()
|
||||
var x = fposmod(num_collumns * angle / TAU, 1) * cell_height
|
||||
|
|
@ -65,7 +84,10 @@ func get_offset_from_world_pos(pos : Vector2):
|
|||
var y = fposmod(-(height - ground_radius)/cell_height, 1) * cell_height
|
||||
return Vector2(x, y)
|
||||
|
||||
# Resets objects in the grids and the item pool.
|
||||
# Also initializes vines_per_node.
|
||||
func reset():
|
||||
ItemSpawn.item_pool = ResourceLoader.load("res://items/generic/item_pool.tres","",ResourceLoader.CACHE_MODE_IGNORE)
|
||||
for obj in get_children():
|
||||
obj.free()
|
||||
buildings = []
|
||||
|
|
@ -76,22 +98,22 @@ func reset():
|
|||
arr.append([])
|
||||
vines_per_node.append(arr)
|
||||
|
||||
# Returns all vines present at given grid location.
|
||||
func get_vines_at(location) -> Array:
|
||||
location = Global.vec_mod(location, num_collumns, true)
|
||||
return vines_per_node[location.x][location.y]
|
||||
|
||||
# Registers a vine at a given grid location.
|
||||
func add_vine_to(vine, location) -> void:
|
||||
if location.y > max_bud_height + 1 or location.y < 0:
|
||||
return
|
||||
location = Global.vec_mod(location, num_collumns, true)
|
||||
vines_per_node[location.x][location.y].append(vine)
|
||||
|
||||
# for testing
|
||||
#func _ready() -> void:
|
||||
#
|
||||
#
|
||||
#
|
||||
#for i in range(100):
|
||||
#var test_building = packed_buildings[0].instantiate()
|
||||
#var collumn = randi() % 60
|
||||
#add_building_to_collumn(test_building, collumn)
|
||||
# Calls _enter_grid on all children.
|
||||
func grid_entrance_callback(object : Node):
|
||||
# Objects receive a callback when placed, starting from the deepest children
|
||||
for child in object.get_children():
|
||||
grid_entrance_callback(child)
|
||||
if object.has_method("_enter_grid"):
|
||||
object.call_deferred("_enter_grid")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue