Input Handler

Input Handler

in

WIP Right of Way Right of Way

InputHandler

extends Node
@onready var chara = get_parent()
var direction : Vector2 = Vector2.ZERO
var inputDirection : Vector2 = Vector2.ZERO;
var queuing := false #this is never directly set, as queuedActionName sets it
var queueStunned := false
var queueGatling := false
var queueWaitTime := 0.0:
	set(val):
		queueWaitTime = val
		$QueueWaitTimer.set_wait_time(val)
		$QueueWaitTimer.start()
var queuedActionName := "":
	set(val):
		queuedActionName = val
		if val != "":
			$QueueExpireTimer.start()
		#else:
		#	queueGatling = false
@export var currentInput : int
@export var previousInputs : Array
var previousInputsAtTimerStart : Array
@export var motionInput : int = 0:
	set(inputInNumbers):
		get_parent().motionInput = inputInNumbers
	get:
		return get_parent().motionInput
var previousDirection : Vector2 = Vector2.ZERO;
var holding = false
var parryCD := false
var enabled = true
@onready var stateMachine = get_parent().get_node("StateMachine")
@onready var currentState = stateMachine.currentState
@onready var audioHandler = chara.get_node("AudioHandler")

@onready var moveListFromStorage = chara.get_node("MoveListStorage").getListFromCharacter[chara.character]
@onready var fullMoveList = moveListFromStorage[0]
@onready var inputList = moveListFromStorage[1]
@onready var inputListAir = moveListFromStorage[2]

var inputHistory = []
var inputHistoryFrame := 0
var inputHistoryEnabled := true #true when the round starts, false when the round ends so that only the right stuff is considered, and so that the inputs are synced (instead of being thrown off when someone loads in before the other)
var inputHistoryReiterating := false
var inputHistoryReiteratingFrame := 0
var positionHistory = []
var velocityHistory = []
var directionHistory = []
var meterHistory = []
var stockHistory = []

var nearbyInputs = {
	"0":[0],
	"1":[1,2],
	"2":[2,1,3],
	"3":[3,2],
	"4":[4],
	"5":[5],
	"6":[6],
	"7":[7],
	"8":[8],
	"9":[9]
}

func getInputName(event): #TODO get rid of ui inputs
	var all_actions: Array[StringName] = InputMap.get_actions()
	# Filter and find the actions that match the current event
	var matching_actions: Array[StringName] = all_actions.filter(func(action_name: StringName): 
		return event.is_action(action_name)
	)
	if matching_actions.size() <=0:
		return
	#print(matching_actions)
	var inputName = matching_actions[0]
	#print_debug(inputName)
	if (inputName.contains("p2") and chara.flipped == 1) or (not inputName.contains("p2") and chara.flipped == -1):
		return
	if inputName.contains("p2"):
		inputName = inputName.substr(2)
	return(inputName)

func updateStateInputs():
	if chara.aiMode:
		return
	for state in stateMachine.states:
		state.inputDirection = inputDirection
	
func _input(event : InputEvent):
	if get_parent().flipped == 1 and event.is_action_pressed("menu"):
		get_tree().current_scene.menu()
	if chara.aiMode or not (event is InputEventKey) or inputHistoryReiterating or not enabled:
		return
		
	if event.is_action_pressed("debug"):
		if get_parent().aiMode:
			get_parent().get_node("AIHandler").setLabelVisibility()
	elif event.is_action_pressed("reset"):
		get_tree().current_scene.reset()
	elif(Input.is_action_just_pressed("override")) and chara.flipped == 1:
		get_tree().current_scene.overrideCamera(chara)
		return
	if (Input.is_action_just_pressed(inputs("parry"))) and motionInput == 632146 and currentState.canDoActionName("Priority") and chara.superStocks > 0 and chara.meter > 33:
		get_tree().current_scene.summonProjectile(chara,"Disengage",Vector2(-5,0),0.0,Vector2(-1,1))
		get_tree().current_scene.vfx(chara,"SuperStrike",Vector2(-10,3),false,0.0,Vector2(1,1))
		#get_tree().current_scene.summonProjectile(chara,"Super",Vector2(-50,0),0.0,Vector2(-1,1))
		#chara.activatePriority()
		#get_tree().current_scene.usePriority(chara)
		currentState.doActionName("Priority")
		return
		
	
	var inputName = getInputName(event)
	if event != null and inputName != null and inputHistoryEnabled and inputHistoryFrame > 0:
		#print((chara.flipped ==1 and not inputName.contains("p2")))
		#print((chara.flipped ==-1 and inputName.contains("p2")))
		#if inputHistoryEnabled and inputHistoryFrame > 0 and ((chara.flipped ==1 and not inputName.contains("p2")) or (chara.flipped ==-1 and inputName.contains("p2"))):
		#if inputHistoryFrame>inputHistory.size():
		#	inputHistory.append([[event,inputName]]) #first input for a given frame
		#else:
		#print(inputHistory)
		#print(inputHistory.size())
		if inputHistory[inputHistoryFrame-1]==[]:
			inputHistory[inputHistoryFrame-1] = [[event,inputName]]
		else:
			#inputHistory[inputHistoryFrame-1] = [inputHistory[inputHistoryFrame-1],[event,inputName]] #add inputs to this frame, incase the player presses hella buttons at once
			inputHistory[inputHistoryFrame-1].append([event,inputName])
	
	
	processInput(event, inputName)


func processInput(event, inputName):
	#if event ==[] or inputName==[]: #TODO this is such a fucking spaghetti code workaround
	#	return
	if not parryCD and (inputName=="right" or inputName=="p2right") and stateMachine.currentState==stateMachine.currentState.neutralState:
		parryCD = true
		$ForwardParryCD.start()
		#var progressBar = get_parent().get_node("ParryProgressBar")
		#if progressBar != null:
		#	progressBar.begin(9)
		chara.canForwardParry = true
		await get_tree().create_timer(9 * .0167).timeout
		chara.canForwardParry = false
		
	if event.is_action_pressed(inputs("parry")) and motionInput == 214:
		get_tree().current_scene.get_node("CharacterHandler").spendMeterForStock(chara)
	#print("Motion input: "+str(motionInput))
	#direction = Input.get_vector(inputs("left"), inputs("right"), inputs("up"),inputs("down")).sign()
	
	"""
	if inputHistoryReiterating:
		if inputName.contains("right"): #TODO this is a shitty workaround because i dont know how to get the input direction otherwise with just the events
			inputDirection = Vector2i(1,0)
		elif inputName.contains("left"):
			inputDirection = Vector2i(-1,0)
		elif inputName.contains("down"):
			inputDirection = Vector2i(0,1)
		elif inputName.contains("up"):
			inputDirection = Vector2i(0,-1)
	"""
	
	var all_actions: Array[StringName] = InputMap.get_actions()
	# Filter and find the actions that match the current event
	var matching_actions: Array[StringName] = all_actions.filter(func(action_name: StringName): 
		return event.is_action(action_name)
	)
	if matching_actions.size() <=0:
		return
	#print(matching_actions)
	var motionInputName = matching_actions[0]+str(motionInput)
	inputName = matching_actions[0]+str(Vector2i(inputDirection)) #TODO I wonder if this will cause problems later?
	if not searchAction(motionInputName):
		searchAction(inputName)
		

func searchAction(inputName):
	if (inputName.contains("p2") and chara.flipped == 1) or (not inputName.contains("p2") and chara.flipped == -1):
		return false
	if inputName.contains("p2"):
		inputName = inputName.substr(2)
	#print(inputName)
	var actionName
	if inputList.has(inputName):
		actionName = inputList[inputName]
	else:
		#print_debug("Inputted event could not be found! "+inputName)
		return false
	if fullMoveList.has(actionName):
		if queueStunned:
			queuedActionName = actionName
			return true
		elif currentState.canDoActionName(actionName):
			currentState.doActionName(actionName)
		#queuedActionName = ""
			return true
	return false

func _process(delta: float) -> void:
	currentInput = 0 #TODO the following is a big batch of bullshit
	
	if (Input.is_action_just_pressed(inputs("up")) and inputDirection.x == -1) or (Input.is_action_just_pressed(inputs("left")) and inputDirection.y == -1):
		currentInput = 7
	elif (Input.is_action_just_pressed(inputs("up")) and inputDirection.x == 1) or (Input.is_action_just_pressed(inputs("right")) and inputDirection.y == -1):
		currentInput = 9
	elif (Input.is_action_just_pressed(inputs("down")) and inputDirection.x == -1) or (Input.is_action_just_pressed(inputs("left")) and inputDirection.y == 1):
		currentInput = 1
	elif (Input.is_action_just_pressed(inputs("down")) and inputDirection.x == 1) or (Input.is_action_just_pressed(inputs("right")) and inputDirection.y == 1):
		currentInput = 3
	elif Input.is_action_just_pressed(inputs("up")) or ((Input.is_action_just_released(inputs("left")) or Input.is_action_just_released(inputs("right"))) and inputDirection.y == -1):
		currentInput = 8
	elif Input.is_action_just_pressed(inputs("down")) or ((Input.is_action_just_released(inputs("left")) or Input.is_action_just_released(inputs("right"))) and inputDirection.y == 1):
		currentInput = 2
	elif Input.is_action_just_pressed(inputs("left")) or ((Input.is_action_just_released(inputs("up")) or Input.is_action_just_released(inputs("down"))) and inputDirection.x == -1):
		currentInput = 4
	elif Input.is_action_just_pressed(inputs("right")) or ((Input.is_action_just_released(inputs("up")) or Input.is_action_just_released(inputs("down"))) and inputDirection.x == 1):
		currentInput = 6
	
	if currentInput != 0:
		addToInputsArray(currentInput)
		$ExpireInputs.stop()
		previousInputsAtTimerStart = previousInputs
		$ExpireInputs.set_wait_time(11 * .0167)
		$ExpireInputs.start()
	
	if currentState==currentState.neutralState or currentState==currentState.blockState:
		if inputDirection.x==-1:
			chara.blocking = true
			if inputDirection.y==1:
				chara.blockingLow = true
			else:
				chara.blockingLow = false
		else:
			chara.blocking = false
			chara.blockingLow = false
	else:
		chara.blocking = false
		chara.blockingLow = false
	if not queueStunned and queuedActionName != "":
		if currentState.canDoActionName(queuedActionName):
			currentState.doActionName(queuedActionName)
			queuedActionName = ""
	"""
	if queuing:
		if (not (queueWaitTime != 0.0 or queueStunned)) and currentState.canDoActionName(queuedActionName):
			print("Can do queued action!")
			currentState.doActionName(queuedActionName)
			if queueGatling:
				currentState.gatling()
			queuedActionName = ""
		"""
		
func _physics_process(delta: float) -> void:
	currentState = stateMachine.currentState
	
	if chara.aiMode:
		return
	if not inputHistoryReiterating and not chara.aiMode:
		inputDirection = Vector2i(
			int(sign(Input.get_action_strength(inputs("right"))) - sign(Input.get_action_strength(inputs("left")))),
			int(sign(Input.get_action_strength(inputs("down"))) - sign(Input.get_action_strength(inputs("up"))))
		)
	
	if inputHistoryEnabled:
		inputHistory.append([])
		inputHistoryFrame+=1
		positionHistory.append(chara.global_position)
		velocityHistory.append(chara.velocity)
		directionHistory.append(inputDirection)
		meterHistory.append(chara.meter)
		stockHistory.append(chara.superStocks)
	if inputHistoryReiterating:
		inputHistoryReiteratingFrame+=1
		if inputHistoryReiteratingFrame >=inputHistoryFrame:
			doneReiterating()
			return
		for input in inputHistory[inputHistoryReiteratingFrame]:
			if input != null and input != []: 
				#print_debug(str(input)+"   "+str(input[0]))
				#print(input)
				processInput(input[0],input[1])
		chara.velocity = velocityHistory[inputHistoryReiteratingFrame]
		inputDirection = directionHistory[inputHistoryReiteratingFrame]
		chara.meter = meterHistory[inputHistoryReiteratingFrame]
		chara.superStocks = stockHistory[inputHistoryReiteratingFrame]
	updateStateInputs()

func addToInputsArray(currentInput1):
	previousInputs.append(currentInput1)
	#print("Previous inputs: "+str(previousInputs))
	previousDirection = inputDirection
	if previousInputs.size() >= 11:
		previousInputs.remove_at(0)
	#print(previousInputs)
	checkForMotionInputs()
	
func checkForMotionInputs():
	#if previousInputs[len(previousInputs)-1] == 6 and previousInputs[len(previousInputs)-2] == 3 and previousInputs[len(previousInputs)-3] == 2:
	if getNearbyInput(6,6) and getNearbyInput(5,3) and getNearbyInput(4,2) and getNearbyInput(3,1) and getNearbyInput(2,4) and getNearbyInput(1,6):
		motionInput = 632146
		$ExpireMotionInput.start()
	elif getNearbyInput(6,4) and getNearbyInput(5,1) and getNearbyInput(4,2) and getNearbyInput(3,3) and getNearbyInput(2,6) and getNearbyInput(1,4):
		motionInput = 412364
		$ExpireMotionInput.start()
	elif getNearbyInput(5,4) and getNearbyInput(4,1) and getNearbyInput(3,2) and getNearbyInput(2,3) and getNearbyInput(1,6):
		motionInput = 41236
		$ExpireMotionInput.start()
	elif getNearbyInput(3,2) and getNearbyInput(2,3) and getNearbyInput(1,6):
		if motionInput == 236:
			motionInput = 236236
		else:
			motionInput = 236
		$ExpireMotionInput.start()
	elif getNearbyInput(3,2) and getNearbyInput(2,1) and getNearbyInput(1,4):
		if motionInput == 214:
			motionInput = 214214
		else:
			motionInput = 214
		$ExpireMotionInput.start()
		#print("Motion input! "+str(motionInput))
	#elif getNearbyInput(5,4) and getNearbyInput(4,4) and getNearbyInput(3,1) and getNearbyInput(2,6) and getNearbyInput(1,6):
	#elif getNearbyInput(6,4) and getNearbyInput(5,1) and getNearbyInput(4,2) and getNearbyInput(3,3) and getNearbyInput(2,6) and getNearbyInput(1,6):
	elif getNearbyInput(5,2) and getNearbyInput(4,1) and getNearbyInput(3,4) and getNearbyInput(2,6) and getNearbyInput(1,6):
		#motionInput = 412366
		motionInput = 21466
		$ExpireMotionInput.start()
		if stateMachine.currentState.canDoActionName("Dash"):
			checkForward()
	elif getNearbyInput(5,2) and getNearbyInput(4,3) and getNearbyInput(3,6) and getNearbyInput(2,4) and getNearbyInput(1,4):
		motionInput = 23644
		$ExpireMotionInput.start()
		if stateMachine.currentState.canDoActionName("Backdash"):
			checkBackward()
	elif getNearbyInput(3,6) and getNearbyInput(2,2) and getNearbyInput(1,3):
		motionInput = 623
		$ExpireMotionInput.start()
	elif getNearbyInput(2,6) and getNearbyInput(1,6):
		#motionInput = 66
		doActionName("Dash")
	elif getNearbyInput(2,4) and getNearbyInput(1,4):
		#motionInput = 44
		doActionName("Backdash")

func inputs(input):
	if get_parent().flipped == -1:
		return "p2"+input
	return input


func getNearbyInput(indexSubtract,desiredInput):
	if len(previousInputs)<indexSubtract:
		return false
	#print(str(previousInputs[len(previousInputs)-indexSubtract]))
	#print(nearbyInputs[str(previousInputs[len(previousInputs)-indexSubtract])])
	return nearbyInputs[str(previousInputs[len(previousInputs)-indexSubtract])].has(desiredInput)

func checkForward():
	stateMachine.currentState.doActionName("Dash")
	chara.get_node("SFXDashDodge").play()
	chara.dodging = true
	chara.dodgeAnim()
func checkBackward():
	stateMachine.currentState.doActionName("Backdash")
	chara.get_node("SFXDashDodge").play()
	chara.dodging = true
	chara.dodgeAnim()


func doActionName(namee):
	previousInputs.clear()
	motionInput = 0
	if stateMachine.currentState.canDoActionName(namee):
		stateMachine.currentState.doActionName(namee)

		
func _on_expire_motion_input_timeout():
	motionInput = 0


func _on_forward_parry_cd_timeout() -> void:
	parryCD = false


func _on_expire_inputs_timeout() -> void:
	if previousInputs == previousInputsAtTimerStart:
		previousInputs.clear()

func block():
	stateMachine.currentState.block()




func _on_queue_expire_timer_timeout() -> void: #time for a queued action to go away (not queue anymore cause the window expired)
	queuedActionName = ""


func _on_queue_wait_timer_timeout() -> void: #time for a queued action to wait before it happens. if this is 0.0, then the action comes out as soon as possible, or another input can override it. if not 0.0, the queued action HAS to happen. cool!
	queueWaitTime = 0.0

func resetInputHistory():
	inputHistoryEnabled = false
	inputHistory = []
	directionHistory = []
	positionHistory = []
	velocityHistory = []
	meterHistory = []
	stockHistory = []
	inputHistoryFrame = 0

func startInputHistory():
	inputHistoryEnabled = true
	inputHistory = []
	inputHistoryFrame = 0

func disableInputHistory():
	inputHistoryEnabled = false

func reiterateInputHistory(frameOffset):
	inputHistoryEnabled = false
	#print(inputHistoryFrame)
	if frameOffset < 0 or frameOffset >= inputHistoryFrame:
		var difference = abs(frameOffset-inputHistoryFrame)
		chara.position = positionHistory[0]
		await get_tree().create_timer(difference * .0167).timeout
		inputHistoryReiteratingFrame = 0
		print_debug("INPUT HISTORY REITERATING FRAME MESSED UP because of frame offset or inputHistoryFrame! It was set to 0...")
	else:
		inputHistoryReiteratingFrame = inputHistoryFrame - frameOffset
		chara.position = positionHistory[inputHistoryReiteratingFrame]
	inputHistoryReiterating = true

func doneReiterating():
	inputHistoryReiterating = false
	resetInputHistory()
	chara.doneReiterating()