Hey there, gonna be talking about the technical parts of my Retro Cowboy Movement Shooter, TIME COWBOY.

(By the way, the demo is out!)

I'll be talking about Action Meter, Weapon System and... Mister Milton.

To begin, let's start with one of the core features, the Action Meter.


Action Meter


 The Action Meter in Time Cowboy is like Ultrakill's Style Meter and Hotline Miami's Action Meter combined.

It gives you points based on a lot of factors, including, but not limited to

There is more, but these are what you'll usually get.

The way the game and Action Meter gets this information is really easy as it uses Godot's Signals.

Signals

If you don't know what Signals are, they let parts of your game have specific functions called when a 'Signal' is emitted. For example, if a button is pressed, you can connect it to a function that plays a sound effect or in our case a Parry triggering the Action Meter.

These actions will cause their own respective signal to be emitted.

With Hurting and Killing, when those signals are emitted, the custom data is a HitResult, a data class that is passed every time an Enemy or the Player is given to be damaged.

It contains info on the Attacker, Damage Type and obviously Damage Amount.

Calculations

In short, the Action Meter is connected to the signals and gets that data, it then parses that data between a couple of functions.

parse, parse_hit_result, parse_weapon_special

Parse calls the other two functions, both of which return the accumulated points based on whether you did a weapon combo, parried, killed an enemy with an explosion or fists, etc.

Then it also calculates the amount of points to give you based on mostly what I said above (mid-air checks, 65 point check), but also checks if you haven't used the same weapon more than 25 times.

As for 25, it gives space for the player to experiment without being punished for repeating actions too soon.

If you have, it then adds a 25% penalty (stackable) to the 'staleness_modifier' and divides your accumulated_points.

if last_weapon_used != plyr.weapon:
      accumulate_points += 5
      last_weapon_used = plyr.weapon
      times_reused_weapon = 0
      staleness_reduction = 1.0
else:
      times_reused_weapon += 1
     
      if times_reused_weapon >= 25:
            staleness_reduction += 0.25
 
points = accumulate_points / staleness_reduction

As for how I decided which number to assign,

it's completely arbitrary 💀

I basically just assigned it based on whether I thought it took more skill or time to do.
Balance was in mind while assigning, but certain actions I made unbalanced.

So performing those actions and combining it with a bunch of other actions, it'll give you a ton of points, but these actions are pretty situational or take some skill to pull off, so that makes it balanced (mostly).

Pretty much the Action Meter is just there to push Players into playing the fun way, going fast, blasting people and switching weapons.

Moving on to Weapons.


Weapon Code


Weapons in Time Cowboy aren't really "real".

They aren't guns with stats, they are very modular systems where the visuals and behavior are very loosely connected, giving me and modders massive freedom to tweak the guns.

Basically, the Weapon class named 'Weapon', which is a class for weapons, called 'Weapon', which stores stats and other important details for weapons that is loaded in when equipping a weapon.

KennyNL's System

KennyNL's (well-known asset creator) version of this System that game with the FPS Template he created, it contained the bare minimum.

Sounds good and all, which it is, but it needed more.

The system didn't support animations, instead it was just lerped between 2 positions. It also didn't support custom scripts for Weapon Specific mechanics.

Which is why I began to modify it for the game's needs.

The Modified System

My version of this system is a lot more expandable, containing everything (but fire-rate, it depends on the animation's length), animations, custom scripts and other needed properties (ex: damage type, shot count).

Coding in the custom scripts allowed to me to add a ton of weapon specific mechanic, for example, the Revolver's Bullet Time or 6 Shot, 3 Shot, the Shotgun's Airblast or Explosive Bullets or the Fists' Uppercut and Healing Mechanic.

The original system simply did not allow for this, if I hadn't made these changes I'd have to... hardcode it.

Which, I think is obvious why I don't want to do that, but basically hardcoding stuff makes it hard to edit and just bloats scripts for no reason.

As for animations, that was a hard one as I didn't really have much experience in loading Animation Libraries.

My initial method was:

Equip Weapon > Delete Previous Animation Library (if applicable) > Load new Animation Library

Which caused a whole heap of issues, like Animations not playing, being loaded incorrectly or straight up crashing the Game silently.

Which I then came up with a new method of:

Load Weapon > Load Animation Library (if havent)

Yeah, so I'd just load the library and kept it the entire time the scene is loaded.

Simple fix really, but wow took me a while to think of it.

Alright, let's see, what's next...

Oh no.


Mister Milton


Mister Milton.

The first boss in the game and the dumbest character I've written.

To summarize Milton, he is a very goofy and egotistical cowboy that moves like a looney tunes character, and he's always yelling.

His boss fight is (accidentally) just a "You, but better" fight, but reversed.
He's you, but stupider and slightly below your skill level.

His AI was not technically challenging to make, but it took awhile to make balanced and fun.

Attack Pattern

Milton's attack are listed in order of his attack pattern.

  1. Revolver Fanning
    Milton just spams Projectiles in this attack and moves erratically

  2. Shotgun War Crime/Shotgun Slamfire
    Milton takes out his shotgun and shoots 9 projectiles while standing still.
    In Tuberculosis, he'll move toward you if you're too far.

    Also, like with the Shotgunner Enemy, there is always one accurate projectile.

  3. Charge
    Oh boy.
    Milton charges headfirst, literally, at you while doing a battle cry screaming "Hank", the Protagonist's name.

    This attack is unparryable, cause if everything is parryable, then that makes some fights too easy and just removes the tension.
    Players should be having fun and occasionally parrying, not relying on it like it's Elden Ring.

    If he hits you, he automatically swaps to Revolver Fanning
    If he doesn't, he stops moving for a couple seconds to give a moment to deal damage and then switches to Revolver Fanning,

    In Tuberculosis, he has a chance to do the Charge 2x.

Revolver Fanning and Charge took a bit to balance or fix, as Revolver Fanning had to be oppressive enough to make the player keep their distance, but not so oppressive you have to play like you're in the Sans fight in Undertale.

As for the Charge attack, it was pretty much balancing it and a bug I found when switching from Physic Proccessed Navigation to Timer Based Navigation for Performance, where he'd continue to move even though he had the 'can_move' flag disabled.

Which was fixed by adding a check if the 'can_move" flag is enabled, like it should've been.

What was hard to code was having the AttackHandler and NavigationHandler interact, as they were designed to not interact at all to eliminate any bloat.

The challenge was making them interact, but then I remembered...

Not everything has to be modular or super reusable.

I just made both systems just grab each other from the ModulAI Variables, which does store both NavigationHandler and AttackHandler, then it was smooth sailing from there.

Animations

Alright, so I mentioned this in the last post, where I mentioned how ModulAI supported Animated 3D Sprites more than it did for 3D Models.

Since we can't depend on ModulAI's SpriteAnimator, I had to resort to calling Milton's Model's AnimationPlayer by `get_tree()`.

Anytime I need to change animations, I would call the AnimationPlayer

get_tree().call_group("model_anim","play","charge_attack")

Which isn't bad, but is annoying to type out everytime and can sometimes make portions of code hard to read.

The One Bug I'll never fix

There comes a point time where you have to embrace the jank.

And this is that time.

Remember Milton's charge attack?

That attack naturally will make him chase you, but not only on ground, he will chase you into the air just to hit you

This is cause his velocity is set to your direction, no fancy path finding, no gravity checks, nothing.

Just you and his pea sized brain trying to hit you while he’s doing a battle cry.

Best part is his Shotgun attack, remember that? 

Where on Tuberculosis, if you go too far, he'll move toward you.

Instead of making him pathfind his way toward you, I got lazy and just reused the charge code and made it less crazy.

But I didn't patch it out for the shotgun either.

Why won't I fix this?

Cause it's stupid and funny, plus it’s funny when you try to OOB and turn around and he’s like right behind you in the air.

 

And, I think that's all I have today.
I really do enjoy writing these posts, even if it does take me a while to do so.
Either way,


Thank you for reading.


By the way, if you want to Milton's AI in action, get the game on Steam!
And, join the Discord if you want to see sneak peeks of the game or more frequent updates.