private bool FindFreeTargetPos(ref LocAndOffsets loc, float radius)
    {
        var loc2d = loc.ToInches2D();

        if (!IsBlocked(loc2d, radius))
        {
            return(true);
        }

        var i = 0;

        for (var j = -1; j > -18; j--)
        {
            int k;
            var yOffset = i * locXY.INCH_PER_SUBTILE;
            var altPos  = loc2d;
            for (k = j; k < i; k++)
            {
                var xOffset = k * locXY.INCH_PER_SUBTILE;

                altPos = loc2d + new Vector2(xOffset, -yOffset);
                if (!IsBlocked(altPos, radius))
                {
                    break;
                }

                altPos = loc2d + new Vector2(-xOffset, yOffset);
                if (!IsBlocked(altPos, radius))
                {
                    break;
                }

                altPos = loc2d + new Vector2(-xOffset, -yOffset);
                if (!IsBlocked(altPos, radius))
                {
                    break;
                }

                altPos = loc2d + new Vector2(xOffset, yOffset);
                if (!IsBlocked(altPos, radius))
                {
                    break;
                }
            }

            if (k != i)
            {
                loc = LocAndOffsets.FromInches(altPos);
                return(true);
            }

            i++;
        }

        return(i != 0);
    }
Esempio n. 2
0
    public void ConversionRoundtrip()
    {
        var zeroTile = LocAndOffsets.FromInches(0, 0);

        zeroTile.location.locx.Should().Be(0);
        zeroTile.location.locy.Should().Be(0);
        zeroTile.off_x.Should().Be(-locXY.INCH_PER_HALFTILE);
        zeroTile.off_y.Should().Be(-locXY.INCH_PER_HALFTILE);

        var conversionBack = zeroTile.ToInches2D();

        conversionBack.X.Should().Be(0);
        conversionBack.Y.Should().Be(0);
    }
    private bool IsBlocked(Vector2 pos, float radius)
    {
        var loc = LocAndOffsets.FromInches(pos);

        using var raycastPacket = new RaycastPacket();
        raycastPacket.origin    = loc;
        raycastPacket.targetLoc = loc;
        raycastPacket.radius    = radius;
        raycastPacket.flags    |= RaycastFlag.StopAfterFirstFlyoverFound
                                  | RaycastFlag.StopAfterFirstBlockerFound
                                  | RaycastFlag.ExcludeItemObjects
                                  | RaycastFlag.HasSourceObj
                                  | RaycastFlag.HasRadius;

        return(raycastPacket.RaycastShortRange() > 0);
    }
    private LocAndOffsets AverageLocation(LocAndOffsets a, LocAndOffsets b)
    {
        var averaged = (a.ToInches3D() + b.ToInches3D()) / 2;

        return(LocAndOffsets.FromInches(averaged));
    }
    public int PartySelectedFormationMoveToPosition(LocAndOffsets loc, bool mayWalk)
    {
        var selectedMembers = GameSystems.Party.Selected;

        if (selectedMembers.Count == 0)
        {
            return(1);
        }

        var maxRadius = float.MinValue;

        foreach (var selectedMember in selectedMembers)
        {
            if (!GameSystems.Critter.IsDeadNullDestroyed(selectedMember))
            {
                maxRadius = Math.Max(maxRadius, selectedMember.GetRadius());
            }
        }

        Span <LocAndOffsets> plannedPositions = stackalloc LocAndOffsets[selectedMembers.Count];

        if (!FindFreeTargetPos(ref loc, maxRadius))
        {
            return(1);
        }

        // NOTE: There seems to have been a bug in Vanilla where it wanted to
        // find the average center of the party, but never actually updated the obj
        // it took the position of. So it ended up just taking the leader's position.
        var leaderPos = selectedMembers[0].GetLocationFull().ToInches2D();
        var targetPos = loc.ToInches2D();

        // This rotation is used to rotate the formation at the target point
        var formationRotation = MathF.PI - MathF.Atan2(targetPos.X - leaderPos.X, targetPos.Y - leaderPos.Y);

        if (!GameSystems.Critter.IsDeadNullDestroyed(selectedMembers[0]))
        {
            plannedPositions[0] = loc;
        }

        for (var selectedIdx = 0; selectedIdx < selectedMembers.Count; selectedIdx++)
        {
            var obj = selectedMembers[selectedIdx];
            if (GameSystems.Critter.IsDeadNullDestroyed(obj))
            {
                continue;
            }

            var partyMemberRadius = obj.GetRadius();

            GetFormationOffset(obj, formationRotation, out var formationOffX, out var formationOffY);
            if (selectedMembers.Count < 2)
            {
                plannedPositions[selectedIdx] = loc;
            }
            else
            {
                var formationOffLen = MathF.Sqrt(formationOffY * formationOffY + formationOffX * formationOffX);
                // Remember that the length of the formation offset can be 0 in case it's dead-center
                if (formationOffLen > locXY.INCH_PER_SUBTILE)
                {
                    // Normalize the formation offset
                    var formationOffNormX = formationOffX / formationOffLen;
                    var formationOffNormY = formationOffY / formationOffLen;
                    var formationOffDir   = new Vector2(formationOffNormX, formationOffNormY);

                    var local_90 = 0.0f;
                    for (; local_90 < formationOffLen; local_90 += locXY.INCH_PER_SUBTILE)
                    {
                        var formationShift = local_90 * formationOffDir;
                        var formationPos   = targetPos + formationShift;

                        if (!IsBlocked(formationPos, maxRadius))
                        {
                            plannedPositions[selectedIdx] = LocAndOffsets.FromInches(formationPos);
                            break;
                        }
                    }

                    if (local_90 >= locXY.INCH_PER_SUBTILE)
                    {
                        // It somehow falls back to the previous location even if unblocked ???
                        var formationPos = targetPos + (local_90 - locXY.INCH_PER_SUBTILE) * formationOffDir;
                        plannedPositions[selectedIdx] = LocAndOffsets.FromInches(formationPos);
                    }
                }
                else
                {
                    plannedPositions[selectedIdx] = loc;
                }
            }

            var previousPositions = plannedPositions.Slice(0, selectedIdx);

            // We have to do conflict resolution if another party member before this one
            // chose a colliding position
            if (FindIntersectingPosition(selectedMembers, previousPositions,
                                         plannedPositions[selectedIdx].ToInches2D(), partyMemberRadius))
            {
                int upperBound;
                for (upperBound = 1; upperBound < 18; upperBound++)
                {
                    var local_84 = upperBound * locXY.INCH_PER_SUBTILE;

                    int local_8c;
                    var alternatePos = plannedPositions[selectedIdx].ToInches2D();
                    for (local_8c = -upperBound; local_8c < upperBound; local_8c++)
                    {
                        alternatePos    = plannedPositions[selectedIdx].ToInches2D();
                        alternatePos.X += local_8c * locXY.INCH_PER_SUBTILE;
                        alternatePos.Y -= local_84;
                        if (!FindIntersectingPosition(selectedMembers, previousPositions, alternatePos,
                                                      partyMemberRadius) &&
                            !IsBlocked(alternatePos, partyMemberRadius))
                        {
                            break;
                        }

                        alternatePos    = plannedPositions[selectedIdx].ToInches2D();
                        alternatePos.X -= local_8c * locXY.INCH_PER_SUBTILE;
                        alternatePos.Y += local_84;
                        if (!FindIntersectingPosition(selectedMembers, previousPositions, alternatePos,
                                                      partyMemberRadius) &&
                            !IsBlocked(alternatePos, partyMemberRadius))
                        {
                            break;
                        }

                        alternatePos    = plannedPositions[selectedIdx].ToInches2D();
                        alternatePos.X -= local_8c * locXY.INCH_PER_SUBTILE;
                        alternatePos.Y -= local_84;
                        if (!FindIntersectingPosition(selectedMembers, previousPositions, alternatePos,
                                                      partyMemberRadius) &&
                            !IsBlocked(alternatePos, partyMemberRadius))
                        {
                            break;
                        }

                        alternatePos    = plannedPositions[selectedIdx].ToInches2D();
                        alternatePos.X += local_8c * locXY.INCH_PER_SUBTILE;
                        alternatePos.Y += local_84;
                        if (!FindIntersectingPosition(selectedMembers, previousPositions, alternatePos,
                                                      partyMemberRadius) &&
                            !IsBlocked(alternatePos, partyMemberRadius))
                        {
                            break;
                        }
                    }

                    if (local_8c != upperBound)
                    {
                        plannedPositions[selectedIdx] = LocAndOffsets.FromInches(alternatePos);
                        break;
                    }
                }

                if (upperBound == 18)
                {
                    Logger.Info("Unable to find suitable formation position after 18 iterations.");
                }
            }
        }

        for (var i = 0; i < selectedMembers.Count; i++)
        {
            var obj = selectedMembers[i];
            if (!GameSystems.Critter.IsDeadNullDestroyed(obj))
            {
                PartySelectedFormationMoveToPosition_FindPath(obj, selectedMembers.Count, loc,
                                                              plannedPositions[i], out var actualTargetPos);
                if (!mayWalk)
                {
                    GameSystems.Anim.PushRunToTile(obj, actualTargetPos);
                    GameSystems.Anim.TurnOnRunning();
                }
                else
                {
                    GameSystems.Anim.PushMoveToTile(obj, actualTargetPos);
                }
            }
        }

        return(1);
    }
Esempio n. 6
0
    // TODO: Separate logic+rendering
    public void AdvanceAndRender(
        IGameViewport viewport,
        GameObject giantFrog,
        AnimatedModelParams animParams,
        IAnimatedModel model,
        IList <Light3d> lights,
        float alpha)
    {
        var grappleState = GetGrappleState(giantFrog);

        if (grappleState.state == 0)
        {
            return;
        }

        model.GetBoneWorldMatrixByName(animParams, "Tongue_Ref", out var worldMatrixFrog);

        var   grappledOpponent = GetGrappledOpponent(giantFrog);
        float tongueLength, tonguePosX, tonguePosZ;

        if (grappledOpponent != null)
        {
            var opponentModel      = grappledOpponent.GetOrCreateAnimHandle();
            var opponentAnimParams = grappledOpponent.GetAnimParams();
            opponentModel.GetBoneWorldMatrixByName(opponentAnimParams, "Bip01 Spine1", out var worldMatrixOpponent);

            tonguePosX = worldMatrixOpponent.M41;
            tonguePosZ = worldMatrixOpponent.M43;

            var tongueDirX = worldMatrixOpponent.M41 - worldMatrixFrog.M41;
            var tongueDirY = worldMatrixOpponent.M42 - worldMatrixFrog.M42;
            var tongueDirZ = worldMatrixOpponent.M43 - worldMatrixFrog.M43;
            tongueLength        = MathF.Sqrt(tongueDirX * tongueDirX + tongueDirY * tongueDirY + tongueDirZ * tongueDirZ);
            worldMatrixFrog.M31 = tongueDirX / tongueLength;
            worldMatrixFrog.M32 = tongueDirY / tongueLength;
            worldMatrixFrog.M33 = tongueDirZ / tongueLength;
            if (tongueLength > 0)
            {
                tongueLength -= 6.0f;
            }
        }
        else
        {
            tongueLength = 120.0f;
            tonguePosX   = worldMatrixFrog.M31 * 120.0f + worldMatrixFrog.M41;
            tonguePosZ   = worldMatrixFrog.M33 * 120.0f + worldMatrixFrog.M43;
        }

        switch (grappleState.state)
        {
        // This state seems to mean . Extending tongue to full length
        case 1:
            grappleState.currentLength += locXY.INCH_PER_TILE;
            if (grappleState.currentLength > tongueLength)
            {
                grappleState.state         = 2;
                grappleState.currentLength = tongueLength;
            }

            break;

        // This state seems to mean . Retracting tongue
        case 2:
            grappleState.currentLength -= locXY.INCH_PER_TILE;
            if (grappleState.currentLength <= 0)
            {
                grappleState.state         = 0;
                grappleState.currentLength = 0;
            }

            break;

        case 3:
            grappleState.currentLength += locXY.INCH_PER_TILE;
            if (grappleState.currentLength > tongueLength)
            {
                grappleState.state         = 4;
                grappleState.currentLength = tongueLength;
                var frogAnim = GameSystems.Critter.GetAnimId(giantFrog,
                                                             WeaponAnim.Special2);
                giantFrog.SetAnimId(frogAnim);

                var opponentAnim = GameSystems.Critter.GetAnimId(grappledOpponent,
                                                                 WeaponAnim.Panic);
                grappledOpponent.SetAnimId(opponentAnim);
            }

            break;

        case 4:
            // Maintain Tongue between frog and opponent without progressing
            grappleState.currentLength = tongueLength;
            break;

        case 5:
        case 6:
        {
            if (grappleState.state == 5)
            {
                grappleState.targetLength = tongueLength - 12.0f;
                if (grappleState.targetLength < 0)
                {
                    grappleState.targetLength = 0;
                }

                grappleState.state = 6;
            }

            grappleState.currentLength = grappleState.currentLength - locXY.INCH_PER_HALFTILE;
            // Move the opponent closer to the frog
            float newX   = tonguePosX - worldMatrixFrog.M31 * locXY.INCH_PER_HALFTILE;
            float newZ   = tonguePosZ - worldMatrixFrog.M33 * locXY.INCH_PER_HALFTILE;
            var   newLoc = LocAndOffsets.FromInches(newX, newZ);
            GameSystems.MapObject.Move(grappledOpponent, newLoc);

            if (grappleState.currentLength < grappleState.targetLength)
            {
                newX   = worldMatrixFrog.M41 + grappleState.targetLength * worldMatrixFrog.M31;
                newZ   = worldMatrixFrog.M43 + grappleState.targetLength * worldMatrixFrog.M33;
                newLoc = LocAndOffsets.FromInches(newX, newZ);
                GameSystems.MapObject.Move(grappledOpponent, newLoc);
                grappleState.currentLength = grappleState.targetLength;
                grappleState.state         = 4;
            }
        }
        break;

        case 7:
        {
            grappleState.currentLength = grappleState.currentLength - locXY.INCH_PER_HALFTILE;
            // Move the opponent closer to the frog
            float newX   = tonguePosX - worldMatrixFrog.M31 * locXY.INCH_PER_HALFTILE;
            float newZ   = tonguePosZ - worldMatrixFrog.M33 * locXY.INCH_PER_HALFTILE;
            var   newLoc = LocAndOffsets.FromInches(newX, newZ);
            GameSystems.MapObject.Move(grappledOpponent, newLoc);

            if (grappleState.currentLength < 0)
            {
                newX   = worldMatrixFrog.M41;
                newZ   = worldMatrixFrog.M43;
                newLoc = LocAndOffsets.FromInches(newX, newZ);
                GameSystems.MapObject.Move(grappledOpponent, newLoc);
                GameSystems.ObjFade.FadeTo(grappledOpponent, 0, 0, 16, 0);
                grappleState.currentLength = 0;
                grappleState.state         = 0;

                // Probably the swallow animation
                var animId = GameSystems.Critter.GetAnimId(giantFrog, WeaponAnim.Special3);
                giantFrog.SetAnimId(animId);
            }
        }
        break;

        default:
            break;
        }

        // Update to the new grapple state
        SetGrappleState(giantFrog, grappleState);

        // The directional vector of the tongue ref point on the frog
        var tongueUp    = worldMatrixFrog.GetRow(0).ToVector3();
        var tongueRight = worldMatrixFrog.GetRow(1).ToVector3();
        var tongueDir   = worldMatrixFrog.GetRow(2).ToVector3();
        var tonguePos   = worldMatrixFrog.GetRow(3).ToVector3();

        RenderTongue(viewport, grappleState, tongueDir, tongueUp, tongueRight, tonguePos, lights, alpha);
    }