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
|