diff options
Diffstat (limited to 'client/player')
-rw-r--r-- | client/player/character.gd | 28 | ||||
-rw-r--r-- | client/player/controllable_player.gd | 110 | ||||
-rw-r--r-- | client/player/follow_camera.gd | 65 | ||||
-rw-r--r-- | client/player/follow_camera.tscn | 7 | ||||
-rw-r--r-- | client/player/interact_marker.gdshader | 27 | ||||
-rw-r--r-- | client/player/marker.gd | 23 | ||||
-rw-r--r-- | client/player/marker.tscn | 53 | ||||
-rw-r--r-- | client/player/mirror.gd | 31 | ||||
-rw-r--r-- | client/player/player.gd | 81 | ||||
-rw-r--r-- | client/player/player.tscn | 15 |
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) |