Skip to content

PointHelper

Aarnow edited this page Sep 16, 2024 · 35 revisions

Introduction

Sur Nova Life, ce que nous appelons "les points" sont les cercles bleus avec lesquels les joueurs peuvent interagir dès que leur personnage entre en collision avec ceux-ci.
PointHelper simplifie la création, la gestion et l'enregistrement de point dynamique en base de données en passant par le module externe ${\color{Magenta}\normalsize{\textsf{AAMenu}}}$.

PatternData

Pour commencer, il est crucial de comprendre la logique sur laquelle je me suis appuyé pour concevoir PointHelper.
Nous avons des type de points (votre classe), des modèles (vos instances), puis les points eux-mêmes.
Chacune de vos classes doit hériter de l'interface ${\color{LimeGreen}\normalsize{\textsf{PatternData}}}$.
Cette interface garantit à ModKit que votre classe respecte une certaine structure:

//Inutile de créer l'interface, elle est déjà dans modKit prêt à l'emploi, je l'expose uniquement pour faciliter votre compréhension.

public interface PatternData
{
    //Identifiant unique de l'instance de votre modèle
    int Id { get; set; }

    //Nom du type de point
    string TypeName{ get; set; }

    //Nom du modèle
    string PatternName{ get; set; }

    //Référence à ModKit pour profiter de ses fonctionnalités depuis votre classe
    ModKit Context { get; set; }

    //Action à effectuer lorsqu'un joueur interagit avec le point
    void OnPlayerTrigger(Player player);

    //Applique les valeurs récupérés en base de données lors de la génération du point
    Task SetProperties(int id);

    //Récupère les NPoints du type en cours.
    //Cette fonction doit ensuite rediriger vers la fonction "SelectNPoint" listant les NPoints
    Task GetNPoints(Player player);

    //Création d'un nouveau modèle depuis AAMenu
    void SetPatternData(Player player);

    //Sélection d'un modèle depuis AAMenu avant de rediriger vers  SelectPattern
    Task GetPatternData(Player player, bool forEdit);

    //Panel permettant au joueur de créer ou générer un point depuis un modèle existant
    void CreateOrGenerate(Player player);
}

Classe de base

Ne prenez pas peur, le code peut sembler massif mais il est relativement simple et assure une bonne structure.
La majorité permet d'indiquer à ModKit et plus précisément au module externe ${\color{Magenta}\normalsize{\textsf{AAMenu}}}$ comme interagir avec vos modèles et vos points.
Les fonctions englobés dans la ${\color{Goldenrod}\normalsize{\textsf{region DO NOT EDIT}}}$ sont prête et n'ont pas besoin d'être modifié.
Les fonctions englobés dans la ${\color{Goldenrod}\normalsize{\textsf{region REPLACE YOUR CLASS/TYPE AS PARAMETER}}}$ nécessite le remplacement du paramètre de type ${\color{LimeGreen}\normalsize{\textsf{MyPointPattern}}}$ par votre Type.
Vous pouvez copier/coller ce code pour chaque type de point que vous souhaitez réaliser.

//Vous aurez probablement des références suviantes:
using Life.Network;
using Life.UI;
using SQLite;
using System.Threading.Tasks;
using ModKit.Helper;
using ModKit.Helper.PointHelper;
using mk = ModKit.Helper.TextFormattingHelper;
using System.Collections.Generic;
using System.Linq;
// - - - - - - - - - - - - - - - - -

public class MyPointPattern : ModKit.ORM.ModEntity<MyPointPattern>, PatternData
{
    [AutoIncrement][PrimaryKey] public int Id { get; set; }
    public string TypeName { get; set; }
    public string PatternName { get; set; }

    //Declare your other properties here

    [Ignore] public ModKit.ModKit Context { get; set; }

    public MyPointPattern() { }
    public MyPointPattern(bool isCreated)
    {
        TypeName = nameof(MyPointPattern);
    }

    /// <summary>
    /// Applies the properties retrieved from the database during the generation of a point in the game using this model.
    /// </summary>
    /// <param name="patternId">The identifier of the pattern in the database.</param>
    public async Task SetProperties(int patternId)
    {
        var result = await Query(patternId);

        Id = patternId;
        TypeName = nameof(MyPointPattern);
        PatternName = result.PatternName;

        //Add your other properties here
    }

    /// <summary>
    /// Contains the action to perform when a player interacts with the point.
    /// </summary>
    /// <param name="player">The player interacting with the point.</param>
    public void OnPlayerTrigger(Player player)
    {
    }

    /// <summary>
    /// Triggers the function to begin creating a new model.
    /// </summary>
    /// <param name="player">The player initiating the creation of the new model.</param>
    public void SetPatternData(Player player)
    {
        //Set the function to be called when a player clicks on the “create new model” button
        SetName(player);
    }

    /// <summary>
    /// Displays all properties of the pattern specified as parameter.
    /// The user can select one of the properties to make modifications.
    /// </summary>
    /// <param name="player">The player requesting to edit the pattern.</param>
    /// <param name="patternId">The ID of the pattern to be edited.</param>
    public async void EditPattern(Player player, int patternId)
    {
        MyPointPattern pattern = new MyPointPattern(false);
        pattern.Context = Context;
        await pattern.SetProperties(patternId);

        Panel panel = Context.PanelHelper.Create($"Modifier un {pattern.TypeName}", UIPanel.PanelType.Tab, player, () => EditPattern(player, patternId));


        panel.AddTabLine($"{mk.Color("Nom:", mk.Colors.Info)} {pattern.PatternName}", _ => {
            pattern.SetName(player, true);
        });
        //Add tablines for your other properties here

        panel.NextButton("Sélectionner", () => panel.SelectTab());
        panel.PreviousButton();
        panel.CloseButton();

        panel.Display();
    }

    /// <summary>
    /// Allows the player to set a name for the pattern, either during creation or modification.
    /// </summary>
    /// <param name="player">The player interacting with the panel.</param>
    /// <param name="inEdition">A flag indicating if the pattern is being edited.</param>
    public void SetName(Player player, bool isEditing = false)
    {
        Panel panel = Context.PanelHelper.Create($"{(!isEditing ? "Créer" : "Modifier")} un modèle de {TypeName}", UIPanel.PanelType.Input, player, () => 
        SetName(player));

        panel.TextLines.Add("Donner un nom à votre modèle");
        panel.inputPlaceholder = "3 caractères minimum";

        if (!isEditing)
        {
            panel.NextButton("Suivant", () =>
            {
                if (panel.inputText.Length >= 3)
                {
                    PatternName = panel.inputText;
                    //function to call for the following property
                    // If you want to generate your point
                    // await Save();
                    // ConfirmGeneratePoint(player, this);
                }
                else
                {
                    player.Notify("Attention", "Vous devez donner un nom à votre modèle (3 caractères minimum)", Life.NotificationManager.Type.Warning);
                    SetName(player);
                }
            });
        }
        else
        {
            panel.PreviousButtonWithAction("Confirmer", async () =>
            {
                if (panel.inputText.Length >= 3)
                {
                    PatternName = panel.inputText;
                    if (await Save()) return true;
                    else
                    {
                        player.Notify("Erreur", "échec lors de la sauvegarde de vos changements", Life.NotificationManager.Type.Error);
                        return false;
                    }
                }
                else
                {
                    player.Notify("Attention", "Vous devez donner un nom à votre modèle (3 caractères minimum)", Life.NotificationManager.Type.Warning);
                    return false;
                }
            });
        }
        panel.PreviousButton();
        panel.CloseButton();

        panel.Display();
    }

    #region REPLACE YOUR CLASS/TYPE AS PARAMETER
    /// <summary>
    /// Displays a panel allowing the player to select a pattern from a list of patterns.
    /// </summary>
    /// <param name="player">The player selecting the pattern.</param>
    /// <param name="patterns">The list of patterns to choose from.</param>
    /// <param name="configuring">A flag indicating if the player is configuring.</param>
    public void SelectPattern(Player player, List<MyPointPattern> patterns, bool configuring)
    {
        Panel panel = Context.PanelHelper.Create("Choisir un modèle", UIPanel.PanelType.Tab, player, () => SelectPattern(player, patterns, configuring));

        foreach (var pattern in patterns)
        {
            panel.AddTabLine($"{pattern.PatternName}", _ => { });
        }
        if (patterns.Count == 0) panel.AddTabLine($"Vous n'avez aucun modèle de {TypeName}", _ => { });

        if (!configuring && patterns.Count != 0)
        {
            panel.CloseButtonWithAction("Confirmer", async () =>
            {
                if (await Context.PointHelper.CreateNPoint(player, patterns[panel.selectedTab])) return true;
                else return false;
            });
        }
        else
        {
            panel.NextButton("Modifier", () => {
                EditPattern(player, patterns[panel.selectedTab].Id);
            });
            panel.NextButton("Supprimer", () => {
                ConfirmDeletePattern(player, patterns[panel.selectedTab]);
            });
        }

        panel.AddButton("Retour", ui =>
        {
            AAMenu.AAMenu.menu.AdminPointsSettingPanel(player);
        });
        panel.CloseButton();

        panel.Display();
    }

    /// <summary>
    /// Confirms the generation of a point with a previously saved pattern.
    /// </summary>
    /// <param name="player">The player confirming the point generation.</param>
    /// <param name="pattern">The pattern to generate the point from.</param>
    public void ConfirmGeneratePoint(Player player, MyPointPattern pattern)
    {
        Panel panel = Context.PanelHelper.Create($"Modèle \"{pattern.PatternName}\" enregistré !", UIPanel.PanelType.Text, player, () => 
        ConfirmGeneratePoint(player, pattern));

        panel.TextLines.Add($"Voulez-vous générer un point sur votre position avec ce modèle \"{PatternName}\"");

        panel.CloseButtonWithAction("Générer", async () =>
        {
            if (await Context.PointHelper.CreateNPoint(player, pattern)) return true;
            else return false;
        });
        panel.PreviousButton();
        panel.CloseButton();

        panel.Display();
    }
    #endregion

    #region DO NOT EDIT
    /// <summary>
    /// Base panel allowing the user to choose between creating a pattern from scratch
    /// or generating a point from an existing pattern.
    /// </summary>
    /// <param name="player">The player initiating the creation or generation.</param>
    public void CreateOrGenerate(Player player)
    {
        Panel panel = Context.PanelHelper.Create($"Créer ou générer un {TypeName}", UIPanel.PanelType.Text, player, () => CreateOrGenerate(player));

panel.TextLines.Add(mk.Pos($"{mk.Align($"{mk.Color("Générer", mk.Colors.Info)} utiliser un modèle existant. Les données sont partagés entre les points utilisant un même modèle.", mk.Aligns.Left)}", 5));
panel.TextLines.Add("");
panel.TextLines.Add($"{mk.Align($"{mk.Color("Créer:", mk.Colors.Info)} définir un nouveau modèle de A à Z.", mk.Aligns.Left)}");

        panel.NextButton("Créer", () =>
        {
            SetPatternData(player);
        });
        panel.NextButton("Générer", async () =>
        {
            await GetPatternData(player, false);
        });
        panel.AddButton("Retour", ui =>
        {
            AAMenu.AAMenu.menu.AdminPointsPanel(player);
        });
        panel.CloseButton();

        panel.Display();
    }

    /// <summary>
    /// Retrieves all patterns before redirecting to a panel allowing the user various actions (CRUD).
    /// </summary>
    /// <param name="player">The player initiating the retrieval of pattern data.</param>
    /// <param name="configuring">A flag indicating if the user is configuring.</param>
    public async Task GetPatternData(Player player, bool configuring)
    {
        var patterns = await QueryAll();
        SelectPattern(player, patterns, configuring);
    }

    /// <summary>
    /// Confirms the deletion of the specified pattern.
    /// </summary>
    /// <param name="player">The player confirming the deletion.</param>
    /// <param name="patternData">The pattern data to be deleted.</param>
    public async void ConfirmDeletePattern(Player player, PatternData patternData)
    {
        var pattern = await Query(patternData.Id);

        Panel panel = Context.PanelHelper.Create($"Supprimer un modèle de {pattern.TypeName}", UIPanel.PanelType.Text, player, () => 
        ConfirmDeletePattern(player, patternData));

        panel.TextLines.Add($"Cette suppression entrainera également celle des points.");
        panel.TextLines.Add($"Êtes-vous sûr de vouloir supprimer le modèle \"{pattern.PatternName}\" ?");

        panel.PreviousButtonWithAction("Confirmer", async () =>
        {
            if (await Context.PointHelper.DeleteNPointsByPattern(player, pattern))
            {
                if (await pattern.Delete())
                {
                    return true;
                }
                else
                {
                    player.Notify("Erreur", $"Nous n'avons pas pu supprimer le modèle \"{PatternName}\"", Life.NotificationManager.Type.Error, 6);
                    return false;
                }
            }
            else
            {
                player.Notify("Erreur", "Certains points n'ont pas pu être supprimés.", Life.NotificationManager.Type.Error, 6);
                return false;
            }
        });
        panel.PreviousButton();
        panel.CloseButton();

        panel.Display();
    }

    /// <summary>
    /// Retrieves all NPoints before redirecting to a panel allowing various actions by the user.
    /// </summary>
    /// <param name="player">The player retrieving the NPoints.</param>
    public async Task GetNPoints(Player player)
    {
        var points = await NPoint.Query(e => e.TypeName == nameof(MyPointPattern));
        SelectNPoint(player, points);
    }

    /// <summary>
    /// Lists the points using this pattern.
    /// </summary>
    /// <param name="player">The player selecting the points.</param>
    /// <param name="points">The list of points to choose from.</param>
    public async void SelectNPoint(Player player, List<NPoint> points)
    {
        var patterns = await QueryAll();
        Panel panel = Context.PanelHelper.Create($"Points de type {nameof(MyPointPattern)}", UIPanel.PanelType.Tab, player, () => SelectNPoint(player, points));

        if (points.Count > 0)
        {
            foreach (var point in points)
            {
                var currentPattern = patterns.FirstOrDefault(p => p.Id == point.PatternId);
                panel.AddTabLine($"point n° {point.Id}: {(currentPattern != default ? currentPattern.PatternName : "???")}", _ => { });
            }

            panel.NextButton("Voir", () =>
            {
                DisplayNPoint(player, points[panel.selectedTab]);
            });
            panel.NextButton("Supprimer", async () =>
            {
                await Context.PointHelper.DeleteNPoint(points[panel.selectedTab]);
                await GetNPoints(player);
            });
        }
        else
        {
            panel.AddTabLine($"Aucun point de ce type", _ => { });
        }
        panel.AddButton("Retour", ui =>
        {
            AAMenu.AAMenu.menu.AdminPointsSettingPanel(player);
        });
        panel.CloseButton();

        panel.Display();
    }

    /// <summary>
    /// Displays the information of a point and allows the user to modify it.
    /// </summary>
    /// <param name="player">The player viewing the point information.</param>
    /// <param name="point">The point to display information for.</param>
    public async void DisplayNPoint(Player player, NPoint point)
    {
        var pattern = await Query(p => p.Id == point.PatternId);
        Panel panel = Context.PanelHelper.Create($"Point n° {point.Id}", UIPanel.PanelType.Tab, player, () => DisplayNPoint(player, point));

        panel.AddTabLine($"Type: {point.TypeName}", _ => { });
        panel.AddTabLine($"Modèle: {(pattern[0] != null ? pattern[0].PatternName : "???")}", _ => { });
        panel.AddTabLine($"", _ => { });
        panel.AddTabLine($"Position: {point.Position}", _ => { });


        panel.AddButton("TP", ui =>
        {
            Context.PointHelper.PlayerSetPositionToNPoint(player, point);
        });
        panel.AddButton("Définir pos.", async ui =>
        {
            await Context.PointHelper.SetNPointPosition(player, point);
            panel.Refresh();
        });
        panel.PreviousButton();
        panel.CloseButton();

        panel.Display();
    }
    #endregion
}

Fonctions à compléter ou créer

Dans le code partagé précédemment, les fonctions en dehors des regions doivent être complétés.

  • ${\color{NavyBlue}\normalsize{\textsf{SetProperties(int patternId)}}}$ : Applique les propriétés récupérées en base de données lors de la génération d'un point dans le jeu.
  • ${\color{NavyBlue}\normalsize{\textsf{OnPlayerTrigger(Player player)}}}$ : Contient l'action à effectuer lorsqu'un joueur interagit avec le point.
  • ${\color{NavyBlue}\normalsize{\textsf{SetPatternData(Player player)}}}$ : Déclenche la fonction pour commencer à créer un nouveau modèle.
  • ${\color{NavyBlue}\normalsize{\textsf{EditPattern(Player player, int patternId)}}}$ : L'utilisateur peut sélectionner l'une des propriétés pour effectuer des modifications à des fins d'édition.
  • ${\color{NavyBlue}\normalsize{\textsf{SetName(Player player, bool isEditing = false)}}}$ : Permet au joueur de définir le nom du modèle, soit lors de la création, soit lors de la modification selon la valeur du flag (isEditing) en paramètre.

Vous devrez très probablement créer d'autres fonctions similaire à ${\color{NavyBlue}\normalsize{\textsf{SetName}}}$ pour compléter la configuration de vos modèles au travers du module externe ${\color{Magenta}\normalsize{\textsf{AAMenu}}}$.

Enregistrer votre type de point en base de données

Utilisons l'ORM de ModKit en appelant la méthode ${\color{NavyBlue}\normalsize{\textsf{RegisterTable}}}$ afin de créer la table qui va stocker les modèles de votre point.
La méthode ${\color{NavyBlue}\normalsize{\textsf{AddPattern}}}$ permet à ModKit de reconstruire vos points.

public override void OnPluginInit()
{
    base.OnPluginInit();
    Orm.RegisterTable<MyPointsPattern>();
    PointHelper.AddPattern("MyPointsPattern", new MyPointsPattern(false));
}

Intégrer votre type de point à AAMenu

Avec la fonction ${\color{NavyBlue}\normalsize{\textsf{AddBuilder}}}$, votre type de point sera accessible depuis le menu administration.
Les admins de votre serveurs pourront facilement créer/modifier/supprimer les modèles et les points associés à votre type de point.

public override void OnPluginInit()
{
    AAMenu.AAMenu.menu.AddBuilder(PluginInformations, "TextPoint", new TextPoint(false), this);
}

📒 Wiki

🧱 AAMenu

🔗 ORM

⚡️ EventHelper

🚚 JobHelper

🌀 PointHelper

🎭 SkinHelper

📂 PanelHelper

✒️ TextFormattingHelper

💜 DiscordHelper

🌱 NetworkAreaHelper

🧰 CraftHelper

🚕 VehicleHelper

📡 ProximityHelper

⚙️ OverrideHelper

🧾 JsonHelper

📦 Utilitaires

Clone this wiki locally