@startuml classes
scale 1

class Node {}

class AbilityManager {
    - abilities: <abilityId, Node>
    --
    + canStartAbility(abilityId: string): bool
    + getAbility(abilityId: string): Ability
}

''Store every effect of the ability
class Ability {
    .. Signals ..
    # onReady
    # onCast
    # onCooldownWindow
    # onWindow
    # onCooldown
    .. Exported attributes ..
    + id: string
    .. Class attributes ..
    '' The timer will be usefull for UI only
    - currentEffect: int
    - timer: float
    --
    '' Return application isAvailable
    + isAvailable(): bool
    '' Increment currentEffect so I can keep track of which effect is played
    + doApplication(): void
    '' Clear the ability (ie: currentEffect = 0 && timer = 0)
    '' if deep == true -> attribute.currentValue = attribute.defaultValue
    + clearAbility(deep: bool): void

    + startTimer(): void
    + setTimer(): void
}

'' The core of an ability
class AbilityApplication {
    .. Signals ..
    # onCast
    # onApplication
    # onCooldownWindow
    # onWindow
    # onCooldown
    '' I chose to add a onCooldownEnded because an AbilityApplication can't be ready
    '' Either it is started or not
    # onCooldownEnded
    .. Exported attributes ..
    + id: string
    + castDuration: float
    + cooldownWindowDuration: float
    + windowDuration: float
    + cooldownDuration: float
    --
    + doCast(): void
    + doApplication(): void
    + doCooldownWindow(): void
    + doWindow(): void
    + doCooldown(): void
}

class AbilityRequirement {
    .. Exported attributes ..
    + attributeId: string
    + valueRequired: float
}

'' Facilitator to facilitate attribute developpement
class AttributeManager {
    - attributes: Dictionary<attributeId, Node>
    --
    + getAttribute(attributeId: string): Attribute
}

class Attribute {
    .. Signals ..
    # onUpdated
    # onMinReached
    # onMaxReached
    .. Exported attributes ..
    + id: string
    + minValue: float
    + maxValue: float
    + defaultValue: float
    .. Class attributes ..
    - linkedAttributes: Dictionary<attributeId, Node>
    - currentValue: float
    --
    /'
    If you use a health/armor system, you can calculate the damage ratio 
    using owner.findAttribute("Armor") in health attribute and override 
    the change/remove function
    '/
    + changeValue(delta: float): float
    + getValue(): float
}

class AttributeModifier {
    .. Exported attributes ..
    '' ID of the attribute to stick to
    + attributeId: string
    + amountToChange: float
    .. Class attributes ..
    - twin: AttributeModifier
    --
    + activate(): void
    + desactivate(): void
    .. Usefull for AbilityApplication ..
    + apply_twin(): void
    + clear_twin(): void
}

class AttributeBurstEffect {
}

class AttributeDurationEffect {
    .. Exported attributes ..
    + duration: float
}

class AttributeInfiniteEffect {
}

Node --o "0..1" AttributeManager
Node --o "0..1" AbilityManager

AbilityManager --* "1..*" Ability

AttributeManager --* "1..*" Attribute
AttributeManager "0..1" o-- Ability

Ability --* "1..*" AbilityApplication

Attribute --o "*" AttributeModifier
Attribute " * linkedAttributes" <- Attribute

AbilityApplication -o "*" AttributeModifier
AbilityApplication --o "*" AbilityRequirement

AttributeModifier "0..1 twin" <- AttributeModifier
AttributeModifier <|-- AttributeBurstEffect
AttributeModifier <|-- AttributeDurationEffect
AttributeModifier <|-- AttributeInfiniteEffect

@enduml