You can have the NPC read input, yes. However, that is unlikely to be enough to counter the player, as - chances are - positioning is important.
For example, having the NPC to shoot a projectile leading the player requires to look into the player character speed, the distance to it, and the speed of the projectile to compute the optimal direction to shoot. There are question on the topic on this site, try searching.
In that case the speed of the character, and its position, are part of its state. We can easily turn input into state with the approach that Engineer comments: if player is performing an action (e.g. because of user input) set a bool
flag, or change a state
variable.
Then, for the case where there are multiple player characters, the NPC can use a "sight" area where it detects player characters, keep track of them, and pick the closest one. Having a reference to the player character, it can read its state.
Furthermore, changing the input scheme would not require to update the NPC code.
Adapting to player patterns require to first identify the patterns. And then have some strategy to counter them. Presumably you would have a finite number of strategies programmed (barring machine learning), thus I suggest to think about the problem the other way around: for each strategy the NPC can use, what are the conditions for it to work, and then figure out how to check for those conditions. You would, of course, use feedback from player testing to improve it.
Checking if an strategy might work could - again - require to check positioning (e.g. distance to the player character), and might involve line of sight (i.e. raycast) or dedicated detection areas, in addition of checking the state of the player character (e.g. check if it is invulnerable to some attack).
In fact, you might not limit yourself to a single player character! That is, instead of checking which strategy can be applied to the closest player character, you might check on which player character each strategy can be applied. You might have it pick an strategy at random until it finds one that works, or go down the list in a predefined order, but be careful to not harm performance by doing too many checks in one frame.
About prediction, you need to think how the states of the player character will change. For example, you might know that a typical jump of the player has it landing in 1.5 seconds (again, as an example), using that the NPC can use an attack that - approximately - will hit the player when it lands. Something similar can be applied to temporary buffs or player abilities that have timeouts.
I will ultimately depend on your game mechanics, but I hope this give some idea of how to go about it.
if (up arrow pressed) then set playerJumped = true
, and thereafter in your AI reaction phase code, checkif (playerJumped) ...
Prediction is a hard topic. I'd keep my expectations low if I were you, as it sounds like this is one of your first projects. \$\endgroup\$