I do a bit of solo game development and always get stuck on some part of the architecture where I can't decide what the best way to tackle a problem is. I've just run into a good architecture problem, and I'm hoping I'll be able to use lessons learned from it down the road. I'll abstract it down a bit to a theoretical problem:
Say my game has two abilities, "Heal Player" and "Stun Enemies". Each of the abilities is reusable with a cooldown. The game will need a separate button to activate each of the abilities.
The core class for gameplay is called GameplayCore. My GUI is all contained in an HUD class, and the enemies are all controlled by an EnemyManager. The Player class contains the player's health. GameplayCore creates and stores the HUD instance, EnemyManager instance, and Player instance.
Obviously I want to separate the UI code from the functionality code. So say on the GUI side I've got HealButton, StunButton, and HealthBar classes that define the appearance and behavior of those controls. On the functionality side, I've got the Player class with a health property, an AbstractAbility class that defines shared ability behavior including cooldown, and the HealAbility and StunAbility classes that extend it to define concrete implementations.
To keep GameplayCore from getting too bloated, I make an AbilitySet class to create and manage the HealAbility and StunAbility instances, and make an instance of AbilitySet in GameplayCore.
Here's where things start to fall apart. I want to be able to change the appearance of the buttons as their cooldowns elapse. I want the cooldown code to be self-contained in AbstractAbility, so HealAbility and StunAbility need references to their corresponding buttons. Now I need to pass those references back from HUD to GameplayCore and then into the constructor of AbilitySet so they can be passed into the constructors for the ability buttons. HealAbility needs references to the HealthBar (in HUD) and the Player (in GameplayCore). StunAbility needs a reference to EnemyManager so it can retrieve the collection of enemies to loop through and stun them. But HUD needs references to the HealAbility and StunAbility so it can call them when the buttons are pressed.
Everything has gotten pretty tangled; there is too much coupling and I'm honestly not sure where the best place to construct the ability classes is. I'm very tempted to merge all of the ability code into the ability buttons, which would greatly simplify everything, but then I'd have important functionality code mixed into a UI class.
What's a good organizational approach to this problem? What are some good architectural strategies I could use to keep the code from getting too tangled? Is my entire approach heavily flawed?
edit: I should note this particular project is in ActionScript 3, which does include an event system (with its own quirks and limitations).
I want the cooldown code to be self-contained in AbstractAbility, so HealAbility and StunAbility need references to their corresponding buttons.
Why? The UI can ask instances of AbstractAbility for the cooldown status, and the UI already has these references.