Buildings are now constructed by citizens.
This commit is contained in:
parent
412736b9d5
commit
78bfcde7a0
51
node_3d.tscn
51
node_3d.tscn
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,14 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://b1fnsl3k1mo5c"]
|
||||
[gd_scene load_steps=10 format=3 uid="uid://b1fnsl3k1mo5c"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dvgo3kpjr2mmj" path="res://scripts/building_components/building.gd" id="1_k07no"]
|
||||
[ext_resource type="Script" uid="uid://bcp3fwf3nds3k" path="res://scripts/nav_obstacle.gd" id="2_0gk2u"]
|
||||
[ext_resource type="Script" uid="uid://kcdpck5ufgcc" path="res://scripts/item.gd" id="2_fahfx"]
|
||||
[ext_resource type="Resource" uid="uid://dxlb2ixt3fx7l" path="res://items/ore.tres" id="3_w18nb"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_0gk2u"]
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_cee1v"]
|
||||
material = SubResource("StandardMaterial3D_0gk2u")
|
||||
size = Vector3(2, 2, 2)
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_k07no"]
|
||||
|
@ -18,10 +23,13 @@ agent_radius = 0.2
|
|||
size = Vector2(2, 2)
|
||||
orientation = 1
|
||||
|
||||
[node name="Building" type="StaticBody3D"]
|
||||
[node name="Building" type="StaticBody3D" groups=["Buildings"]]
|
||||
collision_layer = 2
|
||||
collision_mask = 15
|
||||
script = ExtResource("1_k07no")
|
||||
build_materials_needed = Dictionary[ExtResource("2_fahfx"), int]({
|
||||
ExtResource("3_w18nb"): 5
|
||||
})
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
[gd_scene load_steps=6 format=3 uid="uid://chd531mdaek3"]
|
||||
[gd_scene load_steps=7 format=3 uid="uid://chd531mdaek3"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://b1fnsl3k1mo5c" path="res://objects/buildings/building.tscn" id="1_8r86l"]
|
||||
[ext_resource type="Script" uid="uid://6dy54s70qf0x" path="res://scripts/buildings/miner.gd" id="2_k13eg"]
|
||||
[ext_resource type="PackedScene" uid="uid://dx13fyjv0d8st" path="res://assets/blends/Miner.blend" id="2_la0h1"]
|
||||
[ext_resource type="Script" uid="uid://kcdpck5ufgcc" path="res://scripts/item.gd" id="3_8y6s2"]
|
||||
[ext_resource type="Resource" uid="uid://dxlb2ixt3fx7l" path="res://items/ore.tres" id="3_k13eg"]
|
||||
[ext_resource type="Script" uid="uid://c4fquatkjmsgu" path="res://scripts/building_components/producer.gd" id="5_65oni"]
|
||||
|
||||
[node name="Miner" instance=ExtResource("1_8r86l")]
|
||||
script = ExtResource("2_k13eg")
|
||||
mine_period = 1.0
|
||||
build_materials_needed = Dictionary[ExtResource("3_8y6s2"), int]({})
|
||||
can_stack = false
|
||||
|
||||
[node name="MeshInstance3D" parent="." index="0"]
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
[gd_scene load_steps=10 format=3 uid="uid://c6e1nifka3h4v"]
|
||||
[gd_scene load_steps=12 format=3 uid="uid://c6e1nifka3h4v"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://b1fnsl3k1mo5c" path="res://objects/buildings/building.tscn" id="1_hxugg"]
|
||||
[ext_resource type="Script" uid="uid://c3bqfgoof2c83" path="res://scripts/buildings/processor.gd" id="2_evfwj"]
|
||||
[ext_resource type="Script" uid="uid://kcdpck5ufgcc" path="res://scripts/item.gd" id="3_3h7kv"]
|
||||
[ext_resource type="Script" uid="uid://kcdpck5ufgcc" path="res://scripts/item.gd" id="3_dx8de"]
|
||||
[ext_resource type="Resource" uid="uid://ed64yksg1y6m" path="res://items/bullets.tres" id="4_3h7kv"]
|
||||
[ext_resource type="Script" uid="uid://bshiyw2k3op02" path="res://scripts/building_components/consumer.gd" id="4_dx8de"]
|
||||
[ext_resource type="Shader" uid="uid://c7ahmb00sly4d" path="res://shaders/hologram.gdshader" id="5_dx8de"]
|
||||
[ext_resource type="Script" uid="uid://c4fquatkjmsgu" path="res://scripts/building_components/producer.gd" id="5_p3dou"]
|
||||
[ext_resource type="Resource" uid="uid://dxlb2ixt3fx7l" path="res://items/ore.tres" id="7_dx8de"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_p3dou"]
|
||||
render_priority = 0
|
||||
shader = ExtResource("5_dx8de")
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_evfwj"]
|
||||
next_pass = SubResource("ShaderMaterial_p3dou")
|
||||
albedo_color = Color(0.023159, 1, 0, 1)
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_3h7kv"]
|
||||
|
@ -17,7 +23,7 @@ size = Vector3(2, 2, 2)
|
|||
|
||||
[node name="Processor" instance=ExtResource("1_hxugg")]
|
||||
script = ExtResource("2_evfwj")
|
||||
ingredients = Dictionary[ExtResource("3_3h7kv"), int]({
|
||||
ingredients = Dictionary[ExtResource("3_dx8de"), int]({
|
||||
ExtResource("7_dx8de"): 2
|
||||
})
|
||||
process_time = 1.0
|
||||
|
@ -27,7 +33,7 @@ mesh = SubResource("BoxMesh_3h7kv")
|
|||
|
||||
[node name="Consumer" type="Node" parent="." index="2"]
|
||||
script = ExtResource("4_dx8de")
|
||||
accepted_items = Array[ExtResource("3_3h7kv")]([ExtResource("7_dx8de")])
|
||||
accepted_items = Array[ExtResource("3_dx8de")]([ExtResource("7_dx8de")])
|
||||
metadata/_custom_type_script = "uid://bshiyw2k3op02"
|
||||
|
||||
[node name="Producer" type="Node" parent="." index="3"]
|
||||
|
|
|
@ -30,7 +30,7 @@ fire_cooldown_time = 0.2
|
|||
reload_time = 1.0
|
||||
bullet_scene = ExtResource("3_kpsgq")
|
||||
fire_position = Vector3(0, 2.1, 0)
|
||||
shot_velocity = 20.0
|
||||
shot_velocity = 50.0
|
||||
lead_shots = true
|
||||
compensate_for_gravity = true
|
||||
use_artillery_firing_solution = false
|
||||
|
|
|
@ -105,12 +105,13 @@ size = Vector2(0.1, 0.1)
|
|||
|
||||
[node name="Bullet" type="RigidBody3D"]
|
||||
collision_layer = 16
|
||||
collision_mask = 5
|
||||
collision_mask = 21
|
||||
continuous_cd = true
|
||||
contact_monitor = true
|
||||
max_contacts_reported = 1
|
||||
angular_velocity = Vector3(0, 0, 6.28319)
|
||||
script = ExtResource("1_rsjgb")
|
||||
damage_per_speed = 100.0
|
||||
damage_per_speed = 2.0
|
||||
min_damage = 1.0
|
||||
lifetime = 5.0
|
||||
|
||||
|
@ -120,7 +121,7 @@ shape = SubResource("CapsuleShape3D_3ndsa")
|
|||
|
||||
[node name="GPUTrail3D" type="GPUParticles3D" parent="."]
|
||||
physics_interpolation_mode = 2
|
||||
transform = Transform3D(0.0233971, -0.0972244, 0, 0.0972244, 0.0233971, 0, 0, 0, 0.1, 0, 0, 0)
|
||||
transform = Transform3D(0.0233971, -0.0972244, 0, 0.0972244, 0.0233971, 0, 0, 0, 0.1, 0, 0.0926043, -0.000724986)
|
||||
amount = 19
|
||||
lifetime = 19.0
|
||||
explosiveness = 1.0
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
@tool
|
||||
extends Unit
|
||||
class_name Citizen
|
||||
|
||||
@onready var body: Sprite3D = $Body
|
||||
@onready var hands: Sprite3D = $Body/Hands
|
||||
|
||||
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"
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
super(delta)
|
||||
var camera: Camera3D
|
||||
|
@ -19,6 +28,48 @@ func _process(delta: float) -> void:
|
|||
var facing_right: bool = camera_local_facing.x > 0
|
||||
|
||||
body.frame = 1 if facing_away else 0
|
||||
hands.frame = 1 if facing_away else 0
|
||||
#hands.frame = 1 if facing_away else 0
|
||||
body.flip_h = facing_right
|
||||
hands.flip_h = facing_right
|
||||
|
||||
if !working and task != null:
|
||||
working = true
|
||||
var ok: bool = await task.execute(self)
|
||||
if !ok:
|
||||
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
|
||||
|
||||
func is_idle() -> bool:
|
||||
return task == null
|
||||
|
||||
func put_item_in_building_materials(building: Building) -> bool:
|
||||
building._add_build_material(held_item)
|
||||
held_item = null
|
||||
return true
|
||||
|
||||
func put_item_in_building_storage(building: Building) -> bool:
|
||||
if building.consumer == null:
|
||||
return false
|
||||
if !building.consumer.offer_item(held_item):
|
||||
return false
|
||||
held_item = null
|
||||
return true
|
||||
|
||||
func build_building(building: Building) -> bool:
|
||||
work_building = building
|
||||
work_status = WorkStatus.CONSTRUCTING_BUILDING
|
||||
var ok: bool = await wait_for_task_update() == TaskStatus.DONE
|
||||
work_building = null
|
||||
work_status = WorkStatus.NONE
|
||||
return ok
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
[ext_resource type="Script" uid="uid://b7xficxq807qd" path="res://objects/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://gf0q5hs4wjva" path="res://assets/models/Ore.obj" id="5_dds8f"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://c6yj8uwsgqxv0" path="res://assets/models/Ingot.obj" id="5_dv62s"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_vpcy6"]
|
||||
albedo_texture = ExtResource("3_pedvu")
|
||||
|
@ -13,13 +13,14 @@ billboard_mode = 2
|
|||
[sub_resource type="QuadMesh" id="QuadMesh_5xmhx"]
|
||||
material = SubResource("StandardMaterial3D_vpcy6")
|
||||
|
||||
[node name="Citizen" instance=ExtResource("1_6046h")]
|
||||
[node name="Citizen" groups=["Citizens"] instance=ExtResource("1_6046h")]
|
||||
collision_layer = 8
|
||||
collision_mask = 5
|
||||
script = ExtResource("2_dv62s")
|
||||
stuck_time = 5.0
|
||||
|
||||
[node name="NavigationAgent3D" parent="." index="0"]
|
||||
target_position = Vector3(21.7818, 1, 12.7739)
|
||||
target_position = Vector3(18.6013, 1, 14.148)
|
||||
velocity = Vector3(0.811107, 0, -0.811107)
|
||||
|
||||
[node name="MeshInstance3D" parent="." index="2"]
|
||||
|
@ -39,7 +40,7 @@ texture = ExtResource("3_pedvu")
|
|||
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.01)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.0522564)
|
||||
billboard = 2
|
||||
shaded = true
|
||||
alpha_cut = 1
|
||||
|
@ -47,6 +48,7 @@ texture_filter = 0
|
|||
texture = ExtResource("4_i1unn")
|
||||
|
||||
[node name="Held Item" type="MeshInstance3D" parent="Body/Hands" index="0"]
|
||||
transform = Transform3D(0.17, 0, 0, 0, 0.17, 0, 0, 0, 0.17, 5.96046e-08, -0.0580646, -0.0773078)
|
||||
mesh = ExtResource("5_dds8f")
|
||||
transform = Transform3D(0.17, 0, 0, 0, 0.17, 0, 0, 0, 0.17, 5.96046e-08, -0.0189633, -0.0170195)
|
||||
visible = false
|
||||
mesh = ExtResource("5_dv62s")
|
||||
skeleton = NodePath("../../..")
|
||||
|
|
|
@ -31,7 +31,7 @@ max_speed = 5.0
|
|||
|
||||
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
|
||||
path_desired_distance = 0.5
|
||||
target_desired_distance = 0.5
|
||||
target_desired_distance = 2.0
|
||||
path_max_distance = 1.01
|
||||
avoidance_enabled = true
|
||||
radius = 1.0
|
||||
|
|
|
@ -47,6 +47,7 @@ gdscript/warnings/unsafe_property_access=1
|
|||
gdscript/warnings/unsafe_method_access=1
|
||||
gdscript/warnings/unsafe_cast=1
|
||||
gdscript/warnings/unsafe_call_argument=2
|
||||
gdscript/warnings/return_value_discarded=1
|
||||
gdscript/warnings/static_called_on_instance=2
|
||||
gdscript/warnings/missing_tool=2
|
||||
gdscript/warnings/assert_always_false=2
|
||||
|
@ -72,6 +73,8 @@ enabled=PackedStringArray("res://addons/GPUTrail/plugin.cfg", "res://addons/Path
|
|||
[global_group]
|
||||
|
||||
Enemies=""
|
||||
Citizens=""
|
||||
Buildings=""
|
||||
|
||||
[input]
|
||||
|
||||
|
@ -121,6 +124,13 @@ building_place={
|
|||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":false,"script":null)
|
||||
]
|
||||
}
|
||||
building_cancel={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":2,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[layer_names]
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
extends Node
|
||||
class_name CitizenManager
|
||||
|
||||
@export var task_assignment_rate: float = 100
|
||||
|
||||
var task_assignment_budget: float = 0
|
||||
var task_queue: Array[Task] = []
|
||||
|
||||
static var _instance: CitizenManager = null
|
||||
|
||||
#signal task_queue_updated(task_queue: Array[Task])
|
||||
|
||||
func _init() -> void:
|
||||
if _instance == null:
|
||||
_instance = self
|
||||
else:
|
||||
assert(false, "A second CitizenManager was created you fucking moron")
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
task_assignment_budget += task_assignment_rate * delta
|
||||
if task_assignment_budget >= task_assignment_rate:
|
||||
task_assignment_budget = task_assignment_rate
|
||||
|
||||
var idle_workers: Array[Citizen] = []
|
||||
for worker in get_children():
|
||||
if worker is Citizen:
|
||||
if worker.is_idle():
|
||||
idle_workers.append(worker)
|
||||
|
||||
#idle_workers.shuffle()
|
||||
|
||||
while !idle_workers.is_empty() and !task_queue.is_empty() and task_assignment_budget >= 1:
|
||||
var task: Task = task_queue.pop_front()
|
||||
task_assignment_budget -= 1
|
||||
if task.is_ready_to_start():
|
||||
# TODO: this currently assigns to the first worker found
|
||||
var worker: Citizen = idle_workers.pop_front()
|
||||
worker.assign_task(task)
|
||||
else:
|
||||
add_task(task)
|
||||
|
||||
func remove_task(task: Task):
|
||||
var idx: int = task_queue.find(task)
|
||||
if idx >= 0:
|
||||
task_queue.remove_at(idx)
|
||||
|
||||
func add_task(task: Task):
|
||||
print("Task added to queue: %s" % task.name)
|
||||
var idx: int = task_queue.bsearch_custom(task, sort_tasks, false)
|
||||
task_queue.insert(idx, task)
|
||||
|
||||
func sort_tasks(a: Task,b: Task) -> bool:
|
||||
return a.priority > b.priority
|
||||
|
||||
const TASK_PRIORITY_ANYTIME = 0
|
||||
const TASK_PRIORITY_BY_WAVE_START = 10
|
||||
const TASK_PRIORITY_ASAP = 20
|
|
@ -0,0 +1 @@
|
|||
uid://ckf7i6ig4twnq
|
|
@ -1,6 +1,10 @@
|
|||
extends Node3D
|
||||
class_name Building
|
||||
|
||||
@export_group("Construction", "build")
|
||||
@export var build_time: float = 5.0 ## Worker-time taken for 1 citizen to finish the building, in citizen-seconds.
|
||||
@export var build_materials_needed: Dictionary[Item, int] = {}
|
||||
|
||||
@export_group("Defence")
|
||||
@export var max_hp: int = 100 ## Amount of damage this building can sustain before being destroyed.
|
||||
|
||||
|
@ -31,14 +35,36 @@ const PLACEMENT_PART_ADDED: int = 0b0100
|
|||
const PLACEMENT_COMPLETED: int = 0b1000
|
||||
|
||||
var hp: int = max_hp
|
||||
var build_progress: float = 0.0:
|
||||
set(val):
|
||||
build_progress = val
|
||||
if build_progress >= build_time:
|
||||
build_state = BuildState.READY
|
||||
var build_percent: float = build_progress / build_time
|
||||
set_visual_build_progress(build_percent)
|
||||
|
||||
var stacked_buildings: Array[Building] = []
|
||||
|
||||
var build_state: BuildState = BuildState.READY:
|
||||
set(state):
|
||||
if state != build_state:
|
||||
build_state = state
|
||||
functional_changed.emit(is_functional())
|
||||
|
||||
if build_state == BuildState.BUILDING:
|
||||
for item in build_materials_needed.keys():
|
||||
for i in range(build_materials_needed.get(item)):
|
||||
var task: Task.FetchItemTask = Task.FetchItemTask.new()
|
||||
task.building = self
|
||||
task.item = item
|
||||
CitizenManager._instance.add_task(task)
|
||||
check_buildable()
|
||||
set_visual_build_progress(0.0)
|
||||
elif build_state == BuildState.READY:
|
||||
set_visual_build_progress(1.0)
|
||||
|
||||
var build_materials_used: Dictionary[Item, int] = {}
|
||||
|
||||
func _ready() -> void:
|
||||
if nav_obstacle != null:
|
||||
functional_changed.connect(func(enable:bool): nav_obstacle.enabled = enable)
|
||||
|
@ -79,7 +105,7 @@ func _start_placement() -> void:
|
|||
build_state = BuildState.UNPLACED
|
||||
|
||||
func _end_placement() -> void:
|
||||
build_state = BuildState.READY
|
||||
build_state = BuildState.BUILDING
|
||||
|
||||
func _placement_select_building(building: Building, confirmed: bool) -> int:
|
||||
while !building.stacked_buildings.is_empty():
|
||||
|
@ -110,3 +136,26 @@ func _placement_select_position(pos: Vector3, confirmed: bool) -> int:
|
|||
ret |= PLACEMENT_COMPLETED
|
||||
|
||||
return ret
|
||||
|
||||
func check_buildable() -> void:
|
||||
if can_build():
|
||||
var task: Task.BuildTask = Task.BuildTask.new()
|
||||
task.building = self
|
||||
CitizenManager._instance.add_task(task)
|
||||
|
||||
func _add_build_material(item: Item) -> void:
|
||||
build_materials_used.set(item, build_materials_used.get(item, 0) + 1)
|
||||
check_buildable()
|
||||
|
||||
func can_build() -> bool:
|
||||
for item in build_materials_needed.keys():
|
||||
if build_materials_used.get(item,0) < build_materials_needed.get(item,0):
|
||||
return false
|
||||
return true
|
||||
|
||||
func set_visual_build_progress(ratio: float) -> void:
|
||||
var geometries: Array[Node] = find_children("", "GeometryInstance3D", true)
|
||||
for node in geometries:
|
||||
var geometry: GeometryInstance3D = node as GeometryInstance3D
|
||||
var opacity: float = 0.5 + (ratio * 0.5)
|
||||
geometry.transparency = 1.0 - opacity
|
||||
|
|
|
@ -10,6 +10,18 @@ signal item_added(item: Item)
|
|||
var storage_total: int = 0
|
||||
var storage: Dictionary[Item, int] = {}
|
||||
|
||||
class ItemRequest:
|
||||
var item: Item
|
||||
var timeout: float
|
||||
var requests: Array[ItemRequest] = []
|
||||
signal requested_item_received(request: ItemRequest)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
for request in requests:
|
||||
request.timeout -= delta
|
||||
if request.timeout <= 0:
|
||||
cancel_request(request)
|
||||
|
||||
func can_accept_item(item: Item) -> bool:
|
||||
return ((storage_total < storage_size) or void_excess_items) \
|
||||
and (accepted_items.has(item) or accepted_items.is_empty()) \
|
||||
|
@ -23,10 +35,19 @@ func offer_item(item: Item) -> bool:
|
|||
var old_count: int = storage.get(item, 0)
|
||||
storage.set(item, old_count + 1)
|
||||
storage_total += 1
|
||||
#print("Added %s to %s storage; previously had %d, new total %d" % [item.name, get_parent().name, old_count, storage_total])
|
||||
if !check_requested_items(item):
|
||||
item_added.emit(item)
|
||||
return true
|
||||
|
||||
func check_requested_items(new_item: Item):
|
||||
for request in requests:
|
||||
if new_item == request.item:
|
||||
requested_item_received.emit(request)
|
||||
break
|
||||
|
||||
func cancel_request(request: ItemRequest):
|
||||
requested_item_received.emit(request)
|
||||
|
||||
func check_storage_for_item(item: Item) -> bool:
|
||||
return check_storage_for_items({item:1})
|
||||
|
||||
|
@ -47,6 +68,19 @@ func take_any_item_from_storage() -> Item:
|
|||
func take_item_from_storage(item: Item) -> bool:
|
||||
return take_items_from_storage({item: 1})
|
||||
|
||||
## Waits for an item to be available at this storage, or a timeout to be reached.
|
||||
func wait_for_item(item: Item, timeout: float) -> bool:
|
||||
if check_storage_for_item(item):
|
||||
return true
|
||||
var request: ItemRequest = ItemRequest.new()
|
||||
request.item = item
|
||||
request.timeout = timeout
|
||||
requests.append(request)
|
||||
var finished_request: ItemRequest = null
|
||||
while finished_request != request:
|
||||
finished_request = await requested_item_received
|
||||
return check_storage_for_item(item)
|
||||
|
||||
func take_items_from_storage(items: Dictionary[Item, int]) -> bool:
|
||||
if !check_storage_for_items(items):
|
||||
return false
|
||||
|
|
|
@ -14,9 +14,13 @@ func start_placement(scene: PackedScene) -> void:
|
|||
func _input(event: InputEvent) -> void:
|
||||
if placing_building == null:
|
||||
return
|
||||
if event is InputEventMouseButton and event.is_action("building_place"):
|
||||
if event is InputEventMouseButton:
|
||||
if event.is_action("building_place"):
|
||||
placement_mouse_input((event as InputEventMouseButton).global_position, true)
|
||||
get_viewport().set_input_as_handled()
|
||||
elif event.is_action("building_cancel"):
|
||||
placement_cancel()
|
||||
get_viewport().set_input_as_handled()
|
||||
elif event is InputEventMouseMotion:
|
||||
placement_mouse_input((event as InputEventMouseMotion).global_position, false)
|
||||
get_viewport().set_input_as_handled()
|
||||
|
@ -41,3 +45,7 @@ func placement_mouse_input(screen_position: Vector2, confirmed: bool) -> void:
|
|||
if placment_feedback & Building.PLACEMENT_COMPLETED:
|
||||
placing_building._end_placement()
|
||||
placing_building = null
|
||||
|
||||
func placement_cancel() -> void:
|
||||
placing_building.queue_free()
|
||||
placing_building = null
|
||||
|
|
|
@ -30,6 +30,12 @@ var output_building: Building = null:
|
|||
var waypoints: Array[Vector3] = []
|
||||
var editing_waypoint: int = 0
|
||||
|
||||
func _exit_tree() -> void:
|
||||
if input_building != null:
|
||||
input_building.producer.consumers.erase(consumer)
|
||||
if output_building != null:
|
||||
producer.consumers.erase(output_building.consumer)
|
||||
|
||||
func _placement_select_building(building: Building, confirmed: bool) -> int:
|
||||
var ret: int = 0
|
||||
if input_building == null:
|
||||
|
@ -95,7 +101,7 @@ func _ready() -> void:
|
|||
path.curve = path.curve.duplicate()
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
belt_moving = check_items_at_end()
|
||||
belt_moving = is_functional() and check_items_at_end()
|
||||
consumer.enabled = belt_moving and (closest_item >= spacing)
|
||||
|
||||
var movement: float = (delta * speed)
|
||||
|
|
|
@ -87,14 +87,14 @@ func fire_at_target() -> void:
|
|||
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)
|
||||
#DebugDraw3D.draw_sphere(aim_target, 0.25, Color.RED, shot_time)
|
||||
|
||||
var fire_direction: Vector3
|
||||
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-global_position, Vector3.UP)
|
||||
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)
|
||||
print("Shot params: θ=%d φ=%d" % [rad_to_deg(elevation), rad_to_deg(azimuth)])
|
||||
if elevation < -PI or elevation > PI:
|
||||
if elevation < -PI or elevation > PI or is_nan(elevation):
|
||||
print("wtf?? θ=%d" % rad_to_deg(elevation))
|
||||
return
|
||||
fire_direction = Vector3.FORWARD
|
||||
|
@ -103,12 +103,12 @@ func fire_at_target() -> void:
|
|||
else:
|
||||
fire_direction = (aim_target - bullet_pos).normalized()
|
||||
|
||||
DebugDraw3D.draw_arrow_ray(bullet_pos, fire_direction, target_range, Color.RED, 0.5, true, shot_time)
|
||||
#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.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,-10))
|
||||
bullet.angular_velocity = (bullet.global_basis * Vector3(0,0,deg_to_rad(1080)))
|
||||
current_target = null
|
||||
ammo -= 1
|
||||
if ammo <= 0:
|
||||
|
|
|
@ -2,3 +2,6 @@ extends Unit
|
|||
class_name Enemy
|
||||
|
||||
var sighted: bool = true
|
||||
|
||||
func _ready() -> void:
|
||||
go_to_destination(Vector3(17,1,15))
|
||||
|
|
|
@ -2,6 +2,7 @@ extends Node3D
|
|||
|
||||
@export var spawn_scene: PackedScene = null
|
||||
@export var spawn_time: float = 5.0
|
||||
@export var enabled: bool = true
|
||||
|
||||
var spawn_timer: float = 0.0
|
||||
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
extends RefCounted
|
||||
class_name Task
|
||||
|
||||
var name: String
|
||||
#var source: TaskManager
|
||||
var solicitation_time: float = 1.0
|
||||
var priority: int = TASK_PRIORITY_ANYTIME
|
||||
var min_worker_count: int = 1
|
||||
var max_worker_count: int = 1
|
||||
var potential_workers: Dictionary[Citizen, float] = {}
|
||||
var workers: Array[Citizen] = []
|
||||
|
||||
const FLOAT64_MAX: float = 2.0**1023 * (2 - 2.0**-52)
|
||||
const TASK_PRIORITY_ANYTIME = 0
|
||||
const TASK_PRIORITY_BY_WAVE_START = 10
|
||||
const TASK_PRIORITY_ASAP = 20
|
||||
|
||||
func is_ready_to_start() -> bool:
|
||||
return true
|
||||
func get_location() -> Vector3:
|
||||
return Vector3()
|
||||
func run() -> void:
|
||||
for worker in workers:
|
||||
worker.assign_task(self)
|
||||
func execute(worker: Citizen) -> bool:
|
||||
return false
|
||||
func interrupt(worker: Citizen) -> void:
|
||||
workers.erase(worker)
|
||||
if workers.size() < min_worker_count:
|
||||
cancel()
|
||||
func cancel() -> void:
|
||||
pass
|
||||
|
||||
static func sort_tasks(a: Task, b: Task) -> bool:
|
||||
return a.priority > b.priority
|
||||
|
||||
class BuildTask extends Task:
|
||||
var building: Building
|
||||
func _init() -> void:
|
||||
priority = 1
|
||||
func get_task_name() -> String:
|
||||
return "Build " + building.name
|
||||
func is_ready_to_start() -> bool:
|
||||
return super() and building.can_build()
|
||||
func get_location() -> Vector3:
|
||||
return building.global_position
|
||||
func execute(worker: Citizen) -> bool:
|
||||
if !await worker.go_to_destination(building.position): return false
|
||||
if !await worker.build_building(building): return false
|
||||
return true
|
||||
|
||||
class FetchItemTask extends Task:
|
||||
var building: Building
|
||||
var item: Item
|
||||
func get_task_name() -> String:
|
||||
return "Transfer " + item.name + " to " + building.name
|
||||
func execute(worker: Citizen) -> bool:
|
||||
# find the item from nearby buildings
|
||||
# TODO: also look for items on the floor?
|
||||
var storage_building: Building = null
|
||||
var closest: float = FLOAT64_MAX
|
||||
|
||||
for building2 in worker.get_tree().get_nodes_in_group("Buildings"):
|
||||
if building2 is not Building:
|
||||
continue
|
||||
if building2.consumer == null:
|
||||
continue
|
||||
if building2.consumer.check_storage_for_item(item):
|
||||
var distance_sqr: float = building2.global_position.distance_squared_to(worker.position)
|
||||
if distance_sqr < closest:
|
||||
storage_building = building2
|
||||
closest = distance_sqr
|
||||
|
||||
if storage_building == null: return false
|
||||
# go to the storage
|
||||
if !await worker.go_to_destination(storage_building.global_position): return false
|
||||
# pick up the item
|
||||
if !await worker.take_item_from_building(item, storage_building): return false
|
||||
# carry the item to the construction site
|
||||
if !await worker.go_to_destination(building.global_position): return false
|
||||
# store the item in the construction site
|
||||
if !await worker.put_item_in_building_materials(building): return false
|
||||
return true
|
|
@ -0,0 +1 @@
|
|||
uid://cc3hwuckhboi1
|
|
@ -19,12 +19,35 @@ 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 move_target: Vector3 = Vector3(16, 1, 13)
|
||||
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:
|
||||
set(val):
|
||||
held_item = val
|
||||
if held_item_meshinstance != null:
|
||||
if held_item != null:
|
||||
held_item_meshinstance.mesh = held_item.model
|
||||
held_item_meshinstance.visible = (held_item != null)
|
||||
|
||||
@onready var shapecast_3d: ShapeCast3D = $ShapeCast3D
|
||||
@onready var nav_agent_3d: NavigationAgent3D = $NavigationAgent3D
|
||||
@onready var label_3d: Label3D = $Label3D
|
||||
@onready var held_item_meshinstance: MeshInstance3D = null
|
||||
|
||||
var action_timeout: float = 0.0
|
||||
|
||||
enum TaskStatus {
|
||||
INTERRUPTED,
|
||||
TIMED_OUT,
|
||||
IMPOSSIBLE,
|
||||
DONE,
|
||||
}
|
||||
signal task_updated(task_status: TaskStatus)
|
||||
|
||||
func _ready() -> void:
|
||||
nav_agent_3d.connect("velocity_computed", avoidance_velocity_computed)
|
||||
|
@ -38,16 +61,25 @@ 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():
|
||||
#target_velocity = Vector3()
|
||||
nav_agent_3d.target_position = move_target + Vector3(randfn(0, move_radius), 0, randfn(0, move_radius))
|
||||
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 = nav_agent_3d.distance_to_target()
|
||||
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:
|
||||
|
@ -59,8 +91,7 @@ func _process(delta: float) -> void:
|
|||
stuck_timer = 0
|
||||
if global_position.y <= -10:
|
||||
unstuck()
|
||||
#DebugDraw3D.draw_sphere(nav_agent_3d.target_position, 0.5, Color.RED)
|
||||
var next_point: Vector3 = nav_agent_3d.get_next_path_position()
|
||||
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)
|
||||
|
@ -101,3 +132,29 @@ func _physics_process(delta: float) -> void:
|
|||
apply_torque(Vector3(0,force_direction.normalized().signed_angle_to(-global_basis.z, Vector3.DOWN) * rotation_torque,0))
|
||||
#DebugDraw3D.draw_line(global_position, global_position - global_basis.z, Color.BLUE)
|
||||
#DebugDraw3D.draw_line(global_position, global_position + force_direction, Color.RED)
|
||||
|
||||
func send_task_update(task_status: TaskStatus) -> void:
|
||||
task_updated.emit(task_status)
|
||||
|
||||
func wait_for_task_update() -> TaskStatus:
|
||||
var status_received: TaskStatus = await task_updated;
|
||||
return status_received
|
||||
|
||||
func go_to_destination(destination: Vector3) -> bool:
|
||||
move_target = destination
|
||||
moving = true
|
||||
return (await wait_for_task_update()) == TaskStatus.DONE
|
||||
|
||||
func take_item_from_building(item: Item, building: Building) -> bool:
|
||||
if held_item != null:
|
||||
return false
|
||||
if building.consumer == null:
|
||||
return false
|
||||
if await building.consumer.wait_for_item(item, 3.0):
|
||||
if !building.consumer.take_item_from_storage(item):
|
||||
return false
|
||||
held_item = item
|
||||
return true
|
||||
|
||||
func drop_item() -> bool:
|
||||
return false
|
||||
|
|
Loading…
Reference in New Issue