summaryrefslogtreecommitdiffstats
path: root/ps_hanging.gd
blob: aa5e0edf9223625e368615f4d3f7829f4e36e236 (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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
class_name PlayerStateHanging
extends PlayerState

var target: HookHanging
var t: float
var L: float
var theta0: float
var last_theta: float
var w: float
var tongue: Line2D

var travel_from: Vector2
var travel_to: Vector2

static var T_mlem: float = 0.2
static var T_travel: float = 0.1

static var swing_force: float = 0.2
static var hook_distance_max: float = 80

enum S { MLEMING, TRAVELING, HANGING }
var state: S = S.MLEMING

func _init(pl: Player, h_target: HookHanging) -> void:
	super(pl)

	t = 0
	L = h_target.hang_distance
	target = h_target
	w = sqrt(p.gravity / L) * 0.7
	
	var dx = target.global_position.x - pl.global_position.x
	pl.anim_sp.flip_h = dx < 0

	state = S.MLEMING

func _to_string() -> String:
	return "PlayerStateHanging %s, θ₀ = %f, θ = %f" % [S.keys()[state], theta0, last_theta]

static func can_hang_from(pl: Player, c_target: HookHanging) -> bool:
	var d = c_target.global_position - pl.global_position
	var angle = atan2(d.y, d.x)
	var revs = floor(angle / (2 * PI))
	angle -= revs * 2 * PI
	
	var is_close = d.length() < hook_distance_max
	var is_ok_angle = angle > deg_to_rad(135) or angle < deg_to_rad(45)
	return is_close and is_ok_angle

func _handle(_delta: float):
	if Input.is_action_just_pressed("move_left"):
		p.anim_sp.flip_h = true
	if Input.is_action_just_pressed("move_right"):
		p.anim_sp.flip_h = false

func _handle_physics(delta: float):
	match state:
		S.MLEMING:	 _mlem(delta)
		S.TRAVELING: _travel(delta)
		S.HANGING:   _hang(delta)
		_: assert(false, "unknown state %s" % str(state))
		
func _mlem(delta: float):
	t += delta
	var d = target.global_position - p.global_position
	
	# Add tongue line
	if not tongue:
		tongue = Line2D.new()
		tongue.width = 2
		tongue.add_point(-d)
		tongue.add_point(-d)
		target.add_child(tongue)
	
	# Update one end to be at player
	tongue.points[0] = -d
	tongue.points[1] = (t / T_mlem - 1) * d

	if t >= T_mlem:
		tongue.points[1] = Vector2.ZERO
		state = S.TRAVELING
		travel_from = p.global_position
		travel_to = target.global_position - d.normalized() * L
		t = 0

func _travel(delta: float):
	t += delta
	p.global_position = travel_from + (t / T_travel) * (travel_to - travel_from)
	
	var d = target.global_position - p.global_position
	tongue.points[0] = -d

	# If we're traveling in the right direction, then travel
	if t >= T_travel:
		p.global_position = travel_to
		state = S.HANGING
		theta0 = atan2(-d.y, -d.x) - PI / 2
		if theta0 > PI: theta0 -= 2 * PI
		if theta0 < -PI: theta0 += 2 * PI
		last_theta = theta0
		t = 0

func _hang(delta: float):
	var d = target.global_position - p.global_position
	tongue.points[0] = -d
	
	t += delta

	if Input.is_action_just_pressed("jump"):
		p.velocity.y = -p.jump_strength
		tongue.queue_free()
		p.request_state_normal()

	var theta = theta0 * cos(t * w)

	# Allow player to add momentum by moving toward the swing direction
	var dtheta = theta - last_theta
	var momentum = Input.get_axis("move_right", "move_left")
	var momentum_dir = sign(momentum) * sign(dtheta)
	theta0 += momentum_dir * sign(theta0) * swing_force * delta

	# Limit max swinging height to horizontal by
	# lerping theta0 to 90deg at the bottom of the swing
	if abs(theta0) > PI / 2 and abs(theta) < PI / 8:
		theta0 = lerp(theta0, sign(theta0) * PI / 2, 0.8)
	
	#if sign(theta) != sign(last_theta) and abs(theta0) > PI / 2:
	#	theta0 = sign(theta0) * PI / 2
	
	p.global_position.x = target.global_position.x + L * cos(theta + PI / 2)
	p.global_position.y = target.global_position.y + L * sin(theta + PI / 2)
	
	last_theta = theta