LOGIC OF THE HUMBLE SKELETON
Shout out to the sketchy scribble of a skeleton warrior in the gifs below, its a test thing so it's ok for it to look like that. For Disavowed I've chosen for the enemies to utilise the mp_grid functions of GMS2. It makes the most sense to allow enemies to follow the player around and avoid walls. I've applied a base tile-map just to have something other than a black screen to look at whilst testing and setting up behaviors.
STATE MACHINE
There's various methods do achieve enemy logic but the form i'm most comfortable with is using an enumerated state machine. I find them easier to read, and understand, this helps a lot when the logic starts to get more cumbersome. and its easier to expand on if you have a few brain waves later on in production. This Skeleton warrior can do a few basic things and i'll likely keep it that way, as they don't need to be too complex right off the bat. The break down of the enum is;
enum EnemyState{ ROAM, PURSUE, ATTACK, COOLDOWN, KNOCKBACK }
While in Roaming, the enemy will pick a random point and move towards it, rest for a moment, then pick another direction to move in.
switch (state) { case EnemyState.ROAM: attack_cooldown = 0; pursue_Speed = 2; if (roam_timer <= 0) { roam_direction = irandom(359); roam_timer = roam_timer_max; // Calculate a random point to roam to var roam_target_x = x + lengthdir_x(100, roam_direction); var roam_target_y = y + lengthdir_y(100, roam_direction); //make roaming path respects walls if (mp_grid_path(obj_gameControl.mp_grid, path, x, y, roam_target_x, roam_target_y, true)) { path_start(path, roam_speed, path_action_stop, false); } else { roam_timer = 1; // finding a valid path in the next step } } else { roam_timer--; } if (instance_exists(obj_player) && distance_to_object(obj_player) < 80) { // Detection range state = EnemyState.PURSUE; } break;
This lets them bumble about and not be just static. Once the player gets near enough, they will come after them, close the distance until they are in their attack range (this range will be unique to the enemy as some will be ranged enemies) but our sword Skelly here will rush in for some melee attacks. I won't break down all the code, but you can see how from the ROAM block in the state machine, its easier and tidy to keep these actions segregated. I've added a variable in for "sword_swing_time" so this will give the player a visual cue when the skeleton raises its sword before striking. the 10 frames should be plenty to hold on a small animation with the sword glinting. Although the mechanic isn't in yet, the player will be able to block attacks if timed correctly.
Having the enumerator will allow to easily add more logic to the enemies as the game expands. this could mean three different skeleton soldiers all using the same enumerator but with certain sections blocked out for "ranged skeleton" and "shield skeleton" shield skeletons could block players sword attacks for example where as those unfortunate to be without one can only get slapped by the players steel!
MAGIC
The player will be able to collect scrolls to learn different spells. Currently you can just find them lying around to make it easier for testing, but I plan to have them in chests, or purchasable from the village area. Spells, once acquired will use mana, you'll never lose them once learned, you just need to make sure you've got enough mana. I'll dive deeper on those in another devlog, but my reason for mentioning it here is that there is an override on the distance detection for all enemies if they get hit be a projectile. so if you decide to snipe ol bone bonce from the other side of the room, he will ignore his regular distance check and just come at you!
All the enemies base logic for moving around is being handled by a parent and the enemies are child objects of that parent. doing my very best to keep things nice and tidy so I can run the game fluidly without performance loses. There are a few things that help with that, the skeleton (and other enemies) will only check for the players position every few frames and randomises its timer so all enemies check for the position on different frames. This may be overkill as I don't plan on the player having to face down HORDES of enemies at one time, but its a nice to have and good to set up now. Trying to account for that down the road would mean a lot of re writes of how the enemies work and its just not worth the hassle.
Development moves swiftly on, I'll likely spend some more time working on some better sprites now, as it would be good to get the logic working with those too. specifically the skeletons attack telegraph. as that would allow me to open up some testing with the player using timed blocks. This won't be anything CRAZY, this is after all a small dungeon adventure! But I do want to make it look nice!