You use a bit more memory then needed by having different objects and by setting them invisible instead of destroying them.
Setting them invisible will not do the trick. They still collide/overlap. They still count in .. in the iterations and the evaluations, and therefor they also bring performance down. If you really worry about memory and performance, then you destroy them when not needed. Only destroyed sprites have no graphics in memory and are not seen by the events.
You can of course add 'is visible' to each event. But then you are even farther away from home.
You think to much in 'IF this THEN that'. You must learn to think in service of the PICKLIST (selected objects list)
To choose a spawner that is not occupied by a 'sprite' you do this ...
Is spawner overlapping sprite (inverted)
Pick random spawner.
The first condition picks all spawners that do not overlap a sprite. That is the picklist. The second condition takes that picklist, and pick from there a random spawner. Done and over with. Just need a exception for when all is occupied. (wich you currently dont have and can not do that way, would be an endless loop)
Then, to minimise code, you should work with instances of the same object. There is a plugin "nickname" that can help you if you really want different objects. But. Really. A 'healer' is an instance with an instance variable/boolean set to 'healer'. Another instance can be a 'hunter' by setting that instance variable to 'hunter'. As simple as that. So to spawn a 'soldier', or 'priest', or a 'worker' ..you do something like ...
(I have no idea why your enemypicker goes in steps of 2)
Spawn Sprite
Set Sprite.type to enemypicker (a dot to say it is an instance variable)
Set sprite's animation to str(enemypicker)
Set some more abilitys, or have an array with the abilitys on the Y-axis, with the X-axis sync with the types.
One event for everything.
I wonder why you need an array with the UID's. You can build that array on any moment with a simple loop, on any moment. Without maintaining it with pushes. Why do you need that anywayz ?
Check if 'player' collides with 'healer' is as simple as ...
Global variable 'healer' = 1 (if healer is first type)
On 'player' colliding with 'Sprite'
Sprite.Type = healer ?
Do stuf .....
All this goes for different sprites in a family too. Besides that you can not create members of a family the same way. Need the 'nickname' plugin to do that.