diff --git a/scenes/maps/testmap.tscn b/scenes/maps/testmap.tscn index 4401a62..8daa951 100644 --- a/scenes/maps/testmap.tscn +++ b/scenes/maps/testmap.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=9 format=3 uid="uid://cbyee7drds7qu"] [ext_resource type="Script" path="res://scripts/maps/map.gd" id="1_4npcs"] -[ext_resource type="Script" path="res://scripts/maps/objectives/objective.gd" id="2_yv1d0"] +[ext_resource type="Script" path="res://scripts/maps/objectives/hungry.gd" id="2_qrp84"] [ext_resource type="PackedScene" uid="uid://bsghm187n6ykx" path="res://scenes/objects/closet.tscn" id="2_yvpvm"] [ext_resource type="PackedScene" uid="uid://cvnjpnvchvakj" path="res://scenes/entities/npc.tscn" id="3_x3gyc"] @@ -34,15 +34,20 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3, 15) [node name="0" type="Node3D" parent="NPCRestPlaces"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6, 2, -28) -script = ExtResource("2_yv1d0") +script = ExtResource("2_qrp84") [node name="1" type="Node3D" parent="NPCRestPlaces"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 2, -32) -script = ExtResource("2_yv1d0") +script = ExtResource("2_qrp84") [node name="2" type="Node3D" parent="NPCRestPlaces"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, 2, -48) -script = ExtResource("2_yv1d0") +script = ExtResource("2_qrp84") + +[node name="NPCCarryables" type="Node3D" parent="."] + +[node name="Closet" parent="NPCCarryables" instance=ExtResource("2_yvpvm")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 29, 10, -30) [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] transform = Transform3D(-0.899519, 0.375, 0.224144, -0.224144, -0.836516, 0.5, 0.375, 0.399519, 0.836516, 0, 0, 0) @@ -50,9 +55,6 @@ transform = Transform3D(-0.899519, 0.375, 0.224144, -0.224144, -0.836516, 0.5, 0 [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_pq0iv") -[node name="Closet" parent="." instance=ExtResource("2_yvpvm")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 10, 2) - [node name="Npc1" parent="." instance=ExtResource("3_x3gyc")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 25, 6, -33) diff --git a/scenes/objects/closet.tscn b/scenes/objects/closet.tscn index 3794920..8d31308 100644 --- a/scenes/objects/closet.tscn +++ b/scenes/objects/closet.tscn @@ -1,15 +1,26 @@ -[gd_scene load_steps=3 format=3 uid="uid://bsghm187n6ykx"] +[gd_scene load_steps=5 format=3 uid="uid://bsghm187n6ykx"] + +[ext_resource type="Script" path="res://scripts/maps/carryable/carryable.gd" id="1_ma8bn"] [sub_resource type="BoxMesh" id="BoxMesh_qilbd"] size = Vector3(1, 2, 1) -[sub_resource type="BoxShape3D" id="BoxShape3D_by26a"] +[sub_resource type="BoxShape3D" id="BoxShape3D_2lhrk"] size = Vector3(1, 2, 1) +[sub_resource type="BoxShape3D" id="BoxShape3D_by26a"] +size = Vector3(1.5, 2, 1.5) + [node name="Closet" type="RigidBody3D"] +script = ExtResource("1_ma8bn") [node name="MeshInstance3D" type="MeshInstance3D" parent="."] mesh = SubResource("BoxMesh_qilbd") [node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("BoxShape3D_2lhrk") + +[node name="Area3D" type="Area3D" parent="."] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Area3D"] shape = SubResource("BoxShape3D_by26a") diff --git a/scripts/entities/npc.gd b/scripts/entities/npc.gd index 82f259b..6294177 100644 --- a/scripts/entities/npc.gd +++ b/scripts/entities/npc.gd @@ -1,16 +1,19 @@ ## SPDX-License-Identifier: GPL-3.0-or-later ## Copyright (c) 2024 interstellardevelopment.org +class_name NPC extends CharacterBody3D var movement_speed: float = 3.0 -var angry_meter: int = 5 +var angry_meter: int = 0 var target: Node3D +var carrying: bool = false # Legend: # 0 - get an objective # 1 - wait -# 2 - chase a player +# 2 - get carryable object +# 3 - chase a player var phase: int = 0 @onready var timer: Timer = $Timer @@ -23,7 +26,7 @@ func _ready() -> void: if Networking.isManager: if timer.timeout.connect(_timer_done): pass - find_rest() + find_objective() func _timer_done() -> void: if angry_meter > 0: @@ -32,12 +35,12 @@ func _timer_done() -> void: ($Label3D as Label3D).text = "%s %ds left" % [(target as Objective).displayed, angry_meter] else: ($Label3D as Label3D).text = "" + (target as Objective).occupied = false timer.stop() phase = 2 - find_player(99999999999999) # determines the closest resting place and navigates to it. -func find_rest() -> void: +func find_objective() -> void: var lowest_distance: float = 99999999999999 for child: Node in get_tree().root.get_node("/root/"+Game.mapname+"/NPCRestPlaces").get_children(): if child is Objective and position.distance_to((child as Objective).position) < lowest_distance and !(child as Objective).occupied: @@ -45,6 +48,15 @@ func find_rest() -> void: navigation_agent.set_target_position(target.position) +# determines the closest resting place and navigates to it. +func find_carryable() -> void: + var lowest_distance: float = 99999999999999 + for child: Node in get_tree().root.get_node("/root/"+Game.mapname+"/NPCCarryables").get_children(): + if child is Carryable and position.distance_to((child as Carryable).position) < lowest_distance and !(child as Carryable).carried: + target = child + + navigation_agent.set_target_position(target.position) + # determines the closest player and navigates to them. func find_player(lowest_distance: float) -> void: for child: Node in get_tree().root.get_node("/root/"+Game.mapname+"/").get_children(): @@ -53,19 +65,43 @@ func find_player(lowest_distance: float) -> void: navigation_agent.set_target_position(target.position) +func pick_up(node: Carryable) -> void: + carrying = true + node.get_parent().remove_child(node) + node.carry() + node.position = Vector3(0, 1, -1) + add_child(node) + find_player(99999999999) + phase = 3 + +func set_down(node: Carryable) -> void: + carrying = false + node.get_parent().remove_child(node) + node.uncarry() + node.position = position + Vector3(0,0,1) + get_tree().root.get_node("/root/"+Game.mapname+"/NPCCarryables").add_child(node) + find_player(99999999999) + phase = 0 + func _physics_process(_delta: float) -> void: if Networking.isManager and phase != 1: - if phase == 2: + if phase == 3: find_player(position.distance_to(target.position)) + elif phase == 2 and target is Objective: + find_carryable() elif phase == 0 and target is Player: - find_rest() + find_objective() if navigation_agent.is_navigation_finished(): if phase == 0: if target is Objective: - (target as Objective).occupied = false - phase = 1 - timer.start() + if !(target as Objective).occupied: + find_objective() + (target as Objective).occupied = true + phase = 1 + angry_meter = (target as Objective).time + 1 + _timer_done() + timer.start() return var current_agent_position: Vector3 = global_position @@ -73,6 +109,8 @@ func _physics_process(_delta: float) -> void: velocity = current_agent_position.direction_to(next_path_position) * movement_speed + look_at(Vector3(target.position.x, position.y, target.position.z)) + if move_and_slide(): pass diff --git a/scripts/entities/player.gd b/scripts/entities/player.gd index 41ccb57..aa19e53 100644 --- a/scripts/entities/player.gd +++ b/scripts/entities/player.gd @@ -76,6 +76,5 @@ func _input(event: InputEvent) -> void: camera.rotation.x = -1.6 rotate(Vector3.DOWN, camera_rotation.x) -# this way we can check for the player. -func playerstub() -> void: +func incapacitate() -> void: pass diff --git a/scripts/maps/carryable/carryable.gd b/scripts/maps/carryable/carryable.gd new file mode 100644 index 0000000..d8aaf2c --- /dev/null +++ b/scripts/maps/carryable/carryable.gd @@ -0,0 +1,29 @@ +class_name Carryable +extends RigidBody3D + +var carried: bool = false +var carried_by: NPC + +func _ready() -> void: + if ($Area3D as Area3D).body_entered.connect(_on_body_entered): + pass + +func carry() -> void: + ($CollisionShape3D as CollisionShape3D).disabled = true + freeze = true + carried = true + +func uncarry() -> void: + ($CollisionShape3D as CollisionShape3D).disabled = false + freeze = false + carried = false + +func _on_body_entered(body: Node) -> void: + if Networking.isManager: + if body is NPC and (body as NPC).phase == 2: + carried_by = body + Networking.pick_up_sync_call(carried_by, self) + + if body is Player and carried: + Networking.set_down_sync_call(carried_by, self) + (body as Player).incapacitate() diff --git a/scripts/maps/objectives/hungry.gd b/scripts/maps/objectives/hungry.gd index 66f79c4..bd4ce39 100644 --- a/scripts/maps/objectives/hungry.gd +++ b/scripts/maps/objectives/hungry.gd @@ -3,3 +3,4 @@ extends Objective func _ready() -> void: displayed = "Hungry" + time = 5 diff --git a/scripts/maps/objectives/objective.gd b/scripts/maps/objectives/objective.gd index ffc30ca..4a10a05 100644 --- a/scripts/maps/objectives/objective.gd +++ b/scripts/maps/objectives/objective.gd @@ -5,4 +5,5 @@ class_name Objective extends Node3D var occupied: bool = false +var time: int var displayed: String diff --git a/scripts/utils/networking.gd b/scripts/utils/networking.gd index 15d31c0..0220965 100644 --- a/scripts/utils/networking.gd +++ b/scripts/utils/networking.gd @@ -271,7 +271,7 @@ func player_sync_call(position: Vector3, rotation: Vector3) -> void: Log.warning("Couldn't send RPC to %d!" % peer) # Synchronizes the player state. -@rpc("any_peer", "call_local", "reliable") +@rpc("any_peer", "call_remote", "reliable") func player_sync(position: Vector3, rotation: Vector3) -> void: if verify_id(multiplayer.get_remote_sender_id()): for peer: String in peers: @@ -287,9 +287,35 @@ func npc_sync_call(position: Vector3, rotation: Vector3, node_name: String) -> v Log.warning("Couldn't send RPC to %d!" % peer) # Synchronizes the npc state. -@rpc("any_peer", "call_local", "reliable") +@rpc("any_peer", "call_remote", "reliable") func npc_sync(position: Vector3, rotation: Vector3, node_name: String) -> void: if managerID == multiplayer.get_remote_sender_id(): var npc: CharacterBody3D = get_node("/root/"+Game.mapname+"/"+node_name) npc.position = position npc.rotation = rotation + +func pick_up_sync_call(npc: NPC, carryable: Carryable) -> void: + var npc_path: NodePath = npc.get_path() + var carryable_path: NodePath = carryable.get_path() + for peer: int in Networking.get_ids(): + if rpc_id(peer, "pick_up_sync", npc_path, carryable_path) != OK: + Log.warning("Couldn't send RPC to %d!" % peer) + +# Synchronizes the object that is picked up by the npc. +@rpc("any_peer", "call_local", "reliable") +func pick_up_sync(npc: NodePath, carryable: NodePath) -> void: + if managerID == multiplayer.get_remote_sender_id(): + (get_node(npc) as NPC).pick_up(get_node(carryable) as Carryable) + +func set_down_sync_call(npc: NPC, carryable: Carryable) -> void: + var npc_path: NodePath = npc.get_path() + var carryable_path: NodePath = carryable.get_path() + for peer: int in Networking.get_ids(): + if rpc_id(peer, "set_down_sync", npc_path, carryable_path) != OK: + Log.warning("Couldn't send RPC to %d!" % peer) + +# Synchronizes the object that is picked up by the npc. +@rpc("any_peer", "call_local", "reliable") +func set_down_sync(npc: NodePath, carryable: NodePath) -> void: + if managerID == multiplayer.get_remote_sender_id(): + (get_node(npc) as NPC).set_down(get_node(carryable) as Carryable)