├── Lemegeton ├── Core │ ├── ContentItem.cs │ ├── InventoryItem.cs │ ├── CustomPropertyInterface.cs │ ├── Automarker.cs │ ├── Action.cs │ ├── Attributes.cs │ ├── Command.cs │ ├── Notification.cs │ ├── XmlSerializer.cs │ ├── ContentCategory.cs │ ├── Percentage.cs │ ├── AutomarkerPayload.cs │ ├── Language.cs │ ├── SerializableDictionary.cs │ ├── Content.cs │ ├── Party.cs │ ├── GameNetwork.cs │ ├── I18n.cs │ ├── AutomarkerTiming.cs │ ├── Blueprint.cs │ ├── Context.cs │ ├── Overlay.cs │ ├── ContentModule.cs │ ├── FoodSelector.cs │ ├── Config.cs │ ├── SigLocator.cs │ ├── AutomarkerSigns.cs │ └── TimelineRecorder.cs ├── Action │ ├── IngameCommand.cs │ ├── ChatMessage.cs │ └── Notification.cs ├── PacketHeaders │ ├── ActorCast.cs │ ├── EventPlay.cs │ ├── EffectResult.cs │ ├── StatusEffectList.cs │ ├── Ability.cs │ └── ActorControl.cs ├── ContentCategory │ ├── Trial.cs │ ├── Criterion.cs │ ├── Raid.cs │ ├── Debug.cs │ ├── DawntrailRaids.cs │ ├── EndwalkerRaids.cs │ ├── Miscellaneous.cs │ ├── DeepDungeon.cs │ └── Ultimate.cs ├── Content │ ├── EurekaOrthos.cs │ ├── PilgrimsTraverse.cs │ ├── HeavenOnHigh.cs │ ├── PalaceOfTheDead.cs │ ├── Hack.cs │ ├── UltWeaponsRefrain.cs │ ├── EwRaidAbyssos.cs │ ├── GenericDeepDungeon.cs │ ├── Timelines.cs │ └── Automation.cs └── Lemegeton.csproj ├── LICENSE ├── Lemegeton.sln ├── README.md ├── .gitattributes └── .gitignore /Lemegeton/Core/ContentItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Lemegeton.Core 4 | { 5 | 6 | public abstract class ContentItem : ContentModule 7 | { 8 | 9 | public ContentItem(State st) : base(st) 10 | { 11 | } 12 | 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Lemegeton/Core/InventoryItem.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.Game; 2 | using Lumina.Excel.Sheets; 3 | 4 | public class InventoryItem 5 | { 6 | 7 | public InventoryType Type; 8 | public Item Item; 9 | public int Slot; 10 | public bool HQ; 11 | 12 | } -------------------------------------------------------------------------------- /Lemegeton/Action/IngameCommand.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Core; 2 | using System.Xml.Serialization; 3 | 4 | namespace Lemegeton.Action 5 | { 6 | 7 | public class IngameCommand : Core.Action 8 | { 9 | 10 | [XmlAttribute] 11 | public string Command { get; set; } = ""; 12 | 13 | public override void Execute(Context ctx) 14 | { 15 | ctx.State.PostCommand(ctx.ParseText(this, Command)); 16 | } 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Lemegeton/PacketHeaders/ActorCast.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lemegeton.PacketHeaders 4 | { 5 | 6 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 7 | internal struct ActorCast 8 | { 9 | 10 | [FieldOffset(0)] public ushort actionId; 11 | [FieldOffset(8)] public float castTime; 12 | [FieldOffset(12)] public uint targetId; 13 | [FieldOffset(16)] public float rotation; 14 | 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Lemegeton/Core/CustomPropertyInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Lemegeton.Core 8 | { 9 | 10 | public abstract class CustomPropertyInterface 11 | { 12 | 13 | public abstract void RenderEditor(string path); 14 | public abstract string Serialize(); 15 | public abstract void Deserialize(string data); 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Lemegeton/PacketHeaders/EventPlay.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lemegeton.PacketHeaders 4 | { 5 | 6 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 7 | internal struct EventPlay 8 | { 9 | 10 | [FieldOffset(0)] public ulong actorId; 11 | [FieldOffset(8)] public uint eventId; 12 | [FieldOffset(12)] public ushort scene; 13 | [FieldOffset(16)] public uint flags; 14 | [FieldOffset(20)] public uint param1; 15 | [FieldOffset(24)] public ushort param2; 16 | [FieldOffset(28)] public byte param3; 17 | [FieldOffset(29)] public uint param4; 18 | 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Lemegeton/PacketHeaders/EffectResult.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lemegeton.PacketHeaders 4 | { 5 | 6 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 7 | public struct EffectResultEntry 8 | { 9 | 10 | [FieldOffset(2)] public ushort statusId; 11 | [FieldOffset(4)] public ushort stacks; 12 | [FieldOffset(8)] public float duration; 13 | [FieldOffset(12)] public uint srcActorId; 14 | 15 | } 16 | 17 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 18 | public unsafe struct EffectResult 19 | { 20 | 21 | [FieldOffset(25)] public byte entryCount; 22 | //[FieldOffset(28)] public fixed byte entries[4 * 4 * 4]; 23 | 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Lemegeton/Core/Automarker.cs: -------------------------------------------------------------------------------- 1 | namespace Lemegeton.Core 2 | { 3 | 4 | internal abstract class Automarker : ContentItem 5 | { 6 | 7 | public override FeaturesEnum Features 8 | { 9 | get 10 | { 11 | return _state.cfg.AutomarkerSoft == false && AsSoftmarker == false ? FeaturesEnum.Automarker : FeaturesEnum.Drawing; 12 | } 13 | } 14 | 15 | [DebugOption] 16 | [AttributeOrderNumber(500)] 17 | public bool SelfMarkOnly { get; set; } 18 | 19 | [DebugOption] 20 | [AttributeOrderNumber(501)] 21 | public bool AsSoftmarker { get; set; } 22 | 23 | public Automarker(State state) : base(state) 24 | { 25 | } 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Lemegeton/Core/Action.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml.Serialization; 3 | 4 | namespace Lemegeton.Core 5 | { 6 | 7 | [XmlInclude(typeof(Lemegeton.Action.ChatMessage))] 8 | [XmlInclude(typeof(Lemegeton.Action.Notification))] 9 | [XmlInclude(typeof(Lemegeton.Action.IngameCommand))] 10 | public abstract class Action 11 | { 12 | 13 | internal Guid Id = Guid.NewGuid(); 14 | 15 | public abstract void Execute(Context ctx); 16 | 17 | public virtual string Describe() 18 | { 19 | return I18n.Translate("Timelines/ActionTypes/" + GetType().Name); 20 | } 21 | 22 | public virtual Action Duplicate() 23 | { 24 | return (Action)MemberwiseClone(); 25 | } 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Lemegeton/Core/Attributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Lemegeton.Core 4 | { 5 | 6 | [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] 7 | public class AttributeOrderNumber : Attribute 8 | { 9 | 10 | private int _OrderNumber; 11 | public int OrderNumber 12 | { 13 | get 14 | { 15 | return _OrderNumber; 16 | } 17 | set 18 | { 19 | _OrderNumber = value; 20 | } 21 | } 22 | 23 | public AttributeOrderNumber(int num = 0) 24 | { 25 | OrderNumber = num; 26 | } 27 | 28 | } 29 | 30 | [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] 31 | public class DebugOption : Attribute 32 | { 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Lemegeton/Action/ChatMessage.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Core; 2 | using System.Xml.Serialization; 3 | 4 | namespace Lemegeton.Action 5 | { 6 | 7 | public class ChatMessage : Core.Action 8 | { 9 | 10 | public enum ChatSeverityEnum 11 | { 12 | Normal, 13 | Error, 14 | } 15 | 16 | [XmlAttribute] 17 | public ChatSeverityEnum ChatSeverity { get; set; } = ChatSeverityEnum.Normal; 18 | [XmlAttribute] 19 | public string Text { get; set; } = ""; 20 | 21 | public override void Execute(Context ctx) 22 | { 23 | switch (ChatSeverity) 24 | { 25 | case ChatSeverityEnum.Normal: 26 | ctx.State.cg.Print(ctx.ParseText(this, Text)); 27 | break; 28 | case ChatSeverityEnum.Error: 29 | ctx.State.cg.PrintError(ctx.ParseText(this, Text)); 30 | break; 31 | } 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Lemegeton/Core/Command.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace Lemegeton.Core 6 | { 7 | 8 | [StructLayout(LayoutKind.Explicit)] 9 | internal struct Command : IDisposable 10 | { 11 | 12 | [FieldOffset(0)] 13 | private IntPtr p; 14 | 15 | [FieldOffset(8)] 16 | private ulong unk1; 17 | 18 | [FieldOffset(16)] 19 | private ulong len; 20 | 21 | [FieldOffset(24)] 22 | private ulong unk2; 23 | 24 | internal Command(string text) 25 | { 26 | byte[] b = Encoding.UTF8.GetBytes(text); 27 | p = Marshal.AllocHGlobal(b.Length + 30); 28 | Marshal.Copy(b, 0, p, b.Length); 29 | Marshal.WriteByte(p + b.Length, 0); 30 | len = (ulong)(b.Length + 1); 31 | unk1 = 64; 32 | unk2 = 0; 33 | } 34 | 35 | public void Dispose() 36 | { 37 | Marshal.FreeHGlobal(p); 38 | } 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Lemegeton/Core/Notification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Lemegeton.Core 8 | { 9 | 10 | public class Notification 11 | { 12 | 13 | public enum NotificationSeverityEnum 14 | { 15 | Critical, 16 | Important, 17 | Normal, 18 | } 19 | 20 | public Lemegeton.Action.Notification Notif { get; set; } = null; 21 | public string Text { get; set; } = "(undefined)"; 22 | public State.SoundEffectEnum SoundEffect { get; set; } = State.SoundEffectEnum.None; 23 | public NotificationSeverityEnum Severity { get; set; } = NotificationSeverityEnum.Normal; 24 | public DateTime SpawnTime { get; set; } = DateTime.Now; 25 | public float TTL { get; set; } = 5.0f; 26 | public bool FirstDisplay { get; set; } = true; 27 | public bool TTS { get; set; } = false; 28 | public Context ctx { get; set; } = null; 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Lemegeton/ContentCategory/Trial.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Core; 2 | using System.Collections.Generic; 3 | 4 | namespace Lemegeton.ContentCategory 5 | { 6 | 7 | public class Trial : Core.ContentCategory 8 | { 9 | 10 | public override FeaturesEnum Features => FeaturesEnum.None; 11 | 12 | public override ContentCategoryTypeEnum ContentCategoryType => ContentCategoryTypeEnum.Content; 13 | 14 | protected override Dictionary InitializeSubcategories(State st) 15 | { 16 | Dictionary items = new Dictionary(); 17 | return items; 18 | } 19 | 20 | protected override Dictionary InitializeContentItems(State st) 21 | { 22 | Dictionary items = new Dictionary(); 23 | return items; 24 | } 25 | 26 | public Trial(State st) : base(st) 27 | { 28 | Enabled = false; 29 | } 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Lemegeton/PacketHeaders/StatusEffectList.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lemegeton.PacketHeaders 4 | { 5 | 6 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 7 | public struct StatusEffectListEntry 8 | { 9 | [FieldOffset(0)] public ushort statusId; 10 | [FieldOffset(2)] public ushort stacks; 11 | [FieldOffset(4)] public float duration; 12 | [FieldOffset(8)] public uint srcActorId; 13 | } 14 | 15 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 16 | public unsafe struct StatusEffectList 17 | { 18 | 19 | //[FieldOffset(20)] public fixed byte entries[30 * 3 * 4]; 20 | 21 | } 22 | 23 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 24 | public unsafe struct StatusEffectList2 25 | { 26 | 27 | //[FieldOffset(24)] public fixed byte entries[30 * 3 * 4]; 28 | 29 | } 30 | 31 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 32 | public unsafe struct StatusEffectList3 33 | { 34 | 35 | //[FieldOffset(0)] public fixed byte entries[30 * 3 * 4]; 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Paissa Heavy Industries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Lemegeton/ContentCategory/Criterion.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Content; 2 | using Lemegeton.Core; 3 | using System.Collections.Generic; 4 | 5 | namespace Lemegeton.ContentCategory 6 | { 7 | 8 | public class Criterion : Core.ContentCategory 9 | { 10 | 11 | public override FeaturesEnum Features => FeaturesEnum.None; 12 | 13 | public override ContentCategoryTypeEnum ContentCategoryType => ContentCategoryTypeEnum.Content; 14 | 15 | protected override Dictionary InitializeSubcategories(State st) 16 | { 17 | Dictionary items = new Dictionary(); 18 | return items; 19 | } 20 | 21 | protected override Dictionary InitializeContentItems(State st) 22 | { 23 | Dictionary items = new Dictionary(); 24 | items["EwCritAloalo"] = new EwCritAloalo(st); 25 | return items; 26 | } 27 | 28 | public Criterion(State st) : base(st) 29 | { 30 | } 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Lemegeton/Content/EurekaOrthos.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Core; 2 | 3 | namespace Lemegeton.Content 4 | { 5 | 6 | public class EurekaOrthos : GenericDeepDungeon 7 | { 8 | 9 | private bool ZoneOk = false; 10 | 11 | protected override bool ExecutionImplementation() 12 | { 13 | if (ZoneOk == true) 14 | { 15 | return base.ExecutionImplementation(); 16 | } 17 | return false; 18 | } 19 | 20 | public EurekaOrthos(State st) : base(st) 21 | { 22 | st.OnZoneChange += OnZoneChange; 23 | } 24 | 25 | private void OnZoneChange(ushort newZone) 26 | { 27 | bool newZoneOk = (newZone >= 1099 && newZone <= 1108); 28 | if (newZoneOk == true && ZoneOk == false) 29 | { 30 | Log(State.LogLevelEnum.Info, null, "Content available"); 31 | } 32 | else if (newZoneOk == false && ZoneOk == true) 33 | { 34 | Log(State.LogLevelEnum.Info, null, "Content unavailable"); 35 | } 36 | ZoneOk = newZoneOk; 37 | } 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Lemegeton/Content/PilgrimsTraverse.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Core; 2 | 3 | namespace Lemegeton.Content 4 | { 5 | 6 | public class PilgrimsTraverse : GenericDeepDungeon 7 | { 8 | 9 | private bool ZoneOk = false; 10 | 11 | protected override bool ExecutionImplementation() 12 | { 13 | if (ZoneOk == true) 14 | { 15 | return base.ExecutionImplementation(); 16 | } 17 | return false; 18 | } 19 | 20 | public PilgrimsTraverse(State st) : base(st) 21 | { 22 | st.OnZoneChange += OnZoneChange; 23 | } 24 | 25 | private void OnZoneChange(ushort newZone) 26 | { 27 | bool newZoneOk = (newZone >= 1281 && newZone <= 1290); 28 | if (newZoneOk == true && ZoneOk == false) 29 | { 30 | Log(State.LogLevelEnum.Info, null, "Content available"); 31 | } 32 | else if (newZoneOk == false && ZoneOk == true) 33 | { 34 | Log(State.LogLevelEnum.Info, null, "Content unavailable"); 35 | } 36 | ZoneOk = newZoneOk; 37 | } 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Lemegeton/ContentCategory/Raid.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Content; 2 | using Lemegeton.Core; 3 | using System.Collections.Generic; 4 | 5 | namespace Lemegeton.ContentCategory 6 | { 7 | 8 | public class Raid : Core.ContentCategory 9 | { 10 | 11 | public override FeaturesEnum Features => FeaturesEnum.None; 12 | 13 | public override ContentCategoryTypeEnum ContentCategoryType => ContentCategoryTypeEnum.Content; 14 | 15 | protected override Dictionary InitializeSubcategories(State st) 16 | { 17 | Dictionary items = new Dictionary(); 18 | items["EndwalkerRaids"] = new EndwalkerRaids(st); 19 | items["DawntrailRaids"] = new DawntrailRaids(st); 20 | return items; 21 | } 22 | 23 | protected override Dictionary InitializeContentItems(State st) 24 | { 25 | Dictionary items = new Dictionary(); 26 | return items; 27 | } 28 | 29 | public Raid(State st) : base(st) 30 | { 31 | } 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Lemegeton/ContentCategory/Debug.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Content; 2 | using Lemegeton.Core; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Lemegeton.ContentCategory 7 | { 8 | 9 | public class Debug : Core.ContentCategory 10 | { 11 | 12 | public override FeaturesEnum Features => FeaturesEnum.None; 13 | 14 | public override ContentCategoryTypeEnum ContentCategoryType => ContentCategoryTypeEnum.Other; 15 | 16 | protected override Dictionary InitializeSubcategories(State st) 17 | { 18 | Dictionary items = new Dictionary(); 19 | return items; 20 | } 21 | 22 | protected override Dictionary InitializeContentItems(State st) 23 | { 24 | Dictionary items = new Dictionary(); 25 | items["Debugger"] = new Debugger(st); 26 | items["Timelines"] = new Timelines(st); 27 | return items; 28 | } 29 | 30 | public Debug(State _state) : base(_state) 31 | { 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Lemegeton/Content/HeavenOnHigh.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Core; 2 | 3 | namespace Lemegeton.Content 4 | { 5 | 6 | public class HeavenOnHigh : GenericDeepDungeon 7 | { 8 | 9 | private bool ZoneOk = false; 10 | 11 | protected override bool ExecutionImplementation() 12 | { 13 | if (ZoneOk == true) 14 | { 15 | return base.ExecutionImplementation(); 16 | } 17 | return false; 18 | } 19 | 20 | public HeavenOnHigh(State st) : base(st) 21 | { 22 | st.OnZoneChange += OnZoneChange; 23 | } 24 | 25 | private void OnZoneChange(ushort newZone) 26 | { 27 | bool newZoneOk = ((newZone >= 770 && newZone <= 775) || (newZone >= 782 && newZone <= 785)); 28 | if (newZoneOk == true && ZoneOk == false) 29 | { 30 | Log(State.LogLevelEnum.Info, null, "Content available"); 31 | } 32 | else if (newZoneOk == false && ZoneOk == true) 33 | { 34 | Log(State.LogLevelEnum.Info, null, "Content unavailable"); 35 | } 36 | ZoneOk = newZoneOk; 37 | } 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Lemegeton/ContentCategory/DawntrailRaids.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Content; 2 | using Lemegeton.Core; 3 | using System.Collections.Generic; 4 | 5 | namespace Lemegeton.ContentCategory 6 | { 7 | 8 | public class DawntrailRaids : Core.ContentCategory 9 | { 10 | 11 | public override FeaturesEnum Features => FeaturesEnum.None; 12 | 13 | public override ContentCategoryTypeEnum ContentCategoryType => ContentCategoryTypeEnum.Subcategory; 14 | 15 | protected override Dictionary InitializeSubcategories(State st) 16 | { 17 | Dictionary items = new Dictionary(); 18 | return items; 19 | } 20 | 21 | protected override Dictionary InitializeContentItems(State st) 22 | { 23 | Dictionary items = new Dictionary(); 24 | items["DTRaidLightHeavy"] = new DtRaidLightHeavy(st); 25 | items["DTRaidCruiser"] = new DtRaidCruiser(st); 26 | return items; 27 | } 28 | 29 | public DawntrailRaids(State st) : base(st) 30 | { 31 | } 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Lemegeton/ContentCategory/EndwalkerRaids.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Content; 2 | using Lemegeton.Core; 3 | using System.Collections.Generic; 4 | 5 | namespace Lemegeton.ContentCategory 6 | { 7 | 8 | public class EndwalkerRaids : Core.ContentCategory 9 | { 10 | 11 | public override FeaturesEnum Features => FeaturesEnum.None; 12 | 13 | public override ContentCategoryTypeEnum ContentCategoryType => ContentCategoryTypeEnum.Subcategory; 14 | 15 | protected override Dictionary InitializeSubcategories(State st) 16 | { 17 | Dictionary items = new Dictionary(); 18 | return items; 19 | } 20 | 21 | protected override Dictionary InitializeContentItems(State st) 22 | { 23 | Dictionary items = new Dictionary(); 24 | items["EwRaidAbyssos"] = new EwRaidAbyssos(st); 25 | items["EwRaidAnabaseios"] = new EwRaidAnabaseios(st); 26 | return items; 27 | } 28 | 29 | public EndwalkerRaids(State st) : base(st) 30 | { 31 | } 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Lemegeton/Action/Notification.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Core; 2 | using static Lemegeton.Core.State; 3 | using System.Xml.Serialization; 4 | 5 | namespace Lemegeton.Action 6 | { 7 | 8 | public class Notification : Core.Action 9 | { 10 | 11 | [XmlAttribute] 12 | public Core.Notification.NotificationSeverityEnum NotificationSeverity { get; set; } = Core.Notification.NotificationSeverityEnum.Normal; 13 | [XmlAttribute] 14 | public string Text { get; set; } = ""; 15 | [XmlAttribute] 16 | public SoundEffectEnum SoundEffect { get; set; } = SoundEffectEnum.None; 17 | [XmlAttribute] 18 | public float TTL { get; set; } = 5.0f; 19 | [XmlAttribute] 20 | public bool TTS { get; set; } = false; 21 | 22 | public override void Execute(Context ctx) 23 | { 24 | ctx.State.plug.AddNotification(new Core.Notification() 25 | { 26 | Notif = this, 27 | Severity = this.NotificationSeverity, 28 | Text = this.Text, 29 | SoundEffect = this.SoundEffect, 30 | TTL = this.TTL, 31 | TTS = this.TTS, 32 | ctx = ctx, 33 | }); 34 | } 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Lemegeton/Core/XmlSerializer.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | using System.Xml; 4 | using System.Xml.Serialization; 5 | 6 | namespace Lemegeton.Core 7 | { 8 | 9 | static class XmlSerializer 10 | { 11 | 12 | public static T Deserialize(string input) 13 | { 14 | XmlSerializer xs = new XmlSerializer(typeof(T)); 15 | byte[] buf = UTF8Encoding.UTF8.GetBytes(input); 16 | using (MemoryStream ms = new MemoryStream(buf)) 17 | { 18 | return (T)xs.Deserialize(ms); 19 | } 20 | } 21 | 22 | public static T Deserialize(XmlDocument doc) 23 | { 24 | return Deserialize(doc.OuterXml); 25 | } 26 | 27 | public static string Serialize(T obj) 28 | { 29 | XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 30 | ns.Add("", ""); 31 | XmlSerializer xs = new XmlSerializer(typeof(T)); 32 | using (MemoryStream ms = new MemoryStream()) 33 | { 34 | xs.Serialize(ms, obj, ns); 35 | ms.Position = 0; 36 | using (StreamReader sr = new StreamReader(ms)) 37 | { 38 | return sr.ReadToEnd(); 39 | } 40 | } 41 | } 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Lemegeton/Content/PalaceOfTheDead.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Core; 2 | 3 | namespace Lemegeton.Content 4 | { 5 | 6 | public class PalaceOfTheDead : GenericDeepDungeon 7 | { 8 | 9 | private bool ZoneOk = false; 10 | 11 | protected override bool ExecutionImplementation() 12 | { 13 | if (ZoneOk == true) 14 | { 15 | return base.ExecutionImplementation(); 16 | } 17 | return false; 18 | } 19 | 20 | public PalaceOfTheDead(State st) : base(st) 21 | { 22 | st.OnZoneChange += OnZoneChange; 23 | } 24 | 25 | private void OnZoneChange(ushort newZone) 26 | { 27 | bool newZoneOk = ( 28 | (newZone >= 561 && newZone <= 565) 29 | || 30 | (newZone == 570) 31 | || 32 | (newZone >= 593 && newZone <= 607) 33 | ); 34 | if (newZoneOk == true && ZoneOk == false) 35 | { 36 | Log(State.LogLevelEnum.Info, null, "Content available"); 37 | } 38 | else if (newZoneOk == false && ZoneOk == true) 39 | { 40 | Log(State.LogLevelEnum.Info, null, "Content unavailable"); 41 | } 42 | ZoneOk = newZoneOk; 43 | } 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Lemegeton/ContentCategory/Miscellaneous.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Content; 2 | using Lemegeton.Core; 3 | using System.Collections.Generic; 4 | 5 | namespace Lemegeton.ContentCategory 6 | { 7 | 8 | public class Miscellaneous : Core.ContentCategory 9 | { 10 | 11 | public override FeaturesEnum Features => FeaturesEnum.None; 12 | 13 | public override ContentCategoryTypeEnum ContentCategoryType => ContentCategoryTypeEnum.Other; 14 | 15 | protected override Dictionary InitializeSubcategories(State st) 16 | { 17 | Dictionary items = new Dictionary(); 18 | return items; 19 | } 20 | 21 | protected override Dictionary InitializeContentItems(State st) 22 | { 23 | Dictionary items = new Dictionary(); 24 | items["Radar"] = new Radar(st); 25 | items["Overlays"] = new Overlays(st); 26 | items["VisualEnhancement"] = new VisualEnhancement(st); 27 | #if !SANS_GOETIA 28 | items["Hack"] = new Hack(st); 29 | items["Automation"] = new Automation(st); 30 | #endif 31 | return items; 32 | } 33 | 34 | public Miscellaneous(State _state) : base(_state) 35 | { 36 | } 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Lemegeton/ContentCategory/DeepDungeon.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Content; 2 | using Lemegeton.Core; 3 | using System.Collections.Generic; 4 | 5 | namespace Lemegeton.ContentCategory 6 | { 7 | 8 | #if !SANS_GOETIA 9 | 10 | public class DeepDungeon : Core.ContentCategory 11 | { 12 | 13 | public override FeaturesEnum Features => FeaturesEnum.None; 14 | 15 | public override ContentCategoryTypeEnum ContentCategoryType => ContentCategoryTypeEnum.Content; 16 | 17 | protected override Dictionary InitializeSubcategories(State st) 18 | { 19 | Dictionary items = new Dictionary(); 20 | return items; 21 | } 22 | 23 | protected override Dictionary InitializeContentItems(State st) 24 | { 25 | Dictionary items = new Dictionary(); 26 | items["PalaceOfTheDead"] = new PalaceOfTheDead(st); 27 | items["HeavenOnHigh"] = new HeavenOnHigh(st); 28 | items["EurekaOrthos"] = new EurekaOrthos(st); 29 | items["PilgrimsTraverse"] = new PilgrimsTraverse(st); 30 | return items; 31 | } 32 | 33 | public DeepDungeon(State st) : base(st) 34 | { 35 | Enabled = false; 36 | } 37 | 38 | } 39 | 40 | #endif 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Lemegeton/PacketHeaders/Ability.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lemegeton.PacketHeaders 4 | { 5 | 6 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 7 | internal unsafe struct Ability1 8 | { 9 | 10 | [FieldOffset(28)] public ushort actionId; 11 | [FieldOffset(48 + (16 * 1 * 4))] public fixed ulong targetId[1]; 12 | 13 | } 14 | 15 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 16 | internal unsafe struct Ability8 17 | { 18 | 19 | [FieldOffset(28)] public ushort actionId; 20 | [FieldOffset(48 + (16 * 8 * 4))] public fixed ulong targetId[8]; 21 | 22 | } 23 | 24 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 25 | internal unsafe struct Ability16 26 | { 27 | 28 | [FieldOffset(28)] public ushort actionId; 29 | [FieldOffset(48 + (16 * 16 * 4))] public fixed ulong targetId[16]; 30 | 31 | } 32 | 33 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 34 | internal unsafe struct Ability24 35 | { 36 | 37 | [FieldOffset(28)] public ushort actionId; 38 | [FieldOffset(48 + (16 * 24 * 4))] public fixed ulong targetId[24]; 39 | 40 | } 41 | 42 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 43 | internal unsafe struct Ability32 44 | { 45 | 46 | [FieldOffset(28)] public ushort actionId; 47 | [FieldOffset(48 + (16 * 32 * 4))] public fixed ulong targetId[32]; 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Lemegeton/ContentCategory/Ultimate.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Content; 2 | using Lemegeton.Core; 3 | using System.Collections.Generic; 4 | 5 | namespace Lemegeton.ContentCategory 6 | { 7 | 8 | public class Ultimate : Core.ContentCategory 9 | { 10 | 11 | public override FeaturesEnum Features => FeaturesEnum.None; 12 | 13 | public override ContentCategoryTypeEnum ContentCategoryType => ContentCategoryTypeEnum.Content; 14 | 15 | protected override Dictionary InitializeSubcategories(State st) 16 | { 17 | Dictionary items = new Dictionary(); 18 | return items; 19 | } 20 | 21 | protected override Dictionary InitializeContentItems(State st) 22 | { 23 | Dictionary items = new Dictionary(); 24 | items["UltUcob"] = new UltUcob(st); 25 | items["UltWeaponsRefrain"] = new UltWeaponsRefrain(st); 26 | items["UltAlexander"] = new UltAlexander(st); 27 | items["UltDragonsongReprise"] = new UltDragonsongReprise(st); 28 | items["UltOmegaProtocol"] = new UltOmegaProtocol(st); 29 | items["UltFuturesRewritten"] = new UltFuturesRewritten(st); 30 | return items; 31 | } 32 | 33 | public Ultimate(State st) : base(st) 34 | { 35 | } 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Lemegeton/Core/ContentCategory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Lemegeton.Core 4 | { 5 | 6 | public abstract class ContentCategory : ContentModule 7 | { 8 | 9 | public enum ContentCategoryTypeEnum 10 | { 11 | Content, 12 | Other, 13 | Subcategory 14 | } 15 | 16 | public Dictionary Subcategories; 17 | public Dictionary ContentItems; 18 | 19 | public abstract ContentCategoryTypeEnum ContentCategoryType { get; } 20 | 21 | protected abstract Dictionary InitializeSubcategories(State st); 22 | protected abstract Dictionary InitializeContentItems(State st); 23 | 24 | public override void Reset() 25 | { 26 | foreach (var kp in Subcategories) 27 | { 28 | kp.Value.Reset(); 29 | } 30 | foreach (var kp in ContentItems) 31 | { 32 | kp.Value.Reset(); 33 | } 34 | } 35 | 36 | public ContentCategory(State st) : base(st) 37 | { 38 | Subcategories = InitializeSubcategories(st); 39 | foreach (var x in Subcategories) 40 | { 41 | x.Value.Owner = this; 42 | } 43 | ContentItems = InitializeContentItems(st); 44 | foreach (var x in ContentItems) 45 | { 46 | x.Value.Owner = this; 47 | } 48 | } 49 | 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Lemegeton/PacketHeaders/ActorControl.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Lemegeton.PacketHeaders 4 | { 5 | 6 | public enum ActorControlCategory : ushort 7 | { 8 | GainStatus = 20, // unreliable for anything serious 9 | LoseStatus = 21, // unreliable for anything serious 10 | Headmarker = 34, 11 | Tether = 35, 12 | Director = 109, 13 | Sign = 502, 14 | }; 15 | 16 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 17 | public struct ActorControl 18 | { 19 | 20 | [FieldOffset(0)] public ActorControlCategory category; 21 | [FieldOffset(4)] public uint param1; 22 | [FieldOffset(8)] public uint param2; 23 | [FieldOffset(12)] public uint param3; 24 | [FieldOffset(16)] public uint param4; 25 | 26 | } 27 | 28 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 29 | internal struct ActorControlSelf 30 | { 31 | 32 | [FieldOffset(0)] public ActorControlCategory category; 33 | [FieldOffset(4)] public uint param1; 34 | [FieldOffset(8)] public uint param2; 35 | [FieldOffset(12)] public uint param3; 36 | [FieldOffset(16)] public uint param4; 37 | [FieldOffset(20)] public uint param5; 38 | [FieldOffset(24)] public uint param6; 39 | 40 | } 41 | 42 | [StructLayout(LayoutKind.Explicit, Pack = 1)] 43 | internal struct ActorControlTarget 44 | { 45 | 46 | [FieldOffset(0)] public ActorControlCategory category; 47 | [FieldOffset(4)] public uint param1; 48 | [FieldOffset(8)] public uint param2; 49 | [FieldOffset(12)] public uint param3; 50 | [FieldOffset(16)] public uint param4; 51 | [FieldOffset(24)] public uint targetId; 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Lemegeton/Core/Percentage.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace Lemegeton.Core 4 | { 5 | 6 | internal class Percentage 7 | { 8 | 9 | private float _CurrentValue = 0.0f; 10 | public float CurrentValue 11 | { 12 | get 13 | { 14 | return _CurrentValue; 15 | } 16 | set 17 | { 18 | _CurrentValue = value; 19 | Validate(); 20 | } 21 | } 22 | 23 | private float _MinValue = 0.0f; 24 | internal float MinValue 25 | { 26 | get 27 | { 28 | return _MinValue; 29 | } 30 | set 31 | { 32 | _MinValue = value; 33 | Validate(); 34 | } 35 | } 36 | 37 | internal float _MaxValue = 100.0f; 38 | internal float MaxValue 39 | { 40 | get 41 | { 42 | return _MaxValue; 43 | } 44 | set 45 | { 46 | _MaxValue = value; 47 | Validate(); 48 | } 49 | } 50 | 51 | private void Validate() 52 | { 53 | if (_CurrentValue < MinValue) 54 | { 55 | _CurrentValue = MinValue; 56 | } 57 | if (_CurrentValue > MaxValue) 58 | { 59 | _CurrentValue = MaxValue; 60 | } 61 | } 62 | 63 | public string Serialize() 64 | { 65 | return _CurrentValue.ToString(CultureInfo.InvariantCulture); 66 | } 67 | 68 | public void Deserialize(string data) 69 | { 70 | float.TryParse(data, out _CurrentValue); 71 | Validate(); 72 | } 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Lemegeton/Core/AutomarkerPayload.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.Objects.Types; 2 | using System.Collections.Generic; 3 | 4 | namespace Lemegeton.Core 5 | { 6 | 7 | internal class AutomarkerPayload 8 | { 9 | 10 | private State _st; 11 | public bool markSelfOnly { get; set; } = false; 12 | public bool softMarker { get; set; } = false; 13 | 14 | public Dictionary> assignments = new Dictionary>(); 15 | 16 | public AutomarkerPayload(State st, bool selfOnly, bool soft) 17 | { 18 | _st = st; 19 | markSelfOnly = selfOnly; 20 | softMarker = (_st.cfg.AutomarkerSoft == true || soft == true); 21 | } 22 | 23 | public void Assign(AutomarkerSigns.SignEnum sign, Party.PartyMember pm) 24 | { 25 | Assign(sign, pm.GameObject); 26 | } 27 | 28 | public void Assign(AutomarkerSigns.SignEnum sign, IGameObject go) 29 | { 30 | if (markSelfOnly == true) 31 | { 32 | if (go.GameObjectId != _st.cs.LocalPlayer.GameObjectId) 33 | { 34 | return; 35 | } 36 | } 37 | if (sign != AutomarkerSigns.SignEnum.AttackNext && sign != AutomarkerSigns.SignEnum.BindNext && sign != AutomarkerSigns.SignEnum.IgnoreNext) 38 | { 39 | assignments[sign] = new List(new IGameObject[] { go }); 40 | } 41 | else 42 | { 43 | if (assignments.ContainsKey(sign) == false) 44 | { 45 | assignments[sign] = new List(); 46 | } 47 | assignments[sign].Add(go); 48 | } 49 | } 50 | 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Lemegeton/Core/Language.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Bindings.ImGui; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Lemegeton.Core 7 | { 8 | 9 | public abstract class Language 10 | { 11 | 12 | public enum GlyphRangeEnum 13 | { 14 | Undefined, 15 | ChineseSimplifiedCommon, 16 | ChineseFull 17 | } 18 | 19 | public abstract bool IsDefault { get; } 20 | public abstract string LanguageName { get; } 21 | public abstract bool FontDownloadNecessary { get; } 22 | public abstract string FontDownload { get; } 23 | public abstract GlyphRangeEnum GlyphRange { get; } 24 | 25 | internal State _state { get; set; } = null; 26 | internal ImFontPtr? Font { get; set; } = null; 27 | internal float Coverage { get; set; } 28 | 29 | private Dictionary Translations { get; set; } 30 | 31 | public Language(State st) 32 | { 33 | Translations = new Dictionary(); 34 | _state = st; 35 | } 36 | 37 | internal void AddEntry(string key, string value) 38 | { 39 | Translations[key] = value; 40 | } 41 | 42 | public bool HasTranslationFor(string key) 43 | { 44 | return Translations.ContainsKey(key); 45 | } 46 | 47 | public string Translate(string key, params object[] args) 48 | { 49 | if (Translations.ContainsKey(key) == true) 50 | { 51 | return String.Format(Translations[key], args); 52 | } 53 | return String.Format(key, args); 54 | } 55 | 56 | internal void CalculateCoverage(Language master) 57 | { 58 | Coverage = (float)Translations.Count / (float)master.Translations.Count; 59 | } 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /Lemegeton/Core/SerializableDictionary.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml; 3 | using System.Xml.Schema; 4 | using System.Xml.Serialization; 5 | 6 | namespace Lemegeton.Core 7 | { 8 | 9 | public class SerializableDictionary : Dictionary, IXmlSerializable 10 | { 11 | 12 | public XmlSchema GetSchema() 13 | { 14 | return null; 15 | } 16 | 17 | public void ReadXml(XmlReader x) 18 | { 19 | if (x.IsEmptyElement == true) 20 | { 21 | return; 22 | } 23 | x.Read(); 24 | XmlSerializer xsk = new XmlSerializer(typeof(TKey)); 25 | XmlSerializer xsv = new XmlSerializer(typeof(TValue)); 26 | while (x.NodeType != XmlNodeType.EndElement) 27 | { 28 | TKey key; 29 | TValue value; 30 | x.ReadStartElement("Item"); 31 | x.ReadStartElement("Key"); 32 | key = (TKey)xsk.Deserialize(x); 33 | x.ReadEndElement(); 34 | x.ReadStartElement("Value"); 35 | value = (TValue)xsv.Deserialize(x); 36 | x.ReadEndElement(); 37 | x.ReadEndElement(); 38 | this[key] = value; 39 | x.MoveToContent(); 40 | } 41 | x.Read(); 42 | } 43 | 44 | public void WriteXml(XmlWriter x) 45 | { 46 | XmlSerializer xsk = new XmlSerializer(typeof(TKey)); 47 | XmlSerializer xsv = new XmlSerializer(typeof(TValue)); 48 | XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 49 | ns.Add("", ""); 50 | foreach (TKey k in Keys) 51 | { 52 | x.WriteStartElement("Item"); 53 | x.WriteStartElement("Key"); 54 | xsk.Serialize(x, k, ns); 55 | x.WriteEndElement(); 56 | x.WriteStartElement("Value"); 57 | xsv.Serialize(x, this[k], ns); 58 | x.WriteEndElement(); 59 | x.WriteEndElement(); 60 | } 61 | } 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Lemegeton/Core/Content.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.Game; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using static Lemegeton.Core.State; 6 | using static Lemegeton.Plugin; 7 | 8 | namespace Lemegeton.Core 9 | { 10 | 11 | public abstract class Content : ContentModule 12 | { 13 | 14 | public Dictionary Items; 15 | 16 | public override void Reset() 17 | { 18 | foreach (var kp in Items) 19 | { 20 | kp.Value.Reset(); 21 | } 22 | } 23 | 24 | public void LogItems() 25 | { 26 | foreach (KeyValuePair kp in Items) 27 | { 28 | kp.Value.Log(LogLevelEnum.Info, null, "Active = {0}, Enabled = {1}", kp.Value.Active, kp.Value.Enabled); 29 | } 30 | } 31 | 32 | public Content(State st) : base(st) 33 | { 34 | Items = new Dictionary(); 35 | foreach (Type type in GetType().GetNestedTypes().Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(Core.ContentItem)))) 36 | { 37 | try 38 | { 39 | Core.ContentItem ci = (Core.ContentItem)Activator.CreateInstance(type, new object[] { _state }); 40 | ci.Owner = this; 41 | Items[type.Name] = ci; 42 | } 43 | catch (Exception ex) 44 | { 45 | st.Log(LogLevelEnum.Error, ex, "Couldn't initialize ContentItem type {0} due to exception: {1} at {2}", type.Name, ex.Message, ex.StackTrace); 46 | } 47 | } 48 | foreach (Type type in GetType().BaseType.GetNestedTypes().Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(Core.ContentItem)))) 49 | { 50 | try 51 | { 52 | Core.ContentItem ci = (Core.ContentItem)Activator.CreateInstance(type, new object[] { _state }); 53 | ci.Owner = this; 54 | Items[type.Name] = ci; 55 | } 56 | catch (Exception ex) 57 | { 58 | st.Log(LogLevelEnum.Error, ex, "Couldn't initialize ContentItem type {0} due to exception: {1} at {2}", type.Name, ex.Message, ex.StackTrace); 59 | } 60 | } 61 | } 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Lemegeton.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.32126.317 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lemegeton", "Lemegeton\Lemegeton.csproj", "{7C171267-8812-4C8F-9F75-8EEA8F719347}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug (Mini)|Any CPU = Debug (Mini)|Any CPU 11 | Debug (Mini)|x64 = Debug (Mini)|x64 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Release (Mini)|Any CPU = Release (Mini)|Any CPU 15 | Release (Mini)|x64 = Release (Mini)|x64 16 | Release|Any CPU = Release|Any CPU 17 | Release|x64 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Debug (Mini)|Any CPU.ActiveCfg = Debug (Mini)|Any CPU 21 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Debug (Mini)|Any CPU.Build.0 = Debug (Mini)|Any CPU 22 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Debug (Mini)|x64.ActiveCfg = Debug (Mini)|x64 23 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Debug (Mini)|x64.Build.0 = Debug (Mini)|x64 24 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Debug|Any CPU.ActiveCfg = Debug (Full)|Any CPU 25 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Debug|Any CPU.Build.0 = Debug (Full)|Any CPU 26 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Debug|x64.ActiveCfg = Debug (Full)|x64 27 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Debug|x64.Build.0 = Debug (Full)|x64 28 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Release (Mini)|Any CPU.ActiveCfg = Release (Mini)|Any CPU 29 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Release (Mini)|Any CPU.Build.0 = Release (Mini)|Any CPU 30 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Release (Mini)|x64.ActiveCfg = Release (Mini)|x64 31 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Release (Mini)|x64.Build.0 = Release (Mini)|x64 32 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Release|Any CPU.ActiveCfg = Release (Full)|Any CPU 33 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Release|Any CPU.Build.0 = Release (Full)|Any CPU 34 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Release|x64.ActiveCfg = Release (Full)|x64 35 | {7C171267-8812-4C8F-9F75-8EEA8F719347}.Release|x64.Build.0 = Release (Full)|x64 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {4947B455-DD1B-4CF3-8970-EABFAFA043B7} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Lemegeton in a nutshell 2 | 3 | Lemegeton is a Dalamud plugin which is geared to being your ultimate Final Fantasy XIV raiding and ingame companion. With a wide variety of helpful features for all kinds of content, you'll never want to queue into an instance or leave home without it! Lemegeton does not have triggers or callouts, but it's meant to complement the feature set of Triggernometry, Cactbot, et al with a variety of exciting features and functionality. 4 | 5 | The plugin is nearly standalone and does not require ACT, Telesto, or anything else to operate aside from Dalamud, which you already have anyway because who isn't using Quicklauncher? It's very easy to set up and manage, and requires no configuration out of the box - aside from you simply turning on the things you want to use for yourself. While some features may be a little more questionable than others, you are not required to use or activate anything you wouldn't want to; if you don't use those features, there is no risk to you. 6 | 7 | Current features include but are not limited to: 8 | 9 | - UWU / Weapon's Refrain: Titan jail automarker 10 | - DSR / Dragonsong's Reprise: Wroth Flames automarker 11 | - TOP / The Omega Protocol: P1 Program Loop automarker 12 | - TOP / The Omega Protocol: P1 Pantokrator automarker 13 | - TOP / The Omega Protocol: P3 Transition automarker 14 | - TOP / The Omega Protocol: P5 Delta/Sigma/Omega automarkers 15 | - TOP / The Omega Protocol: Draw tether, boss monitor, etc.. 16 | - and so much more, not just automarkers - join the Discord! 17 | 18 | ## Contribute 19 | 20 | Lemegeton also has a Wiki, containing useful information and documentation if you would like to read more about the project, or contribute to it: 21 | 22 | https://github.com/paissaheavyindustries/Lemegeton/wiki 23 | 24 | Contributions are certainly welcome, and you can read more about how you can participate on the Discord! 25 | 26 | ## Installing 27 | 28 | The only thing Lemegeton needs is Dalamud, and that comes with the Quicklauncher you should already be using. If you're not, be sure to install it first and then never look back at the original launcher: 29 | 30 | https://github.com/goatcorp/FFXIVQuickLauncher 31 | 32 | If you're all set up with Quicklauncher, head on over to my Dalamud repository and follow the instructions on how to add it to Dalamud: 33 | 34 | https://github.com/paissaheavyindustries/Dalamud-Repo 35 | 36 | ## Discord 37 | 38 | Lemegeton also has a publicly available Discord server for announcements, suggestions, and questions related to the plugin. Feel free to join at: 39 | 40 | https://discord.gg/6f9MY55 41 | -------------------------------------------------------------------------------- /Lemegeton/Core/Party.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.Objects.Types; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Lemegeton.Core 6 | { 7 | 8 | internal class Alliance 9 | { 10 | 11 | public List Alliances = new List(); 12 | 13 | } 14 | 15 | internal class Party 16 | { 17 | 18 | public class PartyMember 19 | { 20 | 21 | public int Index { get; set; } 22 | public string Name { get; set; } 23 | public IGameObject GameObject { get; set; } = null; 24 | public ulong ObjectId { get; set; } = 0; 25 | public int Selection { get; set; } = 0; 26 | 27 | public uint Job 28 | { 29 | get 30 | { 31 | return GameObject != null ? ((IBattleChara)GameObject).ClassJob.RowId : 0; 32 | } 33 | } 34 | 35 | public float X 36 | { 37 | get 38 | { 39 | return GameObject != null ? GameObject.Position.X : 0.0f; 40 | } 41 | } 42 | 43 | public float Y 44 | { 45 | get 46 | { 47 | return GameObject != null ? GameObject.Position.Y : 0.0f; 48 | } 49 | } 50 | 51 | public float Z 52 | { 53 | get 54 | { 55 | return GameObject != null ? GameObject.Position.Z : 0.0f; 56 | } 57 | } 58 | 59 | } 60 | 61 | public List Members { get; set; } = new List(); 62 | 63 | public PartyMember GetByIndex(int index) 64 | { 65 | foreach (PartyMember pm in Members) 66 | { 67 | if (pm.Index == index) 68 | { 69 | return pm; 70 | } 71 | } 72 | return null; 73 | } 74 | 75 | public PartyMember GetByActorId(uint actorId) 76 | { 77 | foreach (PartyMember pm in Members) 78 | { 79 | if (pm.ObjectId == actorId) 80 | { 81 | return pm; 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | public List GetByActorIds(IEnumerable actorIds) 88 | { 89 | return (from ix in Members join jx in actorIds on ix.ObjectId equals jx select ix).ToList(); 90 | } 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Lemegeton/Core/GameNetwork.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.Network; 2 | using Dalamud.Hooking; 3 | using Dalamud.Plugin.Services; 4 | using FFXIVClientStructs.FFXIV.Client.Network; 5 | using Serilog; 6 | using System; 7 | using System.Runtime.InteropServices; 8 | 9 | namespace Lemegeton.Core 10 | { 11 | 12 | internal class GameNetwork : IDisposable 13 | { 14 | 15 | public delegate void OnNetworkMessageDelegate(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction); 16 | 17 | public event OnNetworkMessageDelegate? NetworkMessage; 18 | 19 | private Hook processZonePacketDownHook; 20 | 21 | public GameNetwork(IGameInteropProvider io) 22 | { 23 | unsafe 24 | { 25 | var onReceivePacketAddress = (nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket; 26 | processZonePacketDownHook = io.HookFromAddress(onReceivePacketAddress, this.ProcessZonePacketDownDetour); 27 | processZonePacketDownHook.Enable(); 28 | } 29 | } 30 | 31 | public void Dispose() 32 | { 33 | if (processZonePacketDownHook != null) 34 | { 35 | processZonePacketDownHook.Dispose(); 36 | processZonePacketDownHook = null; 37 | } 38 | } 39 | 40 | private unsafe void ProcessZonePacketDownDetour(PacketDispatcher* dispatcher, uint targetId, IntPtr dataPtr) 41 | { 42 | 43 | // Go back 0x10 to get back to the start of the packet header 44 | dataPtr -= 0x10; 45 | 46 | foreach (var d in Delegate.EnumerateInvocationList(this.NetworkMessage)) 47 | { 48 | try 49 | { 50 | d.Invoke( 51 | dataPtr + 0x20, 52 | (ushort)Marshal.ReadInt16(dataPtr, 0x12), 53 | 0, 54 | targetId, 55 | NetworkMessageDirection.ZoneDown); 56 | } 57 | catch (Exception ex) 58 | { 59 | string header; 60 | try 61 | { 62 | var data = new byte[32]; 63 | Marshal.Copy(dataPtr, data, 0, 32); 64 | header = BitConverter.ToString(data); 65 | } 66 | catch (Exception) 67 | { 68 | header = "failed"; 69 | } 70 | 71 | Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header); 72 | } 73 | } 74 | 75 | this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10); 76 | } 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Lemegeton/Core/I18n.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Bindings.ImGui; 2 | using Microsoft.VisualBasic.FileIO; 3 | using System.Collections.Generic; 4 | 5 | namespace Lemegeton.Core 6 | { 7 | 8 | internal static class I18n 9 | { 10 | 11 | internal delegate void FontDownloadRequest(Language lang); 12 | internal static event FontDownloadRequest OnFontDownload; 13 | 14 | internal static Dictionary RegisteredLanguages = new Dictionary(); 15 | internal static Language DefaultLanguage = null; 16 | private static Language _CurrentLanguage = null; 17 | internal static Language CurrentLanguage 18 | { 19 | get 20 | { 21 | return _CurrentLanguage; 22 | } 23 | set 24 | { 25 | if (value != _CurrentLanguage) 26 | { 27 | _CurrentLanguage = value; 28 | if (_CurrentLanguage != null && _CurrentLanguage.Font == null) 29 | { 30 | if (_CurrentLanguage.FontDownload != null && _CurrentLanguage.FontDownloadNecessary == true) 31 | { 32 | OnFontDownload?.Invoke(_CurrentLanguage); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | internal static void AddLanguage(Language ld) 40 | { 41 | if (ld.IsDefault == true) 42 | { 43 | DefaultLanguage = ld; 44 | } 45 | RegisteredLanguages[ld.LanguageName] = ld; 46 | if (_CurrentLanguage == null) 47 | { 48 | _CurrentLanguage = ld; 49 | } 50 | } 51 | 52 | internal static ImFontPtr? GetFont() 53 | { 54 | return CurrentLanguage != null ? CurrentLanguage.Font : DefaultLanguage.Font; 55 | } 56 | 57 | internal static string Translate(string key, params object[] args) 58 | { 59 | if (CurrentLanguage != null && CurrentLanguage.HasTranslationFor(key) == true) 60 | { 61 | return CurrentLanguage.Translate(key, args); 62 | } 63 | return DefaultLanguage.Translate(key, args); 64 | } 65 | 66 | internal static bool ChangeLanguage(string langname) 67 | { 68 | if (langname == null) 69 | { 70 | _CurrentLanguage = null; 71 | CurrentLanguage = DefaultLanguage; 72 | return true; 73 | } 74 | else 75 | { 76 | if (RegisteredLanguages.ContainsKey(langname) == true) 77 | { 78 | _CurrentLanguage = null; 79 | CurrentLanguage = RegisteredLanguages[langname]; 80 | return true; 81 | } 82 | else 83 | { 84 | _CurrentLanguage = null; 85 | CurrentLanguage = DefaultLanguage; 86 | return false; 87 | } 88 | } 89 | } 90 | 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /Lemegeton/Core/AutomarkerTiming.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Globalization; 4 | 5 | namespace Lemegeton.Core 6 | { 7 | 8 | internal class AutomarkerTiming 9 | { 10 | 11 | public enum TimingTypeEnum 12 | { 13 | Inherit, 14 | Explicit 15 | } 16 | 17 | public TimingTypeEnum TimingType { get; set; } = TimingTypeEnum.Inherit; 18 | internal AutomarkerTiming Parent = null; 19 | 20 | public float IniDelayMin { get; set; } = 0.3f; 21 | public float IniDelayMax { get; set; } = 0.7f; 22 | public float SubDelayMin { get; set; } = 0.1f; 23 | public float SubDelayMax { get; set; } = 0.3f; 24 | 25 | private double GetRandom() 26 | { 27 | Random r = new Random(); 28 | return r.NextDouble(); 29 | } 30 | 31 | public int SampleInitialTime() 32 | { 33 | if (TimingType == TimingTypeEnum.Inherit && Parent != null) 34 | { 35 | return Parent.SampleInitialTime(); 36 | } 37 | double rng = GetRandom(); 38 | int delay = (int)Math.Ceiling((IniDelayMin + ((IniDelayMax - IniDelayMin) * rng)) * 1000.0); 39 | return delay; 40 | } 41 | 42 | public int SampleSubsequentTime() 43 | { 44 | if (TimingType == TimingTypeEnum.Inherit && Parent != null) 45 | { 46 | return Parent.SampleSubsequentTime(); 47 | } 48 | double rng = GetRandom(); 49 | int delay = (int)Math.Ceiling((SubDelayMin + ((SubDelayMax - SubDelayMin) * rng)) * 1000.0); 50 | return delay; 51 | } 52 | 53 | public string Serialize() 54 | { 55 | return String.Format("TimingType={0};IniDelayMin={1};IniDelayMax={2};SubDelayMin={3};SubDelayMax={4}", 56 | TimingType.ToString(), 57 | IniDelayMin.ToString(CultureInfo.InvariantCulture), IniDelayMax.ToString(CultureInfo.InvariantCulture), 58 | SubDelayMin.ToString(CultureInfo.InvariantCulture), SubDelayMax.ToString(CultureInfo.InvariantCulture) 59 | ); 60 | } 61 | 62 | public void Deserialize(string data) 63 | { 64 | string[] items = data.Split(";"); 65 | foreach (string item in items) 66 | { 67 | string[] kp = item.Split("="); 68 | switch (kp[0]) 69 | { 70 | case "TimingType": 71 | TimingType = (TimingTypeEnum)Enum.Parse(typeof(TimingTypeEnum), kp[1]); 72 | break; 73 | case "IniDelayMin": 74 | IniDelayMin = float.Parse(kp[1], CultureInfo.InvariantCulture); 75 | break; 76 | case "IniDelayMax": 77 | IniDelayMax = float.Parse(kp[1], CultureInfo.InvariantCulture); 78 | break; 79 | case "SubDelayMin": 80 | SubDelayMin = float.Parse(kp[1], CultureInfo.InvariantCulture); 81 | break; 82 | case "SubDelayMax": 83 | SubDelayMax = float.Parse(kp[1], CultureInfo.InvariantCulture); 84 | break; 85 | } 86 | } 87 | } 88 | 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /Lemegeton/Lemegeton.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net10.0 5 | AnyCPU;x64 6 | Debug (Full);Release (Full);Debug (Mini);Release (Mini) 7 | 8 | 9 | 10 | $(appdata)\XIVLauncher\addon\Hooks\dev\ 11 | true 12 | 13 | 14 | 15 | $(DALAMUD_HOME)/ 16 | 17 | 18 | 19 | 20 | 21 | $(DefineConstants);SANS_GOETIA 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | $(DefineConstants);SANS_GOETIA 30 | 31 | 32 | 33 | 34 | 35 | $(DefineConstants);SANS_GOETIA 36 | 37 | 38 | 39 | $(DefineConstants);SANS_GOETIA 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | C:\Users\Moskau\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Dalamud.dll 49 | false 50 | 51 | 52 | C:\Users\Moskau\AppData\Roaming\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll 53 | false 54 | 55 | 56 | C:\Users\Moskau\AppData\Roaming\XIVLauncher\addon\Hooks\dev\InteropGenerator.Runtime.dll 57 | false 58 | 59 | 60 | $(DalamudLibPath)Newtonsoft.Json.dll 61 | false 62 | 63 | 64 | $(DalamudLibPath)ImGui.NET.dll 65 | false 66 | 67 | 68 | $(DalamudLibPath)ImGuiScene.dll 69 | false 70 | 71 | 72 | $(DalamudLibPath)Lumina.dll 73 | false 74 | 75 | 76 | $(DalamudLibPath)Lumina.Excel.dll 77 | false 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Lemegeton/Core/Blueprint.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml.Serialization; 3 | 4 | namespace Lemegeton.Core 5 | { 6 | 7 | public class Blueprint 8 | { 9 | 10 | public enum DebugFlags 11 | { 12 | None = 0, 13 | StatusEffects = 0x01, 14 | Opcodes = 0x02, 15 | All = StatusEffects | Opcodes, 16 | } 17 | 18 | public class Region 19 | { 20 | 21 | public class Opcode 22 | { 23 | 24 | [XmlAttribute] 25 | public string Name { get; set; } 26 | [XmlAttribute] 27 | public ushort Id { get; set; } 28 | 29 | } 30 | 31 | public class Warning 32 | { 33 | 34 | public enum TypeEnum 35 | { 36 | Warning, 37 | Information 38 | } 39 | 40 | [XmlAttribute] 41 | public string Text { get; set; } 42 | 43 | [XmlAttribute] 44 | public TypeEnum Type { get; set; } = TypeEnum.Warning; 45 | 46 | } 47 | 48 | [XmlAttribute] 49 | public string Name { get; set; } 50 | [XmlAttribute] 51 | public string Version { get; set; } 52 | [XmlAttribute] 53 | public DebugFlags DebugFlags { get; set; } 54 | private string _DebugInstanceData = ""; 55 | [XmlAttribute(AttributeName = "DebugInstances")] 56 | public string DebugInstanceData 57 | { 58 | get 59 | { 60 | return _DebugInstanceData; 61 | } 62 | set 63 | { 64 | if (_DebugInstanceData != value) 65 | { 66 | DebugInstances.Clear(); 67 | _DebugInstanceData = value; 68 | if (_DebugInstanceData != null) 69 | { 70 | string[] insts = _DebugInstanceData.Split(","); 71 | foreach (string inst in insts) 72 | { 73 | if (int.TryParse(inst, out int ii) == true) 74 | { 75 | DebugInstances.Add(ii); 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } 82 | 83 | internal List DebugInstances = new List(); 84 | public List Warnings { get; set; } = new List(); 85 | public List Opcodes { get; set; } = new List(); 86 | internal Dictionary OpcodeLookup = new Dictionary(); 87 | 88 | } 89 | 90 | public List Regions { get; set; } = new List(); 91 | internal Dictionary RegionLookup = new Dictionary(); 92 | 93 | internal void BuildLookups() 94 | { 95 | RegionLookup.Clear(); 96 | foreach (Region r in Regions) 97 | { 98 | RegionLookup[r.Name] = r; 99 | foreach (Region.Opcode o in r.Opcodes) 100 | { 101 | r.OpcodeLookup[o.Name] = o; 102 | } 103 | } 104 | } 105 | 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /Lemegeton/Core/Context.cs: -------------------------------------------------------------------------------- 1 | using Lemegeton.Action; 2 | using System; 3 | using System.Text.RegularExpressions; 4 | using static Lemegeton.Core.Timeline.Entry; 5 | 6 | namespace Lemegeton.Core 7 | { 8 | 9 | public class Context 10 | { 11 | 12 | // ${...} 13 | internal static Regex rex = new Regex(@"\$\{(?[^${}]*)\}"); 14 | 15 | public State State { get; set; } = null; 16 | 17 | public DateTime Created { get; set; } = DateTime.Now; 18 | public string SourceName { get; set; } = "(source name)"; 19 | public string DestName { get; set; } = "(destination name)"; 20 | public string EffectName { get; set; } = "(effect name)"; 21 | 22 | internal Context Duplicate() 23 | { 24 | return (Context)MemberwiseClone(); 25 | } 26 | 27 | internal string ParseExpressions(Action a, string text) 28 | { 29 | int i = 0; 30 | while (true) 31 | { 32 | Match m = rex.Match(text); 33 | if (m.Success == false) 34 | { 35 | break; 36 | } 37 | string val = ""; 38 | switch (m.Groups["id"].Value.ToLower()) 39 | { 40 | case "_triggeredtime": 41 | val = Created.ToString(); 42 | break; 43 | case "_currenttime": 44 | val = DateTime.Now.ToString(); 45 | break; 46 | case "_since": 47 | val = Math.Floor((DateTime.Now - Created).TotalSeconds).ToString(); 48 | break; 49 | case "_sincems": 50 | val = Math.Floor((DateTime.Now - Created).TotalMilliseconds).ToString(); 51 | break; 52 | case "_ttl": 53 | if (a is Lemegeton.Action.Notification) 54 | { 55 | Lemegeton.Action.Notification n = (Lemegeton.Action.Notification)a; 56 | val = (Math.Ceiling(n.TTL) - Math.Floor((DateTime.Now - Created).TotalSeconds)).ToString(); 57 | } 58 | else 59 | { 60 | val = "0"; 61 | } 62 | break; 63 | case "_ttlms": 64 | if (a is Lemegeton.Action.Notification) 65 | { 66 | Lemegeton.Action.Notification n = (Lemegeton.Action.Notification)a; 67 | val = (Math.Ceiling(n.TTL * 1000.0f) - Math.Floor((DateTime.Now - Created).TotalMilliseconds)).ToString(); 68 | } 69 | else 70 | { 71 | val = "0"; 72 | } 73 | break; 74 | case "_effect": 75 | val = EffectName; 76 | break; 77 | case "_src": 78 | val = SourceName; 79 | break; 80 | case "_dest": 81 | val = DestName; 82 | break; 83 | } 84 | text = text.Substring(0, m.Index) + val + text.Substring(m.Index + m.Length); 85 | i++; 86 | if (i > 100) 87 | { 88 | break; 89 | } 90 | } 91 | return text; 92 | } 93 | 94 | internal string ParseText(Action a, string text) 95 | { 96 | return ParseExpressions(a, text); 97 | } 98 | 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /Lemegeton/Core/Overlay.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Utility; 2 | using Dalamud.Bindings.ImGui; 3 | using System.Numerics; 4 | 5 | namespace Lemegeton.Core 6 | { 7 | 8 | internal class Overlay 9 | { 10 | 11 | public int X { get; set; } = 100; 12 | public int Y { get; set; } = 100; 13 | 14 | public int _Width = 300; 15 | public int Width 16 | { 17 | get { return _Width; } 18 | set 19 | { 20 | _Width = value; 21 | if (_Width < 50) 22 | { 23 | _Width = 50; 24 | } 25 | } 26 | } 27 | 28 | public int _Height = 300; 29 | public int Height 30 | { 31 | get { return _Height; } 32 | set 33 | { 34 | _Height = value; 35 | if (_Height < 50) 36 | { 37 | _Height = 50; 38 | } 39 | } 40 | } 41 | 42 | public int _Padding = 5; 43 | public int Padding 44 | { 45 | get { return _Padding; } 46 | set 47 | { 48 | _Padding = value; 49 | if (_Padding < 0) 50 | { 51 | _Padding = 0; 52 | } 53 | if (_Padding > 10) 54 | { 55 | _Padding = 10; 56 | } 57 | } 58 | } 59 | 60 | public Vector4 BackgroundColor { get; set; } = new Vector4(0.0f, 0.0f, 0.0f, 0.5f); 61 | 62 | public delegate void OverlayRenderDelegate(ImDrawListPtr draw, bool configuring); 63 | public OverlayRenderDelegate Renderer; 64 | 65 | public Overlay() 66 | { 67 | } 68 | 69 | public string Serialize() 70 | { 71 | return string.Format("{0};{1};{2};{3};{4};{5}", X, Y, _Width, _Height, _Padding, ImGui.GetColorU32(BackgroundColor)); 72 | } 73 | 74 | public void Deserialize(string data) 75 | { 76 | string[] boop = data.Split(';'); 77 | for (int i = 0; i < boop.Length; i++) 78 | { 79 | switch (i) 80 | { 81 | case 0: 82 | { 83 | if (int.TryParse(boop[i], out int tenp) == true) 84 | { 85 | X = tenp; 86 | } 87 | } 88 | break; 89 | case 1: 90 | { 91 | if (int.TryParse(boop[i], out int tenp) == true) 92 | { 93 | Y = tenp; 94 | } 95 | } 96 | break; 97 | case 2: 98 | { 99 | if (int.TryParse(boop[i], out int tenp) == true) 100 | { 101 | Width = tenp; 102 | } 103 | } 104 | break; 105 | case 3: 106 | { 107 | if (int.TryParse(boop[i], out int tenp) == true) 108 | { 109 | Height = tenp; 110 | } 111 | } 112 | break; 113 | case 4: 114 | { 115 | if (int.TryParse(boop[i], out int tenp) == true) 116 | { 117 | Padding = tenp; 118 | } 119 | } 120 | break; 121 | case 5: 122 | { 123 | if (uint.TryParse(boop[i], out uint tenp) == true) 124 | { 125 | BackgroundColor = ImGui.ColorConvertU32ToFloat4(tenp); 126 | } 127 | } 128 | break; 129 | } 130 | } 131 | } 132 | 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /Lemegeton/Core/ContentModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Lemegeton.Core 5 | { 6 | 7 | public abstract class ContentModule 8 | { 9 | 10 | [Flags] 11 | public enum FeaturesEnum 12 | { 13 | None = 0x00, 14 | Automarker = 0x01, 15 | Drawing = 0x02, 16 | Sound = 0x04, 17 | #if !SANS_GOETIA 18 | Hack = 0x08, 19 | Automation = 0x10, 20 | #endif 21 | Experimental = 0x80, 22 | } 23 | 24 | public abstract FeaturesEnum Features { get; } 25 | 26 | public virtual string Author { get; } = ""; 27 | 28 | protected State _state = null; 29 | private bool _enabled = false; 30 | private bool _active = false; 31 | internal bool _debugDisplayToggled = false; 32 | private ContentModule _owner = null; 33 | internal State.LogLevelEnum LogLevel { get; set; } = State.LogLevelEnum.Debug; 34 | 35 | internal delegate void StateChangeDelegate(bool newState); 36 | internal event StateChangeDelegate OnActiveChanged; 37 | internal event StateChangeDelegate OnEnabledChanged; 38 | 39 | internal List Children = new List(); 40 | 41 | internal ContentModule Owner 42 | { 43 | get 44 | { 45 | return _owner; 46 | } 47 | set 48 | { 49 | if (_owner != value) 50 | { 51 | if (_owner != null) 52 | { 53 | _owner.Children.Remove(this); 54 | } 55 | _owner = value; 56 | if (_owner != null) 57 | { 58 | _owner.Children.Add(this); 59 | } 60 | Active = ((_owner == null || _owner.Active == true) && Enabled == true); 61 | } 62 | } 63 | } 64 | 65 | internal bool Active 66 | { 67 | get 68 | { 69 | return _active; 70 | } 71 | set 72 | { 73 | if (_active != value) 74 | { 75 | _active = value; 76 | OnActiveChanged?.Invoke(value); 77 | Log(State.LogLevelEnum.Debug, null, "Active changed to {0}", _active); 78 | foreach (ContentModule cm in Children) 79 | { 80 | cm.Active = (_active == true && cm.Enabled == true); 81 | } 82 | } 83 | } 84 | } 85 | 86 | [AttributeOrderNumber(-1000)] 87 | public bool Enabled 88 | { 89 | get 90 | { 91 | return _enabled; 92 | } 93 | set 94 | { 95 | if (_enabled != value) 96 | { 97 | _enabled = value; 98 | OnEnabledChanged?.Invoke(value); 99 | Log(State.LogLevelEnum.Debug, null, "Enabled changed to {0}", _enabled); 100 | Active = ((_owner == null || _owner.Active == true) && Enabled == true); 101 | } 102 | } 103 | } 104 | 105 | public ContentModule(State st) 106 | { 107 | _state = st; 108 | } 109 | 110 | internal void Log(State.LogLevelEnum level, Exception ex, string message, params object[] args) 111 | { 112 | if (level > LogLevel) 113 | { 114 | return; 115 | } 116 | _state.Log(level, null, GetType().Name + ": " + message, args); 117 | } 118 | 119 | protected virtual bool ExecutionImplementation() 120 | { 121 | return true; 122 | } 123 | 124 | public virtual void Reset() 125 | { 126 | } 127 | 128 | public void Execute() 129 | { 130 | if (Active == false) 131 | { 132 | return; 133 | } 134 | bool escape = false; 135 | if ((Features & FeaturesEnum.Automarker) != 0) 136 | { 137 | _state.NumFeaturesAutomarker++; 138 | escape = (escape == true || _state.cfg.QuickToggleAutomarkers == false); 139 | } 140 | if ((Features & FeaturesEnum.Drawing) != 0) 141 | { 142 | _state.NumFeaturesDrawing++; 143 | escape = (escape == true || _state.cfg.QuickToggleOverlays == false); 144 | } 145 | if ((Features & FeaturesEnum.Sound) != 0) 146 | { 147 | _state.NumFeaturesSound++; 148 | escape = (escape == true || _state.cfg.QuickToggleSound == false); 149 | } 150 | #if !SANS_GOETIA 151 | if ((Features & FeaturesEnum.Hack) != 0) 152 | { 153 | _state.NumFeaturesHack++; 154 | escape = (escape == true || _state.cfg.QuickToggleHacks == false); 155 | } 156 | if ((Features & FeaturesEnum.Automation) != 0) 157 | { 158 | _state.NumFeaturesAutomation++; 159 | escape = (escape == true || _state.cfg.QuickToggleAutomation == false); 160 | } 161 | #endif 162 | if (escape == true) 163 | { 164 | return; 165 | } 166 | if (ExecutionImplementation() == true) 167 | { 168 | foreach (ContentModule cm in Children) 169 | { 170 | cm.Execute(); 171 | } 172 | } 173 | } 174 | 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /Lemegeton/Core/FoodSelector.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Hooking; 2 | using Dalamud.Interface; 3 | using FFXIVClientStructs.FFXIV.Client.Game; 4 | using Dalamud.Bindings.ImGui; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Linq; 9 | using System.Text; 10 | 11 | namespace Lemegeton.Core 12 | { 13 | 14 | public class FoodSelector 15 | { 16 | 17 | internal float MinimumTime { get; set; } 18 | public uint CurrentlySelected { get; set; } = 0; 19 | public bool SelectedHQ { get; set; } = false; 20 | public List FoodList { get; set; } = null; 21 | private bool _firstSeen = false; 22 | 23 | internal State State = null; 24 | 25 | private void RefreshFoodList() 26 | { 27 | FoodList = State.GetAllFoodInInventory(); 28 | } 29 | 30 | public bool Cycle() 31 | { 32 | if (State.CurrentlyWellFed(MinimumTime) == true) 33 | { 34 | return true; 35 | } 36 | if (CurrentlySelected == 0) 37 | { 38 | return true; 39 | } 40 | if (_firstSeen == false) 41 | { 42 | RefreshFoodList(); 43 | _firstSeen = true; 44 | } 45 | InventoryItem ii = State.FindItemInInventory(CurrentlySelected, SelectedHQ); 46 | if (ii == null) 47 | { 48 | State.Log(State.LogLevelEnum.Debug, null, "Out of food"); 49 | return true; 50 | } 51 | State.Log(State.LogLevelEnum.Debug, null, "Using food {0}, hq: {1}", ii.Item.Name, ii.HQ); 52 | unsafe 53 | { 54 | ActionManager* am = ActionManager.Instance(); 55 | uint funky = 0; 56 | switch (ii.Type) 57 | { 58 | case InventoryType.Inventory2: funky += 0x10000; break; 59 | case InventoryType.Inventory3: funky += 0x20000; break; 60 | case InventoryType.Inventory4: funky += 0x30000; break; 61 | } 62 | funky += (uint)ii.Slot; 63 | am->UseAction(ActionType.Item, (uint)(ii.Item.RowId + (ii.HQ == true ? 1000000 : 0)), 0xE0000000, funky, ActionManager.UseActionMode.None, 0, null); 64 | } 65 | return false; 66 | } 67 | 68 | public void Render(string path) 69 | { 70 | string none = I18n.Translate("Misc/None"); 71 | string hq = I18n.Translate("Misc/HQ"); 72 | string selname = ""; 73 | if (CurrentlySelected > 0) 74 | { 75 | if (_firstSeen == false) 76 | { 77 | RefreshFoodList(); 78 | _firstSeen = true; 79 | } 80 | var ii = (from ix in FoodList where ix.Item.RowId == CurrentlySelected select ix).FirstOrDefault(); 81 | if (ii == null) 82 | { 83 | CurrentlySelected = 0; 84 | } 85 | else 86 | { 87 | selname = ii.Item.Name.ExtractText(); 88 | if (SelectedHQ == true) 89 | { 90 | selname += " (" + hq + ")"; 91 | } 92 | } 93 | } 94 | if (CurrentlySelected == 0) 95 | { 96 | selname = none; 97 | } 98 | ImGui.PushItemWidth(200.0f); 99 | ImGui.Text(I18n.Translate(path)); 100 | if (ImGui.BeginCombo("##" + path, selname) == true) 101 | { 102 | if (_firstSeen == false) 103 | { 104 | RefreshFoodList(); 105 | _firstSeen = true; 106 | } 107 | if (ImGui.Selectable(none, CurrentlySelected == 0) == true) 108 | { 109 | CurrentlySelected = 0; 110 | } 111 | foreach (InventoryItem ii in FoodList) 112 | { 113 | string itemname = ii.Item.Name.ExtractText(); 114 | string dispname = itemname; 115 | if (ii.HQ == true) 116 | { 117 | dispname += " (" + hq + ")"; 118 | } 119 | bool selected = ( 120 | (CurrentlySelected == ii.Item.RowId) 121 | && 122 | ( 123 | (ii.HQ == true && SelectedHQ == true) 124 | || 125 | (ii.HQ == false && SelectedHQ == false) 126 | ) 127 | ); 128 | if (ImGui.Selectable(dispname, selected) == true) 129 | { 130 | CurrentlySelected = ii.Item.RowId; 131 | SelectedHQ = ii.HQ; 132 | } 133 | } 134 | ImGui.EndCombo(); 135 | } 136 | ImGui.PopItemWidth(); 137 | ImGui.SameLine(); 138 | ImGui.PushFont(UiBuilder.IconFont); 139 | string ico = FontAwesomeIcon.SyncAlt.ToIconString(); 140 | if (ImGui.Button(ico) == true) 141 | { 142 | RefreshFoodList(); 143 | } 144 | ImGui.PopFont(); 145 | } 146 | 147 | public string Serialize() 148 | { 149 | StringBuilder sb = new StringBuilder(); 150 | sb.Append(String.Format("CurrentlySelected={0};", CurrentlySelected)); 151 | sb.Append(String.Format("SelectedHQ={0};", SelectedHQ)); 152 | return sb.ToString(); 153 | } 154 | 155 | public void Deserialize(string data) 156 | { 157 | string[] items = data.Split(";"); 158 | foreach (string item in items) 159 | { 160 | string[] kp = item.Split("=", 2); 161 | switch (kp[0]) 162 | { 163 | case "CurrentlySelected": 164 | { 165 | CurrentlySelected = uint.Parse(kp[1]); 166 | break; 167 | } 168 | case "SelectedHQ": 169 | { 170 | SelectedHQ = bool.Parse(kp[1]); 171 | break; 172 | } 173 | } 174 | } 175 | } 176 | 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /Lemegeton/Content/Hack.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Bindings.ImGui; 2 | using Lemegeton.Core; 3 | using System; 4 | using System.Numerics; 5 | using GameObject = Dalamud.Game.ClientState.Objects.Types.IGameObject; 6 | using GameObjectPtr = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; 7 | using Vector3 = System.Numerics.Vector3; 8 | 9 | namespace Lemegeton.Content 10 | { 11 | 12 | #if !SANS_GOETIA 13 | 14 | public class Hack : Core.Content 15 | { 16 | 17 | public override FeaturesEnum Features => FeaturesEnum.None; 18 | 19 | public class Teleporter : Core.ContentItem 20 | { 21 | 22 | public override FeaturesEnum Features => FeaturesEnum.Hack; 23 | 24 | [AttributeOrderNumber(1000)] 25 | public System.Action GetCurrent { get; set; } 26 | [AttributeOrderNumber(1001)] 27 | public float X { get; set; } 28 | [AttributeOrderNumber(1002)] 29 | public float Y { get; set; } 30 | [AttributeOrderNumber(1003)] 31 | public float Z { get; set; } 32 | 33 | [AttributeOrderNumber(2000)] 34 | public int PlayersNearby { get; private set; } = 0; 35 | [AttributeOrderNumber(2001)] 36 | public bool AllowRiskyTeleport { get; set; } = false; 37 | [AttributeOrderNumber(2002)] 38 | public System.Action Teleport 39 | { 40 | get 41 | { 42 | return _telepoAction != null && Enabled == true && (PlayersNearby == 0 || AllowRiskyTeleport == true) ? _telepoAction : null; 43 | } 44 | set 45 | { 46 | _telepoAction = value; 47 | } 48 | } 49 | 50 | private System.Action _telepoAction = null; 51 | private DateTime _loaded; 52 | 53 | protected override bool ExecutionImplementation() 54 | { 55 | ImDrawListPtr draw; 56 | ulong myid = _state.cs.LocalPlayer.GameObjectId; 57 | int plys = 0; 58 | foreach (GameObject go in _state.ot) 59 | { 60 | if (go.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player && go.GameObjectId != myid) 61 | { 62 | plys++; 63 | } 64 | } 65 | PlayersNearby = plys; 66 | if (_state.StartDrawing(out draw) == false) 67 | { 68 | return false; 69 | } 70 | Vector3 v1, v2, temp = new Vector3(X, Y, Z); 71 | float time = (float)((DateTime.Now - _loaded).TotalMilliseconds / 200.0); 72 | float bla = (float)Math.Abs(Math.Cos(time)); 73 | float dist = 0.5f + bla; 74 | Vector4 col = new Vector4(1.0f - bla, 1.0f, 0.0f, 1.0f - (bla * 0.5f)); 75 | Vector4 tcol = new Vector4(1.0f, 1.0f, 1.0f, 1.0f); 76 | Vector4 scol = new Vector4(0.0f, 0.0f, 0.0f, 1.0f); 77 | for (int i = 0; i <= 24; i++) 78 | { 79 | Vector3 mauw = _state.plug._ui.TranslateToScreen( 80 | temp.X + (dist * Math.Sin((Math.PI / 12.0) * i)), 81 | temp.Y, 82 | temp.Z + (dist * Math.Cos((Math.PI / 12.0) * i)) 83 | ); 84 | draw.PathLineTo(new Vector2(mauw.X, mauw.Y)); 85 | } 86 | draw.PathStroke( 87 | ImGui.GetColorU32(col), 88 | ImDrawFlags.None, 89 | 2.0f + ((1.0f - bla) * 4.0f) 90 | ); 91 | v1 = _state.plug._ui.TranslateToScreen(temp.X + 3.0f, temp.Y, temp.Z); 92 | v2 = _state.plug._ui.TranslateToScreen(temp.X - 3.0f, temp.Y, temp.Z); 93 | draw.AddLine( 94 | new Vector2(v1.X, v1.Y), 95 | new Vector2(v2.X, v2.Y), 96 | ImGui.GetColorU32(col), 97 | 3.0f 98 | ); 99 | draw.AddText(ImGui.GetFont(), 40.0f, 100 | new Vector2(v1.X + 2.0f, v1.Y + 2.0f), ImGui.GetColorU32(scol), "X+" 101 | ); 102 | draw.AddText(ImGui.GetFont(), 40.0f, 103 | new Vector2(v1.X, v1.Y), ImGui.GetColorU32(tcol), "X+" 104 | ); 105 | v1 = _state.plug._ui.TranslateToScreen(temp.X, temp.Y + 3.0f, temp.Z); 106 | v2 = _state.plug._ui.TranslateToScreen(temp.X, temp.Y - 3.0f, temp.Z); 107 | draw.AddLine( 108 | new Vector2(v1.X, v1.Y), 109 | new Vector2(v2.X, v2.Y), 110 | ImGui.GetColorU32(col), 111 | 3.0f 112 | ); 113 | draw.AddText(ImGui.GetFont(), 40.0f, 114 | new Vector2(v1.X + 2.0f, v1.Y + 2.0f), ImGui.GetColorU32(scol), "Y+" 115 | ); 116 | draw.AddText(ImGui.GetFont(), 40.0f, 117 | new Vector2(v1.X, v1.Y), ImGui.GetColorU32(tcol), "Y+" 118 | ); 119 | v1 = _state.plug._ui.TranslateToScreen(temp.X, temp.Y, temp.Z + 3.0f); 120 | v2 = _state.plug._ui.TranslateToScreen(temp.X, temp.Y, temp.Z - 3.0f); 121 | draw.AddLine( 122 | new Vector2(v1.X, v1.Y), 123 | new Vector2(v2.X, v2.Y), 124 | ImGui.GetColorU32(col), 125 | 3.0f 126 | ); 127 | draw.AddText(ImGui.GetFont(), 40.0f, 128 | new Vector2(v1.X + 2.0f, v1.Y + 2.0f), ImGui.GetColorU32(scol), "Z+" 129 | ); 130 | draw.AddText(ImGui.GetFont(), 40.0f, 131 | new Vector2(v1.X, v1.Y), ImGui.GetColorU32(tcol), "Z+" 132 | ); 133 | return true; 134 | } 135 | 136 | private void GetCurrentPosition() 137 | { 138 | GameObject go = _state.cs.LocalPlayer as GameObject; 139 | X = go.Position.X; 140 | Y = go.Position.Y; 141 | Z = go.Position.Z; 142 | } 143 | 144 | private void PerformTeleport() 145 | { 146 | if (Active == false || _state.cfg.QuickToggleHacks == false || (PlayersNearby > 0 && AllowRiskyTeleport == false)) 147 | { 148 | return; 149 | } 150 | GameObject go = _state.cs.LocalPlayer as GameObject; 151 | unsafe 152 | { 153 | GameObjectPtr* gop = (GameObjectPtr*)go.Address; 154 | gop->Position.X = X; 155 | gop->Position.Y = Y; 156 | gop->Position.Z = Z; 157 | } 158 | } 159 | 160 | public Teleporter(State state) : base(state) 161 | { 162 | Enabled = false; 163 | _loaded = DateTime.Now; 164 | GetCurrent = new System.Action(() => GetCurrentPosition()); 165 | Teleport = new System.Action(() => PerformTeleport()); 166 | } 167 | 168 | } 169 | 170 | public Hack(State st) : base(st) 171 | { 172 | Enabled = false; 173 | } 174 | 175 | } 176 | 177 | #endif 178 | 179 | } 180 | -------------------------------------------------------------------------------- /Lemegeton/Content/UltWeaponsRefrain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Lemegeton.Core; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using static Lemegeton.Core.State; 6 | 7 | namespace Lemegeton.Content 8 | { 9 | 10 | internal class UltWeaponsRefrain : Core.Content 11 | { 12 | 13 | public override FeaturesEnum Features => FeaturesEnum.None; 14 | 15 | private const int AbilityTitanGaol1 = 0x2b6b; 16 | private const int AbilityTitanGaol2 = 0x2b6c; 17 | private const int StatusFetters = 292; 18 | 19 | private bool ZoneOk = false; 20 | private bool _subbed = false; 21 | 22 | private GaolAM _gaolAm; 23 | 24 | #region GaolAM 25 | 26 | public class GaolAM : Automarker 27 | { 28 | 29 | [AttributeOrderNumber(1000)] 30 | public AutomarkerSigns Signs { get; set; } 31 | 32 | [AttributeOrderNumber(2000)] 33 | public AutomarkerPrio Prio { get; set; } 34 | 35 | [DebugOption] 36 | [AttributeOrderNumber(2500)] 37 | public AutomarkerTiming Timing { get; set; } 38 | 39 | [DebugOption] 40 | [AttributeOrderNumber(3000)] 41 | public System.Action Test { get; set; } 42 | 43 | private List _gaols = new List(); 44 | private bool _fired = false; 45 | 46 | public GaolAM(State state) : base(state) 47 | { 48 | Enabled = false; 49 | Signs = new AutomarkerSigns(); 50 | Prio = new AutomarkerPrio(); 51 | Timing = new AutomarkerTiming() { TimingType = AutomarkerTiming.TimingTypeEnum.Inherit, Parent = state.cfg.DefaultAutomarkerTiming }; 52 | Prio.Priority = AutomarkerPrio.PrioTypeEnum.Role; 53 | Prio._prioByRole.Clear(); 54 | Prio._prioByRole.Add(AutomarkerPrio.PrioRoleEnum.Melee); 55 | Prio._prioByRole.Add(AutomarkerPrio.PrioRoleEnum.Tank); 56 | Prio._prioByRole.Add(AutomarkerPrio.PrioRoleEnum.Healer); 57 | Prio._prioByRole.Add(AutomarkerPrio.PrioRoleEnum.Ranged); 58 | Prio._prioByRole.Add(AutomarkerPrio.PrioRoleEnum.Caster); 59 | Signs.SetRole("Gaol1", AutomarkerSigns.SignEnum.Attack1, false); 60 | Signs.SetRole("Gaol2", AutomarkerSigns.SignEnum.Attack2, false); 61 | Signs.SetRole("Gaol3", AutomarkerSigns.SignEnum.Attack3, false); 62 | Test = new System.Action(() => Signs.TestFunctionality(state, Prio, Timing, SelfMarkOnly, AsSoftmarker)); 63 | } 64 | 65 | public override void Reset() 66 | { 67 | Log(State.LogLevelEnum.Debug, null, "Reset"); 68 | _fired = false; 69 | _gaols.Clear(); 70 | } 71 | 72 | internal void FeedAction(uint actorId, uint actionId) 73 | { 74 | if (Active == false) 75 | { 76 | return; 77 | } 78 | Log(State.LogLevelEnum.Debug, null, "Registered action {0} on {1:X}", actionId, actorId); 79 | if (actorId == 0 || _gaols.Contains(actorId) == true) 80 | { 81 | return; 82 | } 83 | _gaols.Add(actorId); 84 | if (_gaols.Count < 3) 85 | { 86 | return; 87 | } 88 | Log(State.LogLevelEnum.Debug, null, "Ready for automarkers"); 89 | Party pty = _state.GetPartyMembers(); 90 | List _gaolsGo = new List( 91 | from ix in pty.Members join jx in _gaols on ix.ObjectId equals jx select ix 92 | ); 93 | Prio.SortByPriority(_gaolsGo); 94 | AutomarkerPayload ap = new AutomarkerPayload(_state, SelfMarkOnly, AsSoftmarker); 95 | ap.Assign(Signs.Roles["Gaol1"], _gaolsGo[0].GameObject); 96 | ap.Assign(Signs.Roles["Gaol2"], _gaolsGo[1].GameObject); 97 | ap.Assign(Signs.Roles["Gaol3"], _gaolsGo[2].GameObject); 98 | _fired = true; 99 | _state.ExecuteAutomarkers(ap, Timing); 100 | } 101 | 102 | internal void FeedStatus(uint statusId, bool gained) 103 | { 104 | if (Active == false) 105 | { 106 | return; 107 | } 108 | Log(State.LogLevelEnum.Debug, null, "Registered status {0} {1}", statusId, gained); 109 | if (gained == false && _fired == true) 110 | { 111 | Log(State.LogLevelEnum.Debug, null, "Clearing automarkers"); 112 | Reset(); 113 | _state.ClearAutoMarkers(); 114 | } 115 | } 116 | 117 | } 118 | 119 | #endregion 120 | 121 | public UltWeaponsRefrain(State st) : base(st) 122 | { 123 | st.OnZoneChange += OnZoneChange; 124 | } 125 | 126 | private void SubscribeToEvents() 127 | { 128 | lock (this) 129 | { 130 | if (_subbed == true) 131 | { 132 | return; 133 | } 134 | _subbed = true; 135 | Log(LogLevelEnum.Debug, null, "Subscribing to events"); 136 | _state.OnAction += OnAction; 137 | _state.OnStatusChange += OnStatusChange; 138 | _state.OnCombatChange += OnCombatChange; 139 | } 140 | } 141 | 142 | private void UnsubscribeFromEvents() 143 | { 144 | lock (this) 145 | { 146 | if (_subbed == false) 147 | { 148 | return; 149 | } 150 | Log(LogLevelEnum.Debug, null, "Unsubscribing from events"); 151 | _state.OnStatusChange -= OnStatusChange; 152 | _state.OnAction -= OnAction; 153 | _state.OnCombatChange -= OnCombatChange; 154 | _subbed = false; 155 | } 156 | } 157 | 158 | protected override bool ExecutionImplementation() 159 | { 160 | if (ZoneOk == true) 161 | { 162 | return base.ExecutionImplementation(); 163 | } 164 | return false; 165 | } 166 | 167 | private void OnStatusChange(uint src, uint dest, uint statusId, bool gained, float duration, int stacks) 168 | { 169 | if (statusId == StatusFetters) 170 | { 171 | _gaolAm.FeedStatus(statusId, gained); 172 | } 173 | } 174 | 175 | private void OnAction(uint src, uint dest, ushort actionId) 176 | { 177 | if (actionId == AbilityTitanGaol1 || actionId == AbilityTitanGaol2) 178 | { 179 | _gaolAm.FeedAction(dest, actionId); 180 | } 181 | } 182 | 183 | private void OnCombatChange(bool inCombat) 184 | { 185 | Reset(); 186 | } 187 | 188 | private void OnZoneChange(ushort newZone) 189 | { 190 | bool newZoneOk = (newZone == 777); 191 | if (newZoneOk == true && ZoneOk == false) 192 | { 193 | Log(State.LogLevelEnum.Info, null, "Content available"); 194 | _gaolAm = (GaolAM)Items["GaolAM"]; 195 | SubscribeToEvents(); 196 | LogItems(); 197 | } 198 | else if (newZoneOk == false && ZoneOk == true) 199 | { 200 | Log(State.LogLevelEnum.Info, null, "Content unavailable"); 201 | UnsubscribeFromEvents(); 202 | } 203 | ZoneOk = newZoneOk; 204 | } 205 | 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /Lemegeton/Core/Config.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Configuration; 2 | using FFXIVClientStructs.FFXIV.Common.Math; 3 | using System.IO; 4 | 5 | namespace Lemegeton.Core 6 | { 7 | 8 | public class Config : IPluginConfiguration 9 | { 10 | 11 | public enum TimelineBarStyleEnum 12 | { 13 | Solid, 14 | Fancy, 15 | } 16 | 17 | public enum TextAlignmentEnum 18 | { 19 | Left, 20 | Center, 21 | Right, 22 | } 23 | 24 | public int Version { get; set; } = 0; 25 | public string Language { get; set; } = "English"; 26 | public string PlogonVersion { get; set; } = "1.0.0.0"; 27 | public bool Opened { get; set; } = true; 28 | public bool ShowShortcut { get; set; } = true; 29 | public int ShortcutScale { get; set; } = 100; 30 | public bool FirstRun { get; set; } = true; 31 | public bool AdvancedOptions { get; set; } = false; 32 | 33 | public string OpcodeUrl { get; set; } = @"https://raw.githubusercontent.com/paissaheavyindustries/Resources/main/Blueprint/blueprint.xml"; 34 | public string OpcodeRegion { get; set; } = "EN/DE/FR/JP"; 35 | public bool LogUnhandledOpcodes { get; set; } = false; 36 | public bool QueueFramework { get; set; } = true; 37 | 38 | public bool QuickToggleAutomarkers { get; set; } = true; 39 | public bool QuickToggleOverlays { get; set; } = false; 40 | public bool QuickToggleSound { get; set; } = false; 41 | #if !SANS_GOETIA 42 | public bool QuickToggleHacks { get; set; } = false; 43 | public bool QuickToggleAutomation { get; set; } = false; 44 | #endif 45 | 46 | public bool NagAboutStreaming { get; set; } = true; 47 | 48 | public bool AutomarkerSoft { get; set; } = false; 49 | public bool AutomarkerCommands { get; set; } = false; 50 | public int AutomarkersServed { get; set; } = 0; 51 | public bool RemoveMarkersAfterCombatEnd { get; set; } = true; 52 | public bool RemoveMarkersAfterWipe { get; set; } = true; 53 | 54 | public Vector4 SoftmarkerTint { get; set; } = new Vector4(1.0f, 1.0f, 1.0f, 1.0f); 55 | public bool SoftmarkerBounce { get; set; } = true; 56 | public bool SoftmarkerBlink { get; set; } = false; 57 | public float SoftmarkerScale { get; set; } = 1.0f; 58 | public float SoftmarkerOffsetWorldX { get; set; } = 0.0f; 59 | public float SoftmarkerOffsetWorldY { get; set; } = 2.0f; 60 | public float SoftmarkerOffsetWorldZ { get; set; } = 0.0f; 61 | public float SoftmarkerOffsetScreenX { get; set; } = 0.0f; 62 | public float SoftmarkerOffsetScreenY { get; set; } = 0.0f; 63 | 64 | public Rectangle TimelineOverlayLocation { get; set; } = new Rectangle() { Left = 100.0f, Top = 100.0f, Bottom = 300.0f, Right = 400.0f }; 65 | public float TimelineOverlayBarHeight { get; set; } = 40.0f; 66 | public Vector4 TimelineOverlayBarColor { get; set; } = new Vector4(0.145f, 0.506f, 0.653f, 1.0f); 67 | public Vector4 TimelineOverlayBarTextColor { get; set; } = new Vector4(1.0f, 1.0f, 1.0f, 1.0f); 68 | public Vector4 TimelineOverlayBarSoonColor { get; set; } = new Vector4(0.722f, 0.146f, 0.146f, 1.0f); 69 | public Vector4 TimelineOverlayBarSoonTextColor { get; set; } = new Vector4(1.0f, 1.0f, 0.0f, 1.0f); 70 | public Vector4 TimelineOverlayBarActiveColor { get; set; } = new Vector4(0.726f, 0.488f, 0.085f, 1.0f); 71 | public Vector4 TimelineOverlayBarActiveTextColor { get; set; } = new Vector4(1.0f, 1.0f, 0.0f, 1.0f); 72 | public Vector4 TimelineOverlayBarBgColor { get; set; } = new Vector4(0.0f, 0.0f, 0.0f, 0.3f); 73 | public float TimelineOverlayBarTextScale { get; set; } = 1.5f; 74 | public float TimelineOverlayBarSoonThreshold { get; set; } = 5.0f; 75 | public float TimelineOverlayBarTimeThreshold { get; set; } = 20.0f; 76 | public float TimelineOverlayBarPastThreshold { get; set; } = 2.0f; 77 | public bool TimelineOverlayBarHead { get; set; } = true; 78 | public bool TimelineOverlayBarName { get; set; } = true; 79 | public bool TimelineOverlayBarTime { get; set; } = true; 80 | public bool TimelineOverlayBarCaps { get; set; } = false; 81 | public bool TimelineOverlayVisible { get; set; } = false; 82 | public bool TimelineOverlayDebug { get; set; } = false; 83 | public TimelineBarStyleEnum TimelineOverlayBarStyle { get; set; } = TimelineBarStyleEnum.Fancy; 84 | public bool TimelineLocalAllowed { get; set; } = true; 85 | public string TimelineLocalFolder { get; set; } = ""; 86 | public bool TimelineTabVisible { get; set; } = false; 87 | 88 | public Rectangle NotificationOverlayLocation { get; set; } = new Rectangle() { Left = 100.0f, Top = 350.0f, Bottom = 550.0f, Right = 400.0f }; 89 | public float NotificationOverlayEntryHeight { get; set; } = 40.0f; 90 | public float NotificationOverlayEntryMargin { get; set; } = 10.0f; 91 | public bool NotificationOverlayVisible { get; set; } = false; 92 | public bool NotificationOrderReverse { get; set; } = false; 93 | public float NotificationOverlayTextScale { get; set; } = 1.5f; 94 | public TextAlignmentEnum NotificationEntryAlignment { get; set; } = TextAlignmentEnum.Center; 95 | public Vector4 NotificationOverlayCriticalTextColor { get; set; } = new Vector4(1.0f, 1.0f, 0.0f, 1.0f); 96 | public Vector4 NotificationOverlayCriticalBgColor { get; set; } = new Vector4(1.0f, 0.0f, 0.0f, 0.5f); 97 | public Vector4 NotificationOverlayImportantTextColor { get; set; } = new Vector4(1.0f, 1.0f, 1.0f, 1.0f); 98 | public Vector4 NotificationOverlayImportantBgColor { get; set; } = new Vector4(1.0f, 1.0f, 0.0f, 0.5f); 99 | public Vector4 NotificationOverlayNormalTextColor { get; set; } = new Vector4(0.3922f, 0.9098f, 1.0f, 1.0f); 100 | public Vector4 NotificationOverlayNormalBgColor { get; set; } = new Vector4(0.0f, 0.0f, 0.0f, 1.0f); 101 | 102 | public bool DebugOnlyLogAutomarkers { get; set; } = false; 103 | 104 | public bool TTSEnabled { get; set; } = true; 105 | public bool TTSAllNotifications { get; set; } = false; 106 | public int TTSSpeed { get; set; } = 0; 107 | public int TTSVolume { get; set; } = 50; 108 | 109 | public string PropertyBlob { get; set; } = ""; 110 | 111 | internal AutomarkerTiming DefaultAutomarkerTiming = new AutomarkerTiming(); 112 | 113 | public Config() 114 | { 115 | TimelineLocalFolder = Path.GetTempPath(); 116 | } 117 | 118 | public float AutomarkerIniDelayMin 119 | { 120 | get 121 | { 122 | return DefaultAutomarkerTiming.IniDelayMin; 123 | } 124 | set 125 | { 126 | DefaultAutomarkerTiming.IniDelayMin = value; 127 | } 128 | } 129 | 130 | public float AutomarkerIniDelayMax 131 | { 132 | get 133 | { 134 | return DefaultAutomarkerTiming.IniDelayMax; 135 | } 136 | set 137 | { 138 | DefaultAutomarkerTiming.IniDelayMax = value; 139 | } 140 | } 141 | 142 | public float AutomarkerSubDelayMin 143 | { 144 | get 145 | { 146 | return DefaultAutomarkerTiming.SubDelayMin; 147 | } 148 | set 149 | { 150 | DefaultAutomarkerTiming.SubDelayMin = value; 151 | } 152 | } 153 | 154 | public float AutomarkerSubDelayMax 155 | { 156 | get 157 | { 158 | return DefaultAutomarkerTiming.SubDelayMax; 159 | } 160 | set 161 | { 162 | DefaultAutomarkerTiming.SubDelayMax = value; 163 | } 164 | } 165 | 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd 364 | /Lemegeton/Lemegeton.json 365 | /Lemegeton/packages.lock.json 366 | -------------------------------------------------------------------------------- /Lemegeton/Core/SigLocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Lemegeton.Core 7 | { 8 | 9 | internal class SigLocator 10 | { 11 | 12 | [StructLayout(LayoutKind.Sequential)] 13 | public struct IMAGE_DOS_HEADER 14 | { 15 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] 16 | public char[] e_magic; // Magic number 17 | public UInt16 e_cblp; // Bytes on last page of file 18 | public UInt16 e_cp; // Pages in file 19 | public UInt16 e_crlc; // Relocations 20 | public UInt16 e_cparhdr; // Size of header in paragraphs 21 | public UInt16 e_minalloc; // Minimum extra paragraphs needed 22 | public UInt16 e_maxalloc; // Maximum extra paragraphs needed 23 | public UInt16 e_ss; // Initial (relative) SS value 24 | public UInt16 e_sp; // Initial SP value 25 | public UInt16 e_csum; // Checksum 26 | public UInt16 e_ip; // Initial IP value 27 | public UInt16 e_cs; // Initial (relative) CS value 28 | public UInt16 e_lfarlc; // File address of relocation table 29 | public UInt16 e_ovno; // Overlay number 30 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] 31 | public UInt16[] e_res1; // Reserved words 32 | public UInt16 e_oemid; // OEM identifier (for e_oeminfo) 33 | public UInt16 e_oeminfo; // OEM information; e_oemid specific 34 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] 35 | public UInt16[] e_res2; // Reserved words 36 | public Int32 e_lfanew; // File address of new exe header 37 | 38 | private string _e_magic 39 | { 40 | get { return new string(e_magic); } 41 | } 42 | 43 | public bool isValid 44 | { 45 | get { return _e_magic == "MZ"; } 46 | } 47 | } 48 | 49 | [StructLayout(LayoutKind.Explicit)] 50 | public struct IMAGE_NT_HEADERS64 51 | { 52 | [FieldOffset(0)] 53 | public uint Signature; 54 | 55 | [FieldOffset(24)] 56 | public IMAGE_OPTIONAL_HEADER64 OptionalHeader; 57 | 58 | } 59 | 60 | [StructLayout(LayoutKind.Explicit)] 61 | public struct IMAGE_OPTIONAL_HEADER64 62 | { 63 | 64 | [FieldOffset(4)] 65 | public uint SizeOfCode; 66 | 67 | [FieldOffset(20)] 68 | public uint BaseOfCode; 69 | 70 | } 71 | 72 | private State _state; 73 | private uint _codeSize; 74 | private uint _codeBase; 75 | private int _dataLength; 76 | private byte[] _data; 77 | 78 | public SigLocator(State state) 79 | { 80 | _state = state; 81 | nint baseaddr = _state.ss.Module.BaseAddress; 82 | IMAGE_DOS_HEADER dos = Marshal.PtrToStructure(baseaddr); 83 | if (dos.isValid == true) 84 | { 85 | IMAGE_NT_HEADERS64 nt = Marshal.PtrToStructure(baseaddr + dos.e_lfanew); 86 | _codeSize = nt.OptionalHeader.SizeOfCode; 87 | _codeBase = nt.OptionalHeader.BaseOfCode; 88 | _dataLength = (int)(_codeBase + _codeSize); 89 | _data = new byte[_dataLength]; 90 | Marshal.Copy(baseaddr, _data, 0, _dataLength); 91 | } 92 | } 93 | 94 | public nint ScanText(string signature) 95 | { 96 | if (_state.ss.TryScanText(signature, out nint addr) == true) 97 | { 98 | return addr; 99 | } 100 | return IntPtr.Zero; 101 | } 102 | 103 | public nint ScanData(string signature) 104 | { 105 | if (_state.ss.TryScanData(signature, out nint addr) == true) 106 | { 107 | return addr; 108 | } 109 | return IntPtr.Zero; 110 | } 111 | 112 | public nint ScanModule(string signature) 113 | { 114 | if (_state.ss.TryScanModule(signature, out nint addr) == true) 115 | { 116 | return addr; 117 | } 118 | return IntPtr.Zero; 119 | } 120 | 121 | public nint GetStaticAddressFromSig(string signature, int offset = 0) 122 | { 123 | if (_state.ss.TryGetStaticAddressFromSig(signature, out nint addr, offset) == true) 124 | { 125 | return addr; 126 | } 127 | return IntPtr.Zero; 128 | } 129 | 130 | public nint OldGetStaticAddressFromSig(string signature, int offset = 0) 131 | { 132 | IntPtr instrAddr = OldScanText(signature); 133 | if (instrAddr == IntPtr.Zero) 134 | { 135 | return IntPtr.Zero; 136 | } 137 | instrAddr = IntPtr.Add(instrAddr, offset); 138 | nint bAddr = _state.ss.Module.BaseAddress; 139 | long num; 140 | do 141 | { 142 | instrAddr = IntPtr.Add(instrAddr, 1); 143 | num = Marshal.ReadInt32(instrAddr) + (long)instrAddr + 4 - bAddr; 144 | } 145 | while (!(num >= _state.ss.DataSectionOffset && num <= _state.ss.DataSectionOffset + _state.ss.DataSectionSize)); 146 | return IntPtr.Add(instrAddr, Marshal.ReadInt32(instrAddr) + 4); 147 | } 148 | 149 | private IntPtr OldScanText(string pattern) 150 | { 151 | var results = FindPattern(pattern); 152 | if (results.Count != 1) 153 | { 154 | _state.Log(State.LogLevelEnum.Error, null, "Signature pattern returned {0} hits", results.Count); 155 | return 0; 156 | } 157 | var scanRet = results[0]; 158 | var insnByte = Marshal.ReadByte(scanRet); 159 | if (insnByte == 0xE8 || insnByte == 0xE9) 160 | { 161 | var jumpOffset = Marshal.ReadInt32(IntPtr.Add(scanRet, 1)); 162 | return IntPtr.Add(scanRet, 5 + jumpOffset); 163 | } 164 | return scanRet; 165 | } 166 | 167 | private List FindPattern(string pattern) 168 | { 169 | var results = FindSignature(HexToPattern(pattern)); 170 | nint baseaddr = _state.ss.Module.BaseAddress; 171 | for (int i = 0; i < results.Count; i++) 172 | { 173 | results[i] = baseaddr + (int)results[i]; 174 | } 175 | return results; 176 | } 177 | 178 | private List HexToPattern(string data) 179 | { 180 | List bytes = new List(); 181 | for (int i = 0; i < data.Length - 1;) 182 | { 183 | if (data[i] == '?') 184 | { 185 | if (data[i + 1] == '?') 186 | { 187 | i++; 188 | } 189 | i++; 190 | bytes.Add(-1); 191 | continue; 192 | } 193 | if (data[i] == ' ') 194 | { 195 | i++; 196 | continue; 197 | } 198 | string bh = data.Substring(i, 2); 199 | var b = byte.Parse(bh, NumberStyles.AllowHexSpecifier); 200 | bytes.Add(b); 201 | i += 2; 202 | } 203 | return bytes; 204 | } 205 | 206 | private List FindSignature(List pattern) 207 | { 208 | List ret = new List(); 209 | uint plen = (uint)pattern.Count; 210 | var dataLength = _dataLength - plen; 211 | for (var i = _codeBase; i < dataLength; i++) 212 | { 213 | if (ByteMatch(_data, (int)i, pattern) == true) 214 | { 215 | ret.Add((IntPtr)i); 216 | } 217 | } 218 | return ret; 219 | } 220 | 221 | private bool ByteMatch(byte[] bytes, int start, List pattern) 222 | { 223 | for (int i = start, j = 0; j < pattern.Count; i++, j++) 224 | { 225 | if (pattern[j] == -1) 226 | { 227 | continue; 228 | } 229 | if (bytes[i] != pattern[j]) 230 | { 231 | return false; 232 | } 233 | } 234 | return true; 235 | } 236 | 237 | } 238 | 239 | } 240 | -------------------------------------------------------------------------------- /Lemegeton/Core/AutomarkerSigns.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Lemegeton.Core 6 | { 7 | 8 | public class AutomarkerSigns 9 | { 10 | 11 | public class Preset 12 | { 13 | 14 | public string Name { get; set; } = "New preset"; 15 | public bool Builtin { get; set; } = true; 16 | public Dictionary Roles { get; set; } = new Dictionary(); 17 | 18 | public string Serialize() 19 | { 20 | List temp = new List(); 21 | temp.Add(string.Format("Name={0}", Plugin.Base64Encode(Name))); 22 | temp.Add(string.Format("Builtin={0}", Builtin)); 23 | foreach (KeyValuePair kp in Roles) 24 | { 25 | temp.Add(string.Format("{0}={1}", kp.Key, kp.Value.ToString())); 26 | } 27 | return string.Join(";", temp); 28 | } 29 | 30 | public void Deserialize(string data) 31 | { 32 | string[] temp = data.Split(";"); 33 | foreach (string t in temp) 34 | { 35 | string[] item = t.Split("=", 2); 36 | switch (item[0]) 37 | { 38 | case "Name": 39 | Name = Plugin.Base64Decode(item[1]); 40 | break; 41 | case "Builtin": 42 | Builtin = bool.Parse(item[1]); 43 | break; 44 | default: 45 | if (Roles.ContainsKey(item[0]) == true) 46 | { 47 | AutomarkerSigns.SignEnum sign = (AutomarkerSigns.SignEnum)Enum.Parse(typeof(AutomarkerSigns.SignEnum), item[1]); 48 | Roles[item[0]] = sign; 49 | } 50 | break; 51 | } 52 | } 53 | } 54 | 55 | } 56 | 57 | public enum SignEnum 58 | { 59 | None, 60 | Attack1, Attack2, Attack3, Attack4, Attack5, 61 | Attack6, Attack7, Attack8, 62 | Bind1, Bind2, Bind3, 63 | Ignore1, Ignore2, 64 | Square, Circle, Plus, Triangle, 65 | AttackNext, BindNext, IgnoreNext, 66 | } 67 | 68 | public Dictionary Presets { get; set; } 69 | public Dictionary Roles { get; set; } 70 | public string SelectedPreset { get; set; } = null; 71 | 72 | public AutomarkerSigns() 73 | { 74 | Roles = new Dictionary(); 75 | Presets = new Dictionary(); 76 | } 77 | 78 | public static int GetSignIndex(SignEnum sign) 79 | { 80 | switch (sign) 81 | { 82 | default: 83 | case SignEnum.None: return 0; 84 | case SignEnum.Attack1: return 1; 85 | case SignEnum.Attack2: return 2; 86 | case SignEnum.Attack3: return 3; 87 | case SignEnum.Attack4: return 4; 88 | case SignEnum.Attack5: return 5; 89 | case SignEnum.Bind1: return 6; 90 | case SignEnum.Bind2: return 7; 91 | case SignEnum.Bind3: return 8; 92 | case SignEnum.Ignore1: return 9; 93 | case SignEnum.Ignore2: return 10; 94 | case SignEnum.Square: return 11; 95 | case SignEnum.Circle: return 12; 96 | case SignEnum.Plus: return 13; 97 | case SignEnum.Triangle: return 14; 98 | case SignEnum.Attack6: return 15; 99 | case SignEnum.Attack7: return 16; 100 | case SignEnum.Attack8: return 17; 101 | } 102 | } 103 | 104 | public void SetRole(string id, SignEnum sign, bool autoswap = true) 105 | { 106 | if (autoswap == true && sign != SignEnum.None && sign != SignEnum.AttackNext && sign != SignEnum.BindNext && sign != SignEnum.IgnoreNext) 107 | { 108 | var existing = (from ix in Roles where ix.Value == sign select ix.Key).FirstOrDefault(); 109 | if (existing != null) 110 | { 111 | if (Roles.ContainsKey(id) == true) 112 | { 113 | Roles[existing] = Roles[id]; 114 | } 115 | else 116 | { 117 | Roles[existing] = SignEnum.None; 118 | } 119 | } 120 | } 121 | Roles[id] = sign; 122 | } 123 | 124 | public void AddPreset(Preset preset) 125 | { 126 | if (Presets.ContainsKey(preset.Name) == true) 127 | { 128 | if (Presets[preset.Name].Builtin == true && preset.Builtin == false) 129 | { 130 | return; 131 | } 132 | } 133 | Presets[preset.Name] = preset; 134 | } 135 | 136 | public void ApplyPreset(string id) 137 | { 138 | if (id == null || Presets.ContainsKey(id) == false) 139 | { 140 | SelectedPreset = null; 141 | return; 142 | } 143 | SelectedPreset = id; 144 | Preset pr = Presets[id]; 145 | foreach (KeyValuePair kp in pr.Roles) 146 | { 147 | SetRole(kp.Key, kp.Value, false); 148 | } 149 | } 150 | 151 | public void SavePreset(string id) 152 | { 153 | Preset pr = new Preset() { Builtin = false, Name = id }; 154 | foreach (KeyValuePair kp in Roles) 155 | { 156 | pr.Roles[kp.Key] = kp.Value; 157 | } 158 | AddPreset(pr); 159 | } 160 | 161 | public void DeletePreset(string id) 162 | { 163 | if (Presets.ContainsKey(id) == false) 164 | { 165 | return; 166 | } 167 | Preset pr = Presets[id]; 168 | if (pr.Builtin == true) 169 | { 170 | return; 171 | } 172 | if (SelectedPreset == id) 173 | { 174 | ApplyPreset(null); 175 | } 176 | Presets.Remove(id); 177 | } 178 | 179 | public string Serialize() 180 | { 181 | List temp = new List(); 182 | temp.Add(string.Format("SelectedPreset={0}", SelectedPreset)); 183 | foreach (KeyValuePair kp in Roles) 184 | { 185 | temp.Add(string.Format("{0}={1}", kp.Key, kp.Value.ToString())); 186 | } 187 | List userpresets = (from ix in Presets.Values where ix.Builtin == false select ix).ToList(); 188 | List prser = new List(); 189 | foreach (Preset pr in userpresets) 190 | { 191 | prser.Add(pr.Serialize()); 192 | } 193 | if (prser.Count > 0) 194 | { 195 | temp.Add(string.Format("UserPresets={0}", Plugin.Base64Encode(string.Join("|", prser)))); 196 | } 197 | return string.Join(";", temp); 198 | } 199 | 200 | public void Deserialize(string data) 201 | { 202 | string[] items = data.Split(";"); 203 | ApplyPreset(null); 204 | string applyPreset = null; 205 | string userPresetBlob = null; 206 | foreach (string item in items) 207 | { 208 | string[] kp = item.Split("=", 2); 209 | if (kp[0] == "SelectedPreset" && kp[1] != "") 210 | { 211 | applyPreset = kp[1]; 212 | } 213 | else if (kp[0] == "UserPresets" && kp[1] != "") 214 | { 215 | userPresetBlob = kp[1]; 216 | } 217 | else 218 | { 219 | if (Roles.ContainsKey(kp[0]) == true) 220 | { 221 | AutomarkerSigns.SignEnum sign = (AutomarkerSigns.SignEnum)Enum.Parse(typeof(AutomarkerSigns.SignEnum), kp[1]); 222 | SetRole(kp[0], sign, false); 223 | } 224 | } 225 | } 226 | if (userPresetBlob != null) 227 | { 228 | string prser = Plugin.Base64Decode(userPresetBlob); 229 | string[] prs = prser.Split("|"); 230 | foreach (string pr in prs) 231 | { 232 | Preset preset = new Preset(); 233 | foreach (KeyValuePair kp in Roles) 234 | { 235 | preset.Roles[kp.Key] = SignEnum.None; 236 | } 237 | preset.Deserialize(pr); 238 | AddPreset(preset); 239 | } 240 | } 241 | if (applyPreset != null) 242 | { 243 | ApplyPreset(applyPreset); 244 | } 245 | } 246 | 247 | internal AutomarkerPayload TestFunctionality(State st, AutomarkerPrio amp, AutomarkerTiming at, bool selfOnly, bool soft) 248 | { 249 | Party pty = st.GetPartyMembers(); 250 | if (amp != null) 251 | { 252 | amp.SortByPriority(pty.Members); 253 | int i = 1; 254 | foreach (Party.PartyMember pm in pty.Members) 255 | { 256 | pm.Selection = i++; 257 | } 258 | } 259 | else 260 | { 261 | st.AssignRandomSelections(pty, Roles.Count); 262 | } 263 | int roleId = 1; 264 | AutomarkerPayload ap = new AutomarkerPayload(st, selfOnly, soft); 265 | foreach (KeyValuePair kp in Roles) 266 | { 267 | var player = (from px in pty.Members where px.Selection == roleId select px).FirstOrDefault(); 268 | if (player != null) 269 | { 270 | st.Log(State.LogLevelEnum.Debug, null, "Assigning test role {0} with sign {1} to {2} (ID {3})", kp.Key, kp.Value, player.GameObject, player.ObjectId); 271 | ap.Assign(kp.Value, player.GameObject); 272 | } 273 | roleId++; 274 | } 275 | st.ExecuteAutomarkers(ap, at); 276 | return ap; 277 | } 278 | 279 | } 280 | 281 | } 282 | -------------------------------------------------------------------------------- /Lemegeton/Content/EwRaidAbyssos.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Lemegeton.Core; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Data; 6 | using static Lemegeton.Core.State; 7 | 8 | namespace Lemegeton.Content 9 | { 10 | 11 | internal class EwRaidAbyssos : Core.Content 12 | { 13 | 14 | public override FeaturesEnum Features => FeaturesEnum.None; 15 | 16 | private const int AbilityInviolateBonds = 30746; 17 | private const int AbilityInviolatePurgation = 30750; 18 | private const int StatusBond1 = 0xced; 19 | private const int StatusBond2 = 0xd46; 20 | private const int StatusPurgation1 = 0xcef; 21 | private const int StatusPurgation2 = 0xd42; 22 | private const int StatusPurgation3 = 0xd43; 23 | private const int StatusPurgation4 = 0xd44; 24 | 25 | private bool ZoneOk = false; 26 | private bool _subbed = false; 27 | 28 | private InviolateAM _inviolateAm; 29 | 30 | #region InviolateAM 31 | 32 | public class InviolateAM : Automarker 33 | { 34 | 35 | [AttributeOrderNumber(1000)] 36 | public AutomarkerSigns Signs { get; set; } 37 | 38 | [AttributeOrderNumber(2000)] 39 | public AutomarkerPrio Prio { get; set; } 40 | 41 | [DebugOption] 42 | [AttributeOrderNumber(2500)] 43 | public AutomarkerTiming Timing { get; set; } 44 | 45 | [DebugOption] 46 | [AttributeOrderNumber(3000)] 47 | public System.Action Test { get; set; } 48 | 49 | private uint _currentAction = 0; 50 | private uint _share1 = 0; 51 | private uint _share2 = 0; 52 | private uint _share3 = 0; 53 | private uint _share4 = 0; 54 | 55 | private Queue payloads = new Queue(); 56 | 57 | public InviolateAM(State state) : base(state) 58 | { 59 | Enabled = false; 60 | Signs = new AutomarkerSigns(); 61 | Prio = new AutomarkerPrio(); 62 | Timing = new AutomarkerTiming() { TimingType = AutomarkerTiming.TimingTypeEnum.Inherit, Parent = state.cfg.DefaultAutomarkerTiming }; 63 | Prio.Priority = AutomarkerPrio.PrioTypeEnum.Job; 64 | Signs.SetRole("ShareTarget", AutomarkerSigns.SignEnum.Circle, false); 65 | Signs.SetRole("Share1", AutomarkerSigns.SignEnum.Bind1, false); 66 | Signs.SetRole("Share2", AutomarkerSigns.SignEnum.Bind2, false); 67 | Signs.SetRole("Share3", AutomarkerSigns.SignEnum.Bind3, false); 68 | Signs.SetRole("Spread1", AutomarkerSigns.SignEnum.Attack1, false); 69 | Signs.SetRole("Spread2", AutomarkerSigns.SignEnum.Attack2, false); 70 | Signs.SetRole("Spread3", AutomarkerSigns.SignEnum.Attack3, false); 71 | Signs.SetRole("Spread4", AutomarkerSigns.SignEnum.Attack4, false); 72 | Test = new System.Action(() => Signs.TestFunctionality(state, Prio, Timing, SelfMarkOnly, AsSoftmarker)); 73 | } 74 | 75 | public override void Reset() 76 | { 77 | Log(State.LogLevelEnum.Debug, null, "Reset"); 78 | payloads.Clear(); 79 | _currentAction = 0; 80 | _share1 = 0; 81 | _share2 = 0; 82 | _share3 = 0; 83 | _share4 = 0; 84 | } 85 | 86 | internal void FeedAction(uint actionId) 87 | { 88 | if (Active == false) 89 | { 90 | return; 91 | } 92 | Reset(); 93 | Log(State.LogLevelEnum.Debug, null, "Registered action {0}", actionId); 94 | _currentAction = actionId; 95 | } 96 | 97 | internal void NextAutomarkers() 98 | { 99 | AutomarkerPayload ap; 100 | if (payloads.Count > 0) 101 | { 102 | ap = payloads.Dequeue(); 103 | _state.ExecuteAutomarkers(ap, Timing); 104 | } 105 | else 106 | { 107 | _state.ClearAutoMarkers(); 108 | } 109 | } 110 | 111 | internal void FeedStatus(uint dest, uint statusId, bool gained) 112 | { 113 | if (Active == false) 114 | { 115 | return; 116 | } 117 | Log(State.LogLevelEnum.Debug, null, "Registered status {0} {1} on {2:X}", statusId, gained, dest); 118 | switch (statusId) 119 | { 120 | case StatusBond1: 121 | case StatusPurgation1: 122 | if (gained == false) 123 | { 124 | NextAutomarkers(); 125 | } 126 | else 127 | { 128 | _share1 = dest; 129 | } 130 | break; 131 | case StatusBond2: 132 | case StatusPurgation2: 133 | if (gained == false) 134 | { 135 | NextAutomarkers(); 136 | } 137 | else 138 | { 139 | _share2 = dest; 140 | } 141 | break; 142 | case StatusPurgation3: 143 | if (gained == false) 144 | { 145 | NextAutomarkers(); 146 | } 147 | else 148 | { 149 | _share3 = dest; 150 | } 151 | break; 152 | case StatusPurgation4: 153 | if (gained == false) 154 | { 155 | NextAutomarkers(); 156 | } 157 | else 158 | { 159 | _share4 = dest; 160 | } 161 | break; 162 | } 163 | if ( 164 | (_currentAction == AbilityInviolateBonds && _share1 > 0 && _share2 > 0) 165 | || 166 | (_currentAction == AbilityInviolatePurgation && _share1 > 0 && _share2 > 0 && _share3 > 0 && _share4 > 0) 167 | ) 168 | { 169 | Log(State.LogLevelEnum.Debug, null, "Ready for automarkers"); 170 | CreatePayloadForShare(_share1); 171 | CreatePayloadForShare(_share2); 172 | if (_currentAction == AbilityInviolatePurgation) 173 | { 174 | CreatePayloadForShare(_share3); 175 | CreatePayloadForShare(_share4); 176 | } 177 | NextAutomarkers(); 178 | } 179 | } 180 | 181 | internal void CreatePayloadForShare(uint shareId) 182 | { 183 | AutomarkerPayload ap; 184 | AutomarkerPrio.PrioArchetypeEnum role; 185 | Party.PartyMember pm; 186 | List _sharesGo; 187 | List _spreadsGo; 188 | Party pty = _state.GetPartyMembers(); 189 | pm = (from ix in pty.Members where ix.ObjectId == _share1 select ix).First(); 190 | ap = new AutomarkerPayload(_state, SelfMarkOnly, AsSoftmarker); 191 | role = AutomarkerPrio.JobToArchetype(pm.Job); 192 | _sharesGo = new List( 193 | from ix in pty.Members 194 | where AutomarkerPrio.JobToArchetype(ix.Job) == role && ix.ObjectId != _share1 195 | select ix); 196 | _spreadsGo = new List( 197 | from ix in pty.Members 198 | where AutomarkerPrio.JobToArchetype(ix.Job) != role 199 | select ix); 200 | Prio.SortByPriority(_sharesGo); 201 | Prio.SortByPriority(_spreadsGo); 202 | ap.Assign(Signs.Roles["ShareTarget"], pm.GameObject); 203 | ap.Assign(Signs.Roles["Share1"], _sharesGo[0].GameObject); 204 | ap.Assign(Signs.Roles["Share2"], _sharesGo[1].GameObject); 205 | ap.Assign(Signs.Roles["Share3"], _sharesGo[2].GameObject); 206 | ap.Assign(Signs.Roles["Spread1"], _spreadsGo[0].GameObject); 207 | ap.Assign(Signs.Roles["Spread2"], _spreadsGo[1].GameObject); 208 | ap.Assign(Signs.Roles["Spread3"], _spreadsGo[2].GameObject); 209 | ap.Assign(Signs.Roles["Spread4"], _spreadsGo[3].GameObject); 210 | payloads.Enqueue(ap); 211 | } 212 | 213 | } 214 | 215 | #endregion 216 | 217 | public EwRaidAbyssos(State st) : base(st) 218 | { 219 | st.OnZoneChange += OnZoneChange; 220 | } 221 | 222 | protected override bool ExecutionImplementation() 223 | { 224 | if (ZoneOk == true) 225 | { 226 | return base.ExecutionImplementation(); 227 | } 228 | return false; 229 | } 230 | 231 | private void SubscribeToEvents() 232 | { 233 | lock (this) 234 | { 235 | if (_subbed == true) 236 | { 237 | return; 238 | } 239 | _subbed = true; 240 | Log(LogLevelEnum.Debug, null, "Subscribing to events"); 241 | _state.OnCastBegin += _state_OnCastBegin; 242 | _state.OnStatusChange += _state_OnStatusChange; 243 | } 244 | } 245 | 246 | private void _state_OnStatusChange(uint src, uint dest, uint statusId, bool gained, float duration, int stacks) 247 | { 248 | if (statusId == StatusBond1 || statusId == StatusBond2 || statusId == StatusPurgation1 || statusId == StatusPurgation2 || statusId == StatusPurgation3 || statusId == StatusPurgation4) 249 | { 250 | _inviolateAm.FeedStatus(dest, statusId, gained); 251 | } 252 | } 253 | 254 | private void UnsubscribeFromEvents() 255 | { 256 | lock (this) 257 | { 258 | if (_subbed == false) 259 | { 260 | return; 261 | } 262 | Log(LogLevelEnum.Debug, null, "Unsubscribing from events"); 263 | _state.OnStatusChange -= _state_OnStatusChange; 264 | _state.OnCastBegin -= _state_OnCastBegin; 265 | _subbed = false; 266 | } 267 | } 268 | 269 | private void _state_OnCastBegin(uint src, uint dest, ushort actionId, float castTime, float rotation) 270 | { 271 | if (actionId == AbilityInviolateBonds || actionId == AbilityInviolatePurgation) 272 | { 273 | _inviolateAm.FeedAction(actionId); 274 | } 275 | } 276 | 277 | private void OnCombatChange(bool inCombat) 278 | { 279 | Reset(); 280 | if (inCombat == true) 281 | { 282 | SubscribeToEvents(); 283 | } 284 | else 285 | { 286 | UnsubscribeFromEvents(); 287 | } 288 | } 289 | 290 | private void OnZoneChange(ushort newZone) 291 | { 292 | bool newZoneOk = (newZone == 1086); 293 | if (newZoneOk == true && ZoneOk == false) 294 | { 295 | Log(State.LogLevelEnum.Info, null, "Content available"); 296 | _inviolateAm = (InviolateAM)Items["InviolateAM"]; 297 | _state.OnCombatChange += OnCombatChange; 298 | LogItems(); 299 | } 300 | else if (newZoneOk == false && ZoneOk == true) 301 | { 302 | Log(State.LogLevelEnum.Info, null, "Content unavailable"); 303 | _state.OnCombatChange -= OnCombatChange; 304 | } 305 | ZoneOk = newZoneOk; 306 | } 307 | 308 | } 309 | 310 | } 311 | -------------------------------------------------------------------------------- /Lemegeton/Content/GenericDeepDungeon.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.Objects.Types; 2 | using Dalamud.Bindings.ImGui; 3 | using Lemegeton.Core; 4 | using System; 5 | using System.Numerics; 6 | 7 | namespace Lemegeton.Content 8 | { 9 | 10 | public abstract class GenericDeepDungeon : Core.Content 11 | { 12 | 13 | public override FeaturesEnum Features => FeaturesEnum.None; 14 | 15 | public class DrawEnemies : Core.ContentItem 16 | { 17 | 18 | public override FeaturesEnum Features => FeaturesEnum.Drawing; 19 | 20 | [AttributeOrderNumber(1000)] 21 | public Vector4 ObjectColor { get; set; } = new Vector4(1.0f, 0.0f, 0.0f, 1.0f); 22 | 23 | [AttributeOrderNumber(2000)] 24 | public bool DrawLine { get; set; } = true; 25 | 26 | [AttributeOrderNumber(3000)] 27 | public bool ShowNames { get; set; } = true; 28 | [AttributeOrderNumber(3001)] 29 | public bool IncludeDistance { get; set; } = true; 30 | [AttributeOrderNumber(3002)] 31 | public Vector4 TextColor { get; set; } = new Vector4(1.0f, 0.8f, 0.8f, 1.0f); 32 | 33 | [AttributeOrderNumber(4000)] 34 | public bool ShowNameBg { get; set; } = true; 35 | [AttributeOrderNumber(4001)] 36 | public Vector4 BgColor { get; set; } = new Vector4(0.0f, 0.0f, 0.0f, 0.7f); 37 | 38 | protected override bool ExecutionImplementation() 39 | { 40 | ImDrawListPtr draw; 41 | if (_state.StartDrawing(out draw) == false) 42 | { 43 | return false; 44 | } 45 | Vector3 me = _state.cs.LocalPlayer.Position; 46 | Vector2 pt = new Vector2(); 47 | me = _state.plug._ui.TranslateToScreen(me.X, me.Y, me.Z); 48 | float defSize = ImGui.GetFontSize(); 49 | float mul = 20.0f / defSize; 50 | foreach (IGameObject go in _state.ot) 51 | { 52 | if (go.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.BattleNpc && go.SubKind == 5 && go.IsDead == false) 53 | { 54 | IBattleChara bc = (IBattleChara)go; 55 | if (bc.CurrentHp > 0 && (_state.GetStatusFlags(bc) & Dalamud.Game.ClientState.Objects.Enums.StatusFlags.Hostile) != 0 && bc.ClassJob.RowId == 0) 56 | { 57 | double dist = Vector3.Distance(_state.cs.LocalPlayer.Position, go.Position); 58 | Vector3 temp = _state.plug._ui.TranslateToScreen(go.Position.X, go.Position.Y, go.Position.Z); 59 | pt.Y = temp.Y + 10.0f; 60 | string name = IncludeDistance == true ? String.Format("{0} ({1:0})", go.Name.ToString(), dist) : String.Format("{0}", go.Name.ToString()); 61 | Vector2 sz = ImGui.CalcTextSize(name); 62 | sz.X *= mul; 63 | sz.Y *= mul; 64 | pt.X = temp.X - (sz.X / 2.0f); 65 | if (ShowNames == true) 66 | { 67 | if (ShowNameBg == true) 68 | { 69 | draw.AddRectFilled( 70 | new Vector2(pt.X - 5, pt.Y - 5), 71 | new Vector2(pt.X + sz.X + 5, pt.Y + sz.Y + 5), 72 | ImGui.GetColorU32(BgColor), 73 | 1.0f 74 | ); 75 | } 76 | draw.AddText( 77 | ImGui.GetFont(), 78 | 20.0f, 79 | pt, 80 | ImGui.GetColorU32(TextColor), 81 | name 82 | ); 83 | } 84 | if (dist < 50.0f && DrawLine == true && (_state.GetStatusFlags(bc) & Dalamud.Game.ClientState.Objects.Enums.StatusFlags.OffhandOut) == 0) 85 | { 86 | draw.AddLine( 87 | new Vector2(temp.X, temp.Y), 88 | new Vector2(me.X, me.Y), 89 | ImGui.GetColorU32(ObjectColor), 90 | dist < 20.0f ? 10.0f : (dist < 30.0f ? 5.0f : 2.0f) 91 | ); 92 | } 93 | draw.AddCircleFilled( 94 | new Vector2(temp.X, temp.Y), 95 | 10.0f, 96 | ImGui.GetColorU32(ObjectColor), 97 | 20 98 | ); 99 | } 100 | } 101 | } 102 | return true; 103 | } 104 | 105 | public DrawEnemies(State state) : base(state) 106 | { 107 | } 108 | 109 | } 110 | 111 | public class DrawPylons : Core.ContentItem 112 | { 113 | 114 | public override FeaturesEnum Features => FeaturesEnum.Drawing; 115 | 116 | [AttributeOrderNumber(1000)] 117 | public Vector4 ObjectColor { get; set; } = new Vector4(0.0f, 1.0f, 0.0f, 1.0f); 118 | 119 | [AttributeOrderNumber(2000)] 120 | public bool ShowNames { get; set; } = true; 121 | [AttributeOrderNumber(2001)] 122 | public bool IncludeDistance { get; set; } = true; 123 | [AttributeOrderNumber(2002)] 124 | public Vector4 TextColor { get; set; } = new Vector4(0.8f, 1.0f, 0.8f, 1.0f); 125 | 126 | [AttributeOrderNumber(3000)] 127 | public bool ShowNameBg { get; set; } = true; 128 | [AttributeOrderNumber(3001)] 129 | public Vector4 BgColor { get; set; } = new Vector4(0.0f, 0.0f, 0.0f, 0.7f); 130 | 131 | protected override bool ExecutionImplementation() 132 | { 133 | ImDrawListPtr draw; 134 | if (_state.StartDrawing(out draw) == false) 135 | { 136 | return false; 137 | } 138 | Vector2 pt = new Vector2(); 139 | float defSize = ImGui.GetFontSize(); 140 | float mul = 20.0f / defSize; 141 | foreach (IGameObject go in _state.ot) 142 | { 143 | // 2007187 dead return 144 | // 2009506 hoh return 145 | // 2009507 hoh passage 146 | // 2013286 orthos return 147 | // 2013287 orthos passage 148 | if ( 149 | (go.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.EventObj) 150 | && 151 | ( 152 | (go.BaseId == 2007187 || go.BaseId == 2007188) 153 | || 154 | (go.BaseId == 2009506 || go.BaseId == 2009507) 155 | || 156 | (go.BaseId == 2013286 || go.BaseId == 2013287) 157 | ) 158 | ) 159 | { 160 | double dist = Vector3.Distance(_state.cs.LocalPlayer.Position, go.Position); 161 | Vector3 temp = _state.plug._ui.TranslateToScreen(go.Position.X, go.Position.Y, go.Position.Z); 162 | pt.Y = temp.Y + 10.0f; 163 | string name = IncludeDistance == true ? String.Format("{0} ({1:0})", go.Name.ToString(), dist) : String.Format("{0}", go.Name.ToString()); 164 | Vector2 sz = ImGui.CalcTextSize(name); 165 | sz.X *= mul; 166 | sz.Y *= mul; 167 | pt.X = temp.X - (sz.X / 2.0f); 168 | if (ShowNames == true) 169 | { 170 | if (ShowNameBg == true) 171 | { 172 | draw.AddRectFilled( 173 | new Vector2(pt.X - 5, pt.Y - 5), 174 | new Vector2(pt.X + sz.X + 5, pt.Y + sz.Y + 5), 175 | ImGui.GetColorU32(BgColor), 176 | 1.0f 177 | ); 178 | } 179 | draw.AddText( 180 | ImGui.GetFont(), 181 | 20.0f, 182 | pt, 183 | ImGui.GetColorU32(TextColor), 184 | name 185 | ); 186 | } 187 | draw.AddCircleFilled( 188 | new Vector2(temp.X, temp.Y), 189 | 10.0f, 190 | ImGui.GetColorU32(ObjectColor), 191 | 20 192 | ); 193 | } 194 | } 195 | return true; 196 | } 197 | 198 | public DrawPylons(State state) : base(state) 199 | { 200 | } 201 | 202 | } 203 | 204 | public class DrawChests : Core.ContentItem 205 | { 206 | 207 | public override FeaturesEnum Features => FeaturesEnum.Drawing; 208 | 209 | [AttributeOrderNumber(1000)] 210 | public Vector4 GoldColor { get; set; } = new Vector4(0.9686f, 0.7608f, 0.0392f, 1.0f); 211 | [AttributeOrderNumber(1001)] 212 | public Vector4 SilverColor { get; set; } = new Vector4(0.7f, 0.7f, 0.7f, 1.0f); 213 | [AttributeOrderNumber(1002)] 214 | public Vector4 BronzeColor { get; set; } = new Vector4(0.635f, 0.364f, 0.102f, 1.0f); 215 | [AttributeOrderNumber(1003)] 216 | public Vector4 BandedColor { get; set; } = new Vector4(0.455f, 0.714f, 0.455f, 1.0f); 217 | [AttributeOrderNumber(1004)] 218 | public Vector4 HoardColor { get; set; } = new Vector4(1.0f, 0.957f, 0.286f, 1.0f); 219 | 220 | [AttributeOrderNumber(2000)] 221 | public bool ShowNames { get; set; } = true; 222 | [AttributeOrderNumber(2001)] 223 | public bool IncludeDistance { get; set; } = true; 224 | 225 | [AttributeOrderNumber(3000)] 226 | public bool ShowNameBg { get; set; } = true; 227 | [AttributeOrderNumber(3001)] 228 | public Vector4 BgColor { get; set; } = new Vector4(0.0f, 0.0f, 0.0f, 0.7f); 229 | 230 | protected override bool ExecutionImplementation() 231 | { 232 | ImDrawListPtr draw; 233 | if (_state.StartDrawing(out draw) == false) 234 | { 235 | return false; 236 | } 237 | Vector2 pt = new Vector2(); 238 | float defSize = ImGui.GetFontSize(); 239 | float mul = 20.0f / defSize; 240 | Vector4 itemcol = GoldColor; 241 | foreach (IGameObject go in _state.ot) 242 | { 243 | if ( 244 | (go.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Treasure) 245 | || 246 | (go.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.EventObj && (go.BaseId == 2007357 || go.BaseId == 2007358 || go.BaseId == 2007542 || go.BaseId == 2007543)) 247 | ) 248 | { 249 | switch (go.BaseId) 250 | { 251 | case 2007358: 252 | itemcol = GoldColor; 253 | break; 254 | case 2007357: 255 | itemcol = SilverColor; 256 | break; 257 | case 2007543: 258 | itemcol = BandedColor; 259 | break; 260 | case 2007542: 261 | itemcol = HoardColor; 262 | break; 263 | default: 264 | itemcol = BronzeColor; 265 | break; 266 | } 267 | double dist = Vector3.Distance(_state.cs.LocalPlayer.Position, go.Position); 268 | Vector3 temp = _state.plug._ui.TranslateToScreen(go.Position.X, go.Position.Y, go.Position.Z); 269 | pt.Y = temp.Y + 10.0f; 270 | string rname = go.BaseId != 2007542 ? go.Name.ToString() : I18n.Translate("Content/DeepDungeon/AccursedHoard"); 271 | string name = IncludeDistance == true ? String.Format("{0} ({1:0})", rname, dist) : String.Format("{0}", rname); 272 | Vector2 sz = ImGui.CalcTextSize(name); 273 | sz.X *= mul; 274 | sz.Y *= mul; 275 | pt.X = temp.X - (sz.X / 2.0f); 276 | if (ShowNames == true) 277 | { 278 | if (ShowNameBg == true) 279 | { 280 | draw.AddRectFilled( 281 | new Vector2(pt.X - 5, pt.Y - 5), 282 | new Vector2(pt.X + sz.X + 5, pt.Y + sz.Y + 5), 283 | ImGui.GetColorU32(BgColor), 284 | 1.0f 285 | ); 286 | } 287 | draw.AddText( 288 | ImGui.GetFont(), 289 | 20.0f, 290 | pt, 291 | ImGui.GetColorU32(itemcol), 292 | name 293 | ); 294 | } 295 | draw.AddCircleFilled( 296 | new Vector2(temp.X, temp.Y), 297 | 10.0f, 298 | ImGui.GetColorU32(itemcol), 299 | 20 300 | ); 301 | } 302 | } 303 | return true; 304 | } 305 | 306 | public DrawChests(State state) : base(state) 307 | { 308 | } 309 | 310 | } 311 | 312 | public GenericDeepDungeon(State st) : base(st) 313 | { 314 | } 315 | 316 | } 317 | 318 | } 319 | -------------------------------------------------------------------------------- /Lemegeton/Core/TimelineRecorder.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.Objects.Types; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CharacterStruct = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; 8 | using static Lemegeton.Core.State; 9 | using System.Xml.Linq; 10 | 11 | namespace Lemegeton.Core 12 | { 13 | 14 | internal class TimelineRecorder 15 | { 16 | 17 | private enum RecordingStateEnum 18 | { 19 | Idle, 20 | Recording, 21 | Suspended 22 | } 23 | 24 | [Flags] 25 | internal enum RecordedEventsEnum 26 | { 27 | AutoAttack = 0x0001, 28 | HostileAction = 0x0010, 29 | HostileSpawn = 0x0020, 30 | HostileStatusGain = 0x0040, 31 | HostileStatusLost = 0x0080, 32 | FriendlyAction = 0x0100, 33 | FriendlySpawn = 0x0200, 34 | FriendlyStatusGain = 0x0400, 35 | FriendlyStatusLost = 0x0800, 36 | } 37 | 38 | public RecordedEventsEnum RecordedEvents = RecordedEventsEnum.HostileAction | RecordedEventsEnum.HostileSpawn; 39 | 40 | public bool StartRecordingOnCombat { get; set; } = false; 41 | public bool ResumeRecordingOnTargettable { get; set; } = false; 42 | 43 | public bool SuspendRecordingAfterCombat { get; set; } = false; 44 | public bool SuspendRecordingOnUntargettable { get; set; } = false; 45 | 46 | public bool StopRecordingAfterCombat { get; set; } = false; 47 | public bool StopRecordingOnUntargettable { get; set; } = false; 48 | 49 | private bool _subbed = false; 50 | private DateTime _startTime = DateTime.Now; 51 | private State _state = null; 52 | 53 | internal delegate void RecordingFinishedDelegate(Timeline result); 54 | internal event RecordingFinishedDelegate RecordingFinished; 55 | 56 | private RecordingStateEnum _recState = RecordingStateEnum.Idle; 57 | private RecordingStateEnum recState 58 | { 59 | get 60 | { 61 | return _recState; 62 | } 63 | set 64 | { 65 | _recState = value; 66 | } 67 | } 68 | 69 | private uint _lastActionId = 0; 70 | private DateTime _lastActionTs = DateTime.Now; 71 | private uint _lastSpawnNameId = 0; 72 | private DateTime _lastSpawnTs = DateTime.Now; 73 | 74 | private Timeline _tl = null; 75 | private Timeline.Encounter _enc = null; 76 | 77 | public TimelineRecorder(State state) 78 | { 79 | _state = state; 80 | recState = RecordingStateEnum.Idle; 81 | } 82 | 83 | private void StartRecording() 84 | { 85 | if (recState == RecordingStateEnum.Recording) 86 | { 87 | return; 88 | } 89 | if (recState == RecordingStateEnum.Suspended) 90 | { 91 | _startTime = DateTime.Now; 92 | _state.Log(State.LogLevelEnum.Debug, null, "Resuming timeline recording with next encounter"); 93 | Timeline.Encounter enc = new Timeline.Encounter() { Id = _tl.Encounters.Count + 1 }; 94 | enc.Triggers.Add(new Timeline.Encounter.Trigger() { Type = Timeline.Encounter.Trigger.EventTypeEnum.Start, EventType = Timeline.Encounter.Trigger.TriggerTypeEnum.OnCombatStart }); 95 | enc.Triggers.Add(new Timeline.Encounter.Trigger() { Type = Timeline.Encounter.Trigger.EventTypeEnum.Stop, EventType = Timeline.Encounter.Trigger.TriggerTypeEnum.OnCombatEnd }); 96 | enc.Triggers.Add(new Timeline.Encounter.Trigger() { Type = Timeline.Encounter.Trigger.EventTypeEnum.Select, EventType = Timeline.Encounter.Trigger.TriggerTypeEnum.Default }); 97 | _tl.Encounters.Add(enc); 98 | _enc = enc; 99 | } 100 | else 101 | { 102 | _startTime = DateTime.Now; 103 | _state.Log(State.LogLevelEnum.Debug, null, "Starting new timeline recording"); 104 | _tl = new Timeline(); 105 | _tl.Territory = _state.cs.TerritoryType; 106 | _tl.Description = String.Format("Recorded by TimelineRecorder on {0}", _startTime); 107 | Timeline.Encounter enc = new Timeline.Encounter() { Id = 1 }; 108 | enc.Triggers.Add(new Timeline.Encounter.Trigger() { Type = Timeline.Encounter.Trigger.EventTypeEnum.Start, EventType = Timeline.Encounter.Trigger.TriggerTypeEnum.OnCombatStart }); 109 | enc.Triggers.Add(new Timeline.Encounter.Trigger() { Type = Timeline.Encounter.Trigger.EventTypeEnum.Stop, EventType = Timeline.Encounter.Trigger.TriggerTypeEnum.OnCombatEnd }); 110 | enc.Triggers.Add(new Timeline.Encounter.Trigger() { Type = Timeline.Encounter.Trigger.EventTypeEnum.Select, EventType = Timeline.Encounter.Trigger.TriggerTypeEnum.Default }); 111 | _tl.Encounters.Add(enc); 112 | _enc = enc; 113 | } 114 | recState = RecordingStateEnum.Recording; 115 | } 116 | 117 | private void SuspendRecording() 118 | { 119 | if (recState != RecordingStateEnum.Recording) 120 | { 121 | return; 122 | } 123 | _state.Log(State.LogLevelEnum.Debug, null, "Timeline recording suspended"); 124 | recState = RecordingStateEnum.Suspended; 125 | } 126 | 127 | private void StopRecording() 128 | { 129 | if (recState == RecordingStateEnum.Idle) 130 | { 131 | return; 132 | } 133 | _lastActionId = 0; 134 | _lastActionTs = DateTime.Now; 135 | _lastSpawnNameId = 0; 136 | _lastSpawnTs = DateTime.Now; 137 | if (_tl != null && (_tl.Encounters.Count > 1 || _enc.Entries.Count > 0)) 138 | { 139 | _state.Log(LogLevelEnum.Debug, null, "Finishing recorded timeline"); 140 | RecordingFinished?.Invoke(_tl); 141 | } 142 | else 143 | { 144 | _state.Log(State.LogLevelEnum.Debug, null, "No encounters or events captured, not finishing timeline"); 145 | } 146 | recState = RecordingStateEnum.Idle; 147 | } 148 | 149 | private void SubscribeToEvents() 150 | { 151 | lock (this) 152 | { 153 | if (_subbed == true) 154 | { 155 | return; 156 | } 157 | _subbed = true; 158 | _state.Log(State.LogLevelEnum.Debug, null, "Subscribing to events for timeline recording"); 159 | _state.OnAction += _state_OnAction; 160 | _state.OnCombatChange += _state_OnCombatChange; 161 | _state.OnZoneChange += _state_OnZoneChange; 162 | _state.OnTargettable += _state_OnTargettable; 163 | _state.OnUntargettable += _state_OnUntargettable; 164 | _state.OnCombatantAdded += _state_OnCombatantAdded; 165 | _state.OnStatusChange += _state_OnStatusChange; 166 | } 167 | } 168 | 169 | private void UnsubscribeFromEvents() 170 | { 171 | lock (this) 172 | { 173 | if (_subbed == false) 174 | { 175 | return; 176 | } 177 | _state.Log(State.LogLevelEnum.Debug, null, "Unsubscribing from events for timeline recording"); 178 | _state.OnAction -= _state_OnAction; 179 | _state.OnCombatChange -= _state_OnCombatChange; 180 | _state.OnZoneChange -= _state_OnZoneChange; 181 | _state.OnTargettable -= _state_OnTargettable; 182 | _state.OnUntargettable -= _state_OnUntargettable; 183 | _state.OnCombatantAdded -= _state_OnCombatantAdded; 184 | _state.OnStatusChange -= _state_OnStatusChange; 185 | _subbed = false; 186 | } 187 | } 188 | 189 | private bool IsHostile(uint id) 190 | { 191 | IGameObject go = _state.GetActorById(id); 192 | return IsHostile(go); 193 | } 194 | 195 | private bool IsFriendly(uint id) 196 | { 197 | return !IsHostile(id); 198 | } 199 | 200 | private bool IsHostile(IGameObject go) 201 | { 202 | if (go is IBattleChara) 203 | { 204 | IBattleChara bc = (IBattleChara)go; 205 | return ((_state.GetStatusFlags(bc) & Dalamud.Game.ClientState.Objects.Enums.StatusFlags.Hostile) != 0); 206 | } 207 | return false; 208 | } 209 | 210 | private bool IsFriendly(IGameObject go) 211 | { 212 | return !IsHostile(go); 213 | } 214 | 215 | private void _state_OnStatusChange(uint src, uint dest, uint statusId, bool gained, float duration, int stacks) 216 | { 217 | Timeline.Entry e = new Timeline.Entry(); 218 | e.StartTime = (float)Math.Round((DateTime.Now - _startTime).TotalSeconds, 1); 219 | e.KeyValues.Add(statusId); 220 | e.Type = gained == true ? Timeline.Entry.EntryTypeEnum.StatusGain : Timeline.Entry.EntryTypeEnum.StatusLoss; 221 | e.Description = "todo"; 222 | _enc.Entries.Add(e); 223 | } 224 | 225 | private void _state_OnAction(uint src, uint dest, ushort actionId) 226 | { 227 | if (recState != RecordingStateEnum.Recording) 228 | { 229 | return; 230 | } 231 | if (_lastActionId == actionId && (DateTime.Now - _lastActionTs).TotalMilliseconds < 50.0) 232 | { 233 | return; 234 | } 235 | _lastActionId = actionId; 236 | _lastActionTs = DateTime.Now; 237 | IGameObject go = _state.GetActorById(src); 238 | if (go is ICharacter) 239 | { 240 | ICharacter ch = (ICharacter)go; 241 | unsafe 242 | { 243 | CharacterStruct* chs = (CharacterStruct*)ch.Address; 244 | if (chs->ModelContainer.ModelCharaId == 0) 245 | { 246 | return; 247 | } 248 | } 249 | } 250 | if (go is IBattleChara) 251 | { 252 | IBattleChara bc = (IBattleChara)go; 253 | if ((_state.GetStatusFlags(bc) & Dalamud.Game.ClientState.Objects.Enums.StatusFlags.Hostile) != 0) 254 | { 255 | Lumina.Excel.Sheets.Action a = _state.dm.Excel.GetSheet().GetRow(actionId); 256 | if ((RecordedEvents & RecordedEventsEnum.AutoAttack) == 0 && a.ActionCategory.RowId == 1) 257 | { 258 | return; 259 | } 260 | string abname = _state.plug.GetActionName(actionId); 261 | if (abname.Trim().Length > 0) 262 | { 263 | Timeline.Entry e = new Timeline.Entry(); 264 | e.StartTime = (float)Math.Round((DateTime.Now - _startTime).TotalSeconds, 1); 265 | e.Type = Timeline.Entry.EntryTypeEnum.Ability; 266 | e.KeyValues.Add(actionId); 267 | e.Description = String.Format("{0} ({1}): {2}", bc.Name, bc.NameId, abname); 268 | _enc.Entries.Add(e); 269 | } 270 | } 271 | } 272 | } 273 | 274 | private void _state_OnCombatChange(bool inCombat) 275 | { 276 | if (inCombat == true && StartRecordingOnCombat == true && recState != RecordingStateEnum.Recording) 277 | { 278 | _state.Log(State.LogLevelEnum.Debug, null, "Combat started, starting timeline recording"); 279 | StartRecording(); 280 | } 281 | if (inCombat == false && recState == RecordingStateEnum.Recording) 282 | { 283 | if (SuspendRecordingAfterCombat == true) 284 | { 285 | _state.Log(State.LogLevelEnum.Debug, null, "Combat ended, suspending timeline recording"); 286 | StopRecording(); 287 | } 288 | else if (StopRecordingAfterCombat == true) 289 | { 290 | _state.Log(State.LogLevelEnum.Debug, null, "Combat ended, stopping timeline recording"); 291 | StopRecording(); 292 | } 293 | } 294 | } 295 | 296 | private void _state_OnZoneChange(ushort newZone) 297 | { 298 | if (recState != RecordingStateEnum.Idle) 299 | { 300 | _state.Log(State.LogLevelEnum.Debug, null, "Zone changed, stopping timeline recording"); 301 | StopRecording(); 302 | } 303 | } 304 | 305 | private void _state_OnTargettable() 306 | { 307 | if (recState == RecordingStateEnum.Suspended && ResumeRecordingOnTargettable == true) 308 | { 309 | _state.Log(State.LogLevelEnum.Debug, null, "Hostiles targettable, resuming timeline recording"); 310 | StartRecording(); 311 | } 312 | } 313 | 314 | private void _state_OnUntargettable() 315 | { 316 | if (recState == RecordingStateEnum.Recording && SuspendRecordingOnUntargettable == true) 317 | { 318 | _state.Log(State.LogLevelEnum.Debug, null, "Hostiles untargettable, suspending timeline recording"); 319 | SuspendRecording(); 320 | } 321 | else if (recState != RecordingStateEnum.Idle && StopRecordingOnUntargettable == true) 322 | { 323 | _state.Log(State.LogLevelEnum.Debug, null, "Hostiles untargettable, stopping timeline recording"); 324 | StopRecording(); 325 | } 326 | } 327 | 328 | private void _state_OnCombatantAdded(IGameObject go) 329 | { 330 | if (recState != RecordingStateEnum.Recording || (RecordedEvents & RecordedEventsEnum.HostileSpawn) == 0) 331 | { 332 | return; 333 | } 334 | if (go is ICharacter) 335 | { 336 | ICharacter ch = (ICharacter)go; 337 | unsafe 338 | { 339 | CharacterStruct* chs = (CharacterStruct*)ch.Address; 340 | if (chs->ModelContainer.ModelCharaId == 0) 341 | { 342 | return; 343 | } 344 | } 345 | } 346 | if (go is IBattleChara) 347 | { 348 | IBattleChara bc = (IBattleChara)go; 349 | if (_lastSpawnNameId == bc.NameId && (DateTime.Now - _lastSpawnTs).TotalMilliseconds < 50.0) 350 | { 351 | return; 352 | } 353 | _lastSpawnNameId = bc.NameId; 354 | _lastSpawnTs = DateTime.Now; 355 | if ((_state.GetStatusFlags(bc) & Dalamud.Game.ClientState.Objects.Enums.StatusFlags.Hostile) != 0 && bc.MaxHp > 0) 356 | { 357 | Timeline.Entry e = new Timeline.Entry(); 358 | e.StartTime = (float)Math.Round((DateTime.Now - _startTime).TotalSeconds, 1); 359 | e.Type = Timeline.Entry.EntryTypeEnum.Spawn; 360 | e.KeyValues.Add(bc.NameId); 361 | e.Description = String.Format("{0} ({1})", bc.Name, bc.NameId); 362 | _enc.Entries.Add(e); 363 | } 364 | } 365 | } 366 | 367 | } 368 | 369 | } 370 | -------------------------------------------------------------------------------- /Lemegeton/Content/Timelines.cs: -------------------------------------------------------------------------------- 1 | using Dalamud.Game.ClientState.Objects.Types; 2 | using Dalamud.Bindings.ImGui; 3 | using Lemegeton.Core; 4 | using System; 5 | using System.Numerics; 6 | using System.Text; 7 | using System.IO; 8 | using static Lemegeton.Core.State; 9 | using GameObject = Dalamud.Game.ClientState.Objects.Types.IGameObject; 10 | using GameObjectPtr = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; 11 | using CharacterStruct = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; 12 | using Vector3 = System.Numerics.Vector3; 13 | using System.Collections.Generic; 14 | using static Lemegeton.Core.Timeline.Encounter; 15 | using static Lemegeton.Core.Timeline; 16 | using Lumina.Excel.Sheets; 17 | using FFXIVClientStructs.FFXIV.Client.Game.Character; 18 | 19 | namespace Lemegeton.Content 20 | { 21 | 22 | public class Timelines : Core.Content 23 | { 24 | 25 | public override FeaturesEnum Features => FeaturesEnum.None; 26 | 27 | public class TimelineRecorder : Core.ContentItem 28 | { 29 | 30 | private enum RecordingStateEnum 31 | { 32 | Idle, 33 | Recording, 34 | Suspended 35 | } 36 | 37 | public override FeaturesEnum Features => FeaturesEnum.None; 38 | 39 | [AttributeOrderNumber(1000)] 40 | public bool StartRecordingOnCombat { get; set; } = false; 41 | [AttributeOrderNumber(1001)] 42 | public bool ResumeRecordingOnTargettable { get; set; } = false; 43 | 44 | [AttributeOrderNumber(1010)] 45 | public bool SuspendRecordingAfterCombat { get; set; } = false; 46 | [AttributeOrderNumber(1011)] 47 | public bool SuspendRecordingOnUntargettable { get; set; } = false; 48 | 49 | [AttributeOrderNumber(1020)] 50 | public bool StopRecordingAfterCombat { get; set; } = false; 51 | [AttributeOrderNumber(1021)] 52 | public bool StopRecordingOnUntargettable { get; set; } = false; 53 | 54 | [AttributeOrderNumber(1500)] 55 | public System.Action StartRecordingAction { get; set; } 56 | [AttributeOrderNumber(1501)] 57 | public System.Action SuspendRecordingAction { get; set; } 58 | [AttributeOrderNumber(1502)] 59 | public System.Action StopRecordingAction { get; set; } 60 | 61 | [AttributeOrderNumber(2000)] 62 | public string TargetFolder { get; set; } 63 | 64 | [AttributeOrderNumber(2500)] 65 | public bool IgnoreAutoAttacks { get; set; } = true; 66 | [AttributeOrderNumber(2501)] 67 | public bool IgnoreSpawns { get; set; } = false; 68 | 69 | [AttributeOrderNumber(3000)] 70 | public string CurrentStatus { get; private set; } = ""; 71 | [AttributeOrderNumber(3001)] 72 | public string CurrentTargetFile { get; private set; } = ""; 73 | 74 | private bool _subbed = false; 75 | private DateTime _startTime = DateTime.Now; 76 | 77 | private RecordingStateEnum _recState = RecordingStateEnum.Idle; 78 | private RecordingStateEnum recState 79 | { 80 | get 81 | { 82 | return _recState; 83 | } 84 | set 85 | { 86 | _recState = value; 87 | CurrentStatus = I18n.Translate("TimelineRecorder/" + _recState); 88 | } 89 | } 90 | 91 | private uint _lastActionId = 0; 92 | private DateTime _lastActionTs = DateTime.Now; 93 | private uint _lastSpawnNameId = 0; 94 | private DateTime _lastSpawnTs = DateTime.Now; 95 | 96 | private Timeline _tl = null; 97 | private Encounter _enc = null; 98 | 99 | public TimelineRecorder(State state) : base(state) 100 | { 101 | Enabled = false; 102 | TargetFolder = Path.GetTempPath(); 103 | OnActiveChanged += EventLogger_OnActiveChanged; 104 | recState = RecordingStateEnum.Idle; 105 | StartRecordingAction = new System.Action(() => StartRecording()); 106 | SuspendRecordingAction = new System.Action(() => SuspendRecording()); 107 | StopRecordingAction = new System.Action(() => StopRecording()); 108 | } 109 | 110 | private void StartRecording() 111 | { 112 | if (Active == false || recState == RecordingStateEnum.Recording) 113 | { 114 | return; 115 | } 116 | if (recState == RecordingStateEnum.Suspended) 117 | { 118 | _startTime = DateTime.Now; 119 | _state.Log(LogLevelEnum.Debug, null, "Resuming timeline recording for {0} with next encounter", CurrentTargetFile); 120 | Encounter enc = new Encounter() { Id = _tl.Encounters.Count + 1 }; 121 | enc.Triggers.Add(new Trigger() { Type = Trigger.EventTypeEnum.Start, EventType = Trigger.TriggerTypeEnum.OnCombatStart }); 122 | enc.Triggers.Add(new Trigger() { Type = Trigger.EventTypeEnum.Stop, EventType = Trigger.TriggerTypeEnum.OnCombatEnd }); 123 | enc.Triggers.Add(new Trigger() { Type = Trigger.EventTypeEnum.Select, EventType = Trigger.TriggerTypeEnum.Default }); 124 | _tl.Encounters.Add(enc); 125 | _enc = enc; 126 | } 127 | else 128 | { 129 | _startTime = DateTime.Now; 130 | string paff = TargetFolder.Trim(); 131 | _state.PrepareFolder(paff); 132 | string filename = String.Format("Lemegeton_{0}_{1}.timeline.xml", _state.cs.TerritoryType, _startTime.ToString("yyyyMMdd_HHmmss")); 133 | CurrentTargetFile = Path.Combine(paff, filename); 134 | _state.Log(LogLevelEnum.Debug, null, "Starting new timeline recording for {0}", CurrentTargetFile); 135 | _tl = new Timeline(); 136 | _tl.Territory = _state.cs.TerritoryType; 137 | _tl.Description = String.Format("Recorded by TimelineRecorder on {0}", _startTime); 138 | Encounter enc = new Encounter() { Id = 1 }; 139 | enc.Triggers.Add(new Trigger() { Type = Trigger.EventTypeEnum.Start, EventType = Trigger.TriggerTypeEnum.OnCombatStart }); 140 | enc.Triggers.Add(new Trigger() { Type = Trigger.EventTypeEnum.Stop, EventType = Trigger.TriggerTypeEnum.OnCombatEnd }); 141 | enc.Triggers.Add(new Trigger() { Type = Trigger.EventTypeEnum.Select, EventType = Trigger.TriggerTypeEnum.Default }); 142 | _tl.Encounters.Add(enc); 143 | _enc = enc; 144 | } 145 | recState = RecordingStateEnum.Recording; 146 | } 147 | 148 | private void SuspendRecording() 149 | { 150 | if (Active == false || recState != RecordingStateEnum.Recording) 151 | { 152 | return; 153 | } 154 | _state.Log(LogLevelEnum.Debug, null, "Timeline recording suspended"); 155 | recState = RecordingStateEnum.Suspended; 156 | } 157 | 158 | private void StopRecording() 159 | { 160 | if (recState == RecordingStateEnum.Idle) 161 | { 162 | return; 163 | } 164 | _lastActionId = 0; 165 | _lastActionTs = DateTime.Now; 166 | _lastSpawnNameId = 0; 167 | _lastSpawnTs = DateTime.Now; 168 | if (_tl != null && (_tl.Encounters.Count > 1 || _enc.Entries.Count > 0)) 169 | { 170 | _state.Log(LogLevelEnum.Debug, null, "Writing recorded timeline to {0}", CurrentTargetFile); 171 | string data = XmlSerializer.Serialize(_tl); 172 | File.WriteAllText(CurrentTargetFile, data); 173 | } 174 | else 175 | { 176 | _state.Log(LogLevelEnum.Debug, null, "No encounters or events captured, won't write timeline to {0}", CurrentTargetFile); 177 | } 178 | recState = RecordingStateEnum.Idle; 179 | } 180 | 181 | private void EventLogger_OnActiveChanged(bool newState) 182 | { 183 | if (newState == true) 184 | { 185 | SubscribeToEvents(); 186 | } 187 | else 188 | { 189 | StopRecording(); 190 | UnsubscribeFromEvents(); 191 | } 192 | } 193 | 194 | private void SubscribeToEvents() 195 | { 196 | lock (this) 197 | { 198 | if (_subbed == true) 199 | { 200 | return; 201 | } 202 | _subbed = true; 203 | Log(LogLevelEnum.Debug, null, "Subscribing to events"); 204 | _state.OnAction += _state_OnAction; 205 | _state.OnCombatChange += _state_OnCombatChange; 206 | _state.OnZoneChange += _state_OnZoneChange; 207 | _state.OnTargettable += _state_OnTargettable; 208 | _state.OnUntargettable += _state_OnUntargettable; 209 | _state.OnCombatantAdded += _state_OnCombatantAdded; 210 | } 211 | } 212 | 213 | private void UnsubscribeFromEvents() 214 | { 215 | lock (this) 216 | { 217 | if (_subbed == false) 218 | { 219 | return; 220 | } 221 | Log(LogLevelEnum.Debug, null, "Unsubscribing from events"); 222 | _state.OnAction -= _state_OnAction; 223 | _state.OnCombatChange -= _state_OnCombatChange; 224 | _state.OnZoneChange -= _state_OnZoneChange; 225 | _state.OnTargettable -= _state_OnTargettable; 226 | _state.OnUntargettable -= _state_OnUntargettable; 227 | _state.OnCombatantAdded -= _state_OnCombatantAdded; 228 | _subbed = false; 229 | } 230 | } 231 | 232 | private void _state_OnAction(uint src, uint dest, ushort actionId) 233 | { 234 | if (recState != RecordingStateEnum.Recording) 235 | { 236 | return; 237 | } 238 | if (_lastActionId == actionId && (DateTime.Now - _lastActionTs).TotalMilliseconds < 50.0) 239 | { 240 | return; 241 | } 242 | _lastActionId = actionId; 243 | _lastActionTs = DateTime.Now; 244 | GameObject go = _state.GetActorById(src); 245 | if (go is ICharacter) 246 | { 247 | ICharacter ch = (ICharacter)go; 248 | unsafe 249 | { 250 | CharacterStruct* chs = (CharacterStruct*)ch.Address; 251 | if (chs->ModelContainer.ModelCharaId == 0) 252 | { 253 | return; 254 | } 255 | } 256 | } 257 | if (go is IBattleChara) 258 | { 259 | IBattleChara bc = (IBattleChara)go; 260 | if ((_state.GetStatusFlags(bc) & Dalamud.Game.ClientState.Objects.Enums.StatusFlags.Hostile) != 0) 261 | { 262 | Lumina.Excel.Sheets.Action a = _state.dm.Excel.GetSheet().GetRow(actionId); 263 | if (IgnoreAutoAttacks == true && a.ActionCategory.RowId == 1) 264 | { 265 | return; 266 | } 267 | string abname = _state.plug.GetActionName(actionId); 268 | if (abname.Trim().Length > 0) 269 | { 270 | Entry e = new Entry(); 271 | e.StartTime = (float)Math.Round((DateTime.Now - _startTime).TotalSeconds, 1); 272 | e.Type = Entry.EntryTypeEnum.Ability; 273 | e.KeyValues.Add(actionId); 274 | e.Description = String.Format("{0} ({1}): {2}", bc.Name, bc.NameId, abname); 275 | _enc.Entries.Add(e); 276 | } 277 | } 278 | } 279 | } 280 | 281 | private void _state_OnCombatChange(bool inCombat) 282 | { 283 | if (inCombat == true && StartRecordingOnCombat == true && recState != RecordingStateEnum.Recording) 284 | { 285 | Log(LogLevelEnum.Debug, null, "Combat started, starting recording"); 286 | StartRecording(); 287 | } 288 | if (inCombat == false && recState == RecordingStateEnum.Recording) 289 | { 290 | if (SuspendRecordingAfterCombat == true) 291 | { 292 | Log(LogLevelEnum.Debug, null, "Combat ended, suspending recording"); 293 | StopRecording(); 294 | } 295 | else if (StopRecordingAfterCombat == true) 296 | { 297 | Log(LogLevelEnum.Debug, null, "Combat ended, stopping recording"); 298 | StopRecording(); 299 | } 300 | } 301 | } 302 | 303 | private void _state_OnZoneChange(ushort newZone) 304 | { 305 | if (recState != RecordingStateEnum.Idle) 306 | { 307 | Log(LogLevelEnum.Debug, null, "Zone changed, stopping recording"); 308 | StopRecording(); 309 | } 310 | } 311 | 312 | private void _state_OnTargettable() 313 | { 314 | if (recState == RecordingStateEnum.Suspended && ResumeRecordingOnTargettable == true) 315 | { 316 | Log(LogLevelEnum.Debug, null, "Hostiles targettable, resuming recording"); 317 | StartRecording(); 318 | } 319 | } 320 | 321 | private void _state_OnUntargettable() 322 | { 323 | if (recState == RecordingStateEnum.Recording && SuspendRecordingOnUntargettable == true) 324 | { 325 | Log(LogLevelEnum.Debug, null, "Hostiles untargettable, suspending recording"); 326 | SuspendRecording(); 327 | } 328 | else if (recState != RecordingStateEnum.Idle && StopRecordingOnUntargettable == true) 329 | { 330 | Log(LogLevelEnum.Debug, null, "Hostiles untargettable, stopping recording"); 331 | StopRecording(); 332 | } 333 | } 334 | 335 | private void _state_OnCombatantAdded(GameObject go) 336 | { 337 | if (recState != RecordingStateEnum.Recording || IgnoreSpawns == true) 338 | { 339 | return; 340 | } 341 | if (go is ICharacter) 342 | { 343 | ICharacter ch = (ICharacter)go; 344 | unsafe 345 | { 346 | CharacterStruct* chs = (CharacterStruct*)ch.Address; 347 | if (chs->ModelContainer.ModelCharaId == 0) 348 | { 349 | return; 350 | } 351 | } 352 | } 353 | if (go is IBattleChara) 354 | { 355 | IBattleChara bc = (IBattleChara)go; 356 | if (_lastSpawnNameId == bc.NameId && (DateTime.Now - _lastSpawnTs).TotalMilliseconds < 50.0) 357 | { 358 | return; 359 | } 360 | _lastSpawnNameId = bc.NameId; 361 | _lastSpawnTs = DateTime.Now; 362 | if ((_state.GetStatusFlags(bc) & Dalamud.Game.ClientState.Objects.Enums.StatusFlags.Hostile) != 0 && bc.MaxHp > 0) 363 | { 364 | Entry e = new Entry(); 365 | e.StartTime = (float)Math.Round((DateTime.Now - _startTime).TotalSeconds, 1); 366 | e.Type = Entry.EntryTypeEnum.Spawn; 367 | e.KeyValues.Add(bc.NameId); 368 | e.Description = String.Format("{0} ({1})", bc.Name, bc.NameId); 369 | _enc.Entries.Add(e); 370 | } 371 | } 372 | } 373 | 374 | } 375 | 376 | public Timelines(State st) : base(st) 377 | { 378 | } 379 | 380 | } 381 | 382 | } 383 | -------------------------------------------------------------------------------- /Lemegeton/Content/Automation.cs: -------------------------------------------------------------------------------- 1 | using FFXIVClientStructs.FFXIV.Client.Game; 2 | using Lemegeton.Core; 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Lemegeton.Content 8 | { 9 | 10 | #if !SANS_GOETIA 11 | 12 | public class Automation : Core.Content 13 | { 14 | 15 | public override FeaturesEnum Features => FeaturesEnum.None; 16 | 17 | public class FishForever : Core.ContentItem 18 | { 19 | 20 | public override FeaturesEnum Features => FeaturesEnum.Automation; 21 | 22 | private enum FishingStateEnum 23 | { 24 | Idle, 25 | Fishing, 26 | Hooking 27 | } 28 | 29 | [AttributeOrderNumber(1000)] 30 | public FoodSelector Food { get; set; } 31 | 32 | [AttributeOrderNumber(2000)] 33 | public bool IgnoreLight { get; set; } = false; 34 | [AttributeOrderNumber(2001)] 35 | public bool IgnoreMedium { get; set; } = false; 36 | [AttributeOrderNumber(2002)] 37 | public bool IgnoreHeavy { get; set; } = false; 38 | 39 | [AttributeOrderNumber(3000)] 40 | public bool UsePatience2 { get; set; } = true; 41 | [AttributeOrderNumber(3001)] 42 | public bool UseMooch { get; set; } = true; 43 | [AttributeOrderNumber(3002)] 44 | public bool UseMooch2 { get; set; } = true; 45 | [AttributeOrderNumber(3003)] 46 | public bool UseThaliakFavor { get; set; } = true; 47 | 48 | [AttributeOrderNumber(4000)] 49 | public bool ReleaseEverything { get; set; } = false; 50 | 51 | private bool _listening = false; 52 | private bool _patience2Active = false; 53 | private int _anglerStacks = 0; 54 | private DateTime _reeval = DateTime.MinValue; 55 | 56 | private DateTime _stateChanged = DateTime.MinValue; 57 | private FishingStateEnum _FishingState = FishingStateEnum.Idle; 58 | private FishingStateEnum FishingState 59 | { 60 | get 61 | { 62 | return _FishingState; 63 | } 64 | set 65 | { 66 | if (_FishingState != value) 67 | { 68 | _FishingState = value; 69 | _stateChanged = DateTime.Now; 70 | Log(Core.State.LogLevelEnum.Debug, null, "State changed to {0}", _FishingState); 71 | } 72 | } 73 | } 74 | 75 | private int GetRandomDelay(int upTo) 76 | { 77 | Random r = new Random(); 78 | return r.Next(upTo); 79 | } 80 | 81 | private bool CanRelease() 82 | { 83 | unsafe 84 | { 85 | ActionManager* am = ActionManager.Instance(); 86 | return (am->GetActionStatus((ActionType)1, 300) == 0); 87 | } 88 | } 89 | 90 | private bool CanCast() 91 | { 92 | unsafe 93 | { 94 | ActionManager* am = ActionManager.Instance(); 95 | return (am->GetActionStatus((ActionType)1, 289) == 0); 96 | } 97 | } 98 | 99 | private bool CanMooch() 100 | { 101 | unsafe 102 | { 103 | ActionManager* am = ActionManager.Instance(); 104 | return (am->GetActionStatus((ActionType)1, 297) == 0); 105 | } 106 | } 107 | 108 | private bool CanMooch2() 109 | { 110 | unsafe 111 | { 112 | ActionManager* am = ActionManager.Instance(); 113 | return (am->GetActionStatus((ActionType)1, 268) == 0 && _state.cs.LocalPlayer.CurrentGp >= 100); 114 | } 115 | } 116 | 117 | private void Cast() 118 | { 119 | unsafe 120 | { 121 | ActionManager* am = ActionManager.Instance(); 122 | if (UseMooch == true && CanMooch() == true) 123 | { 124 | Mooch(); 125 | return; 126 | } 127 | if (UseMooch2 == true && CanMooch2() == true) 128 | { 129 | Mooch2(); 130 | return; 131 | } 132 | Log(Core.State.LogLevelEnum.Debug, null, "Casting"); 133 | if (am->UseAction((ActionType)1, 289) == true) 134 | { 135 | Log(Core.State.LogLevelEnum.Debug, null, "Casted"); 136 | FishingState = FishingStateEnum.Fishing; 137 | } 138 | } 139 | } 140 | 141 | private void Mooch() 142 | { 143 | unsafe 144 | { 145 | ActionManager* am = ActionManager.Instance(); 146 | Log(Core.State.LogLevelEnum.Debug, null, "Casting (Mooch)"); 147 | if (am->UseAction((ActionType)1, 297) == true) 148 | { 149 | Log(Core.State.LogLevelEnum.Debug, null, "Casted (Mooch)"); 150 | FishingState = FishingStateEnum.Fishing; 151 | } 152 | } 153 | } 154 | 155 | private void Release() 156 | { 157 | unsafe 158 | { 159 | ActionManager* am = ActionManager.Instance(); 160 | Log(Core.State.LogLevelEnum.Debug, null, "Releasing"); 161 | if (am->UseAction((ActionType)1, 300) == true) 162 | { 163 | Log(Core.State.LogLevelEnum.Debug, null, "Released"); 164 | } 165 | } 166 | } 167 | 168 | private void Mooch2() 169 | { 170 | unsafe 171 | { 172 | ActionManager* am = ActionManager.Instance(); 173 | Log(Core.State.LogLevelEnum.Debug, null, "Casting (Mooch 2)"); 174 | if (am->UseAction((ActionType)1, 268) == true) 175 | { 176 | Log(Core.State.LogLevelEnum.Debug, null, "Casted (Mooch 2)"); 177 | FishingState = FishingStateEnum.Fishing; 178 | } 179 | } 180 | } 181 | 182 | private void Patience2() 183 | { 184 | unsafe 185 | { 186 | ActionManager* am = ActionManager.Instance(); 187 | Log(Core.State.LogLevelEnum.Debug, null, "Using Patience 2"); 188 | if (am->UseAction((ActionType)1, 4106) == true) 189 | { 190 | Log(Core.State.LogLevelEnum.Debug, null, "Used Patience 2"); 191 | } 192 | } 193 | } 194 | 195 | private void ThaliakFavor() 196 | { 197 | unsafe 198 | { 199 | ActionManager* am = ActionManager.Instance(); 200 | Log(Core.State.LogLevelEnum.Debug, null, "Using Thaliak's Favor"); 201 | if (am->UseAction((ActionType)1, 26804) == true) 202 | { 203 | Log(Core.State.LogLevelEnum.Debug, null, "Used Thaliak's Favor"); 204 | FishingState = FishingStateEnum.Fishing; 205 | } 206 | } 207 | } 208 | 209 | private void Hook() 210 | { 211 | unsafe 212 | { 213 | ActionManager* am = ActionManager.Instance(); 214 | Log(Core.State.LogLevelEnum.Debug, null, "Hooking"); 215 | Task t = new Task(() => 216 | { 217 | Thread.Sleep(500 + GetRandomDelay(500)); 218 | if (am->UseAction((ActionType)1, 296) == true) 219 | { 220 | Log(Core.State.LogLevelEnum.Debug, null, "Hooked"); 221 | FishingState = FishingStateEnum.Hooking; 222 | } 223 | }); 224 | t.Start(); 225 | } 226 | } 227 | 228 | private void PrecisionHookset() 229 | { 230 | unsafe 231 | { 232 | ActionManager* am = ActionManager.Instance(); 233 | Log(Core.State.LogLevelEnum.Debug, null, "Hooking (Precision)"); 234 | Task t = new Task(() => 235 | { 236 | Thread.Sleep(500 + GetRandomDelay(500)); 237 | if (am->UseAction((ActionType)1, 4179) == true) 238 | { 239 | Log(Core.State.LogLevelEnum.Debug, null, "Hooked (Precision)"); 240 | FishingState = FishingStateEnum.Hooking; 241 | } 242 | }); 243 | t.Start(); 244 | } 245 | } 246 | 247 | private void PowerfulHookset() 248 | { 249 | unsafe 250 | { 251 | ActionManager* am = ActionManager.Instance(); 252 | Log(Core.State.LogLevelEnum.Debug, null, "Hooking (Powerful)"); 253 | Task t = new Task(() => 254 | { 255 | Thread.Sleep(500 + GetRandomDelay(500)); 256 | if (am->UseAction((ActionType)1, 4103) == true) 257 | { 258 | Log(Core.State.LogLevelEnum.Debug, null, "Hooked (Powerful)"); 259 | FishingState = FishingStateEnum.Hooking; 260 | } 261 | }); 262 | t.Start(); 263 | } 264 | } 265 | 266 | protected override bool ExecutionImplementation() 267 | { 268 | if (DateTime.Now < _reeval || _state.cs.LocalPlayer.ClassJob.RowId != 18) 269 | { 270 | return false; 271 | } 272 | switch (FishingState) 273 | { 274 | case FishingStateEnum.Idle: 275 | if (_listening == false) 276 | { 277 | _state.OnEventPlay += OnEventPlay; 278 | _state.OnStatusChange += OnStatusChange; 279 | _listening = true; 280 | } 281 | if (ReleaseEverything == true && CanRelease() == true && CanMooch() == false && CanMooch2() == false) 282 | { 283 | Release(); 284 | _reeval = DateTime.Now.AddMilliseconds(500 + GetRandomDelay(500)); 285 | return true; 286 | } 287 | if (CanCast() == true) 288 | { 289 | if (Food.Cycle() == false) 290 | { 291 | Log(Core.State.LogLevelEnum.Debug, null, "Used food"); 292 | _reeval = DateTime.Now.AddMilliseconds(3000 + GetRandomDelay(500)); 293 | return true; 294 | } 295 | if (UseThaliakFavor == true && _state.cs.LocalPlayer.CurrentGp < _state.cs.LocalPlayer.MaxGp - 200 && _anglerStacks >= 3) 296 | { 297 | Log(Core.State.LogLevelEnum.Debug, null, "Applying Thaliak's Favor"); 298 | _reeval = DateTime.Now.AddMilliseconds(500 + GetRandomDelay(500)); 299 | ThaliakFavor(); 300 | return true; 301 | } 302 | else if (UsePatience2 == true && _patience2Active == false) 303 | { 304 | if (CanMooch() == false && CanMooch2() == false) 305 | { 306 | if (_state.cs.LocalPlayer.CurrentGp >= 560) 307 | { 308 | Log(Core.State.LogLevelEnum.Debug, null, "Applying Patience 2"); 309 | _reeval = DateTime.Now.AddMilliseconds(2000 + GetRandomDelay(1000)); 310 | Patience2(); 311 | return true; 312 | } 313 | else 314 | { 315 | Log(Core.State.LogLevelEnum.Debug, null, "Not enough GP for Patience 2"); 316 | } 317 | } 318 | } 319 | _reeval = DateTime.Now.AddMilliseconds(2000); 320 | Cast(); 321 | } 322 | break; 323 | case FishingStateEnum.Fishing: 324 | case FishingStateEnum.Hooking: 325 | if (DateTime.Now > _stateChanged.AddMinutes(2)) 326 | { 327 | Log(Core.State.LogLevelEnum.Debug, null, "Stuck on a state since {0}, resetting", _stateChanged); 328 | FishingState = FishingStateEnum.Idle; 329 | } 330 | break; 331 | } 332 | return true; 333 | } 334 | 335 | public FishForever(State state) : base(state) 336 | { 337 | Food = new FoodSelector(); 338 | Food.State = state; 339 | Food.MinimumTime = 120.0f; 340 | OnEnabledChanged += FishForever_OnEnabledChanged; 341 | Enabled = false; 342 | } 343 | 344 | private void FishForever_OnEnabledChanged(bool newState) 345 | { 346 | if (_listening == true) 347 | { 348 | _state.OnEventPlay -= OnEventPlay; 349 | _state.OnStatusChange -= OnStatusChange; 350 | _listening = false; 351 | } 352 | _anglerStacks = 0; 353 | _patience2Active = false; 354 | FishingState = FishingStateEnum.Idle; 355 | } 356 | 357 | private void OnStatusChange(uint src, uint dest, uint statusId, bool gained, float duration, int stacks) 358 | { 359 | if (statusId == 764) 360 | { 361 | _patience2Active = gained; 362 | Log(Core.State.LogLevelEnum.Debug, null, "Patience 2: {0}", gained); 363 | } 364 | if (statusId == 2778) 365 | { 366 | _anglerStacks = stacks; 367 | } 368 | } 369 | 370 | private void OnEventPlay(uint actorId, uint eventId, ushort scene, uint flags, uint param1, ushort param2, byte param3, uint param4) 371 | { 372 | if (FishingState == FishingStateEnum.Fishing && scene == 5) 373 | { 374 | if ( 375 | (param3 == 36 && IgnoreLight == false) 376 | || 377 | (param3 == 37 && IgnoreMedium == false) 378 | || 379 | (param3 == 38 && IgnoreHeavy == false) 380 | ) 381 | { 382 | if (_patience2Active == true && _state.cs.LocalPlayer.CurrentGp >= 50) 383 | { 384 | if (param3 == 36) 385 | { 386 | PrecisionHookset(); 387 | } 388 | else 389 | { 390 | PowerfulHookset(); 391 | } 392 | } 393 | else 394 | { 395 | Hook(); 396 | } 397 | } 398 | } 399 | if (FishingState == FishingStateEnum.Hooking && scene == 2) 400 | { 401 | if (param2 == 2 && (param3 == 0 || param3 == 27)) 402 | { 403 | Log(Core.State.LogLevelEnum.Debug, null, "Recasting"); 404 | Task t = new Task(() => 405 | { 406 | Thread.Sleep(2000 + GetRandomDelay(500)); 407 | FishingState = FishingStateEnum.Idle; 408 | }); 409 | t.Start(); 410 | } 411 | } 412 | } 413 | 414 | } 415 | 416 | public Automation(State st) : base(st) 417 | { 418 | Enabled = false; 419 | } 420 | 421 | } 422 | 423 | #endif 424 | 425 | } 426 | --------------------------------------------------------------------------------