Added storage building, added detector building component, and changed citizen behaviour to use pluggable actions.
This commit is contained in:
parent
8a0e163e64
commit
0350d48957
21
node_3d.tscn
21
node_3d.tscn
File diff suppressed because one or more lines are too long
|
@ -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="."]
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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="."]
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b3jshlhs24s8d
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
uid://cvmoolyvemu8s
|
|
@ -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
|
|
@ -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))
|
|
@ -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:
|
|
@ -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)
|
|
@ -0,0 +1 @@
|
|||
uid://c42mpvgqhysam
|
Loading…
Reference in New Issue