feat: add configuration for emotes

This commit is contained in:
Anna 2024-07-22 05:49:51 -04:00
parent a08c086323
commit 34be1311d5
Signed by: anna
GPG Key ID: D0943384CD9F87D1
5 changed files with 83 additions and 36 deletions

View File

@ -18,6 +18,7 @@ public class Configuration : IPluginConfiguration {
public bool LockViewer;
public bool ClickThroughViewer;
public bool HideTitlebar;
public bool ShowEmotes = true;
public float ViewerOpacity = 100.0f;
public int DefaultGlyph = 3;
}

View File

@ -77,7 +77,7 @@ public class MessageWithTerritory {
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class EmoteData {
public required uint Id { get; set; }
public required byte[] Customise { get; set; }
public required List<byte> Customise { get; set; }
public required EquipmentData[] Equipment { get; set; }
public required WeaponData[] Weapon { get; set; }
public required uint Glasses { get; set; }
@ -91,7 +91,9 @@ public class EmoteData {
public class EquipmentData {
public required ushort Id { get; set; }
public required byte Variant { get; set; }
[JsonProperty("stain_0")]
public required byte Stain0 { get; set; }
[JsonProperty("stain_1")]
public required byte Stain1 { get; set; }
public required ulong Value { get; set; }
}
@ -101,7 +103,9 @@ public class EquipmentData {
public class WeaponData {
public required WeaponModelId ModelId { get; set; }
public required byte State { get; set; }
[JsonProperty("flags_1")]
public required ushort Flags1 { get; set; }
[JsonProperty("flags_2")]
public required byte Flags2 { get; set; }
}
@ -111,7 +115,9 @@ public class WeaponModelId {
public required ushort Id { get; set; }
public required ushort Kind { get; set; }
public required ushort Variant { get; set; }
[JsonProperty("stain_0")]
public required byte Stain0 { get; set; }
[JsonProperty("stain_1")]
public required byte Stain1 { get; set; }
public required ulong Value { get; set; }
}

View File

@ -121,6 +121,7 @@ internal class Settings : ITab {
anyChanged |= vfx |= ImGui.Checkbox("Disable in cutscenes", ref this.Plugin.Config.DisableInCutscene);
anyChanged |= vfx |= ImGui.Checkbox("Disable in /gpose", ref this.Plugin.Config.DisableInGpose);
anyChanged |= vfx |= ImGui.Checkbox("Remove glow effect from signs", ref this.Plugin.Config.RemoveGlow);
anyChanged |= ImGui.Checkbox("Show player emotes", ref this.Plugin.Config.ShowEmotes);
var tt = this.Plugin.DataManager.GetExcelSheet<TerritoryType>();
if (tt == null) {

View File

@ -3,6 +3,7 @@ using System.Text;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Interface.Textures;
using Dalamud.Interface.Utility;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
@ -28,6 +29,7 @@ internal class Write : ITab {
private (int, int) _word2 = (-1, -1);
private int _glyph;
private int _emoteIdx = -1;
private string _emoteSearch = string.Empty;
private const string Placeholder = "****";
private Pack? Pack => Pack.All.Get(this._pack);
@ -69,6 +71,7 @@ internal class Write : ITab {
this.Emotes = this.Plugin.DataManager.GetExcelSheet<Emote>()!
.Skip(1)
.Where(emote => emote.TextCommand.Row != 0)
.ToList();
this._glyph = this.Plugin.Config.DefaultGlyph;
@ -312,30 +315,50 @@ internal class Write : ITab {
var emoteLabel = this._emoteIdx == -1
? "None"
: this.Emotes[this._emoteIdx].Name.ToDalamudString().TextValue;
if (ImGui.BeginCombo("Emote", emoteLabel)) {
if (ImGui.BeginCombo("Emote", emoteLabel, ImGuiComboFlags.HeightLarge)) {
using var endCombo = new OnDispose(ImGui.EndCombo);
if (ImGui.Selectable("None##no-emote", this._emoteIdx == -1)) {
this._emoteIdx = -1;
if (ImGui.IsWindowAppearing()) {
ImGui.SetKeyboardFocusHere();
}
ImGui.Separator();
ImGui.SetNextItemWidth(-1);
ImGui.InputTextWithHint("###emote-search", "Search...", ref this._emoteSearch, 100, ImGuiInputTextFlags.AutoSelectAll);
for (var i = 0; i < this.Emotes.Count; i++) {
var emote = this.Emotes[i];
var name = emote.Name.ToDalamudString().TextValue;
var unlocked = IsEmoteUnlocked(emote);
if (!unlocked) {
ImGui.BeginDisabled();
using var endChild = new OnDispose(ImGui.EndChild);
if (ImGui.BeginChild("##emote-search-child", new Vector2(0, 150) * ImGuiHelpers.GlobalScale)) {
if (ImGui.Selectable("None##no-emote", this._emoteIdx == -1)) {
this._emoteIdx = -1;
ImGui.CloseCurrentPopup();
}
if (ImGui.Selectable($"{name}##emote-{emote.RowId}", this._emoteIdx == i)) {
this._emoteIdx = i;
}
ImGui.Separator();
if (!unlocked) {
ImGui.EndDisabled();
for (var i = 0; i < this.Emotes.Count; i++) {
var emote = this.Emotes[i];
if (!string.IsNullOrEmpty(this._emoteSearch)) {
if (!emote.Name.ToDalamudString().TextValue.Contains(this._emoteSearch, StringComparison.InvariantCultureIgnoreCase)) {
if (!emote.TextCommand.Value!.Command.ToDalamudString().TextValue.Contains(this._emoteSearch, StringComparison.InvariantCultureIgnoreCase)) {
continue;
}
}
}
var name = emote.Name.ToDalamudString().TextValue;
var unlocked = IsEmoteUnlocked(emote);
if (!unlocked) {
ImGui.BeginDisabled();
}
if (ImGui.Selectable($"{name}##emote-{emote.RowId}", this._emoteIdx == i)) {
this._emoteIdx = i;
ImGui.CloseCurrentPopup();
}
if (!unlocked) {
ImGui.EndDisabled();
}
}
}
}
@ -416,12 +439,11 @@ internal class Write : ITab {
}
private unsafe EmoteData GetEmoteData(Emote emote, IPlayerCharacter player) {
var objMan = ClientObjectManager.Instance();
var chara = (BattleChara*) objMan->GetObjectByIndex(player.ObjectIndex);
var chara = (Character*) GameObjectManager.Instance()->Objects.GetObjectByGameObjectId(player.GameObjectId);
return new EmoteData {
Id = emote.RowId,
Customise = player.Customize,
Customise = player.Customize.ToList(),
Equipment = chara->DrawData.EquipmentModelIds
.ToArray()
.Select(equip => new EquipmentData {
@ -464,6 +486,8 @@ internal class Write : ITab {
this._word1 = (-1, -1);
this._word2 = (-1, -1);
this._glyph = this.Plugin.Config.DefaultGlyph;
this._emoteIdx = -1;
this._emoteSearch = string.Empty;
}
private void ClearIfNecessary() {

View File

@ -19,6 +19,14 @@ internal class ActorManager : IDisposable {
public void Dispose() {
this.Plugin.Ui.Viewer.View -= this.OnView;
this.Plugin.Framework.Update -= this.OnFramework;
if (this._idx != null) {
unsafe {
var objMan = ClientObjectManager.Instance();
new DisableAction().Run(this, objMan);
new DeleteAction().Run(this, objMan);
}
}
}
private unsafe void OnFramework(IFramework framework) {
@ -52,7 +60,7 @@ internal class ActorManager : IDisposable {
Plugin.Log.Debug($"OnView message is {msg}");
this.Despawn();
if (message?.Emote != null) {
if (this.Plugin.Config.ShowEmotes && message?.Emote != null) {
this.Spawn(message);
}
}
@ -122,6 +130,7 @@ internal class ActorManager : IDisposable {
manager._idx = idx;
var emote = message.Emote;
var emoteRow = manager.GetValidEmote(emote.Id);
var chara = (BattleChara*) objMan->GetObjectByIndex((ushort) idx);
@ -131,7 +140,7 @@ internal class ActorManager : IDisposable {
chara->Rotation = message.Yaw;
var drawData = &chara->DrawData;
var maxLen = Math.Min(sizeof(CustomizeData), emote.Customise.Length);
var maxLen = Math.Min(sizeof(CustomizeData), emote.Customise.Count);
var rawCustomise = (byte*) &drawData->CustomizeData;
for (var i = 0; i < maxLen; i++) {
rawCustomise[i] = emote.Customise[i];
@ -144,26 +153,23 @@ internal class ActorManager : IDisposable {
Variant = equip.Variant,
Stain0 = equip.Stain0,
Stain1 = equip.Stain1,
Value = equip.Value,
};
}
for (var i = 0; i < Math.Min(drawData->WeaponData.Length, emote.Weapon.Length); i++) {
var weapon = emote.Weapon[i];
drawData->Weapon((DrawDataContainer.WeaponSlot) i) = new DrawObjectData {
ModelId = new FFXIVClientStructs.FFXIV.Client.Game.Character.WeaponModelId {
if (emoteRow is { DrawsWeapon: true }) {
for (var i = 0; i < Math.Min(drawData->WeaponData.Length, emote.Weapon.Length); i++) {
var weapon = emote.Weapon[i];
drawData->Weapon((DrawDataContainer.WeaponSlot) i).ModelId = new FFXIVClientStructs.FFXIV.Client.Game.Character.WeaponModelId {
Id = weapon.ModelId.Id,
Type = weapon.ModelId.Kind,
Variant = weapon.ModelId.Variant,
Stain0 = weapon.ModelId.Stain0,
Stain1 = weapon.ModelId.Stain1,
Value = weapon.ModelId.Value,
},
DrawObject = chara->DrawObject,
Flags1 = weapon.Flags1,
Flags2 = weapon.Flags2,
State = weapon.State,
};
};
drawData->Weapon((DrawDataContainer.WeaponSlot) i).Flags1 = weapon.Flags1;
drawData->Weapon((DrawDataContainer.WeaponSlot) i).Flags2 = weapon.Flags2;
drawData->Weapon((DrawDataContainer.WeaponSlot) i).State = weapon.State;
}
}
drawData->IsHatHidden = emote.HatHidden;
@ -174,8 +180,8 @@ internal class ActorManager : IDisposable {
chara->Alpha = 0.25f;
chara->SetMode(CharacterModes.AnimLock, 0);
if (manager.Plugin.DataManager.GetExcelSheet<Emote>()?.GetRow(emote.Id) is { } row) {
chara->Timeline.BaseOverride = (ushort) row.ActionTimeline[0].Row;
if (emoteRow != null) {
chara->Timeline.BaseOverride = (ushort) emoteRow.ActionTimeline[0].Row;
}
manager._tasks.Enqueue(new EnableAction());
@ -183,6 +189,15 @@ internal class ActorManager : IDisposable {
}
}
private Emote? GetValidEmote(uint rowId) {
var emote = this.Plugin.DataManager.GetExcelSheet<Emote>()?.GetRow(rowId);
if (emote == null) {
return null;
}
return emote.TextCommand.Row == 0 ? null : emote;
}
private unsafe class EnableAction : BaseActorAction {
public override bool Run(ActorManager manager, ClientObjectManager* objMan) {
if (!this.TryGetBattleChara(manager, objMan, out var chara)) {