コード例 #1
0
    /// <summary>
    /// Displays a fly text in-game on the local player.
    /// </summary>
    /// <param name="kind">The FlyTextKind. See <see cref="FlyTextKind"/>.</param>
    /// <param name="actorIndex">The index of the actor to place flytext on. Indexing unknown. 1 places flytext on local player.</param>
    /// <param name="val1">Value1 passed to the native flytext function.</param>
    /// <param name="val2">Value2 passed to the native flytext function. Seems unused.</param>
    /// <param name="text1">Text1 passed to the native flytext function.</param>
    /// <param name="text2">Text2 passed to the native flytext function.</param>
    /// <param name="color">Color passed to the native flytext function. Changes flytext color.</param>
    /// <param name="icon">Icon ID passed to the native flytext function. Only displays with select FlyTextKind.</param>
    public unsafe void AddFlyText(FlyTextKind kind, uint actorIndex, uint val1, uint val2, SeString text1, SeString text2, uint color, uint icon)
    {
        // Known valid flytext region within the atk arrays
        var numIndex  = 28;
        var strIndex  = 25;
        var numOffset = 147u;
        var strOffset = 28u;

        // Get the UI module and flytext addon pointers
        var gameGui = Service <GameGui> .Get();

        var ui      = (FFXIVClientStructs.FFXIV.Client.UI.UIModule *)gameGui.GetUIModule();
        var flytext = gameGui.GetAddonByName("_FlyText", 1);

        if (ui == null || flytext == IntPtr.Zero)
        {
            return;
        }

        // Get the number and string arrays we need
        var atkArrayDataHolder = ui->RaptureAtkModule.AtkModule.AtkArrayDataHolder;
        var numArray           = atkArrayDataHolder._NumberArrays[numIndex];
        var strArray           = atkArrayDataHolder._StringArrays[strIndex];

        // Write the values to the arrays using a known valid flytext region
        numArray->IntArray[numOffset + 0] = 1;                      // Some kind of "Enabled" flag for this section
        numArray->IntArray[numOffset + 1] = (int)kind;
        numArray->IntArray[numOffset + 2] = unchecked ((int)val1);
        numArray->IntArray[numOffset + 3] = unchecked ((int)val2);
        numArray->IntArray[numOffset + 4] = 5;                      // Unknown
        numArray->IntArray[numOffset + 5] = unchecked ((int)color);
        numArray->IntArray[numOffset + 6] = unchecked ((int)icon);
        numArray->IntArray[numOffset + 7] = 0;                      // Unknown
        numArray->IntArray[numOffset + 8] = 0;                      // Unknown, has something to do with yOffset

        fixed(byte *pText1 = text1.Encode())
        {
            fixed(byte *pText2 = text2.Encode())
            {
                strArray->StringArray[strOffset + 0] = pText1;
                strArray->StringArray[strOffset + 1] = pText2;

                this.addFlyTextNative(
                    flytext,
                    actorIndex,
                    1,
                    (IntPtr)numArray,
                    numOffset,
                    9,
                    (IntPtr)strArray,
                    strOffset,
                    2,
                    0);
            }
        }
    }
コード例 #2
0
        private bool KindCheck(ActionEffectInfo info, FlyTextKind targetKind)
        {
            var result = targetKind == info.kind;

            // Screenlog will log misses from enemies as Named/Miss, but they will show up to us as Named/Dodge
            if (!result)
            {
                return(targetKind is FlyTextKind.NamedDodge or FlyTextKind.Dodge && info.kind is FlyTextKind.NamedMiss or FlyTextKind.Miss);
            }
            return(true);
        }
コード例 #3
0
        public bool TryGetEffect(uint value, FlyTextKind targetKind, uint charaId, List <uint> petIds, out ActionEffectInfo info)
        {
            StoreLog($"Looking for effect {value} {targetKind}...");
            info = default;
            if (!_store.TryGetValue(value, out var list))
            {
                return(false);
            }

            var effect = list.FirstOrDefault(x => x.value == value && x.step == ActionStep.Screenlog && KindCheck(x, targetKind) && TargetCheck(x, charaId, petIds));

            if (!list.Remove(effect))
            {
                return(false);
            }

            info = effect;
            StoreLog($"Retrieved effect {effect}");
            return(true);
        }
コード例 #4
0
        public void UpdateEffect(uint actionId, uint sourceId, uint targetId, uint value, FlyTextKind logKind)
        {
            StoreLog($"Updating effect {actionId} {sourceId} {targetId} {value} with {logKind}...");
            if (!_store.TryGetValue(value, out var list))
            {
                return;
            }

            var effect = list.FirstOrDefault(x => x.step == ActionStep.Effect &&
                                             x.actionId == actionId &&
                                             x.sourceId == sourceId &&
                                             x.targetId == targetId);

            if (!list.Remove(effect))
            {
                return;
            }

            effect.kind = logKind;
            effect.step = ActionStep.Screenlog;

            list.Add(effect);
            StoreLog($"Updated effect {effect}");
        }
コード例 #5
0
        public unsafe IntPtr CreateFlyText(
            IntPtr flyTextMgr, // or something
            UInt32 kind,
            UInt32 val1,
            UInt32 val2,
            IntPtr text1,
            UInt32 color,
            UInt32 icon,
            IntPtr text2,
            float unk3
            )
        {
            uint tColor = color;
            uint tVal1  = val1;

            if (Randomize)
            {
                int ttVal1 = ModifyDamageALittle((int)val1);
                tVal1 = (uint)ttVal1;
            }

            try
            {
                if (Hijack)
                {
                    string hjText1 = Marshal.PtrToStringAnsi(hijackStruct.text1);
                    string hjText2 = Marshal.PtrToStringAnsi(hijackStruct.text2);

                    FlyTextLog(
                        $"flytext hijacked: kind: {hijackStruct.kind}, val1: {hijackStruct.val1}, val2: {hijackStruct.val2}, color: {hijackStruct.color:X}, icon: {hijackStruct.icon}");
                    FlyTextLog($"text1: {hjText1} | text2: {hjText2}");

                    return(createFlyTextHook.Original(flyTextMgr, hijackStruct.kind, hijackStruct.val1,
                                                      hijackStruct.val2, hijackStruct.text1, hijackStruct.color, hijackStruct.icon,
                                                      hijackStruct.text2, unk3));
                }

                FlyTextKind ftKind = (FlyTextKind)kind;

                // wrap this here to lower overhead when not logging
                if (configuration.FlyTextLogEnabled)
                {
                    string strText1 = Marshal.PtrToStringAnsi(text1);
                    string strText2 = Marshal.PtrToStringAnsi(text2);

                    strText1 = strText1?.Replace("%", "%%");
                    strText2 = strText2?.Replace("%", "%%");

                    FlyTextLog(
                        $"flytext created: kind: {ftKind}, val1: {tVal1}, val2: {val2}, color: {color:X}, icon: {icon}");
                    FlyTextLog($"text1: {strText1} | text2: {strText2}");
                }

                if (TryGetFlyTextDamageType(tVal1, out DamageType dmgType, out int sourceId))
                {
                    int charaId = GetCharacterActorId();
                    int petId   = FindCharaPet();

                    if (configuration.OutgoingColorEnabled || configuration.IncomingColorEnabled)
                    {
                        // sourceId == GetCharacterActorId() && configuration.OutgoingColorEnabled || (sourceId != GetCharacterActorId() && sourceId != FindCharaPet() && configuration.IncomingColorEnabled)
                        bool outPlayer = sourceId == charaId && configuration.OutgoingColorEnabled;
                        bool outPet    = sourceId == petId && configuration.PetDamageColorEnabled;
                        bool outCheck  = outPlayer || outPet;

                        bool incCheck = sourceId != charaId && sourceId != petId && configuration.IncomingColorEnabled;

                        // match up the condition with what to check
                        // because right now with this OR, it doesn't care if the source is incoming and outgoing is enabled
                        // so make sure that it oes it right
                        if (outCheck && !incCheck || !outCheck && incCheck)
                        {
                            if (ftKind == FlyTextKind.AutoAttack ||
                                ftKind == FlyTextKind.CriticalHit ||
                                ftKind == FlyTextKind.DirectHit ||
                                ftKind == FlyTextKind.CriticalDirectHit ||
                                ftKind == FlyTextKind.NamedAttack ||
                                ftKind == FlyTextKind.NamedDirectHit ||
                                ftKind == FlyTextKind.NamedCriticalHit ||
                                ftKind == FlyTextKind.NamedCriticalDirectHit)
                            {
                                switch (dmgType)
                                {
                                case DamageType.Physical:
                                    tColor = ImGui.GetColorU32(configuration.PhysicalColor);
                                    break;

                                case DamageType.Magic:
                                    tColor = ImGui.GetColorU32(configuration.MagicColor);
                                    break;

                                case DamageType.Darkness:
                                    tColor = ImGui.GetColorU32(configuration.DarknessColor);
                                    break;
                                }
                            }
                        }
                    }

                    if (configuration.SourceTextEnabled)
                    {
                        bool tgtCheck = sourceId != charaId && sourceId != petId;
                        bool petCheck = sourceId == petId && configuration.PetSourceTextEnabled;

                        if (tgtCheck || petCheck)
                        {
                            string name = GetActorName(sourceId);

                            if (!string.IsNullOrEmpty(name))
                            {
                                string existingText = "";
                                if (text1 != IntPtr.Zero)
                                {
                                    existingText = Marshal.PtrToStringUni(text1);
                                    existingText = Encoding.UTF8.GetString(Encoding.Unicode.GetBytes(existingText));
                                }

                                string combined = $"from {name} {existingText}";
                                combined = Encoding.Unicode.GetString(Encoding.UTF8.GetBytes(combined));
                                text1    = Marshal.StringToHGlobalUni(combined);
                                text.Enqueue(new Tuple <IntPtr, long>(text1, Ms()));
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                PluginLog.Log($"{e.Message} {e.StackTrace}");
            }

            return(createFlyTextHook.Original(flyTextMgr, kind, tVal1, val2, text1, tColor, icon, text2, unk3));
        }