Пример #1
0
    // TODO: Reduce parameter counts by splitting DrawVariables in smaller structs
    private static unsafe Vector2 DrawIcons(
        int currentScaleOffset,
        int componentIconLoopStart,
        int componentIconLoopEnd,
        int centreMarkerOffset,
        float maxDistance,
        float compassUnit,
        float scale,
        float rotationIconHalfWidth,
        float halfWidth40,
        float minScaleFactor,
        float backgroundRounding,
        Vector2 playerForward,
        Vector2 playerPosition,
        Vector2 centre,
        Vector2 backgroundPMin,
        Vector2 backgroundPMax,
        bool useAreaMapAsSource,
        bool enableCentreMarker,
        bool flipCentreMarker,
        AtkUnitBase *unitBase,
        AtkComponentNode *rootComponentNode,
        IReadOnlySet <uint> filteredIconIds
        )
    {
        try {
            var drawList = ImGui.GetWindowDrawList();
            // We loop through all relevant nodes on _NaviMap or AreaMap, depending on the configuration
            // This throws sometimes, but we just ignore those small exceptions, it works a few frames later anyways
            var mapScale       = *(float *)((nint)unitBase + currentScaleOffset);
            var distanceOffset = 20f * mapScale;
            maxDistance *= mapScale;
            for (var i = componentIconLoopStart; i < rootComponentNode->Component->UldManager.NodeListCount; i++)
            {
                var mapIconComponentNode =
                    (AtkComponentNode *)rootComponentNode->Component->UldManager.NodeList[i];
                if (!mapIconComponentNode->AtkResNode.IsVisible)
                {
                    continue;
                }
                for (var j = 2; j < componentIconLoopEnd; j++)
                {
                    var imgNode = (AtkImageNode *)mapIconComponentNode->Component->UldManager.NodeList[j];
                    if (imgNode->AtkResNode.Type != NodeType.Image)
                    {
                        continue;
                    }
                    if (!imgNode->AtkResNode.IsVisible || !imgNode->AtkResNode.ParentNode->IsVisible)
                    {
                        continue;
                    }
                    var part = imgNode->PartsList->Parts[imgNode->PartId];
                    //NOTE Invariant: It should always be a resource
#if DEBUG
                    var type = part.UldAsset->AtkTexture.TextureType;
                    if (type != TextureType.Resource)
                    {
                        SimpleLog.Error($"{i} {j} was not a Resource texture");
                        continue;
                    }
                    ;
#endif
                    var tex = part.UldAsset->AtkTexture.Resource->KernelTextureObject;
                    var texFileNameStdString =
                        part.UldAsset->AtkTexture.Resource->TexFileResourceHandle->ResourceHandle
                        .FileName;
                    // NOTE (Chiv) We are in a try-catch, so we just throw if the read failed.
                    // Cannot act anyways if the texture path is butchered
                    var textureFileName = texFileNameStdString.ToString();
                    var _ = uint.TryParse(
                        textureFileName.Substring(textureFileName.LastIndexOfAny(new[] { '/', '\\' }) + 1, 6),
                        out var iconId);
                    //iconId = 0 (=> success == false as IconID will never be 0) Must have been 'NaviMap(_hr1)?\.tex' (and only that hopefully)
                    if (filteredIconIds.Contains(iconId))
                    {
                        continue;
                    }

                    var textureIdPtr = new IntPtr(tex->D3D11ShaderResourceView);
                    //TODO DEBUG
//                        PluginLog.Debug($"TextureIdPtr is null? {textureIdPtr == IntPtr.Zero}");
                    Vector2 pMin;
                    Vector2 pMax;
                    var     uv         = Vector2.Zero;
                    var     uv1        = Vector2.One;
                    var     tintColour = Constant.WhiteColour;
                    var     rotate     = false;
                    switch (iconId)
                    {
                    case 0 when useAreaMapAsSource:     //0 interpreted as NaviMap texture atlas
                        continue;

                    case 0 when imgNode->PartId == 21:     //Glowy thingy
                        rotate = true;                     // TODO Duplicate code instead of branching?
                        uv     = new Vector2((float)part.U / 448, (float)part.V / 212);
                        uv1    = new Vector2((float)(part.U + 40) / 448, (float)(part.V + 40) / 212);
                        // NOTE (Chiv) Glowy thingy always rotates, but whether its in or outside the mask
                        // determines how to calculate its position on the compass
                        if (mapIconComponentNode->AtkResNode.Rotation == 0)
                        {
                            goto default;
                        }
                        goto case 1;

                    case 0:     //Arrows to quests and fates, part of the Navimap texture atlas
                        // NOTE: We assume part.Width == part.Height == 24
                        // NOTE: We assume tex.Width == 448 && tex.Height == 212
                        var u  = (float)part.U / 448;            // = (float) part.U / tex->Width;
                        var v  = (float)part.V / 212;            // = (float) part.V / tex->Height;
                        var u1 = (float)(part.U + 24) / 448;     // = (float) (part.U + part.Width) / tex->Width;
                        var v1 = (float)(part.V + 24) / 212;     // = (float) (part.V + part.Height) / tex->Height;

                        uv  = new Vector2(u, v);
                        uv1 = new Vector2(u1, v1);
                        // Arrows and such are always rotation based, we draw them slightly on top
                        var naviMapCutIconOffset = compassUnit *
                                                   Util.SignedAngle(mapIconComponentNode->AtkResNode.Rotation,
                                                                    playerForward);
                        // We declare width == height
                        const int naviMapIconHalfWidth = 12;
                        var       naviMapYOffset       = 14 * scale;
                        pMin = new Vector2(centre.X - naviMapIconHalfWidth + naviMapCutIconOffset,
                                           centre.Y - naviMapYOffset - naviMapIconHalfWidth);
                        pMax = new Vector2(centre.X + naviMapCutIconOffset + naviMapIconHalfWidth,
                                           centre.Y - naviMapYOffset + naviMapIconHalfWidth);
                        break;

                    case 1:     // Rotation icons (except naviMap arrows) go here after setting up their UVs
                        // NOTE (Chiv) Rotations for icons on the map are mirrored from the
                        var rotationIconOffset = compassUnit *
                                                 Util.SignedAngle(mapIconComponentNode->AtkResNode.Rotation,
                                                                  playerForward);
                        pMin = new Vector2(centre.X - rotationIconHalfWidth + rotationIconOffset,
                                           centre.Y - rotationIconHalfWidth);
                        pMax = new Vector2(centre.X + rotationIconOffset + rotationIconHalfWidth,
                                           centre.Y + rotationIconHalfWidth);
                        break;

                    case 060443:     //Player Marker
                        if (!enableCentreMarker)
                        {
                            continue;
                        }
                        drawList = ImGui.GetBackgroundDrawList();
                        pMin     = new Vector2(centre.X - halfWidth40,
                                               centre.Y + centreMarkerOffset * scale - halfWidth40);
                        pMax = new Vector2(centre.X + halfWidth40,
                                           centre.Y + centreMarkerOffset * scale + halfWidth40);
                        uv1            = flipCentreMarker ? new Vector2(1, -1) : Vector2.One;
                        playerPosition = new Vector2(mapIconComponentNode->AtkResNode.X, -mapIconComponentNode->AtkResNode.Y);
                        break;

                    //case  >5 and <9 :
                    //    break;
                    case 060495:     // Small Area Circle
                    case 060496:     // Big Area Circle
                    case 060497:     // Another Circle
                    case 060498:     // One More Circle
                        bool inArea;
                        (pMin, pMax, tintColour, inArea)
                            = Util.CalculateAreaCircleVariables(playerPosition, playerForward, mapIconComponentNode,
                                                                imgNode, distanceOffset, compassUnit, halfWidth40, centre,
                                                                maxDistance, minScaleFactor);
                        if (inArea)
                        {
                            //*((byte*) &tintColour + 3) = 0x33  == (0x33FFFFFF) & (tintColour)
                            ImGui.GetBackgroundDrawList().AddRectFilled(backgroundPMin
                                                                        , backgroundPMax
                                                                        , 0x33FFFFFF & tintColour //Set A to 0.2
                                                                        , backgroundRounding
                                                                        );
                        }
                        break;

                    case 060541:     // Arrow UP on Circle
                    case 060542:     // Arrow UP on Circle
                    case 060543:     // Another Arrow UP
                    case 060545:     // Another Arrow DOWN
                    case 060546:     // Arrow DOWN on Circle
                    case 060547:     // Arrow DOWN on Circle
                        (pMin, pMax, tintColour, _)
                            = Util.CalculateAreaCircleVariables(playerPosition, playerForward, mapIconComponentNode,
                                                                imgNode, distanceOffset, compassUnit, halfWidth40, centre,
                                                                maxDistance, minScaleFactor);
                        break;

                    case 071003:     // MSQ Ongoing Marker
                    case 071005:     // MSQ Complete Marker
                    case 071013:     // MSQ Ongoing Red Marker
                    case 071015:     // MSQ Complete Red Marker
                    case 071023:     // Quest Ongoing Marker
                    case 071033:     // Quest Ongoing Red Marker
                    case 071153:     // Quest Ongoing Red Marker 2
                    case 071025:     // Quest Complete Marker
                    case 071035:     // Quest Complete Red Marker
                    case 071155:     // Quest Complete Red Marker
                    case 071063:     // BookQuest Ongoing Marker
                    case 071065:     // BookQuest Complete Marker
                    case 071083:     // LeveQuest Ongoing Marker
                    case 071085:     // LeveQuest Complete Marker
                    case 071143:     // BlueQuest Ongoing Marker
                    case 071145:     // BlueQuest Complete Marker
                    case 071146:     // Weird Blueish Round Exclamation Mark //TODO More of those?
                    case 071026:     // Weird Blueish Round Exclamation Mark 2
                    case 071111:     // Weird Round Quest Mark
                    case 071112:     // Weird Round Quest Complete Mark
                    case 060954:     // Arrow up for quests
                    case 060955:     // Arrow down for quests
                    case 060561:     // Red Flag (Custom marker)
                    case 060437:     // Mining Crop Icon
                    case 060438:     // Mining Crop Icon 2
                    case 060463:     // Mining Crop Icon 3
                    case 060464:     // Mining Crop Icon 4
                    case 060432:     // Botanic Crop Icon
                    case 060433:     // Botanic Crop Icon 2
                    case 060461:     // Botanic Crop Icon 3
                    case 060462:     // Botanic Crop Icon 4
                    case 060455:     // Fishing Icon
                    case 060465:     // Fishing Icon 2
                    case 060466:     // Fishing Icon 3
                    case 060474:     // Waymark A
                    case 060475:     // Waymark B
                    case 060476:     // Waymark C
                    case 060936:     // Waymark D
                    case 060931:     // Waymark 1
                    case 060932:     // Waymark 2
                    case 060933:     // Waymark 3
                    case 063904:     // Waymark 4
                    case 060934:     // FATE EXP Bonus Icon
                        if (mapIconComponentNode->AtkResNode.Rotation == 0)
                        {
                            // => The current quest marker is inside the mask and should be
                            // treated as a map point
                            goto default;
                        }
                        // => The current quest marker is outside the mask and should be
                        // treated as a rotation
                        goto case 1;     //No UV setup needed for quest markers

                    default:
                        // NOTE (Chiv) Remember, Y needs to be flipped to transform to default coordinate system
                        var(distanceScaleFactor, iconAngle, _) = Util.CalculateDrawVariables(playerPosition,
                                                                                             new Vector2(
                                                                                                 mapIconComponentNode->AtkResNode.X,
                                                                                                 -mapIconComponentNode->AtkResNode.Y
                                                                                                 ),
                                                                                             playerForward,
                                                                                             distanceOffset,
                                                                                             maxDistance,
                                                                                             minScaleFactor
                                                                                             );
                        var iconOffset    = compassUnit * iconAngle;
                        var iconHalfWidth = halfWidth40 * distanceScaleFactor;
                        pMin = new Vector2(centre.X - iconHalfWidth + iconOffset, centre.Y - iconHalfWidth);
                        pMax = new Vector2(centre.X + iconOffset + iconHalfWidth, centre.Y + iconHalfWidth);
                        break;
                    }

                    //PluginLog.Debug($"ID: {iconId}; Tintcolor: {tintColour:x8}");
                    if (rotate)
                    {
                        ImGuiHelper.ImageRotated(
                            textureIdPtr,
                            new Vector2(pMin.X + (pMax.X - pMin.X) * 0.5f, pMin.Y + (pMax.Y - pMin.Y) * 0.5f),
                            pMax - pMin,
                            imgNode->AtkResNode.Rotation,
                            uv,
                            uv1,
                            drawList
                            );
                    }
                    else
                    {
                        drawList.AddImage(textureIdPtr, pMin, pMax, uv, uv1, tintColour);
                    }
                }
            }
        }
#if DEBUG
        catch (Exception e) {
            SimpleLog.Error(e);
        }
#else
        catch {
            // ignored
        }
#endif
        return(playerPosition);
    }