1extends KinematicBody
2
3const ANIM_FLOOR = 0
4const ANIM_AIR = 1
5
6const SHOOT_TIME = 1.5
7const SHOOT_SCALE = 2
8const CHAR_SCALE = Vector3(0.3, 0.3, 0.3)
9const TURN_SPEED = 40
10
11var movement_dir = Vector3()
12var linear_velocity = Vector3()
13
14var jumping = false
15
16var air_idle_deaccel = false
17var accel = 19.0
18var deaccel = 14.0
19var sharp_turn_threshold = 140
20var max_speed = 3.1
21
22var prev_shoot = false
23var shoot_blend = 0
24
25onready var gravity = ProjectSettings.get_setting("physics/3d/default_gravity") * ProjectSettings.get_setting("physics/3d/default_gravity_vector")
26
27func _ready():
28	get_node("AnimationTree").set_active(true)
29
30
31func _physics_process(delta):
32	linear_velocity += gravity * delta
33
34	var anim = ANIM_FLOOR
35
36	var vv = linear_velocity.y # Vertical velocity.
37	var hv = Vector3(linear_velocity.x, 0, linear_velocity.z) # Horizontal velocity.
38
39	var hdir = hv.normalized() # Horizontal direction.
40	var hspeed = hv.length() # Horizontal speed.
41
42	# Player input.
43	var cam_basis = get_node("Target/Camera").get_global_transform().basis
44	var dir = Vector3() # Where does the player intend to walk to.
45	dir = (Input.get_action_strength("move_right") - Input.get_action_strength("move_left")) * cam_basis[0]
46	dir += (Input.get_action_strength("move_backwards") - Input.get_action_strength("move_forward")) * cam_basis[2]
47	dir.y = 0
48	dir = dir.normalized()
49
50	var jump_attempt = Input.is_action_pressed("jump")
51	var shoot_attempt = Input.is_action_pressed("shoot")
52
53	if is_on_floor():
54		var sharp_turn = hspeed > 0.1 and rad2deg(acos(dir.dot(hdir))) > sharp_turn_threshold
55
56		if dir.length() > 0.1 and !sharp_turn:
57			if hspeed > 0.001:
58				hdir = adjust_facing(hdir, dir, delta, 1.0 / hspeed * TURN_SPEED, Vector3.UP)
59			else:
60				hdir = dir
61
62			if hspeed < max_speed:
63				hspeed += accel * delta
64		else:
65			hspeed -= deaccel * delta
66			if hspeed < 0:
67				hspeed = 0
68
69		hv = hdir * hspeed
70
71		var mesh_xform = get_node("Armature").get_transform()
72		var facing_mesh = -mesh_xform.basis[0].normalized()
73		facing_mesh = (facing_mesh - Vector3.UP * facing_mesh.dot(Vector3.UP)).normalized()
74
75		if hspeed > 0:
76			facing_mesh = adjust_facing(facing_mesh, dir, delta, 1.0 / hspeed * TURN_SPEED, Vector3.UP)
77		var m3 = Basis(-facing_mesh, Vector3.UP, -facing_mesh.cross(Vector3.UP).normalized()).scaled(CHAR_SCALE)
78
79		get_node("Armature").set_transform(Transform(m3, mesh_xform.origin))
80
81		if not jumping and jump_attempt:
82			vv = 7.0
83			jumping = true
84			get_node("SoundJump").play()
85	else:
86		anim = ANIM_AIR
87
88		if dir.length() > 0.1:
89			hv += dir * (accel * 0.2 * delta)
90			if hv.length() > max_speed:
91				hv = hv.normalized() * max_speed
92		else:
93			if air_idle_deaccel:
94				hspeed = hspeed - (deaccel * 0.2 * delta)
95				if hspeed < 0:
96					hspeed = 0
97				hv = hdir * hspeed
98
99	if jumping and vv < 0:
100		jumping = false
101
102	linear_velocity = hv + Vector3.UP * vv
103
104	if is_on_floor():
105		movement_dir = linear_velocity
106
107	linear_velocity = move_and_slide(linear_velocity, -gravity.normalized())
108
109	if shoot_blend > 0:
110		shoot_blend -= delta * SHOOT_SCALE
111		if (shoot_blend < 0):
112			shoot_blend = 0
113
114	if shoot_attempt and not prev_shoot:
115		shoot_blend = SHOOT_TIME
116		var bullet = preload("res://player/bullet/bullet.tscn").instance()
117		bullet.set_transform(get_node("Armature/Bullet").get_global_transform().orthonormalized())
118		get_parent().add_child(bullet)
119		bullet.set_linear_velocity(get_node("Armature/Bullet").get_global_transform().basis[2].normalized() * 20)
120		bullet.add_collision_exception_with(self) # Add it to bullet.
121		get_node("SoundShoot").play()
122
123	prev_shoot = shoot_attempt
124
125	if is_on_floor():
126		$AnimationTree["parameters/walk/blend_amount"] = hspeed / max_speed
127
128	$AnimationTree["parameters/state/current"] = anim
129	$AnimationTree["parameters/air_dir/blend_amount"] = clamp(-linear_velocity.y / 4 + 0.5, 0, 1)
130	$AnimationTree["parameters/gun/blend_amount"] = min(shoot_blend, 1.0)
131
132
133func adjust_facing(p_facing, p_target, p_step, p_adjust_rate, current_gn):
134	var n = p_target # Normal.
135	var t = n.cross(current_gn).normalized()
136
137	var x = n.dot(p_facing)
138	var y = t.dot(p_facing)
139
140	var ang = atan2(y,x)
141
142	if abs(ang) < 0.001: # Too small.
143		return p_facing
144
145	var s = sign(ang)
146	ang = ang * s
147	var turn = ang * p_adjust_rate * p_step
148	var a
149	if ang < turn:
150		a = ang
151	else:
152		a = turn
153	ang = (ang - a) * s
154
155	return (n * cos(ang) + t * sin(ang)) * p_facing.length()
156