summaryrefslogtreecommitdiff
path: root/godot/scripts/characters/player_controller_new.gd
blob: 62f5ef67c718cc2ce7028a761ff1be780b8b44c8 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
extends "res://scripts/characters/NetworkedCharacter.gd"

# Camera
export var mouse_sensitivity:float = 12.0
export var FOV:float = 90.0
var mouse_axis: Vector2 = Vector2.ZERO
onready var head: Spatial = $Neck/Head
onready var neck: Spatial = $Neck
onready var melee_ray: RayCast = $"%MeleeRay"
onready var use_ray: RayCast = $"%UseRay"
onready var gun_ray: RayCast = $Neck/Head/GunRay
onready var carry_point: Position3D = $Neck/Head/CarryPoint
onready var viewmodel: Spatial = $Neck/Head/VIEWMODEL_ARMS

# Move
var velocity := Vector3()
var direction := Vector3()
var move_axis := Vector2()
var floorspeed := Vector3()
var jumping:bool = false
var use_held:bool = false
var can_jump:bool = true
onready var nav: NavigationAgent = $NavigationAgent

# Walk
const FLOOR_MAX_ANGLE: float = deg2rad(46.0)
export var jump_height: float = 300.0
var in_water: bool = false
var swim_speed: float = 450.0
var climb_speed: float = 5.0

#physics
var player_state: PhysicsDirectBodyState = null
var is_on_floor:bool = false
var floor_normal: Vector3 = Vector3.UP
var acceleration:float = 80.0
export var walk_speed:float = 5.0
var c_friction:float = 4.0
var air_control:float = 0.3

var idle_t: bool = true

#ai
onready var ai_state_machine:StateMachine = get_node("AIStateMachine")
enum AIStates { IDLE, MAN_CANNON }
export(AIStates) var ai_state = AIStates.IDLE

export var ai_target_machine_path := NodePath()
var ai_target_machine: NetMachine = null

var ai_should_pathfind: bool = false
var ai_path_target_global: Vector3 = Vector3.ZERO
var ai_path_array: Array = []
const AI_PATH_PROXIM_DIST_SQ: float = 1.0

var ai_look_target: Vector3 = Vector3.ZERO
var ai_should_look: bool = false
var ai_should_track: bool = false
var ai_track_object: Spatial = null
const AI_LOOK_SPEED: float = 4.0

const DROWN_DICT: Dictionary = {"type" : "DROWN", "attacker_net_id" : 1, "attacker_name" : "Davy Jones", "weapon_name" : "his locker"}

# Called when the node enters the scene tree
func _ready() -> void:
	if weapon_slot1 == null:
		weapon_slot1 = preload("res://scenes/weapons/hands.res")
	weapons = [weapon_slot1,weapon_slot2,weapon_slot3,weapon_slot4,weapon_slot5]
	for w in weapons:
		if w!= null:
			w.init(self)
	weapon = weapons[0]
	world = get_tree().get_root().get_node("GAMEWORLD")

	$"%UseRay".add_exception(self)
	$"%MeleeRay".add_exception(self)
	$"%UseRay".add_exception($AreaDetect)
	$"%MeleeRay".add_exception($AreaDetect)
	
	if ai_target_machine_path:
		ai_target_machine = get_node(ai_target_machine_path)
	if get_tree().get_network_unique_id() != 1: #only server needs ai processing
		get_node("AIStateMachine").queue_free()
	
	#for playerbody in get_tree().get_nodes_in_group("player"):
	#	if playerbody.team == team and playerbody != self:
	#		add_collision_exception_with(playerbody)

func add_weapon_vm(weapon_vm: Spatial, trfrm: Transform):
	viewmodel.get_node("Skeleton/GunBone").add_child(weapon_vm)
	weapon_vm.transform = trfrm

# Called every frame. 'delta' is the elapsed time since the previous frame
func _process(_delta: float) -> void:
	if is_network_master() and !is_player:
		pass
	elif is_player and !world.is_chatting:
		use_held = Input.is_action_pressed("use")
		if Input.is_action_just_pressed("use"):
			initiate_use()
		if controlling_machine:
			if Input.is_action_just_pressed("fire"):
				machine.attack1()
			if Input.is_action_just_pressed("fire2"):
				machine.attack2()
			machine.direction_input(Input.get_action_strength("move_forward"),Input.get_action_strength("move_backward"), Input.get_action_strength("move_right"),Input.get_action_strength("move_left"), Input.get_action_strength("alt_right"),Input.get_action_strength("alt_left"))
			machine.misc_input(Input.get_action_strength("move_duck"),Input.get_action_strength("move_jump"),Input.get_action_strength("move_walk"))
			machine.mouse_input(Input.get_action_strength("fire"), Input.get_action_strength("fire3"),Input.get_action_strength("fire2"))
		else:
			weapon.mouse_input(Input.get_action_strength("fire"), Input.get_action_strength("fire3"),Input.get_action_strength("fire2"))
			jumping = Input.get_action_strength("move_jump")
			walk_speed = 5.0 if Input.get_action_strength("move_walk") else 7.5
			if Input.is_action_just_pressed("fire"):
				weapon.attack1()
			if Input.is_action_just_pressed("reload") and weapon.has_method("reload"):
				weapon.reload()
			move_axis.x = Input.get_action_strength("move_forward") - Input.get_action_strength("move_backward")
			move_axis.y = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
			
			if Input.is_action_just_pressed("slot1") and weapons[0] != null and weapon.deselect() :
				weapon = weapons[0]
				weapon.select()
			elif Input.is_action_just_pressed("slot2")and weapons[1] != null and weapon.deselect():
				weapon = weapons[1]
				weapon.select()
			elif Input.is_action_just_pressed("slot3") and weapons[2] != null and weapon.deselect() :
				weapon = weapons[2]
				weapon.select()
			elif Input.is_action_just_pressed("slot4") and weapons[3] != null and weapon.deselect() :
				weapon = weapons[3]
				weapon.select()
			elif Input.is_action_just_pressed("slot5") and weapons[4] != null and weapon.deselect() :
				weapon = weapons[4]
				weapon.select()

func initiate_use():
	if controlling_machine:
		machine.relinquish_control()
		return
	if ladder_m != null:
		leave_ladder()
		return
	if carrying:
		carrying = false
		carrying_object.rpc("set_nm",1)
		carrying_object = null
		return
	
	#interact with world
	if !use_ray.is_colliding(): return
	var area_c = use_ray.get_collider()
	match area_c.name:
		"SteerArea": #must be a networkedmachine
			if area_c.get_parent().controllable: world.rpc_id(1, "_call_on_server", "_client_request_control_vehicle", {"id" : world.client_id, "machine_path" : area_c.get_parent().get_path(), "char_name" : name})
			elif !area_c.get_parent().loaded and inventory[area_c.get_parent().ammo_type] > 0:
				loading = true
				load_target = area_c.get_parent()
				load_ammo = area_c.get_parent().ammo_type
		"LadderArea":
			mount_ladder(area_c.get_parent())
		"TugArea":
			pass
		"UseArea": #must have method use_generic
			area_c.get_parent().use_generic(self)
		"PickupArea": #must be a networkedprojectile. must have inventory_name property
			var type: String = area_c.get_parent().inventory_name
			if inventory[type] < inventory_caps[type]:
				area_c.get_parent().rpc("net_remove")
				inventory[type] += 1
				rset("inventory", inventory)
		"CarryArea":
			if weapon.name == "HANDS" and weapon.can_pickup():
				carrying = true
				carrying_object = area_c.get_parent()
				carrying_object.rpc("set_nm",get_network_master())
				carry_point.global_transform.origin = carrying_object.global_transform.origin
		_:
			pass

func load_process(delta) -> void:
	if use_ray.is_colliding() and use_ray.get_collider().get_parent() == load_target and use_held:
		var progress: float =load_target.increase_load(delta)
		if progress < 0:
			if get_network_master() != 1: world.hud.hide_progress()
			load_target.reset_load()
			loading = false
			load_target = null
			inventory[load_ammo] -= 1
		else:
			if get_network_master() != 1: world.hud.set_progress(progress)
	else:
		if get_network_master() != 1: world.hud.hide_progress()
		load_target.reset_load()
		loading = false
		load_target = null

func carry_process() -> void:
	if !is_instance_valid(carrying_object) or carrying_object.get_network_master() != get_network_master():
		carrying_object = null
		carrying = false
		return
	var vec = (carry_point.global_transform.origin - carrying_object.global_transform.origin)
	var length = vec.length()
	if length > 1:
		carrying = false
		carrying_object.rpc("set_nm",1)
		carrying_object = null
		return
	var dir = vec.normalized()
	carrying_object.add_central_force(carrying_object.mass*120*dir*length - 90*carrying_object.linear_velocity)

# Called every physics tick. 'delta' is constant
func _physics_process(delta: float) -> void:
	if is_network_master():
		if loading: load_process(delta)
		move_dir_process()
		if !is_player: #ai behavior
			if ai_should_look: ai_look_at()
		if carrying:
			carry_process()
		if ladder_m != null:
			climb_ladder(delta)
		elif !on_floor_test() and in_water:
			swim(delta)
		else:
			walk(delta)
		is_on_floor = false #reset whether is on floor in between frames
	

func move_dir_process():
	direction = Vector3()
	if is_player:
		var aim: Basis = head.get_global_transform().basis
		direction += -move_axis.x * aim.z + move_axis.y * aim.x
		direction.y = 0
		direction = direction.normalized()
	else:
		if ai_should_pathfind:
			if !ai_path_process(): return
			direction = ai_path_target_global - global_transform.origin
			direction.y = 0
			direction = direction.normalized()

# called each physics frame
func on_floor_test() -> bool:
	if $Feet.is_colliding():
		is_on_floor = true
		floor_normal = Vector3.UP
		floorspeed = $Feet.get_collider().get_linear_velocity() if $Feet.get_collider().has_method("get_linear_velocity") else Vector3.ZERO
		return true
	if player_state:
		for i in range(player_state.get_contact_count()):
			var contact_angle_from_up : float = Vector3.UP.angle_to(player_state.get_contact_local_normal(i))
			if contact_angle_from_up < FLOOR_MAX_ANGLE:
				floor_normal = player_state.get_contact_local_normal(i)
				is_on_floor = true
				return true
	return false

#modify simulated physics results
func _integrate_forces(state: PhysicsDirectBodyState) -> void:
	if !is_network_master():
		return
	player_state = state
	velocity = state.get_linear_velocity()
	for i in range(player_state.get_contact_count()):
		var contact_angle_from_up : float = Vector3.UP.angle_to(player_state.get_contact_local_normal(i))
		if contact_angle_from_up > FLOOR_MAX_ANGLE and !is_on_floor:
			physics_material_override.friction = 0
			break
		if i == player_state.get_contact_count() - 1:
			physics_material_override.friction = 1
	
	rpc("set_phys_transform", transform, linear_velocity)
	if global_transform.origin.y < -30:
		rpc("damage", 500000, DROWN_DICT)
	
	nav.set_velocity(velocity)

func walk(_delta: float) -> void:
	if floor_normal != Vector3.UP: direction = direction.rotated(floor_normal.cross(Vector3.UP).normalized(), Vector3.UP.angle_to(floor_normal))
	
	# Jump
	if is_player and jumping and is_on_floor and can_jump:
		jump()
	
	#max walk speed
	var _speed = walk_speed
	var _temp_accel: float = acceleration
	var _cspeed = sqrt(pow(velocity.x-floorspeed.x,2)+pow(velocity.z-floorspeed.z,2))
	
	if not is_on_floor:
		_temp_accel *= air_control

	var projVel = Vector2(velocity.x-floorspeed.x,velocity.z-floorspeed.z).dot(Vector2(direction.x,direction.z))
	
	if is_on_floor:
		add_central_force(-mass*_cspeed*(linear_velocity-floorspeed).normalized()*c_friction)#friction
		if _speed - _cspeed > 0:
			add_central_force (mass*Vector3(direction.x*_temp_accel, 0, direction.z*_temp_accel))#velocity.x += direction.x*_temp_accel
		else:
			add_central_force(mass*Vector3(direction.x*(_speed-projVel), 0, direction.z*(_speed-projVel)))
	elif 1.0 - projVel > 0:
		add_central_force (mass*Vector3(direction.x*_temp_accel, 0, direction.z*_temp_accel))

func jump():
	can_jump = false
	apply_central_impulse(Vector3.UP*jump_height)
	yield(get_tree().create_timer(0.05),"timeout")
	can_jump = true

func swim(_delta: float):
	#drag and buoyancy
	add_central_force(Vector3.UP*weight*1.0)
	add_central_force(-1*linear_velocity*100)
	#controls
	var dir: Basis = head.get_global_transform().basis
	var m_dir: Vector3 = -move_axis.x * dir.z + move_axis.y * dir.x
	m_dir = m_dir.normalized()
	add_central_force(swim_speed*m_dir)
	if jumping:
		add_central_force(Vector3.UP*weight*0.5)
	
func enter_water():
	in_water = true

func exit_water():
	in_water = false

func mount_ladder(target_ladder: Spatial):
	var old_neck_rot = neck.get_global_rotation()
	var ladder_tracker = Spatial.new()
	ladder_tracker.name = name
	target_ladder.add_child(ladder_tracker)
	ladder_tracker.transform = target_ladder.bottom.transform
	
	ladder_tracker.global_transform.origin = target_ladder.get_nearest_point_to_route(global_transform.origin)
	look_at(global_transform.origin + target_ladder.global_transform.basis.x, target_ladder.global_transform.basis.y)
	
	ladder_m = ladder_tracker
	global_transform.origin = ladder_m.global_transform.origin
	global_transform.basis = ladder_m.global_transform.basis.orthonormalized()
	neck.set_global_rotation(old_neck_rot)
	linear_velocity = Vector3.ZERO
	set_gravity_scale(0.0)

#called each frame while climbing ladder
func climb_ladder(delta: float):
	var new_ladder_pos = ladder_m.global_transform.origin + ladder_m.global_transform.basis.y.normalized() * move_axis.x * delta * climb_speed
	var prog = ladder_m.get_parent().get_climb_scalar(new_ladder_pos)
	if prog >= 0.0 and prog <= 1.0:
		ladder_m.global_transform.origin = new_ladder_pos
	global_transform.origin = ladder_m.global_transform.origin
	global_transform.basis = ladder_m.global_transform.basis.orthonormalized()

func leave_ladder():
	var old_neck_rot = neck.get_global_rotation()
	if (ladder_m.get_parent().top.global_transform.origin - global_transform.origin).length_squared() < 0.01:
		apply_central_impulse(-400*ladder_m.global_transform.basis.z)
	global_transform.basis = world.global_transform.basis
	neck.set_global_rotation(Vector3(0,old_neck_rot.y,0))
	set_gravity_scale(1.0)
	ladder_m.queue_free()
	ladder_m = null

remotesync func anim_event(event: Dictionary):
	$"%VIEWMODEL_ARMS".get_node("AnimationPlayer").play(event["VMANIMPLAY"])

remotesync func play_weapon_sound(filepath) -> void:
	$WeaponSound.stream = load(filepath)
	$WeaponSound.play()

remotesync func add_rocket_to_scene(pos, dir, id):
	var rocket = preload("res://scenes/ballistics/Rocket.tscn").instance()
	world.get_node("BALLISTICS").add_child(rocket, true)
	rocket.shooter = name
	if get_network_master() != 1: rocket.shooter+= " (" + world.players_info[get_network_master()][0] + ")"
	rocket.shooter_id = id
	rocket.global_transform.origin = pos
	rocket.global_transform.basis = Basis(-1*dir.z, dir.y, dir.x)
	rocket.add_collision_exception_with(self)

func ai_set_state(state: String):
	ai_state_machine.transition_to(state)

func ai_set_look_status(target, type:String="STATIC"):
	if type == "STATIC":
		ai_look_target = target
		ai_should_look = true
	elif type == "TRACK":
		ai_track_object = target
		ai_should_look = true
		ai_should_track = true
	else:
		ai_should_look = false
		ai_should_track = false

func ai_look_at():
	if ai_should_track:
		if is_instance_valid(ai_track_object):
			ai_look_target = ai_track_object.global_transform.origin
		else:
			ai_track_object = null
			ai_should_track = false
	var p_neck: float = Vector3(ai_look_target.x - neck.global_transform.origin.x, 0, ai_look_target.z - neck.global_transform.origin.z).signed_angle_to(-neck.global_transform.basis.z, neck.global_transform.basis.y )
	var p_head: float = Vector3(ai_look_target.x - neck.global_transform.origin.x, ai_look_target.y - head.global_transform.origin.y, ai_look_target.z - neck.global_transform.origin.z).signed_angle_to( -head.global_transform.basis.z, head.global_transform.basis.x)
	neck.rotation_degrees.y = lerp(neck.rotation_degrees.y, neck.rotation_degrees.y-p_neck, AI_LOOK_SPEED+abs(p_neck/3.1415))
	head.rotation_degrees.x = lerp(head.rotation_degrees.x, head.rotation_degrees.x-p_head, AI_LOOK_SPEED+abs(p_head/3.1415))
	
	if abs(p_neck) <= 0.01 and abs(p_head) <= 0.01 and !ai_should_track:
		ai_should_look = false

func ai_set_path_target(target: Vector3):
	ai_should_pathfind = true
	nav.set_target_location(target)
	#CharacterAIManager.request_find_path(self, target, true)

func ai_set_path_array(arr: PoolVector3Array):
	world.draw_path(arr)
	ai_should_pathfind = true
	ai_path_array = arr
	ai_path_target_global = ai_path_array[0]

func ai_path_process() -> bool:
#	if (global_transform.origin - ai_path_array[0]).length_squared() < AI_PATH_PROXIM_DIST_SQ:
#		ai_path_array.remove(0)
#		if len(ai_path_array) == 0: #destination reached
#			ai_should_pathfind = false
#			return false
#		else: ai_path_target_global = ai_path_array[0]
#	return true
	if nav.is_navigation_finished():
		ai_should_pathfind = false
		return false
	ai_path_target_global = nav.get_next_location()
	return true