Skip to content

Set

Belongs to: Server Owner Guide · Related: Attributes & Values · Action System

A Set lets multiple items, when worn together, activate extra bonuses (attributes / potion effects / active skills), triggered in tiers by piece count worn. Config file: plugins/QinhItems/sets/*.yml.


1. Data Model

kotlin
data class SetDefinition(
    val id: String,
    val displayName: String,                 // MiniMessage
    val icon: String,                        // material name (editor icon)
    val belongingPieces: List<String>,       // member item ID / prefix
    val lore: List<String>,                  // set description
    val bonuses: List<SetBonus>,             // bonuses tiered by piece count
    val loreFormat: SetLoreFormat? = null,   // custom Lore template
)

data class SetBonus(
    val pieces: Int,                         // piece count required to activate
    val name: String,
    val lore: List<String>,                  // shown when activated
    val effects: Map<String,Int>,            // potion effect name→level (0=I)
    val attributes: Map<String,Double>,      // attribute bonus (accumulated)
    val abilities: List<SetAbility>,         // active / event skills
)

data class SetAbility(
    val trigger: String,                     // right_click / on_damage / on_kill …
    val refs: List<ActionRef>,               // action handler references
    val cooldownMs: Long = 0L,
)

2. Full YAML Example

yaml
warrior_bloodlust:                          # set ID
  display_name: "<red>Bloodlust</red>"
  icon: NETHERITE_SWORD
  belonging_pieces:                          # members: exact ID or prefix
    - "warrior_helmet"
    - "warrior_chestplate"
    - "warrior_leggings"
    - "warrior_boots"
    - "warrior_sword"
    - "warrior"                              # prefix: any warrior_xxx counts
  lore:
    - "<gray>The legendary warrior set</gray>"
    - "<gray>Complete the set to activate special effects</gray>"
  bonuses:
    - pieces: 2                              # activates at 2 pieces
      name: "Warrior's Heart"
      lore:
        - "<blue>+5 Physical Damage</blue>"
      attributes:
        物理伤害: 5
    - pieces: 4                              # activates at 4 pieces (stacks with 2-piece)
      name: "Power of Frenzy"
      lore:
        - "<blue>Cumulative +15 Physical Damage</blue>"
        - "<green>+100 Health</green>"
      attributes:
        物理伤害: 10                         # 4 pieces total 5+10=15
        生命: 100
      effects:
        SPEED: 1                             # Haste II (0=I)
        STRENGTH: 0
      abilities:
        - trigger: right_click               # right-click while holding ≥4 pieces
          cooldown: 5s
          actions:
            - handler: "qinhskills:cast"
              payload: "set burst skill name"
        - trigger: on_damage                 # triggers on dealing damage
          cooldown: 3s
          actions:
            - handler: "qinhskills:cast"
              payload: "on-hit counter skill name"

3. How Bonuses Stack

  • Attributes accumulate: wearing 4 pieces → you enjoy both the 2-piece and 4-piece attributes. 物理伤害 = 5 + 10 = 15, applied as a qi:set:<id> source.
  • Potion effects take the highest: 2-piece SPEED:1 + 3-piece SPEED:0SPEED:1.
  • Set attributes go through an independent source of the Attribute System; set perm effects go through PermEffectSync.

4. Set Skill Triggers

TriggerWhen it firesNote
right_click / left_clickClick while holding ≥ piece countManual
shift_right_click / shift_left_clickSneak + clickManual
shift_toggleToggle sneakingManual
on_damageDeal damage to an entityAutomatic
on_hitTake damageAutomatic
on_killKill an entityAutomatic

The handler + payload in a skill's refs are dispatched by QI (e.g. qinhskills:cast is handed to QinhSkills). The cooldown supports 5s / 200ms / 3t (ticks).


5. Member Matching Rules

Each entry in belongingPieces can be:

  • Exact ID: the item ID equals it exactly.
  • Prefix: the item ID starts with it_ (e.g. warrior matches warrior_helmet).
kotlin
SetRegistry.findForItem("warrior_helmet")  // → warrior_bloodlust
SetRegistry.isPieceOf("warrior_helmet", "warrior_bloodlust")  // true

6. Set Lore Display

SetLoreRenderer renders the set section, showing the activated / not-activated state of each tier's bonus:

Default format (when wearing 2/4 pieces):

Bloodlust
◆ [2] Warrior's Heart [Activated]
  +5 Physical Damage
◇ [3] Bloodlust [1 more needed]
◇ [4] Power of Frenzy [2 more needed]
─ Set Description ─
  The legendary warrior set
  Complete the set to activate special effects

You can customize the template with lore_format. Variables: {name} / {pieces} / {need} / {lore} / {desc}:

yaml
  lore_format:
    header: "<dark_gray>─── <white>{name}</white> ───</dark_gray>"
    inactive_line: "◇ <gray>[{pieces}] {name} [{need} more needed]</gray>"
    active_line: "◆ <yellow>[{pieces}] {name} [Activated]</yellow>"
    lore_line: "  <dark_gray>{lore}</dark_gray>"
    lore_divider: "<dark_gray>─ Set Description ─</dark_gray>"
    desc_line: "  <dark_gray>{desc}</dark_gray>"

Real-time piece-count display relies on SetLoreContextHolder: when equipment changes, EquipmentWatcher fills in each player's piece count, and rendering reads the current observer's piece count.

🖼️ [Image placeholder] Hovering over a set item, showing the difference in style between 2/4 activated and 3/4 not activated · suggested assets/set-lore.png


7. Files & Reloading

  • Sets live in sets/<set ID>.yml (one file per set recommended).
  • /qi reload (or a set config change) triggers SetRegistry.load() to reload.
  • When the same ID appears in multiple files, the one whose filename = <id>.yml takes priority; otherwise the last one is used.

8. Developer API

kotlin
SetRegistry.get(id): SetDefinition?
SetRegistry.all(): List<SetDefinition>
SetRegistry.findForItem(itemId): SetDefinition?
SetRegistry.isPieceOf(itemId, setId): Boolean

setDef.activeBonus(matchedCount): SetBonus?   // highest-tier bonus at current piece count
setDef.nextBonus(matchedCount): SetBonus?     // next-tier bonus

For editing sets via GUI, see Set Editor.


Next Steps