using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Artemis.Core.DataModelExpansions; using Artemis.Storage.Entities.Module; using SkiaSharp; namespace Artemis.Core.Modules { /// /// Allows you to add support for new games/applications while utilizing your own data model /// public abstract class Module : Module where T : DataModel { /// /// The data model driving this module /// Note: This default data model is automatically registered upon plugin enable /// public T DataModel { get => InternalDataModel as T ?? throw new InvalidOperationException("Internal datamodel does not match the type of the data model"); internal set => InternalDataModel = value; } /// /// Gets or sets whether this module must also expand the main data model /// /// Note: If expanding the main data model is all you want your plugin to do, create a /// plugin instead. /// /// public bool ExpandsDataModel { get => InternalExpandsMainDataModel; set => InternalExpandsMainDataModel = value; } /// /// Override to provide your own data model description. By default this returns a description matching your plugin /// name and description /// /// public virtual DataModelPropertyAttribute GetDataModelDescription() { return new() {Name = Plugin.Info.Name, Description = Plugin.Info.Description}; } internal override void InternalEnable() { DataModel = Activator.CreateInstance(); DataModel.Feature = this; DataModel.DataModelDescription = GetDataModelDescription(); base.InternalEnable(); } } /// /// Allows you to add support for new games/applications /// public abstract class Module : DataModelPluginFeature { /// /// The modules display name that's shown in the menu /// public string? DisplayName { get; protected set; } /// /// The modules display icon that's shown in the UI accepts: /// /// Either set to the name of a Material Icon see ( for available /// icons) or set to a path relative to the plugin folder pointing to a .svg file /// /// public string? DisplayIcon { get; set; } /// /// Gets whether this module is activated. A module can only be active while its /// are met /// public bool IsActivated { get; internal set; } /// /// Gets whether this module's activation was due to an override, can only be true if is /// /// public bool IsActivatedOverride { get; private set; } /// /// Gets whether this module should update while is . When /// set to and any timed updates will not get called during an /// activation override. /// Defaults to /// public bool UpdateDuringActivationOverride { get; protected set; } /// /// A list of activation requirements /// Note: if empty the module is always activated /// public List ActivationRequirements { get; } = new(); /// /// Gets or sets the activation requirement mode, defaults to /// public ActivationRequirementType ActivationRequirementMode { get; set; } = ActivationRequirementType.Any; /// /// Gets or sets the default priority category for this module, defaults to /// /// public ModulePriorityCategory DefaultPriorityCategory { get; set; } = ModulePriorityCategory.Normal; /// /// Gets the current priority category of this module /// public ModulePriorityCategory PriorityCategory { get; internal set; } /// /// Gets the current priority of this module within its priority category /// public int Priority { get; internal set; } /// /// A list of custom module tabs that show in the UI /// public IEnumerable? ModuleTabs { get; protected set; } /// /// Gets whether updating this module is currently allowed /// public bool IsUpdateAllowed => IsActivated && (UpdateDuringActivationOverride || !IsActivatedOverride); internal DataModel? InternalDataModel { get; set; } internal bool InternalExpandsMainDataModel { get; set; } internal ModuleSettingsEntity? SettingsEntity { get; set; } /// /// Called each frame when the module should update /// /// Time in seconds since the last update public abstract void Update(double deltaTime); /// /// Called each frame when the module should render /// /// Time since the last render /// /// public abstract void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo); /// /// Called when the are met or during an override /// /// /// If true, the activation was due to an override. This usually means the module was activated /// by the profile editor /// public abstract void ModuleActivated(bool isOverride); /// /// Called when the are no longer met or during an override /// /// /// If true, the deactivation was due to an override. This usually means the module was deactivated /// by the profile editor /// public abstract void ModuleDeactivated(bool isOverride); /// /// Evaluates the activation requirements following the and returns the result /// /// The evaluated result of the activation requirements public bool EvaluateActivationRequirements() { if (!ActivationRequirements.Any()) return true; if (ActivationRequirementMode == ActivationRequirementType.All) return ActivationRequirements.All(r => r.Evaluate()); if (ActivationRequirementMode == ActivationRequirementType.Any) return ActivationRequirements.Any(r => r.Evaluate()); return false; } internal virtual void InternalUpdate(double deltaTime) { StartUpdateMeasure(); if (IsUpdateAllowed) Update(deltaTime); StopUpdateMeasure(); } internal virtual void InternalRender(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo) { StartRenderMeasure(); Render(deltaTime, canvas, canvasInfo); StopRenderMeasure(); } internal virtual void Activate(bool isOverride) { if (IsActivated) return; IsActivatedOverride = isOverride; ModuleActivated(isOverride); IsActivated = true; } internal virtual void Deactivate(bool isOverride) { if (!IsActivated) return; IsActivatedOverride = false; IsActivated = false; ModuleDeactivated(isOverride); } internal virtual void Reactivate(bool isDeactivateOverride, bool isActivateOverride) { if (!IsActivated) return; Deactivate(isDeactivateOverride); Activate(isActivateOverride); } internal void ApplyToEntity() { if (SettingsEntity == null) SettingsEntity = new ModuleSettingsEntity(); SettingsEntity.ModuleId = Id; SettingsEntity.PriorityCategory = (int) PriorityCategory; SettingsEntity.Priority = Priority; } } /// /// Describes in what way the activation requirements of a module must be met /// public enum ActivationRequirementType { /// /// Any activation requirement must be met for the module to activate /// Any, /// /// All activation requirements must be met for the module to activate /// All } /// /// Describes the priority category of a module /// public enum ModulePriorityCategory { /// /// Indicates a normal render priority /// Normal, /// /// Indicates that the module renders for a specific application/game, rendering on top of normal modules /// Application, /// /// Indicates that the module renders an overlay, always rendering on top /// Overlay } }