pathfinding rework for now

This commit is contained in:
2025-06-13 00:06:51 +10:00
parent 400415f8a5
commit 6f4f465e37
817 changed files with 5533 additions and 3146 deletions

View File

@ -11,9 +11,9 @@ func apply_effect(effect: Effect, targets: Array[EnemyController]) -> void:
if targets.has(enemy):
enemy.apply_effect(effect)
if Data.preferences.display_tower_damage_indicators and effect.damage > 0:
spawn_damage_indicator(effect.damage, enemy.sprite.global_position)
spawn_damage_indicator(effect.damage, enemy.d_n.global_position)
else:
for enemy: EnemyController in targets:
enemy.apply_effect(effect)
if Data.preferences.display_tower_damage_indicators and effect.damage > 0:
spawn_damage_indicator(effect.damage, enemy.sprite.global_position)
spawn_damage_indicator(effect.damage, enemy.d_n.global_position)

View File

@ -5,4 +5,4 @@ func apply_effect(effect: Effect, targets: Array[EnemyController]) -> void:
for enemy: EnemyController in targets:
enemy.apply_effect(effect)
if Data.preferences.display_tower_damage_indicators and effect.damage > 0:
spawn_damage_indicator(effect.damage, enemy.sprite.global_position)
spawn_damage_indicator(effect.damage, enemy.d_n.global_position)

View File

@ -10,6 +10,7 @@ class_name LeapingController extends PathingController
@export var souths: Sprite3D
@export var box: CSGBox3D
@export var tol: Label
@export var jump_distance: float = 4.0
var tolerance: float = 50.0
var jumping: bool = false
@ -21,20 +22,19 @@ func _process(delta: float) -> void:
func _physics_process(delta: float) -> void:
if !path or jumping:
if !next_node or jumping:
return
var distance_travelled: float = (character.stats.movement_speed * clampf(character.movement_speed_penalty, 0.0, 1.0)) * delta
distance_remaining -= distance_travelled
path_progress += distance_travelled
var sample: Transform3D = path.sample_baked_with_rotation(path_progress, true)
character.global_position = sample.origin
character.look_at(character.global_position + -sample.basis.z)
var closest_point: int = astar.astar.get_closest_point(character.global_position, false)
box.global_position = astar.astar.get_point_position(closest_point)
var east: int = astar.get_east_point(closest_point)
var west: int = astar.get_west_point(closest_point)
var north: int = astar.get_north_point(closest_point)
var south: int = astar.get_south_point(closest_point)
walk(delta)
consider_leap(Vector3.FORWARD)
consider_leap(Vector3.LEFT)
consider_leap(Vector3.BACK)
consider_leap(Vector3.RIGHT)
#var closest_point: int = astar.astar.get_closest_point(character.global_position, false)
#box.global_position = astar.astar.get_point_position(closest_point)
#var east: int = astar.get_east_point(closest_point)
#var west: int = astar.get_west_point(closest_point)
#var north: int = astar.get_north_point(closest_point)
#var south: int = astar.get_south_point(closest_point)
#if east >= 0 and astar.astar.is_point_disabled(east):
#eastl.text = "fuck no"
#else:
@ -51,95 +51,117 @@ func _physics_process(delta: float) -> void:
#southl.text = "fuck no"
#else:
#southl.text = "yeah"
norths.global_position = character.global_position + Vector3(-1.0, 1.0, 0.0)
souths.global_position = character.global_position + Vector3(1.0, 1.0, 0.0)
easts.global_position = character.global_position + Vector3(0.0, 1.0, -1.0)
wests.global_position = character.global_position + Vector3(0.0, 1.0, 1.0)
if east >= 0:
if astar.astar.is_point_disabled(east):
var further_point: int = astar.get_east_point(east)
if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(0.0, 0.0, -4.0))
var current_offset: float = path.get_closest_offset(character.global_position)
var gain: float = expected_offset - current_offset
if gain >= tolerance:
distance_remaining -= gain
path_progress += gain
leap(Vector3(0.0, 0.0, -4.0))
eastl.text = str(gain)
#easts.visible = true
else:
eastl.text = "cant"
else:
eastl.text = "clear"
else:
eastl.text = "invalid"
if west >= 0:
if astar.astar.is_point_disabled(west):
var further_point: int = astar.get_west_point(west)
if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(0.0, 0.0, 4.0))
var current_offset: float = path.get_closest_offset(character.global_position)
var gain: float = expected_offset - current_offset
if gain >= tolerance:
distance_remaining -= gain
path_progress += gain
leap(Vector3(0.0, 0.0, 4.0))
westl.text = str(gain)
#wests.visible = true
else:
westl.text = "cant"
else:
westl.text = "clear"
else:
westl.text = "invalid"
if north >= 0:
if astar.astar.is_point_disabled(north):
var further_point: int = astar.get_north_point(north)
if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(-4.0, 0.0, 0.0))
var current_offset: float = path.get_closest_offset(character.global_position)
var gain: float = expected_offset - current_offset
if gain >= tolerance:
distance_remaining -= gain
path_progress += gain
leap(Vector3(-4.0, 0.0, 0.0))
northl.text = str(gain)
#norths.visible = true
else:
northl.text = "cant"
else:
northl.text = "clear"
else:
northl.text = "invalid"
if south >= 0:
if astar.astar.is_point_disabled(south):
var further_point: int = astar.get_south_point(south)
if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(4.0, 0.0, 0.0))
var current_offset: float = path.get_closest_offset(character.global_position)
var gain: float = expected_offset - current_offset
if gain >= tolerance:
distance_remaining -= gain
path_progress += gain
leap(Vector3(4.0, 0.0, 0.0))
southl.text = str(gain)
#souths.visible = true
else:
southl.text = "cant"
else:
southl.text = "clear"
else:
southl.text = "invalid"
#norths.global_position = character.global_position + Vector3(-1.0, 1.0, 0.0)
#souths.global_position = character.global_position + Vector3(1.0, 1.0, 0.0)
#easts.global_position = character.global_position + Vector3(0.0, 1.0, -1.0)
#wests.global_position = character.global_position + Vector3(0.0, 1.0, 1.0)
#if east >= 0:
#if astar.astar.is_point_disabled(east):
#var further_point: int = astar.get_east_point(east)
#if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
#var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(0.0, 0.0, -4.0))
#var current_offset: float = path.get_closest_offset(character.global_position)
#var gain: float = expected_offset - current_offset
#if gain >= tolerance:
#distance_remaining -= gain
##path_progress += gain
#leap(Vector3(0.0, 0.0, -4.0))
#eastl.text = str(gain)
##easts.visible = true
#else:
#eastl.text = "cant"
#else:
#eastl.text = "clear"
#else:
#eastl.text = "invalid"
#if west >= 0:
#if astar.astar.is_point_disabled(west):
#var further_point: int = astar.get_west_point(west)
#if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
#var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(0.0, 0.0, 4.0))
#var current_offset: float = path.get_closest_offset(character.global_position)
#var gain: float = expected_offset - current_offset
#if gain >= tolerance:
#distance_remaining -= gain
##path_progress += gain
#leap(Vector3(0.0, 0.0, 4.0))
#westl.text = str(gain)
##wests.visible = true
#else:
#westl.text = "cant"
#else:
#westl.text = "clear"
#else:
#westl.text = "invalid"
#if north >= 0:
#if astar.astar.is_point_disabled(north):
#var further_point: int = astar.get_north_point(north)
#if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
#var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(-4.0, 0.0, 0.0))
#var current_offset: float = path.get_closest_offset(character.global_position)
#var gain: float = expected_offset - current_offset
#if gain >= tolerance:
#distance_remaining -= gain
##path_progress += gain
#leap(Vector3(-4.0, 0.0, 0.0))
#northl.text = str(gain)
##norths.visible = true
#else:
#northl.text = "cant"
#else:
#northl.text = "clear"
#else:
#northl.text = "invalid"
#if south >= 0:
#if astar.astar.is_point_disabled(south):
#var further_point: int = astar.get_south_point(south)
#if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
#var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(4.0, 0.0, 0.0))
#var current_offset: float = path.get_closest_offset(character.global_position)
#var gain: float = expected_offset - current_offset
#if gain >= tolerance:
#distance_remaining -= gain
##path_progress += gain
#leap(Vector3(4.0, 0.0, 0.0))
#southl.text = str(gain)
##souths.visible = true
#else:
#southl.text = "cant"
#else:
#southl.text = "clear"
#else:
#southl.text = "invalid"
func consider_leap(direction: Vector3) -> void:
var node: FlowNode = check_jump(character.global_position + (direction * jump_distance))
if node:
var expected_distance_remaining: float = calculate_distance_to_goal(node)
expected_distance_remaining += (character.global_position + (direction * jump_distance)).distance_to(node.global_position)
var gain: float = distance_remaining - expected_distance_remaining
if gain >= tolerance:
distance_remaining -= gain
leap(direction * jump_distance)
next_node = node
func finish_jump() -> void:
jumping = false
func check_jump(destination: Vector3) -> FlowNode:
var closest_point: FlowNode = flow_field.get_closest_traversable_point(destination)
if !closest_point.best_path or closest_point.global_position.distance_to(destination) > 1.2:
return null
return closest_point.best_path
func leap(to_point: Vector3) -> void:
jumping = true
var tween: Tween = create_tween()
tween.tween_property(character, "global_position", character.global_position + (to_point / 2.0) + Vector3.UP, 0.5)
tween.tween_property(character, "global_position", character.global_position + to_point, 0.5)
tween.tween_property(character, "global_position", character.global_position + (to_point / 2.0) + Vector3.UP, 0.3)
tween.tween_property(character, "global_position", character.global_position + to_point, 0.3)
tween.tween_callback(finish_jump)

View File

@ -1,21 +1,47 @@
class_name PathingController extends EnemyMovement
var path: Curve3D
var path_progress: float = 0.0
#var path: Curve3D
#var path_progress: float = 0.0
var flow_field: FlowField
var next_node: FlowNode
func _ready() -> void:
if path:
distance_remaining = path.get_baked_length()
#if path:
# distance_remaining = path.get_baked_length()
next_node = flow_field.get_closest_traversable_point(character.global_position)
distance_remaining += calculate_distance_to_goal(next_node)
func calculate_distance_to_goal(node: FlowNode) -> float:
var distance: float = 0.0
distance += character.global_position.distance_to(node.global_position)
if node.best_path:
var then_next_node: FlowNode = node.best_path
distance += node.global_position.distance_to(then_next_node.global_position)
while then_next_node.best_path:
distance += then_next_node.global_position.distance_to(then_next_node.best_path.global_position)
then_next_node = then_next_node.best_path
return distance
func walk(delta: float) -> void:
var distance_travelled: float = (character.stats.movement_speed * clampf(character.movement_speed_penalty, 0.0, 1.0)) * delta
distance_remaining -= distance_travelled
character.global_position = character.global_position.move_toward(next_node.global_position, distance_travelled)
character.look_at(next_node.global_position)
if character.global_position.distance_to(next_node.global_position) <= 0.05:
next_node = next_node.best_path
func _physics_process(delta: float) -> void:
if !path:
#if !path:
# return
if !next_node:
return
var distance_travelled: float = (character.stats.movement_speed * clampf(character.movement_speed_penalty, 0.0, 1.0)) * delta
distance_remaining -= distance_travelled
path_progress += distance_travelled
var sample: Transform3D = path.sample_baked_with_rotation(path_progress, true)
character.global_position = sample.origin
character.look_at(character.global_position + -sample.basis.z)
var closest_point: Vector3 = path.get_closest_point(character.global_position)
walk(delta)
#path_progress += distance_travelled
#var sample: Transform3D = path.sample_baked_with_rotation(path_progress, true)
#character.global_position = sample.origin
#character.look_at(character.global_position + -sample.basis.z)
#var closest_point: Vector3 = path.get_closest_point(character.global_position)

View File

@ -36,13 +36,13 @@ func hit(target: CharacterBody3D) -> void:
target.apply_effect(effect)
if owner_id == 0:
if Data.preferences.display_tower_damage_indicators:
spawn_damage_indicator(target.sprite.global_position)
spawn_damage_indicator(target.d_n.global_position)
if owner_id == multiplayer.get_unique_id():
if Data.preferences.display_self_damage_indicators:
spawn_damage_indicator(target.sprite.global_position)
spawn_damage_indicator(target.d_n.global_position)
if owner_id != 0 and owner_id != multiplayer.get_unique_id():
if Data.preferences.display_party_damage_indicators:
spawn_damage_indicator(target.sprite.global_position)
spawn_damage_indicator(target.d_n.global_position)
@rpc("reliable")

View File

@ -2,6 +2,7 @@ class_name Enemy extends Resource
@export var title: String = "dog"
@export var target_type: Data.EnemyType
@export var scene: PackedScene
@export var icon: Texture
@export var death_sprite: Texture
@export var sprite: AtlasTexture

View File

@ -6,6 +6,11 @@ var save_slot: int = 0
var twenty_game_history: Array[bool] = []
var wins: int = 0
var losses: int = 0
var winrate: int :
get():
return int((float(twenty_game_history.count(true)) / float(twenty_game_history.size())) * 100.0)
set(_value):
return
#Engineer
var engineer_cards_bought: int = 0

View File

@ -37,4 +37,4 @@ func networked_hit(target_path: String, target_hitbox_path: String) -> void:
var target_hitbox: Hitbox = get_tree().root.get_node(target_hitbox_path) as Hitbox
hit(target, target_hitbox)
if Data.preferences.display_party_damage_indicators:
spawn_damage_indicator(target.sprite.global_position)
spawn_damage_indicator(target.d_n.global_position)

View File

@ -30,7 +30,7 @@ func shoot() -> void:
if target_hitbox is Hitbox:
hit(target, target_hitbox)
if Data.preferences.display_self_damage_indicators:
spawn_damage_indicator(target.sprite.global_position)
spawn_damage_indicator(target.d_n.global_position)
networked_hit.rpc(get_tree().root.get_path_to(target), get_tree().root.get_path_to(target_hitbox))
@ -51,4 +51,4 @@ func networked_hit(target_path: String, target_hitbox_path: String) -> void:
var target_hitbox: Hitbox = get_tree().root.get_node(target_hitbox_path) as Hitbox
hit(target, target_hitbox)
if Data.preferences.display_party_damage_indicators:
spawn_damage_indicator(target.sprite.global_position)
spawn_damage_indicator(target.d_n.global_position)

View File

@ -230,12 +230,13 @@ func find_path() -> bool:
func make_grid() -> void:
for x: int in grid_size.x:
for y: int in grid_size.y:
var point_position: Vector3 = Vector3((x - floori(grid_size.x / 2.0)) * point_gap, 0.5, (y - floori(grid_size.y / 2.0)) * point_gap)
var point_position: Vector3 = Vector3((x - floori(grid_size.x / 2.0)) * point_gap, 0, (y - floori(grid_size.y / 2.0)) * point_gap)
point_position += global_position
astar.add_point(int(x * grid_size.y + y), point_position)
var frame: Node3D = tower_frame_scene.instantiate()
frame.position = point_position
tower_frames.append(frame)
add_child(frame)
frame.global_position = point_position
for x: int in grid_size.x:
for y: int in grid_size.y:

View File

@ -4,7 +4,7 @@ class_name CinematicCamManager extends Node3D
@export var cameras: Array[Camera3D]
@export var pan_speed: float = 1.0
var current_cam: int = 0
var does_its_thing: bool = true
@export var does_its_thing: bool = true
func _ready() -> void:

View File

@ -1,178 +0,0 @@
class_name EditTool extends Node3D
@export var hero: Hero
@export var inventory: Inventory
@export var ray: RayCast3D
@export var wall_preview: TowerBase
@export var progress_bar: TextureProgressBar
var enabled: bool = true
var point_id: int = -1
var obstacle_last_point: int = -1
var valid_point: bool = false
var is_looking_at_tower_base: bool = false
var tower_preview: Tower
var last_tower_base: TowerBase
var last_collider: Object
var last_card: Card
var ray_collider: Object
var ray_point: Vector3
var interact_key_held: bool = false
var interacted_once: bool = false
var interact_held_time: float = 0.0
var interact_hold_time: float = 0.4
func _ready() -> void:
var c: Color = Color.GREEN
c.a = 0.8
wall_preview.set_color(c)
wall_preview.set_float(0.0)
wall_preview.toggle_collision()
func _process(delta: float) -> void:
if !enabled:
ray_collider = null
wall_preview.set_visible(false)
if is_instance_valid(last_collider):
Game.level.a_star_graph_3d.tower_base_ids[last_collider.point_id].set_float(1.0)
last_collider = null
return
if interact_key_held and !interacted_once and valid_point and hero.currency >= Data.wall_cost and ray.is_colliding() and Game.level.a_star_graph_3d.point_is_build_location(point_id):
interact_held_time += delta
set_progress_percent(interact_held_time / interact_hold_time)
wall_preview.set_float(interact_held_time / interact_hold_time)
if interact_held_time >= interact_hold_time:
set_progress_percent(0)
interacted_once = true
build_wall()
if interact_key_held and !interacted_once and last_collider and ray.is_colliding():
interact_held_time += delta
set_progress_percent(interact_held_time / interact_hold_time)
if interact_held_time >= interact_hold_time:
set_progress_percent(0)
interacted_once = true
refund_wall(last_collider)
if !interact_key_held:
interact_held_time = 0.0
interacted_once = false
set_progress_percent(0)
wall_preview.set_float(0.0)
point_id = -1
if !interacted_once and ray.is_colliding():
if !interact_key_held:
wall_preview.set_visible(true)
ray_collider = ray.get_collider()
ray_point = ray.get_collision_point()
is_looking_at_tower_base = ray_collider is TowerBase
if is_looking_at_tower_base:
valid_point = false
point_id = ray_collider.point_id
if obstacle_last_point != point_id:
obstacle_last_point = point_id
if is_instance_valid(last_collider):
Game.level.a_star_graph_3d.tower_base_ids[last_collider.point_id].set_float(1.0)
last_collider = null
if tower_preview:
delete_tower_preview()
wall_preview.set_visible(false)
last_collider = ray_collider
ray_collider.set_color(Color.RED)
ray_collider.set_float(0.0)
if inventory.contents.size() > 0 and !ray_collider.has_card:
if ray_collider != last_tower_base or inventory.selected_item != last_card:
spawn_tower_preview()
elif Game.level:
if is_instance_valid(last_collider):
Game.level.a_star_graph_3d.tower_base_ids[last_collider.point_id].set_float(1.0)
last_collider = null
if tower_preview:
delete_tower_preview()
point_id = Game.level.a_star_graph_3d.astar.get_closest_point(ray_point)
if !Game.level.a_star_graph_3d.point_is_build_location(point_id) or hero.currency < Data.wall_cost:
wall_preview.set_visible(false)
else:
var point_position: Vector3 = Game.level.a_star_graph_3d.astar.get_point_position(point_id)
wall_preview.global_position = point_position
wall_preview.global_rotation = Vector3.ZERO
if obstacle_last_point != point_id:
obstacle_last_point = point_id
if Game.level.a_star_graph_3d.test_path_if_point_toggled(point_id):
var c: Color = Color.GREEN
c.a = 0.8
wall_preview.set_color(c)
wall_preview.set_float(0.0)
valid_point = true
else:
#build_preview_material.albedo_color = Color.RED
#build_preview_material.albedo_color.a = 0.8
valid_point = false
else:
ray_collider = null
is_looking_at_tower_base = false
delete_tower_preview()
wall_preview.set_visible(false)
if !valid_point:
wall_preview.set_visible(false)
func spawn_tower_preview() -> void:
delete_tower_preview()
last_tower_base = ray_collider
var card: Card = inventory.contents.keys()[hero.inventory_selected_index]
last_card = card
tower_preview = card.turret_scene.instantiate() as Tower
tower_preview.stats = card.tower_stats
tower_preview.position = Vector3.UP
tower_preview.preview_range(true)
ray_collider.add_child(tower_preview)
func delete_tower_preview() -> void:
last_tower_base = null
last_card = null
if is_instance_valid(tower_preview):
tower_preview.queue_free()
tower_preview = null
func interact() -> void:
if ray_collider is TowerBase:
var tower_base: TowerBase = ray_collider as TowerBase
put_card_in_tower_base(tower_base)
func build_wall() -> void:
if point_id >= 0 and valid_point and hero.currency >= Data.wall_cost:
hero.currency -= Data.wall_cost
Game.level.a_star_graph_3d.toggle_point(point_id, multiplayer.get_unique_id())
wall_preview.set_visible(false)
func refund_wall(wall: TowerBase) -> void:
if !is_instance_valid(wall):
return
last_collider = null
if wall.has_card:
wall.remove_card()
Game.level.a_star_graph_3d.remove_wall(wall)
func put_card_in_tower_base(tower_base: TowerBase) -> void:
if tower_base.has_card:
tower_base.remove_card()
elif inventory.size > 0:
var card: Card = inventory.remove_at(hero.inventory_selected_index)
if !inventory.contents.has(card):
hero.decrement_selected()
tower_base.add_card(card, multiplayer.get_unique_id())
hero.place_card_audio.play()
func set_progress_percent(value: float) -> void:
progress_bar.value = progress_bar.max_value * value

View File

@ -1 +0,0 @@
uid://ckm02cx0ai624

View File

@ -7,6 +7,7 @@ signal enemy_spawned()
@export var air_enemy_scene: PackedScene
@export var path: VisualizedPath
var astar: AStarGraph3D
@export var flow_field: FlowField
@export var own_id: int = 0
@export var type: Data.EnemyType
@export var dest: Node3D
@ -22,6 +23,12 @@ var enemies_spawned: Dictionary = {}
var enemies_to_spawn: int = 0
var done_spawning: bool = true
var enemy_id: int = 0
var new_path: Path3D
var path_polygon: PackedScene = preload("res://path_polygon.tscn")
func _ready() -> void:
create_path()
func _process(delta: float) -> void:
@ -29,56 +36,88 @@ func _process(delta: float) -> void:
done_spawning = true
return
for x: Enemy in enemy_spawn_timers:
if enemies_spawned[x] == enemy_types_to_spawn[x]:
for enemy: Enemy in enemy_spawn_timers:
if enemies_spawned[enemy] == enemy_types_to_spawn[enemy]:
continue
var enemy_stats: Enemy = x
enemy_spawn_timers[x] += delta
enemy_spawn_timers[enemy] += delta
if enemy_spawn_timers[x] >= enemy_stats.spawn_cooldown:
if enemy_spawn_timers[enemy] >= enemy.spawn_cooldown:
if is_multiplayer_authority():
if type == Data.EnemyType.LAND:
networked_spawn_land_enemy.rpc(Data.enemies.find(enemy_stats), own_id, enemy_id)
networked_spawn_land_enemy.rpc(enemy.title, own_id, enemy_id)
if type == Data.EnemyType.AIR:
var radius: float = 10.0
var random_dir: Vector3 = Vector3(randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1))
var random_pos: Vector3 = randf_range(0, radius) * random_dir.normalized()
networked_spawn_air_enemy.rpc(Data.enemies.find(enemy_stats), random_pos, own_id, enemy_id)
networked_spawn_air_enemy.rpc(enemy.title, random_pos, own_id, enemy_id)
enemy_spawn_timers[x] -= enemy_stats.spawn_cooldown
enemy_spawn_timers[enemy] -= enemy.spawn_cooldown
enemy_spawned.emit()
enemy_id += 1
enemies_spawned[x] += 1
enemies_spawned[enemy] += 1
enemies_to_spawn -= 1
#TODO: not sure enemies need all this info over the network
#TODO: generalize enemy scene selection, i.e. store the scenes in the enemy
#card like towers do
@rpc("reliable", "call_local")
func networked_spawn_land_enemy(enemy_stats: int, id1: int, id2: int) -> void:
func networked_spawn_land_enemy(enemy_stats: String, id1: int, id2: int) -> void:
var e_stats: Enemy = null
for enemy: Enemy in Data.enemies:
if enemy.title == enemy_stats:
e_stats = enemy
var enemy: EnemyController
if enemy_stats != 6:
enemy = land_enemy_scene.instantiate() as EnemyController
else:
enemy = leap_enemy_scene.instantiate() as EnemyController
enemy = e_stats.scene.instantiate()
enemy.name = str(id1) + str(id2)
enemy.stats = Data.enemies[enemy_stats]
enemy.stats = e_stats
enemy.died.connect(enemy_died_callback)
enemy.reached_goal.connect(enemy_reached_goal_callback)
enemy.movement_controller.path = path.curve
enemy.movement_controller.astar = astar
#enemy.movement_controller.path = path.curve
#enemy.movement_controller.astar = astar
enemy.movement_controller.flow_field = flow_field
enemy.position = global_position
enemy_path.add_child(enemy)
func create_path() -> void:
if type != Data.EnemyType.LAND:
return
new_path = Path3D.new()
new_path.curve = Curve3D.new()
add_child(new_path)
var polygon: CSGPolygon3D = path_polygon.instantiate()
new_path.add_child(polygon)
polygon.mode = CSGPolygon3D.MODE_PATH
polygon.path_node = new_path.get_path()
new_path.global_position = Vector3.ZERO
update_path()
func update_path() -> void:
if type != Data.EnemyType.LAND:
return
new_path.curve.add_point(global_position + Vector3(0, 0.5, 0))
new_path.curve = Curve3D.new()
var node: FlowNode = flow_field.get_closest_traversable_point(global_position)
new_path.curve.add_point(node.global_position + Vector3(0, 0.5, 0))
while node.best_path:
node = node.best_path
new_path.curve.add_point(node.global_position + Vector3(0, 0.5, 0))
@rpc("reliable", "call_local")
func networked_spawn_air_enemy(enemy_stats: int, pos: Vector3, id1: int, id2: int) -> void:
var enemy: EnemyController = air_enemy_scene.instantiate() as EnemyController
func networked_spawn_air_enemy(enemy_stats: String, pos: Vector3, id1: int, id2: int) -> void:
var e_stats: Enemy = null
for enemy: Enemy in Data.enemies:
if enemy.title == enemy_stats:
e_stats = enemy
var enemy: EnemyController
enemy = e_stats.scene.instantiate()
enemy.name = str(id1) + str(id2)
enemy.position = pos + global_position
enemy.stats = Data.enemies[enemy_stats]
enemy.stats = e_stats
enemy.died.connect(enemy_died_callback)
enemy.reached_goal.connect(enemy_reached_goal_callback)
enemy.movement_controller.goal = dest

155
Scripts/flow_field_tool.gd Normal file
View File

@ -0,0 +1,155 @@
class_name FlowFieldTool extends Node
@export var flow_field: FlowField
@export var raycast: RayCast3D
@export var project_raycast: RayCast3D
@export var camera: Camera3D
@export var camera_pivot: Node3D
@export var position_field: HBoxContainer
@export var x_field: LineEdit
@export var y_field: LineEdit
@export var z_field: LineEdit
@export var x_size_field: LineEdit
@export var y_size_field: LineEdit
@export var gap_field: LineEdit
var hover: FlowNode = null
var selected: Array[FlowNode] = []
var vector_dirty: bool = false
func _ready() -> void:
camera.make_current()
func _process(delta: float) -> void:
if raycast.is_colliding() and (!hover or hover != raycast.get_collider()):
hover = raycast.get_collider()
if hover and !raycast.is_colliding():
hover = null
if selected.size() == 1 and vector_dirty:
position_field.visible = true
x_field.text = str(selected[0].global_position.x)
y_field.text = str(selected[0].global_position.y)
z_field.text = str(selected[0].global_position.z)
vector_dirty = false
elif selected.size() != 1:
position_field.visible = false
for node: FlowNode in flow_field.nodes:
if node.traversable and node.buildable:
node.set_color(Color.WEB_GRAY)
elif node.traversable and !node.buildable:
node.set_color(Color.CORAL)
else:
node.set_color(Color.BLACK)
if flow_field.goals.has(node):
node.set_color(Color.BLUE)
if flow_field.starts.has(node):
node.set_color(Color.PINK)
if selected.has(node):
node.set_color(Color.GREEN)
if node == hover:
node.set_color(Color.RED)
var y: float = Input.get_axis("Move Forward", "Move Backward")
var x: float = Input.get_axis("Move Left", "Move Right")
camera_pivot.position += Vector3(x, 0, y) * delta * 10
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
var from: Vector3 = camera.project_ray_origin(event.position)
var to: Vector3 = camera.project_local_ray_normal(event.position)
raycast.global_position = from
raycast.target_position = to * 1000.0
if event is InputEventMouseButton and event.button_index == 1 and hover:
if !selected.has(hover):
selected.append(hover)
vector_dirty = true
if event is InputEventMouseButton and event.button_index == 2 and selected.size() > 0:
selected = []
func _on_x_field_changed(text: String) -> void:
selected[0].global_position.x = float(text)
func _on_y_field_changed(text: String) -> void:
selected[0].global_position.y = float(text)
func _on_z_field_changed(text: String) -> void:
selected[0].global_position.z = float(text)
func _on_create_button_pressed() -> void:
flow_field.create_node()
func _on_generate_grid_button_pressed() -> void:
flow_field.create_grid(int(x_size_field.text), int(y_size_field.text), float(gap_field.text))
selected.append_array(flow_field.nodes)
func _on_calculate_button_pressed() -> void:
flow_field.calculate()
func _on_connect_button_pressed() -> void:
flow_field.connect_many_nodes(selected[0], selected.slice(1, selected.size()))
func _on_mark_goal_button_pressed() -> void:
flow_field.toggle_goal(selected)
selected = []
vector_dirty = true
func _on_mark_start_button_pressed() -> void:
flow_field.toggle_start(selected)
selected = []
vector_dirty = true
func _on_extrude_button_pressed() -> void:
if selected.size() == 1:
var node: FlowNode = flow_field.create_node(selected[0].position)
node.add_connection(selected[0])
selected[0].add_connection(node)
selected[0].set_color(Color.WEB_GRAY)
selected = []
selected.append(node)
vector_dirty = true
func _on_toggle_traversable_button_pressed() -> void:
for node: FlowNode in selected:
if !flow_field.toggle_traversable(node):
flow_field.toggle_traversable(node)
selected = []
return
selected = []
func _on_toggle_buildable_button_pressed() -> void:
for node: FlowNode in selected:
flow_field.toggle_buildable(node)
func _on_finalize_button_pressed() -> void:
var packed_scene: PackedScene = PackedScene.new()
packed_scene.pack(flow_field)
ResourceSaver.save(packed_scene, "res://flow_field_tool_output.tscn")
#TODO: This doesnt work as you'd expect because of physics frames
func _on_project_downwards_button_pressed() -> void:
for node: FlowNode in selected:
project_raycast.global_position = node.global_position + Vector3.UP
project_raycast.target_position = Vector3.DOWN * 100.0
await get_tree().physics_frame
await get_tree().physics_frame
await get_tree().physics_frame
await get_tree().physics_frame
if project_raycast.is_colliding():
node.global_position = project_raycast.get_collision_point()

View File

@ -0,0 +1 @@
uid://05c5q1v2nv8p

View File

@ -1,8 +1,10 @@
class_name FlowNode extends StaticBody3D
var connections: Array[FlowNode]
var visualisers: Array[CSGBox3D]
var traversable: bool = true
@export var connections: Array[FlowNode]
@export var visualisers: Array[Node3D]
var visual_scene: PackedScene = preload("res://cube2.tscn")
@export var traversable: bool = true
@export var buildable: bool = true
var best_path: FlowNode :
get():
return best_path
@ -12,33 +14,44 @@ var best_path: FlowNode :
set_connector_color(best_path, Color.DARK_GREEN)
func _ready() -> void:
visualisers = []
for node: FlowNode in connections:
var visual: Node3D = visual_scene.instantiate()
add_child(visual)
visual.owner = self
visualisers.append(visual)
set_connector_color(node, Color.WEB_GRAY)
func _process(delta: float) -> void:
for i: int in connections.size():
var distance: float = global_position.distance_to(connections[i].global_position)
visualisers[i].size = Vector3(0.3, 0.3, 1.0 * (distance / 2.0))
visualisers[i].position = to_local(connections[i].global_position) / 4.0
if distance >= 0.05:
visualisers[i].look_at(connections[i].global_position)
if visible:
for i: int in connections.size():
var distance: float = global_position.distance_to(connections[i].global_position)
visualisers[i].scale = Vector3(0.3, 0.3, 1.0 * (distance / 2.0))
visualisers[i].position = to_local(connections[i].global_position) / 4.0
if distance >= 0.05:
visualisers[i].look_at(connections[i].global_position)
func set_color(new_color: Color) -> void:
$CSGSphere3D.material.albedo_color = new_color
$flow_node/Sphere.material_override.albedo_color = new_color
func set_connector_color(node: FlowNode, new_color: Color) -> void:
var i: int = connections.find(node)
visualisers[i].material.albedo_color = new_color
if visible:
var i: int = connections.find(node)
visualisers[i].get_child(0).material_override.albedo_color = new_color
func add_connection(node: FlowNode) -> void:
if !connections.has(node):
var visual: CSGBox3D = CSGBox3D.new()
visual.material = StandardMaterial3D.new()
visual.material.resource_local_to_scene = true
visual.material.albedo_color = Color.DARK_GRAY
var visual: Node3D = visual_scene.instantiate()
add_child(visual)
visual.owner = self
connections.append(node)
visualisers.append(visual)
set_connector_color(node, Color.WEB_GRAY)
func remove_connection(node: FlowNode) -> void:

View File

@ -39,6 +39,17 @@ func _ready() -> void:
UILayer = CanvasLayer.new()
UILayer.layer = 2
get_tree().root.add_child.call_deferred(UILayer)
var version_label: Label = Label.new()
var version: String = ProjectSettings.get_setting("application/config/version")
version_label.text = "WORK IN PROGRESS | ALPHA - VERSION " + version + " | PLAYTEST"
version_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
version_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
version_label.add_theme_font_size_override("font_size", 18)
version_label.add_theme_color_override("font_color", Color(0.85, 0.85, 0.85, 0.7))
version_label.set_anchors_preset(Control.PRESET_TOP_WIDE)
UILayer.add_child(version_label)
Input.set_custom_mouse_cursor(load("res://Assets/Textures/cursor_none.png"), Input.CURSOR_ARROW, Vector2(9, 6))
Input.set_custom_mouse_cursor(load("res://Assets/Textures/bracket_b_vertical.png"), Input.CURSOR_IBEAM, Vector2(16, 16))
@rpc("reliable", "call_local")
@ -169,10 +180,12 @@ func ready_player(player_ready_true: bool) -> void:
func spawn_enemy_wave() -> void:
level.shop.close()
wave += 1
level.a_star_graph_3d.find_path()
level.a_star_graph_3d.disable_all_tower_frames()
level.disable_all_tower_frames()
#level.a_star_graph_3d.find_path()
#level.a_star_graph_3d.disable_all_tower_frames()
level.flow_field.calculate()
for spawn: EnemySpawner in level.enemy_spawns:
spawn.path.disable_visualization()
#spawn.path.disable_visualization()
spawn.spawn_wave()
wave_started.emit(wave)
@ -248,7 +261,7 @@ func end_wave() -> void:
connected_players_nodes[peer_id].unready_self()
for spawn: EnemySpawner in level.enemy_spawns:
spawn.path.enable_visualization()
level.a_star_graph_3d.enable_non_path_tower_frames()
#level.a_star_graph_3d.enable_non_path_tower_frames()
if is_multiplayer_authority():
if randf_in_range(23 * wave, 0.0, 1.0) <= shop_chance:
networked_spawn_shop.rpc()
@ -304,11 +317,14 @@ func start() -> void:
#Relies on rng having been seeded
set_upcoming_wave()
level.a_star_graph_3d.make_grid()
level.flow_field.calculate()
level.enemy_spawns[0].update_path()
#level.a_star_graph_3d.make_grid()
level.generate_obstacles()
level.a_star_graph_3d.disable_all_tower_frames()
level.a_star_graph_3d.enable_non_path_tower_frames()
level.a_star_graph_3d.find_path()
level.enable_non_path_tower_frames()
#level.a_star_graph_3d.disable_all_tower_frames()
#level.a_star_graph_3d.enable_non_path_tower_frames()z
#level.a_star_graph_3d.find_path()
#Start game
game_active = true

View File

@ -1,157 +0,0 @@
class_name KeyIconMap
static var playstation_keys: Dictionary = {
"0" = "res://KennyControllerPrompts/Playstation/playstation_button_color_cross.png",
"1" = "res://KennyControllerPrompts/Playstation/playstation_button_color_circle.png",
"2" = "res://KennyControllerPrompts/Playstation/playstation_button_color_square.png",
"3" = "res://KennyControllerPrompts/Playstation/playstation_button_color_triangle.png",
"4" = "res://KennyControllerPrompts/Playstation/playstation5_button_create.png",
"6" = "res://KennyControllerPrompts/Playstation/playstation5_button_options.png",
"7" = "res://KennyControllerPrompts/Playstation/playstation_stick_side_l.png",
"8" = "res://KennyControllerPrompts/Playstation/playstation_stick_side_r.png",
"9" = "res://KennyControllerPrompts/Playstation/playstation_trigger_l1_alternative.png",
"10" = "res://KennyControllerPrompts/Playstation/playstation_trigger_r1_alternative.png",
"11" = "res://KennyControllerPrompts/Playstation/playstation_dpad_up.png",
"12" = "res://KennyControllerPrompts/Playstation/playstation_dpad_down.png",
"13" = "res://KennyControllerPrompts/Playstation/playstation_dpad_left.png",
"14" = "res://KennyControllerPrompts/Playstation/playstation_dpad_right.png",
"15" = "res://KennyControllerPrompts/Playstation/playstation5_button_mute.png",
}
static var xbox_series_keys: Dictionary = {
"0" = "res://KennyControllerPrompts/Xbox/xbox_button_a_outline.png",
"1" = "res://KennyControllerPrompts/Xbox/xbox_button_b_outline.png",
"2" = "res://KennyControllerPrompts/Xbox/xbox_button_x_outline.png",
"3" = "res://KennyControllerPrompts/Xbox/xbox_button_y_outline.png",
"4" = "res://KennyControllerPrompts/Xbox/xbox_button_view_outline.png",
"5" = "res://KennyControllerPrompts/Xbox/xbox_guide.png",
"6" = "res://KennyControllerPrompts/Xbox/xbox_button_menu_outline.png",
"7" = "res://KennyControllerPrompts/Xbox/xbox_stick_side_l.png",
"8" = "res://KennyControllerPrompts/Xbox/xbox_stick_side_r.png",
"9" = "res://KennyControllerPrompts/Xbox/xbox_lb_outline.png",
"10" = "res://KennyControllerPrompts/Xbox/xbox_rb_outline.png",
"11" = "res://KennyControllerPrompts/Xbox/xbox_dpad_up_outline.png",
"12" = "res://KennyControllerPrompts/Xbox/xbox_dpad_down_outline.png",
"13" = "res://KennyControllerPrompts/Xbox/xbox_dpad_left_outline.png",
"14" = "res://KennyControllerPrompts/Xbox/xbox_dpad_right_outline.png",
"15" = "res://KennyControllerPrompts/Xbox/xbox_button_share_outline.png",
}
static var xbox_360_keys: Dictionary = {
"0" = "res://KennyControllerPrompts/Xbox/xbox_button_color_a.png",
"1" = "res://KennyControllerPrompts/Xbox/xbox_button_color_b.png",
"2" = "res://KennyControllerPrompts/Xbox/xbox_button_color_x.png",
"3" = "res://KennyControllerPrompts/Xbox/xbox_button_color_y.png",
"4" = "res://KennyControllerPrompts/Xbox/xbox_button_back.png",
"5" = "res://KennyControllerPrompts/Xbox/xbox_guide_outline.png",
"6" = "res://KennyControllerPrompts/Xbox/xbox_button_start.png",
"7" = "res://KennyControllerPrompts/Xbox/xbox_stick_side_l.png",
"8" = "res://KennyControllerPrompts/Xbox/xbox_stick_side_r.png",
"9" = "res://KennyControllerPrompts/Xbox/xbox_lb.png",
"10" = "res://KennyControllerPrompts/Xbox/xbox_rb.png",
"11" = "res://KennyControllerPrompts/Xbox/xbox_dpad_round_up.png",
"12" = "res://KennyControllerPrompts/Xbox/xbox_dpad_round_down.png",
"13" = "res://KennyControllerPrompts/Xbox/xbox_dpad_round_left.png",
"14" = "res://KennyControllerPrompts/Xbox/xbox_dpad_round_right.png",
}
static var keys: Dictionary = {
"48" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/0_Key_Light.png",
"49" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/1_Key_Light.png",
"50" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/2_Key_Light.png",
"51" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/3_Key_Light.png",
"52" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/4_Key_Light.png",
"53" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/5_Key_Light.png",
"54" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/6_Key_Light.png",
"55" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/7_Key_Light.png",
"56" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/8_Key_Light.png",
"57" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/9_Key_Light.png",
"65" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/A_Key_Light.png",
"66" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/B_Key_Light.png",
"67" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/C_Key_Light.png",
"68" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/D_Key_Light.png",
"69" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/E_Key_Light.png",
"70" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F_Key_Light.png",
"71" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/G_Key_Light.png",
"72" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/H_Key_Light.png",
"73" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/I_Key_Light.png",
"74" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/J_Key_Light.png",
"75" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/K_Key_Light.png",
"76" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/L_Key_Light.png",
"77" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/M_Key_Light.png",
"78" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/N_Key_Light.png",
"79" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/O_Key_Light.png",
"80" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/P_Key_Light.png",
"81" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Q_Key_Light.png",
"82" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/R_Key_Light.png",
"83" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/S_Key_Light.png",
"84" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/T_Key_Light.png",
"85" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/U_Key_Light.png",
"86" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/V_Key_Light.png",
"87" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/W_Key_Light.png",
"88" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/X_Key_Light.png",
"89" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Y_Key_Light.png",
"90" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Z_Key_Light.png",
"4194328" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Alt_Key_Light.png",
"4194322" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Arrow_Down_Key_Light.png",
"4194319" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Arrow_Left_Key_Light.png",
"4194321" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Arrow_Right_Key_Light.png",
"4194320" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Arrow_Up_Key_Light.png",
"42" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Asterisk_Key_Light.png",
"4194433" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Asterisk_Key_Light.png",
"4194308" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Backspace_Alt_Key_Light.png",
"91" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Bracket_Left_Key_Light.png",
"93" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Bracket_Right_Key_Light.png",
"4194329" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Caps_Lock_Key_Light.png",
"4194327" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Command_Key_Light.png",
"4194326" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Ctrl_Key_Light.png",
"4194312" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Del_Key_Light.png",
"4194318" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/End_Key_Light.png",
"4194309" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Enter_Alt_Key_Light.png",
"4194305" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Esc_Key_Light.png",
"4194332" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F1_Key_Light.png",
"4194333" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F2_Key_Light.png",
"4194334" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F3_Key_Light.png",
"4194335" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F4_Key_Light.png",
"4194336" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F5_Key_Light.png",
"4194337" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F6_Key_Light.png",
"4194338" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F7_Key_Light.png",
"4194339" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F8_Key_Light.png",
"4194340" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F9_Key_Light.png",
"4194341" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F10_Key_Light.png",
"4194342" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F11_Key_Light.png",
"4194343" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/F12_Key_Light.png",
"4194317" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Home_Key_Light.png",
"4194311" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Insert_Key_Light.png",
"60" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Mark_Left_Key_Light.png",
"62" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Mark_Right_Key_Light.png",
"45" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Minus_Key_Light.png",
"4194435" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Minus_Key_Light.png",
"4194330" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Num_Lock_Key_Light.png",
"4194324" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Page_Down_Key_Light.png",
"4194323" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Page_Up_Key_Light.png",
"43" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Plus_Key_Light.png",
"4194437" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Plus_Key_Light.png",
"4194314" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Print_Screen_Key_Light.png",
"63" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Question_Key_Light.png",
"34" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Quote_Key_Light.png",
"59" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Semicolon_Key_Light.png",
"4194325" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Shift_Key_Light.png",
"47" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Slash_Key_Light.png",
"4194434" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Slash_Key_Light.png",
"32" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Space_Key_Light.png",
"4194306" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Tab_Key_Light.png",
"126" = "res://XeluController&KeyPrompts/Keyboard & Mouse/Light/Tilda_Key_Light.png",
}
static var mouse_buttons: Dictionary = {
"1" = "res://KennyControllerPrompts/Mouse/mouse_left.png",
"3" = "res://KennyControllerPrompts/Mouse/mouse_scroll.png",
"2" = "res://KennyControllerPrompts/Mouse/mouse_right.png",
"4" = "res://KennyControllerPrompts/Mouse/mouse_scroll_up.png",
"5" = "res://KennyControllerPrompts/Mouse/mouse_scroll_down.png",
}

View File

@ -1 +0,0 @@
uid://nambgchluofc

View File

@ -1,55 +1,120 @@
class_name Level extends GridMap
@export var enemy_pool: Array[Enemy]
@export var tower_path: Node
@export var player_spawns: Array[Node3D]
@export var enemy_spawns: Array[EnemySpawner]
@export var enemy_goals: Array[Node3D]
@export var corpses: Node3D
@export var a_star_graph_3d: AStarGraph3D
@export var flow_field: FlowField
@export var cinematic_cam: CinematicCamManager
@export var printer: CardPrinter
@export var shop: ShopStand
@export var obstacle_scenes: Array[PackedScene]
var walls: Dictionary[FlowNode, TowerBase] = {}
var wall_id: int = 0
var tower_base_scene: PackedScene = load("res://Scenes/TowerBase/tower_base.tscn")
var tower_frame_scene: PackedScene = load("res://Scenes/tower_frame.tscn")
var tower_frames: Dictionary[FlowNode, Node3D] = {}
func _ready() -> void:
flow_field.path_updated.connect(enemy_spawns[0].update_path)
for node: FlowNode in flow_field.nodes:
if node.buildable:
var frame: Node3D = tower_frame_scene.instantiate()
tower_frames[node] = frame
add_child(frame)
frame.global_position = node.global_position
func disable_all_tower_frames() -> void:
for node: FlowNode in tower_frames:
tower_frames[node].visible = false
func enable_non_path_tower_frames() -> void:
for node: FlowNode in tower_frames:
tower_frames[node].visible = true
disable_path_tower_frames()
func disable_path_tower_frames() -> void:
for node: FlowNode in tower_frames:
if !node.traversable and flow_field.traversable_after_blocking_point(node):
tower_frames[node].visible = true
func set_wall(point: FlowNode, caller_id: int) -> void:
point.traversable = false
flow_field.calculate()
flow_field.path_updated.emit()
if is_multiplayer_authority():
spawn_wall(point, wall_id, caller_id)
wall_id += 1
func remove_wall(point: FlowNode) -> void:
var wall: TowerBase = walls[point]
Game.connected_players_nodes[wall.owner_id].currency += Data.wall_cost
Game.connected_players_nodes[wall.owner_id].unready_self()
walls.erase(point)
wall.queue_free()
point.traversable = true
flow_field.calculate()
flow_field.path_updated.emit()
enable_non_path_tower_frames()
func spawn_wall(point: FlowNode, name_id: int, caller_id: int) -> void:
var base: TowerBase = tower_base_scene.instantiate() as TowerBase
base.position = point.global_position
base.name = "Wall" + str(name_id)
base.owner_id = caller_id
base.point = point
walls[point] = base
tower_path.add_child(base)
disable_path_tower_frames()
func generate_obstacles() -> void:
#print(str(multiplayer.get_unique_id()) + " spawning obstacles with seed: " + str(Game.rng.seed))
var obstacle_count: int = Game.randi_in_range(1, 0, 5)
obstacle_count = 0
for index: int in obstacle_count:
var x: int = Game.randi_in_range(10 * index, 1 - a_star_graph_3d.grid_size.x, a_star_graph_3d.grid_size.x - 1)
var y: int = Game.randi_in_range(32 * index, 1 - a_star_graph_3d.grid_size.y, a_star_graph_3d.grid_size.y - 1)
var chosen_obstacle: int = Game.randi_in_range(4 * index, 0, obstacle_scenes.size() - 1)
var obstacle: GridMap = obstacle_scenes[chosen_obstacle].instantiate() as GridMap
var orientations: Array[int] = [0, 90, 180, 270]
var chosen_orientation: int = Game.randi_in_range(15 * index, 0, orientations.size() - 1)
obstacle.position = Vector3(x, 0, y)
obstacle.set_rotation_degrees(Vector3(0, chosen_orientation, 0))
add_child(obstacle)
for cell: Vector3i in obstacle.get_used_cells():
var cell_coord: Vector3 = obstacle.to_global(obstacle.map_to_local(cell))
remove_world_tile(round(cell_coord.x), round(cell_coord.z))
obstacle.queue_free()
# for index: int in obstacle_count:
# #var x: int = Game.randi_in_range(10 * index, 1 - a_star_graph_3d.grid_size.x, a_star_graph_3d.grid_size.x - 1)
#var y: int = Game.randi_in_range(32 * index, 1 - a_star_graph_3d.grid_size.y, a_star_graph_3d.grid_size.y - 1)
# var chosen_obstacle: int = Game.randi_in_range(4 * index, 0, obstacle_scenes.size() - 1)
# var obstacle: GridMap = obstacle_scenes[chosen_obstacle].instantiate() as GridMap
# var orientations: Array[int] = [0, 90, 180, 270]
# var chosen_orientation: int = Game.randi_in_range(15 * index, 0, orientations.size() - 1)
# #obstacle.position = Vector3(x, 0, y)
# obstacle.set_rotation_degrees(Vector3(0, chosen_orientation, 0))
# add_child(obstacle)
# for cell: Vector3i in obstacle.get_used_cells():
# var cell_coord: Vector3 = obstacle.to_global(obstacle.map_to_local(cell))
# remove_world_tile(round(cell_coord.x), round(cell_coord.z))
# obstacle.queue_free()
func cell_coord_to_astar_point(x: int, y: int) -> int:
var center_point_x: int = floori(a_star_graph_3d.grid_size.x / 2.0) * a_star_graph_3d.grid_size.y
var center_point_y: int = floori(a_star_graph_3d.grid_size.y / 2.0)
return (center_point_x + (int(x / 2.0) * a_star_graph_3d.grid_size.y)) + (center_point_y + int(y / 2.0))
#func cell_coord_to_astar_point(x: int, y: int) -> int:
# var center_point_x: int = floori(a_star_graph_3d.grid_size.x / 2.0) * a_star_graph_3d.grid_size.y
# var center_point_y: int = floori(a_star_graph_3d.grid_size.y / 2.0)
# return (center_point_x + (int(x / 2.0) * a_star_graph_3d.grid_size.y)) + (center_point_y + int(y / 2.0))
func remove_world_tile(x: int, y: int) -> void:
if get_cell_item(Vector3i(x, 0, y)) != 1 or abs(x) >= a_star_graph_3d.grid_size.x or abs(y) >= a_star_graph_3d.grid_size.y:
return
set_cell_item(Vector3i(x, 0, y), INVALID_CELL_ITEM)
var point: int = cell_coord_to_astar_point(x, y)
var north_point: int = cell_coord_to_astar_point(x - 1, y)
var south_point: int = cell_coord_to_astar_point(x + 1, y)
var east_point: int = cell_coord_to_astar_point(x, y + 1)
var west_point: int = cell_coord_to_astar_point(x, y - 1)
if x % 2 == 0 and y % 2 == 0: #If the tile is on a point on the pathfinding grid
a_star_graph_3d.astar.set_point_disabled(point)
if x % 2 == 1 and y % 2 == 0: #If the cell breaks a north-south link
a_star_graph_3d.astar.disconnect_points(north_point, south_point)
if x % 2 == 0 and y % 2 == 1: #If the cell breaks a east-west link
a_star_graph_3d.astar.disconnect_points(east_point, west_point)
#func remove_world_tile(x: int, y: int) -> void:
# if get_cell_item(Vector3i(x, 0, y)) != 1 or abs(x) >= a_star_graph_3d.grid_size.x or abs(y) >= a_star_graph_3d.grid_size.y:
# return
# set_cell_item(Vector3i(x, 0, y), INVALID_CELL_ITEM)
# var point: int = cell_coord_to_astar_point(x, y)
# var north_point: int = cell_coord_to_astar_point(x - 1, y)
# var south_point: int = cell_coord_to_astar_point(x + 1, y)
# var east_point: int = cell_coord_to_astar_point(x, y + 1)
# var west_point: int = cell_coord_to_astar_point(x, y - 1)
# if x % 2 == 0 and y % 2 == 0: #If the tile is on a point on the pathfinding grid
# a_star_graph_3d.astar.set_point_disabled(point)
# if x % 2 == 1 and y % 2 == 0: #If the cell breaks a north-south link
# a_star_graph_3d.astar.disconnect_points(north_point, south_point)
# if x % 2 == 0 and y % 2 == 1: #If the cell breaks a east-west link
# a_star_graph_3d.astar.disconnect_points(east_point, west_point)

View File

@ -1,98 +0,0 @@
class_name TowerBase extends StaticBody3D
@export var inventory: Inventory
@export var block: Node3D
@export var collider: CollisionShape3D
@export var minimap_icon: Sprite3D
@export var north_icon: Sprite3D
@export var south_icon: Sprite3D
@export var east_icon: Sprite3D
@export var west_icon: Sprite3D
@export var north_mesh: CSGBox3D
@export var south_mesh: CSGBox3D
@export var east_mesh: CSGBox3D
@export var west_mesh: CSGBox3D
@export var north_collider: CollisionShape3D
@export var south_collider: CollisionShape3D
@export var east_collider: CollisionShape3D
@export var west_collider: CollisionShape3D
var owner_id: int
var point_id: int
var tower: Tower = null
var has_card: bool :
set(_value):
return
get:
return inventory.size != 0
func set_color(color: Color) -> void:
$MeshInstance3D.set_instance_shader_parameter("Color", color)
func set_float(value: float) -> void:
$MeshInstance3D.set_instance_shader_parameter("Float", value)
func add_card(card: Card, caller_id: int) -> void:
networked_spawn_tower.rpc(Data.cards.find(card), caller_id)
func remove_card() -> void:
networked_remove_tower.rpc()
func toggle_collision() -> void:
collider.disabled = !collider.disabled
func set_north_wall(value: bool) -> void:
north_mesh.set_visible(value)
north_collider.disabled = !value
func set_south_wall(value: bool) -> void:
south_mesh.set_visible(value)
south_collider.disabled = !value
func set_east_wall(value: bool) -> void:
east_mesh.set_visible(value)
east_collider.disabled = !value
func set_west_wall(value: bool) -> void:
west_mesh.set_visible(value)
west_collider.disabled = !value
@rpc("reliable", "call_local", "any_peer")
func networked_spawn_tower(card_index: int, caller_id: int) -> void:
var card: Card = Data.cards[card_index]
inventory.add(card)
tower = inventory.contents.keys()[0].turret_scene.instantiate() as Tower
tower.stats = inventory.contents.keys()[0].tower_stats
tower.name = "tower"
tower.base_name = name
tower.owner_id = caller_id
tower.position = Vector3(0, 1.2, 0)
minimap_icon.modulate = Color.RED
north_icon.modulate = Color.RED
south_icon.modulate = Color.RED
west_icon.modulate = Color.RED
east_icon.modulate = Color.RED
add_child(tower)
@rpc("reliable", "call_local", "any_peer")
func networked_remove_tower() -> void:
Game.connected_players_nodes[tower.owner_id].add_card(inventory.remove_at(0))
Game.connected_players_nodes[tower.owner_id].unready_self()
tower.queue_free()
tower = null
minimap_icon.modulate = Color.GREEN
north_icon.modulate = Color.GREEN
south_icon.modulate = Color.GREEN
west_icon.modulate = Color.GREEN
east_icon.modulate = Color.GREEN

View File

@ -1 +0,0 @@
uid://si58bm4r7r2i

View File

@ -18,7 +18,7 @@ func to_dict() -> Dictionary:
enemy_count = group.enemy.epic_group
elif group.rarity == Data.Rarity.LEGENDARY:
enemy_count = group.enemy.legendary_group
if !dict.has(Data.enemies.find(group.enemy)):
dict[Data.enemies.find(group.enemy)] = 0
dict[Data.enemies.find(group.enemy)] += enemy_count
if !dict.has(group.enemy.title):
dict[group.enemy.title] = 0
dict[group.enemy.title] += enemy_count
return dict

View File

@ -33,6 +33,7 @@ static func generate_wave(spawn_power: int, spawn_pool: Array[Enemy], spawners:
var new_card: EnemyCard = EnemyCard.new()
#First, choose an enemy at random
#TODO: Use seeded random
new_card.enemy = spawn_pool.pick_random()
#Next, we have to figure out if we can actually buy that enemy
@ -61,10 +62,11 @@ static func generate_wave(spawn_power: int, spawn_pool: Array[Enemy], spawners:
#Even the common rarity was too expensive, so we have to choose
#a different enemy and try this process again
var enemy_id: int = spawn_pool.find(new_card.enemy)
enemy_id -= 1
if enemy_id <= 0:
new_card.enemy = spawn_pool[spawn_pool.size() - 1]
else:
new_card.enemy = spawn_pool[enemy_id - 1]
new_card.enemy = spawn_pool[enemy_id]
#Now that we know which rarities we could afford, lets just choose a
#random one