static void Postfix(LineOfSight __instance, Point p0, float height0, Point p1, float height1, Vector3 unitDelta, string targetGuid,
                            ref float __result, CombatGameState ___Combat)
        {
            if (ModState.CurrentTurretForLOS == null)
            {
                return;
            }

            if (p0.X == p1.X && p0.Z == p1.Z)
            {
                __result = 0f;
                return;
            }

            if (!___Combat.MapMetaData.IsWithinBounds(p0) || !___Combat.MapMetaData.IsWithinBounds(p1))
            {
                __result = float.MaxValue;
                return;
            }

            float numCellsX       = Mathf.Abs(unitDelta.x) * (float)MapMetaDataExporter.cellSize;
            float numCellsY       = Mathf.Abs(unitDelta.z) * (float)MapMetaDataExporter.cellSize;
            float lineDeltaX      = (float)(p1.X - p0.X);
            float lineDeltaZ      = (float)(p1.Z - p0.Z);
            float greatestDivisor = Mathf.Max(Mathf.Abs(lineDeltaX), Mathf.Abs(lineDeltaZ));
            float stepHeight      = (height1 - height0) / greatestDivisor;
            float sumVisionCost   = 0f;

            Traverse           projectedHeightAtT = Traverse.Create(__instance).Method("getProjectedHeightAt", new Type[] { typeof(Point), typeof(float), typeof(Point), typeof(float) });
            Traverse           visCostOfCellT     = Traverse.Create(__instance).Method("visCostOfCell", new Type[] { typeof(MapTerrainDataCell), typeof(float) });
            string             shellBuildingGUID  = ModState.AmbushTurretGUIDtoBuilding[ModState.CurrentTurretForLOS.GUID].GUID;
            EncounterLayerData encounterLayerData = ___Combat.EncounterLayerData;

            List <Point> list = BresenhamLineUtil.BresenhamLine(p0, p1);

            for (int i = 1; i < list.Count; i++)
            {
                float stepDelta;
                if (list[i].X != list[i - 1].X)
                {
                    stepDelta = numCellsX;
                }
                else
                {
                    stepDelta = numCellsY;
                }

                // Increment vision cost only slightly if it's inside our shell building
                if (encounterLayerData.mapEncounterLayerDataCells[list[i].Z, list[i].X].HasSpecifiedBuilding(shellBuildingGUID))
                {
                    Mod.Log.Trace?.Write($" Point x={list[i].X} z={list[i].Z} is inside the shell building, adding vision cost normally.");
                    sumVisionCost += stepDelta;
                }
                else
                {
                    float projectedHeightAt = projectedHeightAtT.GetValue <float>(new object[] { p0, height0, list[i], stepHeight });
                    MapTerrainDataCell mapTerrainDataCell = ___Combat.MapMetaData.mapTerrainDataCells[list[i].Z, list[i].X];
                    if (mapTerrainDataCell.cachedHeight > projectedHeightAt)
                    {
                        if (mapTerrainDataCell.MapEncounterLayerDataCell.HasBuilding)
                        {
                            for (int j = 0; j < mapTerrainDataCell.MapEncounterLayerDataCell.buildingList.Count; j++)
                            {
                                if (ObstructionGameLogic.GuidsMatchObjectOrRep(mapTerrainDataCell.MapEncounterLayerDataCell.buildingList[j].buildingGuid, targetGuid))
                                {
                                    __result = sumVisionCost;
                                    return;
                                }
                            }
                        }

                        __result = float.MaxValue;
                        return;
                    }

                    sumVisionCost += visCostOfCellT.GetValue <float>(new object[] { mapTerrainDataCell, projectedHeightAt }) * stepDelta;
                }
            }

            __result = sumVisionCost;
            return;
        }
예제 #2
0
        public CacheNodeLink(CacheNode from, CacheNode to, float distance, int angle, MapMetaData mapMetaData)
        {
            this.From       = from;
            this.To         = to;
            this.Reciprocal = to.NeighborLinks[(angle + 4) % 8];
            if (this.Reciprocal != null)
            {
                this.Reciprocal.Reciprocal = this;
            }

            this.PathCells       = new List <MapTerrainDataCell>();
            this.TargetDistCells = new List <DistCell[]>();
            List <Point> indexLine = BresenhamLineUtil.BresenhamLine(from.CellIndex, to.CellIndex);

            for (int i = 0; i < indexLine.Count - 1; i++)
            {
                // Targets are indexLine[i + 1] and the cells in the adjacent cardinal directions
                DistCell[] targets = new DistCell[3];
                Point      current = indexLine[i];
                int        xDiff   = indexLine[i + 1].X - current.X;
                int        zDiff   = indexLine[i + 1].Z - current.Z;

                if (xDiff >= 0)
                {
                    targets[0] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X + 1, current.Z));
                }
                else
                {
                    targets[0] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X - 1, current.Z));
                }

                if (zDiff >= 0)
                {
                    targets[1] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X, current.Z + 1));
                }
                else
                {
                    targets[1] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X, current.Z - 1));
                }

                if (xDiff == 0)
                {
                    targets[2] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X - 1, current.Z));
                }
                else if (zDiff == 0)
                {
                    targets[2] = new DistCell(cellDelta, mapMetaData.GetCellAt(current.X, current.Z - 1));
                }
                else
                {
                    // this should be impossible with the modified bresenham they're using
                    targets[2] = new DistCell(cellDeltaDiag, mapMetaData.GetCellAt(indexLine[i]));
                }

                this.PathCells.Add(mapMetaData.GetCellAt(current));
                this.TargetDistCells.Add(targets);
            }
            this.Distance = distance;
            this.UpdateGrade();
            this.UpdateMaxGrade();
        }
        static void Postfix(LineOfSight __instance, Point p0, float height0, Point p1, float height1, string targetedBuildingGuid, ref Point collisionWorldPos,
                            ref bool __result, CombatGameState ___Combat)
        {
            if (ModState.CurrentTurretForLOF == null)
            {
                return;
            }

            Mod.Log.Trace?.Write($"Recalculating LOF from {CombatantUtils.Label(ModState.CurrentTurretForLOF)} due to collision on building shell. " +
                                 $"CollisonWorldPos=> x={collisionWorldPos.X} z={collisionWorldPos.Z}");

            collisionWorldPos = p1;

            // If the origin and target points are the same, there is a collision
            if (p0.X == p1.X && p0.Z == p1.Z)
            {
                __result = true;
                return;
            }

            // If the origin or target points are outsie the bounds of the map, there is no collision (because how could there be)
            if (!___Combat.MapMetaData.IsWithinBounds(p0) || !___Combat.MapMetaData.IsWithinBounds(p1))
            {
                __result = false;
                return;
            }

            MapMetaData        mapMetaData        = ___Combat.MapMetaData;
            EncounterLayerData encounterLayerData = ___Combat.EncounterLayerData;

            bool   targetIsABuilding = !string.IsNullOrEmpty(targetedBuildingGuid);
            string shellBuildingGUID = ModState.AmbushTurretGUIDtoBuilding[ModState.CurrentTurretForLOF.GUID].GUID;

            List <Point> bresenhamLinePoints  = BresenhamLineUtil.BresenhamLine(p0, p1);
            float        heightDeltaPerPoint  = (height1 - height0) / (float)bresenhamLinePoints.Count;
            float        collisionPointHeight = height0;

            // Walk the bresenham Line, evaluation collision at a speciifc height as we go.
            for (int i = 0; i < bresenhamLinePoints.Count; i++)
            {
                collisionPointHeight += heightDeltaPerPoint;
                Point point = bresenhamLinePoints[i];

                if (encounterLayerData.mapEncounterLayerDataCells[point.Z, point.X].HasSpecifiedBuilding(shellBuildingGUID))
                {
                    Mod.Log.Trace?.Write($" Point x={point.X} z={point.Z} is inside the shell building, continuing.");
                    continue;
                }

                if (targetIsABuilding && encounterLayerData.mapEncounterLayerDataCells[point.Z, point.X].HasSpecifiedBuilding(targetedBuildingGuid))
                {
                    Mod.Log.Trace?.Write($" Building {targetedBuildingGuid} conflicts with the LoS, collision at x={collisionWorldPos.X} z={collisionWorldPos.Z}");
                    collisionWorldPos = bresenhamLinePoints[i];
                    __result          = true;
                    return;
                }

                if (mapMetaData.mapTerrainDataCells[point.Z, point.X].cachedHeight > collisionPointHeight)
                {
                    Mod.Log.Trace?.Write($" Collision on terrain at position x={collisionWorldPos.X} z={collisionWorldPos.Z}");
                    collisionWorldPos = bresenhamLinePoints[i];
                    __result          = false;
                    return;
                }
            }

            Mod.Log.Trace?.Write($"No collision detected, changing LoF to true. CollisonWorldPos => x ={ collisionWorldPos.X} z ={ collisionWorldPos.Z}");

            __result = true;
            return;
        }
        private static bool NotPrefix(
            ref bool __result, Vector3 from, Vector3 to, MapMetaData ___mapMetaData, CombatGameState ___combat, List <Point> ___fbbLine,
            float ___maxGrade, float ___cellDelta, float ___cellDeltaDiag, Point ___incrementZ, Point ___incrementX, Point ___decrementX,
            Point ___decrementZ)
        {
            _stopwatch.Start();
            Counter++;
            ___mapMetaData = ___combat.MapMetaData;
            Point index  = ___mapMetaData.GetIndex(from);
            Point index2 = ___mapMetaData.GetIndex(to);

            if (!___mapMetaData.IsWithinBounds(index) || !___mapMetaData.IsWithinBounds(index))
            {
                return(true);
            }
            ___fbbLine = BresenhamLineUtil.BresenhamLine(index, index2);
            if (___fbbLine.Count < 3)
            {
                return(false);
            }

            float cachedHeight = ___mapMetaData.GetCellAt(___fbbLine[___fbbLine.Count - 1]).cachedHeight;

            using (CancellationTokenSource cts = new CancellationTokenSource())
            {
                availableThreads = Mod.MaxConcurrency;
                Task <bool>[]     tasks = new Task <bool> [3];
                CancellationToken token = cts.Token;

                for (int i = -1; i <= 1; i++)
                {
                    while (availableThreads <= 0)
                    {
                    }

                    Interlocked.Decrement(ref availableThreads);
                    int k = i;
                    tasks[i + 1] = Task.Run(() =>
                    {
                        for (int j = -1; j <= 1; j++)
                        {
                            if (token.IsCancellationRequested)
                            {
                                return(false);
                            }

                            if (k == 0 && j == 0)
                            {
                                continue;
                            }

                            int num  = ___fbbLine[___fbbLine.Count - 1].X + k;
                            int num2 = ___fbbLine[___fbbLine.Count - 1].Z + j;
                            if (___mapMetaData.IsWithinBounds(num, num2))
                            {
                                float cachedHeight2 = ___mapMetaData.GetCellAt(num, num2).cachedHeight;
                                if (PathNodeGrid.CheckForBlocker(cachedHeight, cachedHeight2, ___maxGrade,
                                                                 (k != 0 && j != 0) ? ___cellDeltaDiag : ___cellDelta))
                                {
                                    Interlocked.Increment(ref availableThreads);
                                    cts.Cancel();
                                    return(true);
                                }
                            }
                        }

                        Interlocked.Increment(ref availableThreads);
                        return(false);
                    });
                }

                if (token.IsCancellationRequested)
                {
                    __result = true;
                    PrintExceptions(tasks);
                    return(false);
                }

                foreach (Task <bool> t in tasks)
                {
                    // busy wait
                    while (!t.IsCompleted)
                    {
                    }
                }

                PrintExceptions(tasks);
                if (token.IsCancellationRequested)
                {
                    __result = true;
                    return(false);
                }
            }

            availableThreads = Mod.MaxConcurrency;

            float fromHeight = ___mapMetaData.GetCellAt(___fbbLine[0]).cachedHeight;

            using (CancellationTokenSource cts = new CancellationTokenSource())
            {
                CancellationToken token = cts.Token;
                Task <bool>[]     tasks = new Task <bool> [___fbbLine.Count - 1];

                for (int k = 1; k < ___fbbLine.Count; k++)
                {
                    while (availableThreads <= 0)
                    {
                    }

                    Interlocked.Decrement(ref availableThreads);
                    int i = k - 1;
                    tasks[i] = Task.Run(() =>
                    {
                        cachedHeight = ___mapMetaData.GetCellAt(___fbbLine[i + 1]).cachedHeight;
                        if (___fbbLine[i + 1].Z == ___fbbLine[i].Z)
                        {
                            if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight, ___maxGrade, ___cellDelta))
                            {
                                cts.Cancel();
                                Interlocked.Increment(ref availableThreads);
                                return(true);
                            }
                            float cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___incrementZ).cachedHeight;
                            if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta))
                            {
                                cts.Cancel();
                                Interlocked.Increment(ref availableThreads);
                                return(true);
                            }
                            cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___decrementZ).cachedHeight;
                            if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta))
                            {
                                cts.Cancel();
                                Interlocked.Increment(ref availableThreads);
                                return(true);
                            }
                        }
                        else if (___fbbLine[i + 1].X == ___fbbLine[i].X)
                        {
                            if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight, ___maxGrade, ___cellDelta))
                            {
                                cts.Cancel();
                                Interlocked.Increment(ref availableThreads);
                                return(true);
                            }
                            float cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___decrementX).cachedHeight;
                            if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta))
                            {
                                cts.Cancel();
                                Interlocked.Increment(ref availableThreads);
                                return(true);
                            }
                            cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___incrementX).cachedHeight;
                            if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta))
                            {
                                cts.Cancel();
                                Interlocked.Increment(ref availableThreads);
                                return(true);
                            }
                        }
                        else
                        {
                            if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight, ___maxGrade, ___cellDeltaDiag))
                            {
                                cts.Cancel();
                                Interlocked.Increment(ref availableThreads);
                                return(true);
                            }
                            if (___fbbLine[i + 1].X > ___fbbLine[i].X)
                            {
                                float cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___incrementX).cachedHeight;
                                if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta))
                                {
                                    cts.Cancel();
                                    Interlocked.Increment(ref availableThreads);
                                    return(true);
                                }
                                if (___fbbLine[i + 1].Z > ___fbbLine[i].Z)
                                {
                                    cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___incrementZ).cachedHeight;
                                    if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta))
                                    {
                                        cts.Cancel();
                                        Interlocked.Increment(ref availableThreads);
                                        return(true);
                                    }
                                }
                                else if (___fbbLine[i + 1].Z < ___fbbLine[i].Z)
                                {
                                    cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___decrementZ).cachedHeight;
                                    if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta))
                                    {
                                        cts.Cancel();
                                        Interlocked.Increment(ref availableThreads);
                                        return(true);
                                    }
                                }
                            }
                            else if (___fbbLine[i + 1].X < ___fbbLine[i].X)
                            {
                                float cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___decrementX).cachedHeight;
                                if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta))
                                {
                                    cts.Cancel();
                                    Interlocked.Increment(ref availableThreads);
                                    return(true);
                                }
                                if (___fbbLine[i + 1].Z > ___fbbLine[i].Z)
                                {
                                    cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___incrementZ).cachedHeight;
                                    if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta))
                                    {
                                        cts.Cancel();
                                        Interlocked.Increment(ref availableThreads);
                                        return(true);
                                    }
                                }
                                else if (___fbbLine[i + 1].Z < ___fbbLine[i].Z)
                                {
                                    cachedHeight2 = ___mapMetaData.GetCellAt(___fbbLine[i] + ___decrementZ).cachedHeight;
                                    if (PathNodeGrid.CheckForBlocker(fromHeight, cachedHeight2, ___maxGrade, ___cellDelta))
                                    {
                                        cts.Cancel();
                                        Interlocked.Increment(ref availableThreads);
                                        return(true);
                                    }
                                }
                            }
                        }

                        fromHeight = cachedHeight;
                        Interlocked.Increment(ref availableThreads);
                        return(false);
                    });

                    if (token.IsCancellationRequested)
                    {
                        PrintExceptions(tasks);
                        __result = true;
                        return(false);
                    }
                }

                if (token.IsCancellationRequested)
                {
                    PrintExceptions(tasks);
                    __result = true;
                    return(false);
                }

                foreach (Task <bool> t in tasks)
                {
                    // busy wait
                    while (!t.IsCompleted)
                    {
                        if (token.IsCancellationRequested)
                        {
                            PrintExceptions(tasks);
                            __result = true;
                            return(false);
                        }
                    }
                }
                PrintExceptions(tasks);
            }

            __result = false;
            return(false);

            void PrintExceptions(Task <bool>[] tasks)
            {
                List <Task <bool> > faultyTasks = tasks.Where(t => t?.IsFaulted ?? false).ToList();

                if (faultyTasks.Any())
                {
                    StringBuilder stringBuilder = new StringBuilder();
                    foreach (Task task in faultyTasks)
                    {
                        stringBuilder.AppendLine(task.Exception.Flatten().ToString());
                    }

                    Utils.Logger.LogError($"{Utils.LOG_HEADER}" + stringBuilder);
                }
            }
        }