You are right. But I don't think the problem comes from the "Controls" group/function. I really don't think that it is that complicated. Just pull inputs from devices and store them in an array.
It's the "Player" function (yes there were 2 conditions that didn't do anything - leftovers from previous testing). I think it's not comparing enough stuff and needs more conditions, that's all.
Look at this (it's the exact same function, but Player0 and Player1 branches are split and it works just fine):
As I said it was just my advice to you. Of course you can make it whatever way you think is best and it will most likely end up working.
The reason I gave you that advice is because as your project grows, you are really not interested in "complicated" solutions, you aim for the most simple, optimized solution that you can make, because it makes it so much easier to troubleshoot, add and change later on when you might not have focused on the controlling parts for a while. So you want to be able to quickly go back and read the code and quickly find out where you have to change things. As I wrote in the first post, I think you are making a very complicated solution and its even hard for you to troubleshoot it, so imagine someone having to do it, that don't know the thoughts behind it.
Looking at the screenshot to give you an example:
You call the function "Player" which then calls another function called Control, so I assume that you aim to add something else to this function such as Shooting etc? Because otherwise it would be better to simply call the function "Controls" directly from where you call Function Player.
You then depending on the Player parameter (0,1,2,3) duplicate the same functionality, only difference is whether the passed parameter is (0,1,2,3). But before that you call the function "Controls" which doesn't really handle all the controls, but only part of it, the actual control is done in Function Player depending on a value in the array.
But in each of these events (Value at (Function.Param(0),0) > 0) etc. You have the simulate behaviours. But your code doesn't make sure that for instant (Value at (Function.Param(0),0) > 0) and (Value at (Function.Param(0),20) > 0) and (Value at (Function.Param(0),21) > 0 can't all be true at the same time. So if the array screws up then the movement will bug as well.
So what im saying is if you for instant have a function called "Player Move Left", then already at this point you know what the function does. Its doesn't handle anything else. So to make it work you just need to pass the correct Player and all of them can use the same code.
Function Player Move Left
Pick Platformer.Player = Function.Param(0)
Platformer Simulate Pressing Left
This would be able to handle left movement for all players. So the only thing you have to do, is to add events like:
On key press A
Function.call("Player Move Left", <Player nr, that is linked to this control>)
And that would be it. No need to store anything in arrays or anything, its very easy to read the code and it does exactly what you would expect.
I can read your code, but going through all the values stored in the array, following all the function calls. Making sure that the value in array actually match what it should etc. Just take to long time that I think you are making things a lot more difficult for yourself than it needs to be, you have to troubleshoot and track so much code to figure out where it goes wrong.
Ps. You don't need a Trigger once in functions, as it will automatically only trigger once, when you call it. But anyway, its not causing the problem, its just that it makes no difference whether its there or not.