Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v1.1.0.0 - 2026-06-27
### Added
- New variables: your HP and MP (including percentages), whether you are in combat, whether you have a target, and your Gil and MGP

## v1.0.1.0 - 2026-06-26
### Fixed
- Pressing Escape while editing in the multiline command box no longer clears what you typed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Variables let you insert live game values into a line using curly brace syntax.
/say I am {character}, a level {level} {job} from {world}!
```

Currently supported variables: `{character}`, `{homeworld}`, `{job}`, `{level}`, `{world}`. The Variables tab of the help window lists them with their current values, and the command tester at the top of the help window shows resolved output as you type.
Silkstring includes variables for your character, job, HP and MP, combat state, target, currency, and more, and the list keeps growing. For the full, always-current list with live values, open the Variables tab of the help window (`/silkstring help`). The command tester at the top of that window shows resolved output as you type.

### Parameters

Expand Down
15 changes: 12 additions & 3 deletions Silkstring/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ namespace Silkstring;
public sealed class Plugin : IDalamudPlugin
{
[PluginService] internal static IDalamudPluginInterface PluginInterface { get; private set; } = null!;
[PluginService] internal static ICommandManager CommandManager { get; private set; } = null!;
[PluginService] internal static IGameInteropProvider GameInteropProvider { get; private set; } = null!;
[PluginService] internal static IFramework Framework { get; private set; } = null!;
[PluginService] internal static ICommandManager CommandManager { get; private set; } = null!;
[PluginService] internal static INotificationManager NotificationManager { get; private set; } = null!;
[PluginService] internal static ITargetManager TargetManager { get; private set; } = null!;
[PluginService] internal static IGameInteropProvider GameInteropProvider { get; private set; } = null!;
[PluginService] internal static IPlayerState PlayerState { get; private set; } = null!;
[PluginService] internal static IClientState ClientState { get; private set; } = null!;
[PluginService] internal static ICondition Condition { get; private set; } = null!;

private const string CommandName = "/silkstring";

Expand Down Expand Up @@ -57,7 +60,13 @@ public Plugin()

ECommonsMain.Init(PluginInterface, this);

IVariableProvider[] providers = [new PlayerVariableProvider(PlayerState)];
IVariableProvider[] providers =
[
new PlayerVariableProvider(PlayerState),
new VitalsVariablesProvider(ClientState),
new CombatVariablesProvider(Condition, TargetManager),
new CurrencyVariablesProvider()
];

_commandResolver = new CommandResolver(providers);
_commandHandler = new CommandHandler(_commandResolver, Framework);
Expand Down
2 changes: 1 addition & 1 deletion Silkstring/Services/CommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public async Task ExecuteAsync(IReadOnlyList<string> commands, IReadOnlyList<str
for (var i = 0; i < commands.Count; i++)
{
var cmd = commands[i];
cmd = _resolver.Resolve(cmd, args);
cmd = await _framework.RunOnFrameworkThread(() => _resolver.Resolve(cmd, args));
if (shouldSkip != null && cmd.StartsWith("/"))
{
var parts = cmd.TrimStart('/').Split(' ', StringSplitOptions.RemoveEmptyEntries);
Expand Down
24 changes: 24 additions & 0 deletions Silkstring/Services/Variables/CombatVariablesProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;

namespace Silkstring.Services.Variables;

public sealed class CombatVariablesProvider : IVariableProvider
{
private readonly ICondition _condition;
private readonly ITargetManager _targets;

public CombatVariablesProvider(ICondition condition, ITargetManager targets)
{
_condition = condition;
_targets = targets;
}

public IEnumerable<VariableDescriptor> GetVariables()
{
yield return new("incombat", "Whether you are in combat", "Combat", () => _condition[ConditionFlag.InCombat] ? "true" : "false");

yield return new("hastarget", "Whether you have a target", "Combat", () => _targets.Target != null ? "true" : "false");
}
}
20 changes: 20 additions & 0 deletions Silkstring/Services/Variables/CurrencyVariablesProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Collections.Generic;
using FFXIVClientStructs.FFXIV.Client.Game;

namespace Silkstring.Services.Variables;

public sealed class CurrencyVariablesProvider : IVariableProvider
{
public IEnumerable<VariableDescriptor> GetVariables()
{
yield return new("gil", "Your gil", "Currency", () => Count(1));
yield return new("mgp", "Your MGP", "Currency", () => Count(29));
}

private static unsafe string? Count(uint itemId)
{
var inv = InventoryManager.Instance();
return inv != null ? inv->GetInventoryItemCount(itemId).ToString() : null;
}

}
25 changes: 25 additions & 0 deletions Silkstring/Services/Variables/VitalsVariablesProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using Dalamud.Plugin.Services;
using ECommons.DalamudServices.Legacy;

namespace Silkstring.Services.Variables;

public sealed class VitalsVariablesProvider : IVariableProvider
{
private readonly IClientState _clientState;

public VitalsVariablesProvider(IClientState clientState) => _clientState = clientState;

public IEnumerable<VariableDescriptor> GetVariables()
{
yield return new("hp", "Your current HP", "Vitals", () => _clientState.LocalPlayer?.CurrentHp.ToString());
yield return new("maxhp", "Your maximum HP", "Vitals", () => _clientState.LocalPlayer?.MaxHp.ToString());
yield return new("hpp", "Your HP as a percentage", "Vitals", () => Percent(_clientState.LocalPlayer?.CurrentHp, _clientState.LocalPlayer?.MaxHp));
yield return new("mp", "Your current MP", "Vitals", () => _clientState.LocalPlayer?.CurrentMp.ToString());
yield return new("maxmp", "Your maximum MP", "Vitals", () => _clientState.LocalPlayer?.MaxMp.ToString());
yield return new("mpp", "Your MP as a percentage", "Vitals", () => Percent(_clientState.LocalPlayer?.CurrentMp, _clientState.LocalPlayer?.MaxMp));
}

private static string? Percent(uint? current, uint? max) => current is {} c && max is {} m && m > 0 ? (c * 100 / m).ToString() : null;

}
Loading