✨SagaLoreStats✨Lore-driven RPG attributes with JS custom properties icon

✨SagaLoreStats✨Lore-driven RPG attributes with JS custom properties -----

Forge a Powerful and Flexible Item Stat System for Your Minecraft Server!



✨ SagaLoreStats Plugin ✨
[1.20.1-1.21.5]
Your Ultimate Lore-Driven Custom Attribute System for Minecraft!

These plugins are extensions for SagaLoreStats
SagaSets

LiteLevels
SagaAccessories


Introduction

SagaLoreStats is a powerful custom attribute plugin for Minecraft servers, driven by item Lore recognition. It allows server administrators to create a deep and engaging item system by granting players various attribute bonuses (like Attack Power, Defense, Health, etc.) based on specific text found in an item's Lore.

Core Features

  • Lore-Driven Attribute System: Attributes are dynamically applied by recognizing specific text patterns in item Lores.
  • Diverse Attribute Types: Supports Attack/Defense, Other, Update (time-based), and Runtime (script-based) attributes.
  • Customizable Attribute Formulas: Define your own calculation formulas for attributes, offering fine-grained control over game balance.
  • Combat Power System: Calculates a player's overall combat strength based on their equipped attributes.
  • Equipment Restriction System: Set limits on gear usage based on level, class, item type, and more.
  • Attribute GUI Panel: An intuitive interface (`/sls stats`) for players to view their current attributes.
  • Powerful Scripting System: Extend and create custom attribute functionalities using JavaScript.
  • PlaceholderAPI Support: Provides a rich set of placeholders for integration with other plugins.
  • Probability-Based Trigger System: Implement attributes with chance-based effects (e.g., Critical Hit, Dodge).
  • Advanced Combat Attributes: Includes Armor Penetration, Armor Strength, Armor Break, Critical Evasion, Crit Damage Resistance, and more.
  • Vitality Attribute System: Manage health bonuses, health regeneration per second, and other life-related stats.
  • MythicMobs Compatibility: Seamlessly integrates with MythicMobs.

Attribute System Explained

Attribute Types & Priority
SagaLoreStats categorizes attributes to manage their application and calculation order. Higher priority numbers are processed first.

  1. Attack & Defense Attributes (ATTACK_OR_DEFENSE)
    • Examples: Physical Damage (attack), Physical Defense (defense), Critical Chance (crit), Critical Damage (crit_damage), Dodge Chance (dodge), Lifesteal Chance (vampire), Armor Penetration (armor_penetration), Armor Strength (armor_strength), Armor Break (armor_break), Critical Evasion (crit_resist), Crit Damage Resistance (crit_damage_resist), Damage Bonus (damage_bonus), Health Bonus (health_bonus).
    • Default Priorities: Dodge (100) > Crit Resist (95) > Crit (90) > Crit Damage Resist (85) > Vampire (80) > Damage Bonus (75) > Attack (70) > Armor Penetration (65) > Defense (60) > Armor Break (58) > Armor Strength (55) > Health Bonus (15).

  2. Other Attributes (OTHER)
    • Examples: PvP Damage (pvp_damage), PvE Damage (pve_damage), Hit Rate (hit_rate), Shield Block Chance (shield_block).
    • Default Priorities: Shield Block (50) > Hit Rate (40) > PvP Damage (30) > PvE Damage (20).

  3. Update Attributes (UPDATE)
    • Examples: Health (health), Movement Speed Bonus (moving), Percentage Health Regen (health_regen), Health Regen Per Second (health_regen_second), Dynamic Attribute Test (dynamic_attr_test).
    • Functionality: These attributes are updated via a scheduled task (default: every 5 seconds).
    • Default Priorities: Dynamic Attribute Test (15) > Health (10) > Movement Speed (5) > Health Regen Per Second (2) > Percentage Regen (1).

  4. Runtime Attributes (RUNTIME)
    • Functionality: Custom, periodically triggered attributes primarily implemented and extended via the Scripting System.


    Attribute Recognition & Loading
    1. Lore Parsing:
      • The `LoreParser` uses regular expressions to extract attribute text from item Lores.
      • Supports various formats like `Attribute Name: +Value` or `+Value Attribute Name`.
      • Formats are customizable in `format.yml`.

    2. Attribute Update Triggers:
      • When a player joins the server (configurable).
      • When a player equips or switches items (configurable).
      • Periodically via a scheduled task (default: every 5 seconds).

    3. Probability Attribute Trigger Mechanism:
      • Chance-based attributes (Dodge, Crit, Vampire, etc.) use a random number generator to determine if they activate.
      • The `FormulaExecutor.isProbabilityAttribute` method defines which attributes are probabilistic.


      Attribute Formula System
      1. Formula Parser:
        • Supports variable substitution:
          • `{entityA:AttributeName}`: Attacker's attribute value.
          • `{entityB:AttributeName}`: Target's attribute value.
        • Supports basic variables: `{attackerDamage}`, `{entityDamage}`, `{value}` (current attribute's value), `{damage}` (incoming/outgoing damage).
        • Calculations are performed by `ExpressionEvaluator`.
        • Supports math functions like `min`, `max`.

      2. Example Formulas:
        • Critical Hit Damage: `{attackerAttack}*({entityA:crit_damage}-{entityB:crit_damage_resist})/100` (Note: This is likely intended to be based on the base damage, not attacker's total attack. A more common crit damage formula would be `({damage} * ({entityA:crit_damage}/100)) - ({damage} * ({entityB:crit_damage_resist}/100))`, or simpler if crit_damage is a multiplier.) The original was `{attackerAttack}*({entityA:暴击倍率}-{entityB:暴击抵抗})/100`
        • Lifesteal: `{damage}*{value}/100`
        • Armor Penetration: `min({value}, {entityB:defense})` (Reduces target's defense by `value`, capped at target's defense)
        • Armor Strength (Damage Reduction): `{damage}*{value}/100` (Reduces incoming damage by a percentage based on `value`)
        • Armor Break: `min({value}, {entityB:armor_strength})` (Reduces target's armor strength by `value`, capped at target's armor strength)
        • Critical Evasion: `min({value}, {entityB:crit})` (Reduces incoming critical chance by `value`, capped at attacker's crit chance)
        • Crit Damage Resistance: `min({value}, max(50, {entityB:crit_damage}))` (Reduces incoming crit damage multiplier by `value`, but target's crit_damage is considered at least 50 for this calculation)
        • Damage Bonus: `{entityA:attack}*{value}/100` (Increases damage by a percentage of base physical damage)
        • Health Bonus: `{entityA:health}*{value}/100` (Increases max health by a percentage of base vitality/health attribute)

      3. Combat Power Calculation:
        • Each attribute has a `combatPower` coefficient.
        • Total Combat Power = Sum of (Attribute Value * Attribute Combat Power Coefficient).


        Scripting System: Unleash Your Creativity!

        The Scripting System is one of SagaLoreStats' most versatile features. It allows you to use JavaScript to create entirely new attributes, modify existing ones, or implement complex game mechanics that go beyond simple configuration.

        Understanding Script Basics
        Every script file you create needs a few key pieces of information at the top and specific functions.

        Script Properties (Defined at the top of your .js file):
        • priority: (Number) Determines the order of execution. Higher numbers run first. Example: `var priority = 103;`
        • combatPower: (Number) How much this attribute contributes to the player's overall Combat Power score. Example: `var combatPower = 5.0;`
        • attributeName: (String) The display name of your attribute. Example: `var attributeName = "My Custom Aura";`
        • attributeType: (String) The type of attribute. Can be:
          • `ATTACK_OR_DEFENSE`: For attributes that trigger during combat (attacking or defending).
          • `OTHER`: For miscellaneous attributes.
          • `UPDATE`: For attributes that run on a timer (e.g., regeneration).
          Example: `var attributeType = "ATTACK_OR_DEFENSE";`
        • placeholder: (String) The internal name used for PlaceholderAPI. Example: `var placeholder = "myCustomAura";`

        Core Script Functions:
        • `onLoad(attr)`: Called when the script is loaded (e.g., server start, `/sls reload`). Use this for setup. Must return the `attr` object.
        • `runAttack(attr, attacker, entity, handle)`: For `ATTACK_OR_DEFENSE` types. Called when a player (attacker) attacks another entity. Return `true` to stop further attribute processing for this event, `false` to continue.
        • `runDefense(attr, entity, attacker, handle)`: For `ATTACK_OR_DEFENSE` types. Called when a player (entity) is attacked. Return `true` to stop further processing, `false` to continue.
        • `runUpdate(attr, entity, handle)` or `run(Attr, entity, handle)`: For `UPDATE` types. Called periodically. Return `true` if an action was performed. (Note: The README uses `run` in an example, but `runUpdate` is mentioned elsewhere. Clarify based on plugin's actual expectation or use the one from examples). The example shows `run`.
        • `getInterval()`: For `UPDATE` types. Returns the update interval in seconds. Example: `return 5.0;` for every 5 seconds.

        Handy Scripting Tools (API Objects)
        Your scripts have access to several helpful API objects:
        1. Attr (AttributeScriptAPI): Your main toolkit for attribute operations.
          • `Attr.getValue(entity, "AttributeName")`: Get an entity's value for a SagaLoreStats attribute.
          • `Attr.setValue(entity, "maxHealth", 30.0)`: Set an entity's vanilla Minecraft attribute (e.g., max health).
          • `Attr.setEntityHealth(entity, 20.0)`: Directly set an entity's current health.
          • `Attr.getRandomValue(entity, handle)`: Get the value of the *current script's attribute* for the entity.
          • `Attr.getRandomValue(entity, "PhysicalDefense", handle)`: Get the value of a *specific SagaLoreStats attribute* for the entity.
          • `Attr.addDamage(attacker, damageAmount, targetEntity)`: Deal damage.
            [*`Attr.chance(30.0)`]: Returns `true` with a 30% probability.
          • `Attr.random(1.0, 10.0)`: Get a random decimal between 1.0 and 10.0.
          • `Attr.randomInt(1, 10)`: Get a random integer between 1 and 10.

        2. AttrHelper (ScriptAttributeHelper): Utility for fetching attribute values.
          • `AttrHelper.getAttribute(entity, "%sls_maxhealth%")`: Get attribute using PlaceholderAPI format.
          • `AttrHelper.getAttr(entity, "maxhealth")`: Simpler way to get SagaLoreStats attribute.
          • `AttrHelper.getAttribute(entity, "attack")`: Get custom SagaLoreStats attribute 'attack'.

        3. AttributeAPI: For manipulating attribute sources on an entity.
          • `var data = Attr.getData(entity, handle);`: Get the entity's attribute data container.
          • `AttributeAPI.add(data, "MyEffectSourceName", Arrays.asList("Physical Damage: 10", "Critical Chance: 5%"));`: Add temporary attributes from a source.
          • `AttributeAPI.take(data, "MyEffectSourceName");`: Remove attributes from that source.

        4. Utils: General utility functions.
          • `Utils.registerOtherAttribute("Magic Defense", 0.0, "magic_defense_placeholder")`: Register a new attribute type that other scripts or configs can use.
          • `Utils.sendMessage(player, "§6[SagaLoreStats] §fYour attack dealt §a10 §fdamage!");`: Send a message to a player.
          • `Utils.applyEffect(entity, "slowness", 100, 1)`: Apply a potion effect (slowness, 100 ticks = 5 seconds, amplifier 1).

        5. logger: For printing messages to the server console (useful for debugging).
          • `logger.info("Script loaded!");`
          • `logger.debug("Player health: " + currentHealth);` (Only shows if debug mode is on)
          • `logger.warning("Something might be wrong!");`


          Example: Creating a "Magic Attack" Script
          Let's say you want an attribute that deals extra magic damage.

          Code (Javascript):

          // Script Properties
          var priority = 60 ; // Executes after some defense attributes, before base attack
          var combatPower = 1.5 ;
          var attributeName = "Magic Attack" ;
          var attributeType = "ATTACK_OR_DEFENSE" ;
          var placeholder = "magic_attack" ;

          /**
           * Called when the script loads.
           * We can register related attributes here, like Magic Defense.
           */

          function onLoad (Attr ) {
              Utils. registerOtherAttribute ( "Magic Defense" , 0.0 , "magic_defense" ) ; // Display Name, Default Value, Placeholder
              logger. info ( "Magic Attack script loaded and Magic Defense registered!" ) ;
              return Attr ;
          }

          /**
           * Called when the player (attacker) attacks another entity.
           */

          function runAttack (Attr , attacker , entity , handle ) {
              // Get the attacker's "Magic Attack" value from their gear
              var magicDamageValue = Attr. getRandomValue (attacker , handle ) ; // 'handle' refers to this script's attribute

              if (magicDamageValue <= 0 ) {
                  return false ; // No magic attack value, do nothing
              }

              // Get the target's "Magic Defense" value
              // Note: "Magic Defense" is the display name we registered in onLoad
              var magicDefenseValue = Attr. getRandomValue (entity , "Magic Defense" , handle ) ;

              // Calculate final magic damage
              var finalMagicDamage = Math. max ( 0 , magicDamageValue - magicDefenseValue ) ;

              if (finalMagicDamage > 0 ) {
                  Attr. addDamage (attacker , finalMagicDamage , entity ) ; // Deal the magic damage

                  // Send a message to the attacker if they are a player
                  if (attacker instanceof org. bukkit. entity. Player ) {
                      Utils. sendMessage (attacker , "§b[Magic] §fYour magic strike dealt an extra §e" + finalMagicDamage. toFixed ( 1 ) + " §fdamage!" ) ;
                  }
                  // Optionally, message the target too
                  if (entity instanceof org. bukkit. entity. Player ) {
                       Utils. sendMessage (entity , "§b[Magic] §fYou were struck by magic for §c" + finalMagicDamage. toFixed ( 1 ) + " §fdamage!" ) ;
                  }
              }

              return false ; // Allow other attack attributes to process
          }
           
          Example: Creating a "Health Regeneration Per Second" Script
          This script will heal the player a certain amount every second.

          Code (Javascript):

          // Script Properties
          var priority = 10 ;
          var combatPower = 0.8 ;
          var attributeName = "SecRegen" ; // Shorter display name
          var attributeType = "UPDATE" ;
          var placeholder = "health_regen_second" ;

          function onLoad (Attr ) {
              logger. info ( "Health Regen Per Second script loaded!" ) ;
              return Attr ;
          }

          /**
           * Called periodically for players with this attribute.
           */

          function run (Attr , entity , handle ) { // 'run' or 'runUpdate'
              var currentHealth = entity. getHealth ( ) ;
              // For players, max health can change due to other attributes.
              // For other entities, it's usually fixed unless another plugin changes it.
              var maxHealth = entity. getAttribute (org. bukkit. attribute. Attribute. GENERIC_MAX_HEALTH ). getValue ( ) ;


              if (currentHealth >= maxHealth ) {
                  return false ; // Already at full health
              }

              // Get the "SecRegen" value for this player
              var healAmount = Attr. getRandomValue (entity , handle ) ;

              if (healAmount <= 0 ) {
                  return false ; // No regeneration amount
              }

              var newHealth = Math. min (currentHealth + healAmount , maxHealth ) ;
              Attr. setEntityHealth (entity , newHealth ) ;

              // Optional: Send a subtle message or particle effect
              // if (entity instanceof org.bukkit.entity.Player) {
              //    Utils.sendActionBar(entity, "§a+" + healAmount.toFixed(1) + " HP");
              // }

              return true ; // Indicates an update happened
          }

          /**
           * How often should the 'run' function be called?
           * @returns {number} Interval in seconds
           */

          function getInterval ( ) {
              return 1.0 ; // Every 1 second
          }
           
          Deploying Your Scripts
          1. Create your `.js` file (e.g., `my_magic_attack.js`).
          2. Place it in the `plugins/SagaLoreStats/script/` directory on your server.
          3. Restart your server or run `/sls reload`.
          4. Check the server console for any loading messages or errors from your script (especially those from `logger.info` or `logger.warning`).

          Script Configuration (Optional - `script.yml`)
          You can provide metadata for your scripts in `script.yml`:
          Code (YAML):

          script
          :
            my_magic_attack
          : # This should match your JS file name (without .js)
              enabled
          : true
              description
          : "Adds magic damage based on item lore."
              author
          : "YourName"
              version
          : "1.0.1"
              # config: # You can add custom config values here accessible within the script later if needed
              #   base_particle_effect: "CRIT_MAGIC"
           
          ⚙️ Configuration Files
          SagaLoreStats uses several YAML files for its configuration, located in the `plugins/SagaLoreStats/` folder:
          • config.yml: Main plugin settings (debug mode, update intervals, etc.).
          • message.yml: Customizable messages for various plugin actions and attribute triggers.
          • debugmessage.yml: Messages used specifically when debug mode is enabled.
          • attribute.yml: Core definitions for non-scripted attributes, their priorities, combat power coefficients, and default formulas.
          • format.yml: Defines the text patterns (regex) used to recognize attributes in item Lores.
          • stats.yml: Configuration for the `/sls stats` GUI panel.
          • script.yml: Configuration and metadata for JavaScript-based attributes.
          • script/ (directory): Contains your custom JavaScript files for new attributes.

            • [*_]example.js_
              [*_]health_bonus.js_
              [*_]health_regen_second.js_
              [*_]magic_attack.js_
              [*_]dynamic_attr_test.js_

          ⌨️ Commands & Permissions
          The main command is `/sls`.

          Code (YAML):

          Commands
          :
            /sls reload
          :
              Description
          : Reloads the plugin's configuration files and scripts.
              Permission
          : sagalorestats.admin.reload

            /sls stats [player_name]
          :
              Description
          : Opens the attribute panel for yourself or the specified player.
              Permission
          : sagalorestats.command.stats (for self )
              Permission
          : sagalorestats.command.stats.other (for others )

            /sls attribute <add|remove> <AttributeName> [Value]
          :
              Description
          : Adds or removes a Lore attribute to/from the item held in your main hand.
                           For 'add', AttributeName refers to the key in attribute.yml or your script's placeholder.
                           The Value is what gets written into the lore, e.g., "+10".
              Permission
          : sagalorestats.admin.attribute

            /sls debug
          :
              Description
          : Toggles debug mode on or off for the plugin.
              Permission
          : sagalorestats.admin.debug

          Permissions (General)
          :
            sagalorestats.admin
          : Grants access to all admin commands.
            sagalorestats.user
          : Basic user access (implicitly granted for attribute effects ).
           
          ➕ How to Add New Attributes
          You have flexible ways to introduce new attributes:

          Method 1: Via `attribute.yml` (for Simpler Attributes)
          This method is best for attributes that can be defined with existing types and formulas.
          1. Open `attribute.yml`.
          2. Define your attribute under the `attribute.key.<type>` section:
            Code (YAML):

            attribute
            :
              key
            :
                attackOrDefense
            : # Or 'other', 'update'
                  my_new_stat
            : "My New Stat Display Name" # Internal key: "Display Name"
                # ... other attribute types
              # ...
             
          3. Set its priority:
            Code (YAML):

              priority
            :
                attackOrDefense
            :
                  my_new_stat
            : 80 # Example priority
                # ...
             
          4. Set its combat power coefficient:
            Code (YAML):

              combatPower
            :
                attackOrDefense
            :
                  my_new_stat
            : 1.5
                # ...
             
          5. Define a formula (optional, if it needs one different from direct value):
            Code (YAML):

              formula
            :
                my_new_stat
            : "{value} * 0.5 + {entityA:attack} * 0.1" # Example: 50% of its value + 10% of attacker's base attack
             
          6. Add trigger messages in `message.yml` (optional):
            Code (YAML):

            message
            :
              attribute_trigger
            :
                my_new_stat
            :
                 - "&6Your {attribute} &fflares, dealing &e{damage} &fdamage!" # For attacker
                  - "&cThe enemy's {attribute} &fburns you for &e{damage} &fdamage!" # For victim
             
          7. If it's a new probability-based attribute, you might need to tell the plugin it's probabilistic. (This usually involves Java code changes in `DamageListener.java`'s `isAttributeTriggered` method as per the original docs, or ensure your script handles the probability via `Attr.chance()`). For config-only, it assumes direct application unless a script handles it.
          8. Reload the plugin: `/sls reload`.
          9. Add lore to an item like: `My New Stat Display Name: +10`

          Method 2: Via JavaScript (for Complex/Unique Attributes)
          This is the most powerful method, perfect for attributes with unique logic, custom effects, or interactions.
          1. Create a new `.js` file in `plugins/SagaLoreStats/script/` (e.g., `elemental_fury.js`).
          2. Write your script code (see the "Scripting System" section above for structure and examples).
            • Define `priority`, `combatPower`, `attributeName`, `attributeType`, `placeholder`.
            • Implement `onLoad()`.
            • Implement `runAttack()`, `runDefense()`, or `run()`/`getInterval()` based on `attributeType`.
            • Use the provided APIs (`Attr`, `Utils`, `logger`) for logic, effects, and messages.
          3. Optionally, add metadata to `script.yml`.
          4. Reload the plugin: `/sls reload`.
          5. Add lore to an item like: `Elemental Fury: +5` (where "Elemental Fury" is the `attributeName` in your script).

          Example: A simple "Mana Burn" script attribute
          Code (Javascript):

          // plugins/SagaLoreStats/script/mana_burn.js
          var priority = 78 ;
          var combatPower = 2.0 ;
          var attributeName = "Mana Burn" ;
          var attributeType = "ATTACK_OR_DEFENSE" ;
          var placeholder = "mana_burn" ;

          function onLoad (Attr ) {
              logger. info ( "Mana Burn Script Loaded!" ) ;
              return Attr ;
          }

          function runAttack (Attr , attacker , entity , handle ) {
              var burnValue = Attr. getRandomValue (attacker , handle ) ; // Get "Mana Burn: +X" value
              if (burnValue > 0 && entity. getType ( ). name ( ) === "PLAYER" ) { // Check if target is a Player
                  // Assuming players have "mana" - for real use, integrate with a mana plugin or use XP levels
                  var targetPlayer = entity ;
                  var currentMana = targetPlayer. getLevel ( ) ; // Using XP level as pseudo-mana
                  var manaToBurn = Math. min (currentMana , burnValue ) ;

                  if (manaToBurn > 0 ) {
                      targetPlayer. setLevel (currentMana - manaToBurn ) ;
                      if (attacker instanceof org. bukkit. entity. Player ) {
                          Utils. sendMessage (attacker , "§dYou burn §5" + manaToBurn + " §dof " + targetPlayer. getName ( ) + "'s mana!" ) ;
                      }
                      Utils. sendMessage (targetPlayer , "§5Your mana was burned by §d" + manaToBurn + "§5!" ) ;
                      // You could add a particle effect too
                      // Utils.spawnParticle(entity.getLocation(), "SPELL_WITCH", 10, 0.5, 0.5, 0.5, 0.1);
                  }
              }
              return false ; // Don't stop other attributes
          }
           
          To use this: Add `Mana Burn: +5` to an item's lore.

          Method 3: Hybrid Approach (Config + Script)
          Define basic aspects in `attribute.yml` and then enhance or control them with scripts.
          1. Define base attribute names in `attribute.yml` (e.g., `fire_affinity_damage`, `fire_affinity_resistance`).
          2. In a script, fetch these values using `Attr.getRandomValue(entity, "Fire Affinity Damage", handle)` and implement complex interactions, conditional effects, or combine them in unique ways.
          This offers a good balance between easy configuration for base values and powerful scripting for nuanced mechanics.

          Important Notes
          • Attribute priorities significantly affect calculation order and combat outcomes. Plan them carefully!
          • Balance probabilistic attributes (crit, dodge) to avoid unbalancing gameplay.
          • Design formulas with inter-attribute balance in mind.
          • Maximum values for attributes can often be capped or managed via formulas or script logic. The original docs mention `value` node in `attribute.yml` for limits, check this.
          • When scripting, use `Attribute.MAX_HEALTH` for Minecraft's max health attribute. In Java, it would be `Attribute.GENERIC_MAX_HEALTH`.

          Project Core Principles
          • Minimal Vanilla Dependency: Primarily uses vanilla damage and health events as a base. Most attributes are custom-implemented, avoiding reliance on vanilla potion effects or attribute systems for core functionality.
          • Independent Attribute Management: SagaLoreStats controls the calculation, application, and updating of its custom attributes.

          ️ Installation & Basic Usage
          1. Download the SagaLoreStats `.jar` file.
          2. Place it into your server's `plugins` folder.
          3. Start (or restart) your server. This will generate the default configuration files.
          4. Modify the configuration files (especially `attribute.yml`, `format.yml`, and potentially `script/` for custom attributes) to suit your server's needs.
          5. Use `/sls reload` to apply configuration changes without a full server restart.
          6. To add attributes to items, edit their Lore in-game using commands from other plugins (like Essentials `/lore add`) or an NBT editor, following the patterns defined in `format.yml` and your attribute definitions. Example Lore line: `Physical Damage: +10-15` or `Critical Chance: +5%`.
          7. Players can check their total attributes using `/sls stats`.
Resource Information
Author:
----------
Total Downloads: 376
First Release: May 19, 2025
Last Update: Yesterday at 4:01 PM
Category: ---------------
All-Time Rating:
3 ratings
Version -----
Released: --------------------
Downloads: ------
Version Rating:
----------------------
-- ratings