aboutsummaryrefslogtreecommitdiff
path: root/client/player
diff options
context:
space:
mode:
Diffstat (limited to 'client/player')
-rw-r--r--client/player/character.gd28
-rw-r--r--client/player/controllable_player.gd110
-rw-r--r--client/player/follow_camera.gd65
-rw-r--r--client/player/follow_camera.tscn7
-rw-r--r--client/player/interact_marker.gdshader27
-rw-r--r--client/player/marker.gd23
-rw-r--r--client/player/marker.tscn53
-rw-r--r--client/player/mirror.gd31
-rw-r--r--client/player/player.gd81
-rw-r--r--client/player/player.tscn15
10 files changed, 440 insertions, 0 deletions
diff --git a/client/player/character.gd b/client/player/character.gd
new file mode 100644
index 00000000..847bd7b6
--- /dev/null
+++ b/client/player/character.gd
@@ -0,0 +1,28 @@
+# Undercooked - a game about cooking
+# Copyright 2024 tpart
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License only.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+extends Node3D
+class_name Character
+
+@onready var hand_animations = $HandAnimations
+
+func _ready():
+ play_animation("idle")
+
+func play_animation(name_: String):
+ hand_animations.play(name_)
+
+func _on_hand_animations_animation_finished(name_):
+ hand_animations.play(name_)
diff --git a/client/player/controllable_player.gd b/client/player/controllable_player.gd
new file mode 100644
index 00000000..4c157349
--- /dev/null
+++ b/client/player/controllable_player.gd
@@ -0,0 +1,110 @@
+# Undercooked - a game about cooking
+# Copyright 2024 nokoe
+# Copyright 2024 metamuffin
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License only.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+class_name ControllablePlayer
+extends Player
+
+
+const PLAYER_SPEED: float = 25.;
+
+var facing = Vector2(1, 0)
+var velocity_ = Vector2(0, 0)
+
+var target: Vector2i = Vector2i(0, 0)
+
+func _ready():
+ var timer = Timer.new()
+ timer.one_shot = false
+ timer.wait_time = 1. / 25.
+ add_child(timer)
+ timer.start()
+ timer.connect("timeout", func():
+ Multiplayer.send_position(Vector2(position.x, position.z), rotation.y)
+ )
+
+func _process(delta):
+ var input = Vector2(Input.get_axis("left", "right"), Input.get_axis("forward", "backwards")).normalized()
+ input = input.rotated(-game.camera.angle_target)
+ position_anim = position_
+ rotation_anim = rotation_
+ if Input.is_action_pressed("interact") or Input.is_action_just_released("interact"):
+ input *= 0
+ else:
+ target = Vector2i(
+ int(floor(position.x + sin(rotation.y))),
+ int(floor(position.z + cos(rotation.y)))
+ )
+ interact()
+ update(delta, input)
+ super(delta)
+
+func update(dt: float, input: Vector2):
+ var direction = input.limit_length(1.);
+ rotation.y = atan2(self.facing.x, self.facing.y);
+ if direction.length() > 0.1:
+ self.facing = direction + (self.facing - direction) * exp(-dt * 10.);
+ self.velocity_ += direction * dt * PLAYER_SPEED;
+ self.position_ += self.velocity_ * dt;
+ self.velocity_ = self.velocity_ * exp(-dt * 5.);
+ collide(dt);
+
+func collide(dt: float):
+ for xo in range(-1,2):
+ for yo in range(-1,2):
+ var tile = Vector2i(xo, yo) + Vector2i(self.position_);
+ if !Multiplayer.get_tile_collision(tile): continue
+ tile = Vector2(tile)
+ var d = aabb_point_distance(tile, tile + Vector2.ONE, self.position_);
+ if d > PLAYER_SIZE: continue
+ var h = 0.01;
+ var d_sample_x = aabb_point_distance(tile, tile + Vector2.ONE, self.position_ + Vector2(h, 0));
+ var d_sample_y = aabb_point_distance(tile, tile + Vector2.ONE, self.position_ + Vector2(0, h));
+ var grad = (Vector2(d_sample_x - d, d_sample_y - d)) / h;
+
+ self.position_ += (PLAYER_SIZE - d) * grad;
+ self.velocity_ -= grad * grad.dot(self.velocity_)
+
+ for player: Player in game.players.values():
+ var diff = self.position_ - player.position_
+ var d = diff.length()
+ if d < 0.01: continue
+ if d >= PLAYER_SIZE * 2: continue
+ var norm = diff.normalized();
+ var f = 100 / (1 + d)
+ self.velocity_.x += norm.x * f * dt
+ self.velocity_.y += norm.y * f * dt
+
+func aabb_point_distance(mi: Vector2, ma: Vector2, p: Vector2) -> float:
+ return (p - p.clamp(mi, ma)).length()
+
+func update_position(new_position: Vector2, _new_rotation: float):
+ if (new_position - position_).length() > 3.:
+ position_ = new_position
+
+func interact():
+ var tile_idx = str(target)
+ var t: Floor = game.map.tile_by_pos.get(tile_idx)
+ if t != null:
+ game.marker.set_interactive(Multiplayer.get_tile_interactive(target))
+ game.marker.visible = true
+ game.marker_target = t.item_base.global_position
+ if Input.is_action_just_pressed("interact"):
+ Multiplayer.send_interact(target, true)
+ t.interact()
+ elif Input.is_action_just_released("interact"):
+ Multiplayer.send_interact(target, false)
+ else:
+ game.marker.visible = false
diff --git a/client/player/follow_camera.gd b/client/player/follow_camera.gd
new file mode 100644
index 00000000..74355688
--- /dev/null
+++ b/client/player/follow_camera.gd
@@ -0,0 +1,65 @@
+# Undercooked - a game about cooking
+# Copyright 2024 nokoe
+# Copyright 2024 metamuffin
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License only.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+class_name FollowCamera
+extends Camera3D
+
+const ROTATE_SPEED: float = 1.5
+const ROTATE_WEIGHT: float = 8.0
+const ROTATE_UP_SPEED: float = 0.7
+const ROTATE_UP_WEIGHT: float = 4.0
+const ANGLE_UP_MIN: float = 0.5
+const ANGLE_UP_MAX: float = 1.2
+const LOOK_WEIGHT: float = 8.0
+const MOVE_WEIGHT: float = 2.0
+const CAMERA_DISTANCE: float = 10
+
+@export var target: Node3D
+
+var angle_target: float = 0
+var angle: float = 0
+var angle_up_target: float = 1
+var angle_up: float = 1
+var ground: Vector3
+
+func _ready():
+ if target == null:
+ push_warning("target is not set")
+ else:
+ ground = target.position
+
+func _process(delta):
+ if target != null:
+ follow(delta)
+
+func follow(delta):
+ var new_transform = transform.looking_at(target.position)
+
+ transform.basis = Basis.from_euler(Vector3(
+ lerp_angle(transform.basis.get_euler().x, new_transform.basis.get_euler().x, delta * LOOK_WEIGHT),
+ lerp_angle(transform.basis.get_euler().y, new_transform.basis.get_euler().y, delta * LOOK_WEIGHT),
+ lerp_angle(transform.basis.get_euler().z, new_transform.basis.get_euler().z, delta * LOOK_WEIGHT)
+ ))
+
+ angle_target += Input.get_axis("rotate_left", "rotate_right") * ROTATE_SPEED * delta
+ angle = lerp_angle(angle, angle_target, delta * ROTATE_WEIGHT)
+
+ angle_up_target += Input.get_axis("rotate_down", "rotate_up") * ROTATE_UP_SPEED * delta
+ angle_up_target = clamp(angle_up_target, ANGLE_UP_MIN, ANGLE_UP_MAX)
+ angle_up = lerp_angle(angle_up, angle_up_target, delta * ROTATE_UP_WEIGHT)
+
+ ground = ground.lerp(target.position, delta * MOVE_WEIGHT)
+ position = ground + Vector3(0, sin(angle_up) * CAMERA_DISTANCE, cos(angle_up) * CAMERA_DISTANCE).rotated(Vector3.UP, angle)
diff --git a/client/player/follow_camera.tscn b/client/player/follow_camera.tscn
new file mode 100644
index 00000000..816563ba
--- /dev/null
+++ b/client/player/follow_camera.tscn
@@ -0,0 +1,7 @@
+[gd_scene load_steps=2 format=3 uid="uid://b31mlnao6ybt8"]
+
+[ext_resource type="Script" path="res://scripts/follow_camera.gd" id="1_qipju"]
+
+[node name="FollowCamera" type="Camera3D"]
+fov = 45.0
+script = ExtResource("1_qipju")
diff --git a/client/player/interact_marker.gdshader b/client/player/interact_marker.gdshader
new file mode 100644
index 00000000..94b7323d
--- /dev/null
+++ b/client/player/interact_marker.gdshader
@@ -0,0 +1,27 @@
+shader_type spatial;
+
+uniform float max_width = .1;
+uniform float marker_length = .5;
+uniform float pulse_speed = 4.;
+uniform bool interactive = false;
+
+void fragment() {
+ if (interactive) {
+ ALBEDO = vec3(15., 0., 0.);
+ } else {
+ ALBEDO = vec3(.1, .1, .1);
+ }
+ vec2 uv = abs(2. * UV.xy - 1.);
+ float m_length = marker_length / max_width;
+ float anim;
+ if (interactive) {
+ anim = sin(TIME * pulse_speed) * .5 + 1.;
+ } else {
+ anim = .5;
+ }
+ float alpha = step(
+ 1. - max_width * anim, max(uv.x, uv.y))
+ * step(1. - max_width * m_length, min(uv.x, uv.y)
+ );
+ ALPHA = alpha;
+}
diff --git a/client/player/marker.gd b/client/player/marker.gd
new file mode 100644
index 00000000..6e3e6ffa
--- /dev/null
+++ b/client/player/marker.gd
@@ -0,0 +1,23 @@
+# Undercooked - a game about cooking
+# Copyright 2024 nokoe
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License only.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+class_name Marker
+extends Node3D
+
+@onready var _cube: MeshInstance3D = $Cube
+
+func set_interactive(val: bool):
+ var mat: ShaderMaterial = _cube.get_active_material(0)
+ mat.set_shader_parameter("interactive", val)
diff --git a/client/player/marker.tscn b/client/player/marker.tscn
new file mode 100644
index 00000000..7f21e199
--- /dev/null
+++ b/client/player/marker.tscn
@@ -0,0 +1,53 @@
+[gd_scene load_steps=7 format=3 uid="uid://c0euiv7duqfp4"]
+
+[ext_resource type="Script" path="res://scripts/marker.gd" id="1_3njdu"]
+[ext_resource type="Shader" path="res://textures/interact_marker.gdshader" id="2_dejwy"]
+
+[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_o4v68"]
+
+[sub_resource type="ArrayMesh" id="ArrayMesh_2tdb0"]
+_surfaces = [{
+"aabb": AABB(-1, -1, -1, 2, 2, 2.00001),
+"format": 34896613377,
+"index_count": 36,
+"index_data": PackedByteArray(0, 0, 3, 0, 1, 0, 0, 0, 2, 0, 3, 0, 2, 0, 7, 0, 3, 0, 2, 0, 6, 0, 7, 0, 6, 0, 5, 0, 7, 0, 6, 0, 4, 0, 5, 0, 4, 0, 1, 0, 5, 0, 4, 0, 0, 0, 1, 0, 2, 0, 4, 0, 6, 0, 2, 0, 0, 0, 4, 0, 7, 0, 1, 0, 3, 0, 7, 0, 5, 0, 1, 0),
+"primitive": 3,
+"uv_scale": Vector4(0, 0, 0, 0),
+"vertex_count": 8,
+"vertex_data": PackedByteArray(0, 0, 0, 0, 254, 255, 0, 0, 0, 0, 255, 255, 254, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 254, 255, 0, 0, 255, 255, 255, 255, 254, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0)
+}]
+blend_shape_mode = 0
+
+[sub_resource type="ArrayMesh" id="ArrayMesh_2ie13"]
+resource_name = "marker_Cube_001"
+_surfaces = [{
+"aabb": AABB(-1, -1, -1, 2, 2, 2.00001),
+"attribute_data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255),
+"format": 34896613399,
+"index_count": 36,
+"index_data": PackedByteArray(2, 0, 11, 0, 5, 0, 2, 0, 8, 0, 11, 0, 6, 0, 21, 0, 9, 0, 6, 0, 18, 0, 21, 0, 20, 0, 17, 0, 23, 0, 20, 0, 14, 0, 17, 0, 12, 0, 3, 0, 15, 0, 12, 0, 0, 0, 3, 0, 7, 0, 13, 0, 19, 0, 7, 0, 1, 0, 13, 0, 22, 0, 4, 0, 10, 0, 22, 0, 16, 0, 4, 0),
+"material": SubResource("StandardMaterial3D_o4v68"),
+"primitive": 3,
+"uv_scale": Vector4(0, 0, 0, 0),
+"vertex_count": 24,
+"vertex_data": PackedByteArray(0, 0, 0, 0, 254, 255, 255, 191, 0, 0, 0, 0, 254, 255, 255, 191, 0, 0, 0, 0, 254, 255, 84, 213, 0, 0, 255, 255, 254, 255, 255, 191, 0, 0, 255, 255, 254, 255, 255, 255, 0, 0, 255, 255, 254, 255, 84, 213, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 191, 0, 0, 0, 0, 0, 0, 84, 213, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 84, 213, 255, 255, 0, 0, 254, 255, 255, 191, 255, 255, 0, 0, 254, 255, 255, 191, 255, 255, 0, 0, 254, 255, 84, 213, 255, 255, 255, 255, 254, 255, 255, 191, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 254, 255, 84, 213, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 191, 255, 255, 0, 0, 0, 0, 84, 213, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 84, 213, 255, 255, 255, 255, 0, 0, 255, 127, 84, 213, 84, 213, 255, 255, 255, 255, 255, 127, 255, 191, 84, 213, 84, 213, 255, 191, 255, 191, 0, 0, 255, 127, 84, 213, 84, 213, 255, 191, 255, 191, 255, 127, 255, 191, 84, 213, 84, 213, 255, 255, 255, 255, 0, 0, 255, 127, 170, 42, 170, 42, 255, 255, 255, 255, 255, 127, 255, 191, 170, 42, 170, 42, 255, 191, 255, 191, 0, 0, 255, 127, 170, 42, 170, 42, 255, 191, 255, 191, 255, 127, 255, 191, 170, 42, 170, 42)
+}]
+blend_shape_mode = 0
+shadow_mesh = SubResource("ArrayMesh_2tdb0")
+
+[sub_resource type="ShaderMaterial" id="ShaderMaterial_wuj1v"]
+render_priority = 0
+shader = ExtResource("2_dejwy")
+shader_parameter/max_width = 0.1
+shader_parameter/marker_length = 0.5
+shader_parameter/pulse_speed = 4.0
+shader_parameter/interactive = false
+
+[node name="Marker" type="Node3D"]
+script = ExtResource("1_3njdu")
+
+[node name="Cube" type="MeshInstance3D" parent="."]
+transform = Transform3D(0.25, 0, 0, 0, 0.25, 0, 0, 0, 0.25, 0, 0.25, 0)
+mesh = SubResource("ArrayMesh_2ie13")
+skeleton = NodePath("")
+surface_material_override/0 = SubResource("ShaderMaterial_wuj1v")
diff --git a/client/player/mirror.gd b/client/player/mirror.gd
new file mode 100644
index 00000000..ccec0e65
--- /dev/null
+++ b/client/player/mirror.gd
@@ -0,0 +1,31 @@
+# Undercooked - a game about cooking
+# Copyright 2024 tpart
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License only.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+@tool
+
+# Hand mirror helper script, useful for animating characters
+
+extends Node3D
+
+@export var enabled := true
+
+@onready var hand_right = $Main/HandRight
+@onready var hand_left = $Main/HandLeft
+
+
+func _process(delta):
+ if enabled:
+ hand_right.position = hand_left.position * Vector3(-1,1,1)
+ hand_right.rotation = hand_left.rotation * Vector3(1, -1, -1) - Vector3(0.2 * PI, 0, 0)
diff --git a/client/player/player.gd b/client/player/player.gd
new file mode 100644
index 00000000..423855e9
--- /dev/null
+++ b/client/player/player.gd
@@ -0,0 +1,81 @@
+# Undercooked - a game about cooking
+# Copyright 2024 nokoe
+# Copyright 2024 metamuffin
+# Copyright 2024 tpart
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License only.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+class_name Player
+extends Node3D
+
+const PLAYER_SIZE: float = 0.4
+const SPEED: float = 25.
+
+var game: Game
+var position_ = Vector2(0, 0)
+
+var mesh = preload("res://scenes/player.tscn").instantiate()
+
+var hand: Node3D = null
+
+var _anim_angle: float = 0.0
+
+const HAND_BASE_POSITION: Vector3 = Vector3(0, .25, .4)
+
+@onready var character: Character = $Player/Character
+
+func _init(_id: int, new_name: String, pos: Vector2, _character: int, new_game: Game):
+ add_child(mesh)
+ position_ = pos
+ name = new_name
+ game = new_game
+
+func update_position(new_position: Vector2, new_rotation: float):
+ position_ = new_position
+ rotation_ = new_rotation
+
+func set_item(i: Item):
+ i.owned_by = hand_base
+ if i == null:
+ push_error("tile is null")
+ hand = i
+ character.play_animation("hold")
+
+func remove_item() -> Item:
+ var i = hand
+ if i == null:
+ push_error("holding nothing")
+ hand = null
+ character.play_animation("idle")
+ return i
+
+func take_item(tile: Floor):
+ if hand != null:
+ push_error("already holding an item")
+ var i = tile.take_item()
+ if i == null:
+ push_error("tile is null")
+ hand = i
+
+func put_item(tile: Floor):
+ var i = remove_item()
+ tile.put_item(i)
+
+func _process(delta):
+ _anim_angle = fmod(_anim_angle + delta, TAU)
+ hand_base.position.y = HAND_BASE_POSITION.y + 0.05 * sin(_anim_angle * 3)
+ position_anim = lerp(position_anim, position_, delta * 10)
+ rotation_anim = lerp_angle(rotation_anim, rotation_, delta * 10)
+ position.x = position_anim.x
+ position.z = position_anim.y
+ rotation.y = rotation_anim
diff --git a/client/player/player.tscn b/client/player/player.tscn
new file mode 100644
index 00000000..9c633575
--- /dev/null
+++ b/client/player/player.tscn
@@ -0,0 +1,15 @@
+[gd_scene load_steps=3 format=3 uid="uid://d30bj2cp1m7gd"]
+
+[ext_resource type="PackedScene" uid="uid://b3hhir2fvnunu" path="res://models/prefabs/characters/guy.tscn" id="1_g5ma4"]
+
+[sub_resource type="CapsuleMesh" id="CapsuleMesh_4w71x"]
+
+[node name="Player" type="Node3D"]
+
+[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
+transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0.5, 0)
+visible = false
+mesh = SubResource("CapsuleMesh_4w71x")
+
+[node name="Character" parent="." instance=ExtResource("1_g5ma4")]
+transform = Transform3D(0.33, 0, 0, 0, 0.33, 0, 0, 0, 0.33, 0, 0, 0)