aboutsummaryrefslogtreecommitdiff
path: root/client/player/controllable_player.gd
blob: 3012f5571a820d71f014ba07aa57d977ec8568d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# 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 = 65.;

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():
		game.mp.send_position(position_, rotation_)
	)

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)
	character.walking = input.length_squared() > 0.1

func update(dt: float, input: Vector2):
	var direction = input.limit_length(1.);
	rotation_ = 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 * 15.);
	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 !game.mp.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: Tile = game.map.tile_by_pos.get(tile_idx)
	if t != null:
		game.marker.set_interactive(game.mp.get_tile_interactive(target))
		game.marker.visible = true
		game.marker_target = t.item_base.global_position
		if Input.is_action_just_pressed("interact"):
			game.mp.send_interact(target, true)
			t.interact()
		elif Input.is_action_just_released("interact"):
			game.mp.send_interact(target, false)
	else:
		game.marker.visible = false