feat: add configuration for emotes
This commit is contained in:
parent
a08c086323
commit
34be1311d5
@ -18,6 +18,7 @@ public class Configuration : IPluginConfiguration {
|
|||||||
public bool LockViewer;
|
public bool LockViewer;
|
||||||
public bool ClickThroughViewer;
|
public bool ClickThroughViewer;
|
||||||
public bool HideTitlebar;
|
public bool HideTitlebar;
|
||||||
|
public bool ShowEmotes = true;
|
||||||
public float ViewerOpacity = 100.0f;
|
public float ViewerOpacity = 100.0f;
|
||||||
public int DefaultGlyph = 3;
|
public int DefaultGlyph = 3;
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ public class MessageWithTerritory {
|
|||||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||||
public class EmoteData {
|
public class EmoteData {
|
||||||
public required uint Id { get; set; }
|
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 EquipmentData[] Equipment { get; set; }
|
||||||
public required WeaponData[] Weapon { get; set; }
|
public required WeaponData[] Weapon { get; set; }
|
||||||
public required uint Glasses { get; set; }
|
public required uint Glasses { get; set; }
|
||||||
@ -91,7 +91,9 @@ public class EmoteData {
|
|||||||
public class EquipmentData {
|
public class EquipmentData {
|
||||||
public required ushort Id { get; set; }
|
public required ushort Id { get; set; }
|
||||||
public required byte Variant { get; set; }
|
public required byte Variant { get; set; }
|
||||||
|
[JsonProperty("stain_0")]
|
||||||
public required byte Stain0 { get; set; }
|
public required byte Stain0 { get; set; }
|
||||||
|
[JsonProperty("stain_1")]
|
||||||
public required byte Stain1 { get; set; }
|
public required byte Stain1 { get; set; }
|
||||||
public required ulong Value { get; set; }
|
public required ulong Value { get; set; }
|
||||||
}
|
}
|
||||||
@ -101,7 +103,9 @@ public class EquipmentData {
|
|||||||
public class WeaponData {
|
public class WeaponData {
|
||||||
public required WeaponModelId ModelId { get; set; }
|
public required WeaponModelId ModelId { get; set; }
|
||||||
public required byte State { get; set; }
|
public required byte State { get; set; }
|
||||||
|
[JsonProperty("flags_1")]
|
||||||
public required ushort Flags1 { get; set; }
|
public required ushort Flags1 { get; set; }
|
||||||
|
[JsonProperty("flags_2")]
|
||||||
public required byte Flags2 { get; set; }
|
public required byte Flags2 { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +115,9 @@ public class WeaponModelId {
|
|||||||
public required ushort Id { get; set; }
|
public required ushort Id { get; set; }
|
||||||
public required ushort Kind { get; set; }
|
public required ushort Kind { get; set; }
|
||||||
public required ushort Variant { get; set; }
|
public required ushort Variant { get; set; }
|
||||||
|
[JsonProperty("stain_0")]
|
||||||
public required byte Stain0 { get; set; }
|
public required byte Stain0 { get; set; }
|
||||||
|
[JsonProperty("stain_1")]
|
||||||
public required byte Stain1 { get; set; }
|
public required byte Stain1 { get; set; }
|
||||||
public required ulong Value { get; set; }
|
public required ulong Value { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -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 cutscenes", ref this.Plugin.Config.DisableInCutscene);
|
||||||
anyChanged |= vfx |= ImGui.Checkbox("Disable in /gpose", ref this.Plugin.Config.DisableInGpose);
|
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 |= 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>();
|
var tt = this.Plugin.DataManager.GetExcelSheet<TerritoryType>();
|
||||||
if (tt == null) {
|
if (tt == null) {
|
||||||
|
@ -3,6 +3,7 @@ using System.Text;
|
|||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
using Dalamud.Interface.Textures;
|
using Dalamud.Interface.Textures;
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
@ -28,6 +29,7 @@ internal class Write : ITab {
|
|||||||
private (int, int) _word2 = (-1, -1);
|
private (int, int) _word2 = (-1, -1);
|
||||||
private int _glyph;
|
private int _glyph;
|
||||||
private int _emoteIdx = -1;
|
private int _emoteIdx = -1;
|
||||||
|
private string _emoteSearch = string.Empty;
|
||||||
|
|
||||||
private const string Placeholder = "****";
|
private const string Placeholder = "****";
|
||||||
private Pack? Pack => Pack.All.Get(this._pack);
|
private Pack? Pack => Pack.All.Get(this._pack);
|
||||||
@ -69,6 +71,7 @@ internal class Write : ITab {
|
|||||||
|
|
||||||
this.Emotes = this.Plugin.DataManager.GetExcelSheet<Emote>()!
|
this.Emotes = this.Plugin.DataManager.GetExcelSheet<Emote>()!
|
||||||
.Skip(1)
|
.Skip(1)
|
||||||
|
.Where(emote => emote.TextCommand.Row != 0)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
this._glyph = this.Plugin.Config.DefaultGlyph;
|
this._glyph = this.Plugin.Config.DefaultGlyph;
|
||||||
@ -312,17 +315,35 @@ internal class Write : ITab {
|
|||||||
var emoteLabel = this._emoteIdx == -1
|
var emoteLabel = this._emoteIdx == -1
|
||||||
? "None"
|
? "None"
|
||||||
: this.Emotes[this._emoteIdx].Name.ToDalamudString().TextValue;
|
: 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);
|
using var endCombo = new OnDispose(ImGui.EndCombo);
|
||||||
|
|
||||||
|
if (ImGui.IsWindowAppearing()) {
|
||||||
|
ImGui.SetKeyboardFocusHere();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SetNextItemWidth(-1);
|
||||||
|
ImGui.InputTextWithHint("###emote-search", "Search...", ref this._emoteSearch, 100, ImGuiInputTextFlags.AutoSelectAll);
|
||||||
|
|
||||||
|
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)) {
|
if (ImGui.Selectable("None##no-emote", this._emoteIdx == -1)) {
|
||||||
this._emoteIdx = -1;
|
this._emoteIdx = -1;
|
||||||
|
ImGui.CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
for (var i = 0; i < this.Emotes.Count; i++) {
|
for (var i = 0; i < this.Emotes.Count; i++) {
|
||||||
var emote = this.Emotes[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 name = emote.Name.ToDalamudString().TextValue;
|
||||||
|
|
||||||
var unlocked = IsEmoteUnlocked(emote);
|
var unlocked = IsEmoteUnlocked(emote);
|
||||||
@ -332,6 +353,7 @@ internal class Write : ITab {
|
|||||||
|
|
||||||
if (ImGui.Selectable($"{name}##emote-{emote.RowId}", this._emoteIdx == i)) {
|
if (ImGui.Selectable($"{name}##emote-{emote.RowId}", this._emoteIdx == i)) {
|
||||||
this._emoteIdx = i;
|
this._emoteIdx = i;
|
||||||
|
ImGui.CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unlocked) {
|
if (!unlocked) {
|
||||||
@ -339,6 +361,7 @@ internal class Write : ITab {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.ClearIfNecessary();
|
this.ClearIfNecessary();
|
||||||
|
|
||||||
@ -416,12 +439,11 @@ internal class Write : ITab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private unsafe EmoteData GetEmoteData(Emote emote, IPlayerCharacter player) {
|
private unsafe EmoteData GetEmoteData(Emote emote, IPlayerCharacter player) {
|
||||||
var objMan = ClientObjectManager.Instance();
|
var chara = (Character*) GameObjectManager.Instance()->Objects.GetObjectByGameObjectId(player.GameObjectId);
|
||||||
var chara = (BattleChara*) objMan->GetObjectByIndex(player.ObjectIndex);
|
|
||||||
|
|
||||||
return new EmoteData {
|
return new EmoteData {
|
||||||
Id = emote.RowId,
|
Id = emote.RowId,
|
||||||
Customise = player.Customize,
|
Customise = player.Customize.ToList(),
|
||||||
Equipment = chara->DrawData.EquipmentModelIds
|
Equipment = chara->DrawData.EquipmentModelIds
|
||||||
.ToArray()
|
.ToArray()
|
||||||
.Select(equip => new EquipmentData {
|
.Select(equip => new EquipmentData {
|
||||||
@ -464,6 +486,8 @@ internal class Write : ITab {
|
|||||||
this._word1 = (-1, -1);
|
this._word1 = (-1, -1);
|
||||||
this._word2 = (-1, -1);
|
this._word2 = (-1, -1);
|
||||||
this._glyph = this.Plugin.Config.DefaultGlyph;
|
this._glyph = this.Plugin.Config.DefaultGlyph;
|
||||||
|
this._emoteIdx = -1;
|
||||||
|
this._emoteSearch = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearIfNecessary() {
|
private void ClearIfNecessary() {
|
||||||
|
@ -19,6 +19,14 @@ internal class ActorManager : IDisposable {
|
|||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
this.Plugin.Ui.Viewer.View -= this.OnView;
|
this.Plugin.Ui.Viewer.View -= this.OnView;
|
||||||
this.Plugin.Framework.Update -= this.OnFramework;
|
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) {
|
private unsafe void OnFramework(IFramework framework) {
|
||||||
@ -52,7 +60,7 @@ internal class ActorManager : IDisposable {
|
|||||||
Plugin.Log.Debug($"OnView message is {msg}");
|
Plugin.Log.Debug($"OnView message is {msg}");
|
||||||
this.Despawn();
|
this.Despawn();
|
||||||
|
|
||||||
if (message?.Emote != null) {
|
if (this.Plugin.Config.ShowEmotes && message?.Emote != null) {
|
||||||
this.Spawn(message);
|
this.Spawn(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,6 +130,7 @@ internal class ActorManager : IDisposable {
|
|||||||
|
|
||||||
manager._idx = idx;
|
manager._idx = idx;
|
||||||
var emote = message.Emote;
|
var emote = message.Emote;
|
||||||
|
var emoteRow = manager.GetValidEmote(emote.Id);
|
||||||
|
|
||||||
var chara = (BattleChara*) objMan->GetObjectByIndex((ushort) idx);
|
var chara = (BattleChara*) objMan->GetObjectByIndex((ushort) idx);
|
||||||
|
|
||||||
@ -131,7 +140,7 @@ internal class ActorManager : IDisposable {
|
|||||||
chara->Rotation = message.Yaw;
|
chara->Rotation = message.Yaw;
|
||||||
var drawData = &chara->DrawData;
|
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;
|
var rawCustomise = (byte*) &drawData->CustomizeData;
|
||||||
for (var i = 0; i < maxLen; i++) {
|
for (var i = 0; i < maxLen; i++) {
|
||||||
rawCustomise[i] = emote.Customise[i];
|
rawCustomise[i] = emote.Customise[i];
|
||||||
@ -144,26 +153,23 @@ internal class ActorManager : IDisposable {
|
|||||||
Variant = equip.Variant,
|
Variant = equip.Variant,
|
||||||
Stain0 = equip.Stain0,
|
Stain0 = equip.Stain0,
|
||||||
Stain1 = equip.Stain1,
|
Stain1 = equip.Stain1,
|
||||||
Value = equip.Value,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (emoteRow is { DrawsWeapon: true }) {
|
||||||
for (var i = 0; i < Math.Min(drawData->WeaponData.Length, emote.Weapon.Length); i++) {
|
for (var i = 0; i < Math.Min(drawData->WeaponData.Length, emote.Weapon.Length); i++) {
|
||||||
var weapon = emote.Weapon[i];
|
var weapon = emote.Weapon[i];
|
||||||
drawData->Weapon((DrawDataContainer.WeaponSlot) i) = new DrawObjectData {
|
drawData->Weapon((DrawDataContainer.WeaponSlot) i).ModelId = new FFXIVClientStructs.FFXIV.Client.Game.Character.WeaponModelId {
|
||||||
ModelId = new FFXIVClientStructs.FFXIV.Client.Game.Character.WeaponModelId {
|
|
||||||
Id = weapon.ModelId.Id,
|
Id = weapon.ModelId.Id,
|
||||||
Type = weapon.ModelId.Kind,
|
Type = weapon.ModelId.Kind,
|
||||||
Variant = weapon.ModelId.Variant,
|
Variant = weapon.ModelId.Variant,
|
||||||
Stain0 = weapon.ModelId.Stain0,
|
Stain0 = weapon.ModelId.Stain0,
|
||||||
Stain1 = weapon.ModelId.Stain1,
|
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;
|
drawData->IsHatHidden = emote.HatHidden;
|
||||||
@ -174,8 +180,8 @@ internal class ActorManager : IDisposable {
|
|||||||
|
|
||||||
chara->Alpha = 0.25f;
|
chara->Alpha = 0.25f;
|
||||||
chara->SetMode(CharacterModes.AnimLock, 0);
|
chara->SetMode(CharacterModes.AnimLock, 0);
|
||||||
if (manager.Plugin.DataManager.GetExcelSheet<Emote>()?.GetRow(emote.Id) is { } row) {
|
if (emoteRow != null) {
|
||||||
chara->Timeline.BaseOverride = (ushort) row.ActionTimeline[0].Row;
|
chara->Timeline.BaseOverride = (ushort) emoteRow.ActionTimeline[0].Row;
|
||||||
}
|
}
|
||||||
|
|
||||||
manager._tasks.Enqueue(new EnableAction());
|
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 {
|
private unsafe class EnableAction : BaseActorAction {
|
||||||
public override bool Run(ActorManager manager, ClientObjectManager* objMan) {
|
public override bool Run(ActorManager manager, ClientObjectManager* objMan) {
|
||||||
if (!this.TryGetBattleChara(manager, objMan, out var chara)) {
|
if (!this.TryGetBattleChara(manager, objMan, out var chara)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user