Added storage building, added detector building component, and changed citizen behaviour to use pluggable actions.

This commit is contained in:
Nekojimi 2025-05-11 20:00:22 +01:00
parent 8a0e163e64
commit 0350d48957
28 changed files with 295 additions and 118 deletions

File diff suppressed because one or more lines are too long

View File

@ -34,6 +34,7 @@ ExtResource("3_w18nb"): 5
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
layers = 2
mesh = SubResource("BoxMesh_cee1v")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]

View File

@ -12,7 +12,7 @@ albedo_color = Color(0, 1, 0.979857, 1)
material = SubResource("StandardMaterial3D_nr174")
size = Vector3(2, 2, 2)
[node name="Building" instance=ExtResource("1_7qwib")]
[node name="Storage" instance=ExtResource("1_7qwib")]
script = ExtResource("2_evbsr")
[node name="MeshInstance3D" parent="." index="0"]

View File

@ -1,9 +1,10 @@
[gd_scene load_steps=9 format=3 uid="uid://cw3vtaevqx20y"]
[gd_scene load_steps=10 format=3 uid="uid://cw3vtaevqx20y"]
[ext_resource type="PackedScene" uid="uid://b1fnsl3k1mo5c" path="res://objects/buildings/building.tscn" id="1_cgb0l"]
[ext_resource type="Script" uid="uid://bl78fqp1abxd6" path="res://scripts/buildings/turret.gd" id="2_brx0q"]
[ext_resource type="Script" uid="uid://bshiyw2k3op02" path="res://scripts/building_components/consumer.gd" id="2_wh3b5"]
[ext_resource type="PackedScene" uid="uid://cav22qho14o47" path="res://objects/bullet.tscn" id="3_kpsgq"]
[ext_resource type="Script" uid="uid://b3jshlhs24s8d" path="res://scripts/building_components/detector.gd" id="5_vuktq"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_cgb0l"]
albedo_color = Color(0, 0.00397563, 1, 1)
@ -34,7 +35,9 @@ fire_cooldown_time = 0.2
reload_time = 1.0
bullet_scene = ExtResource("3_kpsgq")
fire_position = Vector3(0, 2.1, 0)
bullets_at_once = 1
shot_velocity = 50.0
deviation = Vector2(1, 1)
lead_shots = true
compensate_for_gravity = true
use_artillery_firing_solution = false
@ -50,8 +53,12 @@ mesh = SubResource("CylinderMesh_kpsgq")
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 1)
mesh = SubResource("CylinderMesh_vuktq")
[node name="Consumer" type="Node" parent="." index="5"]
[node name="Consumer" type="Node" parent="." index="6"]
script = ExtResource("2_wh3b5")
metadata/_custom_type_script = "uid://bshiyw2k3op02"
[node name="Detector" type="Node3D" parent="." index="7"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.31252, 0)
script = ExtResource("5_vuktq")
[connection signal="item_added" from="Consumer" to="." method="_on_consumer_item_added"]

View File

@ -1,10 +1,11 @@
[gd_scene load_steps=8 format=3 uid="uid://skxli3htgn7"]
[gd_scene load_steps=9 format=3 uid="uid://skxli3htgn7"]
[ext_resource type="PackedScene" uid="uid://1gcj3gixy6hs" path="res://objects/units/unit.tscn" id="1_6046h"]
[ext_resource type="Script" uid="uid://b7xficxq807qd" path="res://objects/units/citizen.gd" id="2_dv62s"]
[ext_resource type="Script" uid="uid://b7xficxq807qd" path="res://scripts/units/citizen.gd" id="2_dv62s"]
[ext_resource type="Texture2D" uid="uid://brjswv5ryy8om" path="res://assets/images/lilguy.png" id="3_pedvu"]
[ext_resource type="Texture2D" uid="uid://cpov32m0nxjvh" path="res://assets/images/lilguy_arms.png" id="4_i1unn"]
[ext_resource type="ArrayMesh" uid="uid://c6yj8uwsgqxv0" path="res://assets/models/Ingot.obj" id="5_dv62s"]
[ext_resource type="Script" uid="uid://b3jshlhs24s8d" path="res://scripts/building_components/detector.gd" id="6_pedvu"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_vpcy6"]
albedo_texture = ExtResource("3_pedvu")
@ -32,6 +33,7 @@ text = "HP: 100"
[node name="Body" type="Sprite3D" parent="." index="5"]
transform = Transform3D(3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0)
layers = 4
billboard = 2
shaded = true
alpha_cut = 1
@ -41,6 +43,7 @@ hframes = 2
[node name="Hands" type="Sprite3D" parent="Body" index="0"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.0522564)
layers = 4
billboard = 2
shaded = true
alpha_cut = 1
@ -52,3 +55,9 @@ transform = Transform3D(0.17, 0, 0, 0, 0.17, 0, 0, 0, 0.17, 5.96046e-08, -0.0189
visible = false
mesh = ExtResource("5_dv62s")
skeleton = NodePath("../../..")
[node name="Detector" type="Node3D" parent="." index="6"]
script = ExtResource("6_pedvu")
detection_range = 50.0
scan_period = 0.5
metadata/_custom_type_script = "uid://b3jshlhs24s8d"

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=3 format=3 uid="uid://q80xjurpsmjb"]
[ext_resource type="PackedScene" uid="uid://1gcj3gixy6hs" path="res://objects/units/unit.tscn" id="1_8hi5e"]
[ext_resource type="Script" uid="uid://cl0k7xolx5rf2" path="res://scripts/enemy.gd" id="2_14ipn"]
[ext_resource type="Script" uid="uid://cl0k7xolx5rf2" path="res://scripts/units/enemy.gd" id="2_14ipn"]
[node name="Enemy" groups=["Enemies"] instance=ExtResource("1_8hi5e")]
collision_layer = 4

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=7 format=3 uid="uid://1gcj3gixy6hs"]
[ext_resource type="Script" uid="uid://f0j7u0so2ug5" path="res://scripts/unit.gd" id="1_dberb"]
[ext_resource type="Script" uid="uid://f0j7u0so2ug5" path="res://scripts/units/unit.gd" id="1_dberb"]
[ext_resource type="Texture2D" uid="uid://3javrn230ddq" path="res://assets/images/enemy.png" id="2_2pk7s"]
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_a0tk4"]
@ -43,6 +43,7 @@ debug_path_custom_color = Color(1, 0, 0.0808306, 1)
shape = SubResource("SphereShape3D_a202f")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
layers = 4
mesh = SubResource("QuadMesh_i5arm")
[node name="ShapeCast3D" type="ShapeCast3D" parent="."]

View File

@ -28,11 +28,11 @@ run/main_scene="uid://bwftban1ppo17"
config/features=PackedStringArray("4.4")
config/icon="uid://u1hpdb62rxlc"
addons/icon_finder/preview_size=25
config/git_describe="78bfcde"
[autoload]
IconsFonts="*res://addons/icons-fonts/icons_fonts/IconsFonts.gd"
TowerUtil="*res://scripts/util.gd"
[debug]
@ -146,6 +146,7 @@ building_cancel={
3d_navigation/layer_3="Citizens"
3d_physics/layer_4="Citizens"
3d_physics/layer_5="Projectiles"
3d_physics/layer_32="Mouse Selectable"
[navigation]

View File

@ -23,6 +23,9 @@ class_name Building
@onready var consumer: Consumer = $Consumer
@onready var producer: Producer = $Producer
@onready var collision_shape: CollisionShape3D = $CollisionShape3D
@onready var detector: Detector = $Detector
var initial_collision_layer: int = 1 << 1 # layer 2: buildings
signal functional_changed(functional: bool)
@ -77,7 +80,9 @@ func _ready() -> void:
if producer != null:
functional_changed.connect(func(enable:bool): producer.enabled = enable)
if collision_shape != null:
functional_changed.connect(func(enable:bool): collision_shape.disabled = !enable)
functional_changed.connect(set_collision_enabled)
if detector != null:
functional_changed.connect(func(enable:bool): detector.enabled = enable)
func _process(delta: float) -> void:
if build_state == BuildState.UNPLACED:
@ -177,3 +182,12 @@ func set_visual_build_progress(ratio: float) -> void:
geometry.set_instance_shader_parameter("built_amount", built_amount)
#var opacity: float = 0.5 + (ratio * 0.5)
#geometry.transparency = 1.0 - opacity
func set_collision_enabled(enabled: bool) -> void:
if collision_shape == null:
return
if !enabled:
initial_collision_layer = collision_shape.get_parent().collision_layer
collision_shape.get_parent().collision_layer = 1 << 31
else:
collision_shape.get_parent().collision_layer = initial_collision_layer

View File

@ -0,0 +1,43 @@
extends Node3D
class_name Detector
@export var enabled: bool = true
@export var detection_range: float = 200.0
@export var requires_los: bool = true
@export_flags_3d_physics var los_physics_layers: int = 0b001
@export var detected_group_name: StringName = "Enemies"
@export var scan_period: float = 0.1
@export var debug_draw: bool = true
var scan_timer: float = 0.0
func _process(delta: float) -> void:
if enabled:
scan_timer += delta
if scan_timer >= scan_period:
scan_timer -= scan_period
scan()
func scan() -> void:
for node in get_tree().get_nodes_in_group(detected_group_name):
var enemy: Enemy = node as Enemy
if enemy != null and !enemy.sighted and can_see(enemy):
enemy.sighted = true
func can_see(enemy: Enemy) -> bool:
var distance: float = enemy.global_position.distance_to(global_position)
if distance >= detection_range:
return false
if requires_los:
var params: PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(global_position, enemy.global_position)
params.collision_mask = los_physics_layers
var result: Dictionary = get_world_3d().direct_space_state.intersect_ray(params)
if debug_draw:
TowerUtil.draw_raycast_hit(params, result, 1.0)
#DebugDraw3D.draw_line_hit(global_position, enemy.global_position, result[''] is_hit)
if result.has("collider"):
if result.get("collider") == enemy:
return true
return false # something's in the way
return true

View File

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

View File

@ -31,6 +31,7 @@ func placement_mouse_input(screen_position: Vector2, confirmed: bool) -> void:
var ray_origin: Vector3 = camera.project_ray_origin(screen_position)
var ray_normal: Vector3 = ray_origin + camera.project_ray_normal(screen_position) * 200
var params: PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(ray_origin, ray_normal)
params.collision_mask = (1 << 31) | (1 << 1) | (1) # selectables, buildings, terrain
if placing_building.collision_shape != null:
params.exclude = [placing_building.get_rid()]
var result: Dictionary = get_world_3d().direct_space_state.intersect_ray(params)

View File

@ -3,8 +3,10 @@ extends Building
var held_item: Item = null
func _process(delta: float) -> void:
if !producer.consumers.is_empty():
held_item = consumer.take_any_item_from_storage()
super(delta)
if is_functional():
if !producer.consumers.is_empty() and held_item == null:
held_item = consumer.take_any_item_from_storage()
if held_item != null and producer.send_item(held_item):
held_item = null

View File

@ -9,13 +9,16 @@ class_name Turret
@export_group("Bullet Spawning")
@export var bullet_scene: PackedScene = preload("res://objects/bullet.tscn") ## The entity to spawn as a bullet.
@export var fire_position: Vector3 = Vector3(0,1,0) ## The local position to fire from.
@export var bullets_at_once: int = 5
@export_group("Ballistics")
@export var shot_velocity: float = 100 ## The velocity that bullets are fired at, in m/s.
@export var deviation: Vector2 = Vector2(1.0, 1.0)
@export var lead_shots: bool = true ## Turn on to make the turret aim ahead of moving targets.
@export var compensate_for_gravity: bool = true ## Turn on to make the turret aim above distant targets to arc it's shots.
@export var use_artillery_firing_solution: bool = false ## If compensating for gravity, turn this on to make the turret aim high instead of low.
enum LoadState {UNLOADED, RELOADING, LOADED}
@onready var ammo: int = 0
@ -42,16 +45,15 @@ func _process(delta: float) -> void:
cooldown_timer += delta
if cooldown_timer >= fire_cooldown_time:
cooldown_timer -= fire_cooldown_time
if current_target != null and !is_valid_target(current_target):
current_target = null
if current_target == null:
search_for_enemy()
find_target()
if current_target != null:
fire_at_target()
func check_target_status() -> void:
if current_target.is_queued_for_deletion():
current_target = null
for i in range(bullets_at_once):
fire_at_target()
func search_for_enemy() -> void:
func find_target() -> void:
var enemies: Array[Node] = get_tree().get_nodes_in_group("Enemies")
var closest: float = 9999999999
for enemy: Enemy in enemies:
@ -59,57 +61,64 @@ func search_for_enemy() -> void:
continue
var distance_sqr = global_position.distance_squared_to(enemy.global_position)
if distance_sqr <= closest:
var params: PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.new()
params.collision_mask = 0b00101
params.from = to_global(fire_position)
params.to = enemy.global_position
var result: Dictionary = get_world_3d().direct_space_state.intersect_ray(params)
#DebugDraw3D.draw_line_hit(params.from, params.to, result["position"] if result.has("position") else Vector3(), result.has("position"), 0.25, Color.YELLOW, Color.BLACK, 1.0)
if result.has("collider"):
if !result["collider"] is PhysicsBody3D:
continue
var body: PhysicsBody3D = result["collider"]
if body is Enemy:
current_target = body
closest = body.global_position.distance_squared_to(global_position)
if is_valid_target(enemy):
current_target = enemy
closest = distance_sqr
func is_valid_target(enemy: Enemy) -> bool:
if enemy == null:
return false
if enemy.is_queued_for_deletion():
return false
return detector.can_see(enemy)
func fire_at_target() -> void:
#Engine.time_scale = 0.1
if ammo <= 0 or current_target == null:
return
var bullet: RigidBody3D = bullet_scene.instantiate()
bullet.target = current_target
add_child(bullet)
var bullet_pos: Vector3 = to_global(fire_position)
bullet.global_position = bullet_pos
var aim_target: Vector3 = current_target.global_position
var target_range: float = bullet_pos.distance_to(current_target.global_position)
var target_height: float = (aim_target.y - bullet_pos.y)
var shot_time: float = (target_range / shot_velocity)
var aim_target: Vector3 = current_target.global_position
if lead_shots:
aim_target += current_target.linear_velocity * shot_time
target_range = bullet_pos.distance_to(aim_target)
#DebugDraw3D.draw_sphere(aim_target, 0.25, Color.RED, shot_time)
var fire_direction: Vector3
var elevation: float = 0.0
var azimuth: float = Vector3.FORWARD.signed_angle_to((aim_target-bullet_pos).slide(Vector3.UP), Vector3.UP)
if compensate_for_gravity:
var elevation: float = find_fire_angle(target_range, (aim_target.y - bullet_pos.y), use_artillery_firing_solution)
var azimuth: float = Vector3.FORWARD.signed_angle_to((aim_target-bullet_pos).slide(Vector3.UP), Vector3.UP)
elevation = find_fire_angle(target_range, target_height, use_artillery_firing_solution)
print("Shot params: θ=%d φ=%d" % [rad_to_deg(elevation), rad_to_deg(azimuth)])
if elevation < -PI or elevation > PI or is_nan(elevation):
print("wtf?? θ=%d" % rad_to_deg(elevation))
return
fire_direction = Vector3.FORWARD
fire_direction = fire_direction.rotated(Vector3(1,0,0), elevation)
fire_direction = fire_direction.rotated(Vector3(0,1,0), azimuth)
else:
fire_direction = (aim_target - bullet_pos).normalized()
elevation = atan2(target_height, target_range)
#fire_direction = (aim_target - bullet_pos).normalized()
# apply deviation
elevation += randfn(0, deg_to_rad(deviation.y))
azimuth += randfn(0, deg_to_rad(deviation.x))
var fire_direction: Vector3 = Vector3.FORWARD
fire_direction = fire_direction.rotated(Vector3(1,0,0), elevation)
fire_direction = fire_direction.rotated(Vector3(0,1,0), azimuth)
#DebugDraw3D.draw_arrow_ray(bullet_pos, fire_direction, target_range, Color.RED, 0.5, true, shot_time)
bullet.look_at(bullet_pos+fire_direction)
bullet.linear_velocity = (-bullet.global_basis.z * shot_velocity)
#bullet.apply_impulse((aim_target - bullet.global_position).normalized()*shot_impulse)
bullet.angular_velocity = (bullet.global_basis * Vector3(0,0,deg_to_rad(1080)))
current_target = null
#
ammo -= 1
if ammo <= 0:
Engine.time_scale = 1.0

View File

@ -3,6 +3,9 @@ extends RigidBody3D
@export var damage_per_speed: float = 1.0
@export var min_damage: float = 10.0
@export var lifetime: float = 2.0
@export var homing_force: float = 10.0
var target: Enemy = null
func _on_body_entered(body: Node) -> void:
#print("Bullet collided with %s" % body.name)
@ -19,5 +22,10 @@ func _process(delta: float) -> void:
if global_position.y < -10:
destroy()
func _physics_process(delta: float) -> void:
if homing_force > 0 and target != null:
var distance: float = global_position.distance_to(target.global_position)
var velocity_direction: Vector3 = linear_velocity.limit_length()
func destroy() -> void:
queue_free()

View File

@ -3,8 +3,11 @@ class_name GameCamera
@export var keyboard_movement_speed: float = 10
@export var maximum_movement_speed: float = 30
@export var camera_movement_standard_distance: float = 20.0
@export var mouse_orbit_sensitivity: float = 0.003
@export var zoom_sensitivity: float = 0.1
@export var minimum_camera_distance: float = 1.0
@export var maximum_camera_distance: float = 100.0
@export var focus_follows_terrain: bool = true
var focus_offset: Vector3 = Vector3(10, 10, 10)
@ -33,14 +36,16 @@ func _process(delta: float) -> void:
if focus_object != null:
focus_position = focus_object.global_position
var movement_speed_multiplier: float = (global_position.distance_to(focus_position)) / camera_movement_standard_distance
process_focus_movement(delta)
process_camera_rotation(delta)
process_camera_zoom(delta)
process_focus_movement(delta, movement_speed_multiplier)
process_camera_rotation(delta, movement_speed_multiplier)
process_camera_zoom(delta, movement_speed_multiplier)
var target_position: Vector3 = focus_position + focus_offset
if target_position.distance_squared_to(global_position) > 0:
global_position = global_position.move_toward(target_position, maximum_movement_speed*delta)
global_position = global_position.move_toward(target_position, maximum_movement_speed*delta*movement_speed_multiplier)
#else:
basis = basis.slerp(Basis.looking_at(focus_position - target_position), 0.05)
@ -53,7 +58,7 @@ func _process(delta: float) -> void:
#look_at(focus_position)
func process_focus_movement(delta: float) -> void:
func process_focus_movement(delta: float, movement_speed: float) -> void:
var camera_forwards: Vector3 = -global_basis.z
camera_forwards = camera_forwards.slide(Vector3.UP).normalized()
@ -68,7 +73,7 @@ func process_focus_movement(delta: float) -> void:
focus_object = null
input = input_basis * input
var movement: Vector3 = input * keyboard_movement_speed * delta
var movement: Vector3 = input * keyboard_movement_speed * delta * movement_speed
global_position += movement
focus_position += movement
@ -82,7 +87,7 @@ func process_focus_movement(delta: float) -> void:
if result.has("position"):
focus_position = result["position"]
func process_camera_rotation(delta: float) -> void:
func process_camera_rotation(delta: float, movement_speed: float) -> void:
#var mouse_position: Vector2 = viewport.get_mouse_position()
#var mouse_position: Vector2 = Vector2()
#var mouse_movement: Vector2 = (mouse_position - last_mouse_position) * delta
@ -95,10 +100,13 @@ func process_camera_rotation(delta: float) -> void:
look_at(focus_position)
mouse_movement = Vector2()
func process_camera_zoom(_delta: float) -> void:
func process_camera_zoom(_delta: float, movement_speed: float) -> void:
#var wheel_input: float = Input.get_axis("camera_zoom_in", "camera_zoom_out")
var change: float = 1.0 + (-mouse_wheel_delta * zoom_sensitivity)
focus_offset *= change
focus_offset = focus_offset.limit_length(maximum_camera_distance)
if focus_offset.length() < minimum_camera_distance:
focus_offset = focus_offset.normalized() * minimum_camera_distance
mouse_wheel_delta = 0
func _input(event: InputEvent) -> void:

80
scripts/units/action.gd Normal file
View File

@ -0,0 +1,80 @@
extends RefCounted
class_name UnitAction
var unit: Unit = null
var time_limit: float = 300.0
func process(delta: float) -> Unit.TaskStatus:
time_limit -= delta
if time_limit <= 0:
return Unit.TaskStatus.TIMED_OUT
return Unit.TaskStatus.DONE
class MoveAction extends UnitAction:
var last_distance_to_target: float = 99999999.9
var stuck_timer: float = 0.0
var move_target: Vector3 = Vector3(16, 1, 13):
set(target):
move_target = target
unit.nav_agent_3d.target_position = move_target
func process(delta: float) -> Unit.TaskStatus:
if unit.nav_agent_3d.is_navigation_finished():
#task_updated.emit(TaskStatus.DONE)
unit.target_velocity = Vector3()
#nav_agent_3d.target_position = move_target + Vector3(randfn(0, move_radius), 0, randfn(0, move_radius))
#nav_agent_3d.target_position = NavigationServer3D.map_get_random_point(NavigationServer3D.get_maps()[0], 1, true)
#last_distance_to_target = nav_agent_3d.distance_to_target()
if unit.nav_agent_3d.is_target_reachable():
return Unit.TaskStatus.DONE
else:
return Unit.TaskStatus.IMPOSSIBLE
else:
var next_point: Vector3 = unit.nav_agent_3d.get_next_path_position()
if unit.shapecast_3d.is_colliding():
var distance_to_target: float = unit.global_position.distance_to(next_point)
var progress_rate: float = (last_distance_to_target - distance_to_target) / delta
last_distance_to_target = distance_to_target
if progress_rate < unit.minimum_progress_rate:
stuck_timer += delta
if stuck_timer >= unit.stuck_time:
unit.unstuck()
else:
unit.label_3d.modulate = Color.WHITE
stuck_timer = 0
DebugDraw3D.draw_sphere(unit.nav_agent_3d.target_position, 0.5, Color.RED)
#DebugDraw3D.draw_sphere(next_point, 0.1, Color.YELLOW)
var direction: Vector3 = (next_point - unit.global_position).normalized()
#basis = Basis.looking_at(direction)
#DebugDraw3D.draw_line(global_position, global_position + linear_velocity, Color.BLUE)
unit.target_velocity = direction * unit.max_speed
unit.nav_agent_3d.velocity = unit.target_velocity
#DebugDraw3D.draw_line(global_position, global_positiaon + target_velocity, Color.MAGENTA)
#DebugDraw3D.draw_text(global_position + Vector3(0,1,0), "%f" % nav_agent_3d.distance_to_target())
return Unit.TaskStatus.IN_PROGRESS
class BuildAction extends UnitAction:
var building: Building
func process(delta: float) -> Unit.TaskStatus:
if building == null or building.is_queued_for_deletion():
return Unit.TaskStatus.IMPOSSIBLE
building.build_progress += delta
if building.build_state == Building.BuildState.READY:
return Unit.TaskStatus.DONE
elif building.build_state != Building.BuildState.BUILDING:
return Unit.TaskStatus.IMPOSSIBLE # can't build if the building's not there
else:
return Unit.TaskStatus.IN_PROGRESS
class RepairAction extends UnitAction:
var building: Building
func process(delta: float) -> Unit.TaskStatus:
if building == null or building.is_queued_for_deletion() or building.build_state != Building.BuildState.READY:
return Unit.TaskStatus.IMPOSSIBLE
building.hp += delta * 10.0
if building.hp >= building.max_hp:
building.hp = building.max_hp
return Unit.TaskStatus.DONE
return Unit.TaskStatus.IN_PROGRESS

View File

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

View File

@ -7,10 +7,6 @@ class_name Citizen
var task: Task = null
var working: bool = false
enum WorkStatus { NONE, CONSTRUCTING_BUILDING }
var work_status: WorkStatus
var work_building: Building = null
func _ready() -> void:
held_item_meshinstance = $"Body/Hands/Held Item"
@ -39,13 +35,6 @@ func _process(delta: float) -> void:
CitizenManager._instance.add_task(task)
task = null
working = false
if work_status == WorkStatus.CONSTRUCTING_BUILDING:
work_building.build_progress += delta
if work_building.build_state == Building.BuildState.READY:
send_task_update(TaskStatus.DONE)
elif work_building.build_state != Building.BuildState.BUILDING:
send_task_update(TaskStatus.IMPOSSIBLE)
func assign_task(t: Task) -> void:
task = t
@ -67,9 +56,8 @@ func put_item_in_building_storage(building: Building) -> bool:
return true
func build_building(building: Building) -> bool:
work_building = building
work_status = WorkStatus.CONSTRUCTING_BUILDING
var build_action: UnitAction.BuildAction = UnitAction.BuildAction.new()
build_action.building = building
action = build_action
var ok: bool = await wait_for_task_update() == TaskStatus.DONE
work_building = null
work_status = WorkStatus.NONE
return ok

View File

@ -1,7 +1,11 @@
extends Unit
class_name Enemy
var sighted: bool = true
var sighted: bool = true:
set(val):
sighted = val
visible = sighted
func _ready() -> void:
sighted = false
go_to_destination(Vector3(17,1,15))

View File

@ -17,13 +17,6 @@ class_name Unit
var target_velocity: Vector3 = Vector3()
var avoidance_velocity: Vector3 = Vector3()
var avoidance_timeout: float = 0.0
var last_distance_to_target: float = 0.0
var stuck_timer: float = 0.0
var moving: bool = false
var move_target: Vector3 = Vector3(16, 1, 13):
set(target):
move_target = target
nav_agent_3d.target_position = move_target
var move_radius: float = 5.0
var held_item: Item = null:
@ -39,9 +32,12 @@ var held_item: Item = null:
@onready var label_3d: Label3D = $Label3D
@onready var held_item_meshinstance: MeshInstance3D = null
var action_timeout: float = 0.0
var action: UnitAction = null
var task_timeout: float = 0.0
enum TaskStatus {
IN_PROGRESS,
INTERRUPTED,
TIMED_OUT,
IMPOSSIBLE,
@ -61,59 +57,34 @@ func avoidance_velocity_computed(velocity: Vector3) -> void:
func _process(delta: float) -> void:
label_3d.text = "HP: %d" % hp
if action_timeout > 0:
action_timeout -= delta
if action_timeout <= 0:
task_updated.emit(TaskStatus.TIMED_OUT)
if moving:
if nav_agent_3d.is_target_reached() \
or nav_agent_3d.target_position.is_zero_approx() \
or !nav_agent_3d.is_target_reachable():
moving = false
task_updated.emit(TaskStatus.DONE)
target_velocity = Vector3()
#nav_agent_3d.target_position = move_target + Vector3(randfn(0, move_radius), 0, randfn(0, move_radius))
#nav_agent_3d.target_position = NavigationServer3D.map_get_random_point(NavigationServer3D.get_maps()[0], 1, true)
last_distance_to_target = nav_agent_3d.distance_to_target()
else:
var next_point: Vector3 = nav_agent_3d.get_next_path_position()
if shapecast_3d.is_colliding():
var distance_to_target: float = global_position.distance_to(next_point)
var progress_rate: float = (last_distance_to_target - distance_to_target) / delta
last_distance_to_target = distance_to_target
if progress_rate < minimum_progress_rate:
stuck_timer += delta
if stuck_timer >= stuck_time:
unstuck()
else:
label_3d.modulate = Color.WHITE
stuck_timer = 0
if global_position.y <= -10:
unstuck()
DebugDraw3D.draw_sphere(nav_agent_3d.target_position, 0.5, Color.RED)
#DebugDraw3D.draw_sphere(next_point, 0.1, Color.YELLOW)
var direction: Vector3 = (next_point - global_position).normalized()
#basis = Basis.looking_at(direction)
#DebugDraw3D.draw_line(global_position, global_position + linear_velocity, Color.BLUE)
target_velocity = direction * max_speed
nav_agent_3d.velocity = target_velocity
#DebugDraw3D.draw_line(global_position, global_positiaon + target_velocity, Color.MAGENTA)
#DebugDraw3D.draw_text(global_position + Vector3(0,1,0), "%f" % nav_agent_3d.distance_to_target())
if task_timeout > 0:
task_timeout -= delta
if task_timeout <= 0:
send_task_update(TaskStatus.TIMED_OUT)
if action != null:
var status: TaskStatus = action.process(delta)
if status != TaskStatus.IN_PROGRESS:
action = null
send_task_update(status)
if global_position.y <= -10:
unstuck()
func unstuck() -> void:
# teleport to next path point
linear_velocity = Vector3()
global_position = nav_agent_3d.get_next_path_position()
stuck_timer = 0
func hurt(damage: float) -> void:
hp -= damage
interrupt()
#print("%s hit for %f damage, HP=%f" % [name, damage, hp])
if hp <= 0:
die()
func die() -> void:
interrupt()
queue_free()
func _physics_process(delta: float) -> void:
@ -133,6 +104,9 @@ func _physics_process(delta: float) -> void:
#DebugDraw3D.draw_line(global_position, global_position - global_basis.z, Color.BLUE)
#DebugDraw3D.draw_line(global_position, global_position + force_direction, Color.RED)
func interrupt() -> void:
send_task_update(TaskStatus.INTERRUPTED)
func send_task_update(task_status: TaskStatus) -> void:
task_updated.emit(task_status)
@ -141,8 +115,11 @@ func wait_for_task_update() -> TaskStatus:
return status_received
func go_to_destination(destination: Vector3) -> bool:
move_target = destination
moving = true
var move_action: UnitAction.MoveAction = UnitAction.MoveAction.new()
move_action.unit = self
move_action.move_target = destination
action = move_action
#move_target = destination
return (await wait_for_task_update()) == TaskStatus.DONE
func take_item_from_building(item: Item, building: Building) -> bool:

6
scripts/util.gd Normal file
View File

@ -0,0 +1,6 @@
extends Node
func draw_raycast_hit(params: PhysicsRayQueryParameters3D, result: Dictionary, duration: float = 0.0) -> void:
var is_hit: bool = result.has("collider")
var hit_loc: Vector3 = result.get("position", params.to)
DebugDraw3D.draw_line_hit(params.from, params.to, hit_loc, is_hit, 0.25, Color.RED, Color.BLACK, duration)

1
scripts/util.gd.uid Normal file
View File

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