public static void Postfix(MapTerrainDataCell cell, ref bool __result)
 {
     if (!cell.MapEncounterLayerDataCell.HasBuilding)
     {
         CacheManager.WatchCache[cell] = __result;
     }
 }
 public static void AppendDangerousTerrainText(MoveStatusPreview __instance, AbstractActor actor, Vector3 worldPos)
 {
     try {
         MapTerrainDataCell cell = Combat.EncounterLayerData.GetCellAt(worldPos).relatedTerrainCell;
         bool isLandingZone      = SplatMapInfo.IsDropshipLandingZone(cell.terrainMask),
              isDangerous        = SplatMapInfo.IsDangerousLocation(cell.terrainMask);
         if (!isLandingZone && !isDangerous)
         {
             return;
         }
         DesignMaskDef mask = Combat.MapMetaData.GetPriorityDesignMask(cell);
         if (mask == null)
         {
             return;
         }
         string title = mask.Description.Name, text = mask.Description.Details;
         CombatUIConstantsDef desc = Combat.Constants.CombatUIConstants;
         if (isDangerous)
         {
             title += " <#FF0000>(" + desc.DangerousLocationDesc.Name + ")";
             text  += " <#FF0000>" + desc.DangerousLocationDesc.Details;
             if (isLandingZone)
             {
                 text += " " + desc.DrophipLocationDesc.Details;
             }
         }
         else
         {
             title += " <#FF0000>(" + desc.DrophipLocationDesc.Name + ")";
             text  += " <#FF0000>" + desc.DrophipLocationDesc.Details;
         }
         CombatHUDInfoSidePanel sidePanel = (CombatHUDInfoSidePanel)SidePanelProp?.GetValue(__instance, null);
         sidePanel?.ForceShowSingleFrame(new Text(title), new Text(text), null, false);
     }                 catch (Exception ex) { Error(ex); }
 }
Esempio n. 3
0
        // Duplication of HBS code, avoiding prefix=true for now.
        public static void Postfix(ref BehaviorTreeResults __result, string ___name, BehaviorTree ___tree, AbstractActor ___unit)
        {
            Mod.Log.Info?.Write("CJMCN:T - entered");

            Mech mech = ___unit as Mech;

            if (mech != null && mech.WorkingJumpjets > 0)
            {
                string stayInsideRegionGUID = RegionUtil.GetStayInsideRegionGUID(___unit);

                float acceptableHeat = AIUtil.GetAcceptableHeatLevelForMech(mech);
                float currentHeat    = (float)mech.CurrentHeat;
                Mod.Log.Info?.Write($"CJMCN:T - === actor:{CombatantUtils.Label(mech)} has currentHeat:{currentHeat} and acceptableHeat:{acceptableHeat}");

                List <PathNode> sampledPathNodes = ___unit.JumpPathing.GetSampledPathNodes();
                Mod.Log.Info?.Write($"CJMCN:T - calculating {sampledPathNodes.Count} nodes");
                for (int i = 0; i < sampledPathNodes.Count; i++)
                {
                    Vector3 candidatePos      = sampledPathNodes[i].Position;
                    float   distanceBetween2D = AIUtil.Get2DDistanceBetweenVector3s(candidatePos, ___unit.CurrentPosition);
                    float   distanceBetween3D = Vector3.Distance(candidatePos, ___unit.CurrentPosition);
                    Mod.Log.Info?.Write($"CJMCN:T - calculated distances 2D:'{distanceBetween2D}' 3D:'{distanceBetween3D} ");
                    if (distanceBetween2D >= 1f)
                    {
                        float magnitude = (candidatePos - ___unit.CurrentPosition).magnitude;
                        float jumpHeat  = (float)mech.CalcJumpHeat(magnitude);
                        Mod.Log.Info?.Write($"CJMCN:T - calculated jumpHeat:'{jumpHeat}' from magnitude:'{magnitude}. ");

                        Mod.Log.Info?.Write($"CJMCN:T - comparing heat: [jumpHeat:'{jumpHeat}' + currentHeat:'{currentHeat}'] <= acceptableHeat:'{acceptableHeat}. ");
                        if (jumpHeat + (float)mech.CurrentHeat <= acceptableHeat)
                        {
                            if (stayInsideRegionGUID != null)
                            {
                                MapTerrainDataCell cellAt = ___unit.Combat.MapMetaData.GetCellAt(candidatePos);
                                if (cellAt != null)
                                {
                                    MapEncounterLayerDataCell mapEncounterLayerDataCell = cellAt.MapEncounterLayerDataCell;
                                    if (mapEncounterLayerDataCell != null &&
                                        mapEncounterLayerDataCell.regionGuidList != null &&
                                        !mapEncounterLayerDataCell.regionGuidList.Contains(stayInsideRegionGUID))
                                    {
                                        // Skip this loop iteration if
                                        Mod.Log.Info?.Write($"CJMCN:T - candidate outside of constraint region, ignoring.");
                                        goto CANDIDATE_OUTSIDE_REGION;
                                    }
                                }
                            }

                            Mod.Log.Info?.Write($"CJMCN:T - adding candidate position:{candidatePos}");
                            ___tree.movementCandidateLocations.Add(new MoveDestination(sampledPathNodes[i], MoveType.Jumping));
                        }
                    }

                    CANDIDATE_OUTSIDE_REGION :;
                }
            }

            // Should already be set by prefix method
            //__result = BehaviorTreeResults(BehaviorNodeState.Success);
        }
Esempio n. 4
0
        /// <summary>
        /// Gets incremental incline data for a point and a neighboring point.
        /// </summary>
        /// <param name="mapMetaData">Map meta data.</param>
        /// <param name="startPoint">Start point, using mapmetadata indices.</param>
        /// <param name="endPoint">End point, using mapmetadata indices.</param>
        /// <param name="incline">Incline (out).</param>
        /// <param name="decline">Decline (out).</param>
        /// <param name="distance">Distance (out).</param>
        void getIncrementalInclines(MapMetaData mapMetaData, Point startPoint, Point endPoint, out float incline, out float decline, out float distance)
        {
            Vector3 startVector = mapMetaData.getWorldPos(startPoint);
            Vector3 endVector   = mapMetaData.getWorldPos(endPoint);

            MapTerrainDataCell startCell = mapMetaData.GetCellAt(startPoint);
            MapTerrainDataCell endCell   = mapMetaData.GetCellAt(endPoint);

            float horzDistance = Mathf.Abs(startVector.x - endVector.x) + Mathf.Abs(startVector.z - endVector.z);

            distance = horzDistance;

            float vertDistance = endCell.cachedHeight - startCell.cachedHeight;

            if (vertDistance > 0)
            {
                decline = 0.0f;
                incline = vertDistance / horzDistance;
            }
            else
            {
                incline = 0.0f;
                decline = -(vertDistance / horzDistance);
            }
        }
            public static bool Prefix(MapTerrainDataCell cell)
            {
                if (cell is MapTerrainDataCellEx eCell)
                {
                    return(!eCell.waterLevelCached);
                }

                return(true);
            }
 static bool Prefix(MapMetaData __instance, ref MapTerrainDataCell __result, Point index, MapTerrainDataCell[,] ___mapTerrainDataCells)
 {
     try {
         __result = ___mapTerrainDataCells[index.Z, index.X];
     } catch (IndexOutOfRangeException) {
         __result = InvalidCell;
     }
     return(false);
 }
        public static bool Prefix(MapTerrainDataCell cell, ref bool __result)
        {
            if (cell is null)
            {
                return(true);
            }

            return(!CacheManager.WatchCache.TryGetValue(cell, out __result));
        }
Esempio n. 8
0
        private bool IsCellImpassableOrDeepWater(MapTerrainDataCell cellData)
        {
            TerrainMaskFlags terrainMask             = cellData.terrainMask;
            bool             isImpassableOrDeepWater = SplatMapInfo.IsImpassable(terrainMask) || (SplatMapInfo.IsDeepWater(terrainMask) && !cellData.MapEncounterLayerDataCell.HasBuilding);

            if (isImpassableOrDeepWater)
            {
                Main.LogDebug("[PFM.IsCellImpassableOrDeepWater] Tile is impassable or deep water");
                return(true);
            }
            return(false);
        }
Esempio n. 9
0
        private bool HasPathImpassableOrDeepWaterTiles(CombatGameState combatState, List <Vector3> path)
        {
            if (path == null)
            {
                return(true);
            }

            for (int i = 0; i < path.Count; i++)
            {
                Vector3            position = path[i];
                MapTerrainDataCell cellData = combatState.MapMetaData.GetCellAt(position);
                if (IsCellImpassableOrDeepWater(cellData))
                {
                    Main.LogDebug("[PFM.HasPathImpassableOrDeepWaterTiles] Path has impassable or deep water tiles in it");
                    return(true);
                }
            }

            return(false);
        }
Esempio n. 10
0
        public bool IsSpawnValid(GameObject spawnGo, Vector3 position, Vector3 validityPosition, UnitType type, string identifier)
        {
            CombatGameState    combatState        = UnityGameInstance.BattleTechGame.Combat;
            EncounterLayerData encounterLayerData = MissionControl.Instance.EncounterLayerData;
            MapTerrainDataCell cellData           = combatState.MapMetaData.GetCellAt(position);

            Main.LogDebug($"");
            Main.LogDebug($"-------- [PFM.IsSpawnValid] [{identifier}] --------");

            if (position.IsTooCloseToAnotherSpawn(spawnGo))
            {
                Main.LogDebug($"[PFM] Position '{position}' is too close to another spawn point. Not a valid location.");
                return(false);
            }

            if (cellData.cachedSteepness > MAX_SLOPE_FOR_PATHFINDING)
            {
                Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Spawn point of '{cellData.cachedSteepness}' is too steep (> {MAX_SLOPE_FOR_PATHFINDING}). Not a valid spawn");
                return(false);
            }

            if (IsCellImpassableOrDeepWater(cellData))
            {
                return(false);
            }
            if (!encounterLayerData.IsInEncounterBounds(position))
            {
                return(false);
            }
            if (cellData.cachedHeight > (cellData.terrainHeight + 50f))
            {
                return(false);
            }

            float         pathFindingZoneRadius = 25f;
            AbstractActor pathfindingActor      = GetPathFindingActor(type);

            SetupPathfindingActor(position, pathfindingActor);

            PathNode positionPathNode = null;

            try {
                PathNodeGrid pathfinderPathGrid = pathfindingActor.Pathing.CurrentGrid;
                positionPathNode = pathfinderPathGrid.GetValidPathNodeAt(position, pathfindingActor.Pathing.MaxCost);
                if (positionPathNode == null)
                {
                    Reset();
                    return(false);
                }
            } catch (Exception e) {
                Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Caught error in 'pathfinderPathGrid.GetValidPathNodeAt' chunk. Flagging as invalid spawn. Select a new spawn point. {e.Message}, {e.StackTrace}");
                WasABadPathfindTest(validityPosition);
                return(false);
            }

            if (positionPathNode == null)
            {
                Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] PositionPathNode not set from 'pathfinderPathGrid.GetValidPathNodeAt'. No valid path found so not a valid spawn.");
                WasABadPathfindTest(validityPosition);
                return(false);
            }

            List <Vector3> path = null;

            try {
                DynamicLongRangePathfinder.PointWithCost pointWithCost = new DynamicLongRangePathfinder.PointWithCost(combatState.HexGrid.GetClosestHexPoint3OnGrid(positionPathNode.Position), 0, (validityPosition - positionPathNode.Position).magnitude);
                path = DynamicLongRangePathfinder.GetDynamicPathToDestination(new List <DynamicLongRangePathfinder.PointWithCost>()
                {
                    pointWithCost
                }, validityPosition, float.MaxValue, pathfindingActor, false, new List <AbstractActor>(), pathfindingActor.Pathing.CurrentGrid, pathFindingZoneRadius);
            } catch (Exception e) {
                // TODO: Sometimes this gets triggered in very large amounts. It's usually because the SpawnLogic.GetClosestValidPathFindingHex is increasing
                // the radius larger and larger and the checks keep going off the map
                // I need a way to hard abort out of this and either use the original origin of the focus or trigger the rule logic again (random, around a position etc)
                Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Caught error in 'DynamicLongRangePathfinder' chunk. Flagging as invalid spawn. Select a new spawn point. {e.Message}, {e.StackTrace}");
                WasABadPathfindTest(validityPosition);
                return(false);
            }

            if (path == null)
            {
                Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Path not set from DynamicLongRangePathfinder so not a valid spawn.");
                WasABadPathfindTest(validityPosition);
                return(false);
            }

            Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Path count is: '{path.Count}', Current position is: '{position}'");

            // GUARD: Against deep water and other impassables that have slipped through
            if (HasPathImpassableOrDeepWaterTiles(combatState, path))
            {
                return(false);
            }

            if (path != null && path.Count > 1 && (path[path.Count - 1].DistanceFlat(validityPosition) <= pathFindingZoneRadius))
            {
                Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Path count is: '{path.Count}', Current position is: '{position}'");
                Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Last point is '{path[path.Count - 1]}', Validity position is '{validityPosition}'");
                Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Distance from last path to valdity position is: '{(path[path.Count - 1].DistanceFlat(validityPosition))}' and is it within zone radius? '{(path[path.Count - 1].DistanceFlat(validityPosition) <= pathFindingZoneRadius)}'");
                Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Has valid long range path finding");
                if (HasValidNeighbours(positionPathNode, validityPosition, type))
                {
                    Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Has at least two valid neighbours");

                    if (HasValidLocalPathfinding(positionPathNode, validityPosition, type))
                    {
                        Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Has a valid path");
                        Reset();
                        Main.LogDebug($"-------- END [PFM.IsSpawnValid] [{identifier}] END --------");
                        Main.LogDebug($"");
                        return(true);
                    }
                    else
                    {
                        Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Does NOT have a valid path");
                    }
                }
                else
                {
                    Main.LogDebug($"[PFM.IsSpawnValid] [{identifier}] Does not have two valid neighbours");
                }
            }

            Main.LogDebug($"-------- END [PFM.IsSpawnValid] [{identifier}] END --------");
            Main.LogDebug($"");
            Reset();
            return(false);
        }
        public static bool IsLocationSafe(CombatGameState combat, Vector3 point)
        {
            MapTerrainDataCell dataCell = combat.MapMetaData.GetCellAt(point);

            return(!(SplatMapInfo.IsDropshipLandingZone(dataCell.terrainMask) || SplatMapInfo.IsDropPodLandingZone(dataCell.terrainMask) || SplatMapInfo.IsDangerousLocation(dataCell.terrainMask)));
        }
        /// <summary>
        /// Finds a path from any of start to goal, such that the path has no links that are steeper than the unit's maxGrade.
        /// </summary>
        /// <returns>The path.</returns>
        /// <param name="startPointList">List of PointWithDistances for points to start from</param>
        /// <param name="snappedGoalPoint">HexPoint3 to go to</param>
        /// <param name="unit">moving unit</param>
        /// <param name="moveType">move type - walk, sprint</param>
        /// <param name="targetRadius">how close to get to the target</param>
        /// <param name="actorAware">Discard nodes where other actors reside</param>
        public static List <PointWithCost> FindPath(List <PointWithCost> startPointList, HexPoint3 snappedGoalPoint, AbstractActor unit, MoveType moveType, float targetRadius, bool actorAware)
        {
            MapMetaData mapMetaData = unit.Combat.MapMetaData;
            HexGrid     hexGrid     = unit.Combat.HexGrid;

            unit.Pathing.MoveType = moveType;
            List <AbstractActor> actors = null;

            bool startedInEncounterBounds = false;

            BattleTech.Designed.EncounterBoundaryChunkGameLogic boundaryChunk = unit.Combat.EncounterLayerData.encounterBoundaryChunk;

            for (int spi = 0; spi < startPointList.Count; ++spi)
            {
                PointWithCost sp = startPointList[spi];
                //Vector3 wp = HexPoint3ToWorldPoint(sp.point, hexGrid);

                if (boundaryChunk.IsInEncounterBounds(unit.CurrentPosition))
                {
                    startedInEncounterBounds = true;
                    break;
                }
            }

            actorAware = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Bool_EnableLongRangePathfindingBeActorAware).BoolVal ? true : actorAware;

            if (actorAware)
            {
                actors = unit.Combat.AllActors;
                actors.Remove(unit);
            }

            List <PointWithCost> path = new List <PointWithCost>();

            HeapQueue <PointWithCost> openHeap = new HeapQueue <PointWithCost>();

            Dictionary <HexPoint3, float> bestCostDict = new Dictionary <HexPoint3, float>();

            Dictionary <HexPoint3, PointWithCost> bestPrevPoint = new Dictionary <HexPoint3, PointWithCost>();

            Vector3 worldGoalPoint = HexPoint3ToWorldPoint(snappedGoalPoint, hexGrid);

            float         bestPathCost  = float.MaxValue;
            bool          anyPathFound  = false;
            PointWithCost bestGoalPoint = new PointWithCost(new HexPoint3(-4000, -4000),
                                                            float.MaxValue,
                                                            float.MaxValue);

            for (int startIndex = 0; startIndex < startPointList.Count; ++startIndex)
            {
                PointWithCost pwd = startPointList[startIndex];
                openHeap.Push(pwd);
                bestCostDict[pwd.point]  = pwd.cost;
                bestPrevPoint[pwd.point] = null;

                Vector3 wp = HexPoint3ToWorldPoint(pwd.point, hexGrid);

                if ((pwd.point.Equals(snappedGoalPoint)) ||
                    (AIUtil.Get2DDistanceBetweenVector3s(wp, worldGoalPoint) < targetRadius))
                {
                    if (pwd.cost < bestPathCost)
                    {
                        anyPathFound  = true;
                        bestPathCost  = pwd.cost;
                        bestGoalPoint = pwd;
                    }
                }
            }

            while (!openHeap.IsEmpty())
            {
                PointWithCost ptWithCost = openHeap.PopMinimum();

                if (ptWithCost.estimatedTotalCost > bestPathCost)
                {
                    continue;
                }

                Vector3 worldPoint = HexPoint3ToWorldPoint(ptWithCost.point, hexGrid);

                if (actorAware && CheckForOccupiedPoint(actors, worldPoint))
                {
                    continue;
                }

                if (startedInEncounterBounds && (!boundaryChunk.IsInEncounterBounds(worldPoint)))
                {
                    continue;
                }

                for (int direction = 0; direction < 6; ++direction)
                {
                    HexPoint3 neighborHexPoint   = ptWithCost.point.Step(direction, 1);
                    Vector3   neighborWorldPoint = HexPoint3ToWorldPoint(neighborHexPoint, hexGrid);

                    if ((!mapMetaData.IsWithinBounds(neighborWorldPoint)) ||
                        (unit.Pathing.CurrentGrid.FindBlockerReciprocal(worldPoint, neighborWorldPoint)))
                    {
                        continue;
                    }

                    Debug.DrawLine(worldPoint, neighborWorldPoint, Color.yellow, 15.0f);

                    float linkCost = unit.Pathing.CurrentGrid.GetTerrainModifiedCost(worldPoint, neighborWorldPoint);
                    float newCost  = ptWithCost.cost + linkCost;

                    if (newCost >= bestPathCost)
                    {
                        continue;
                    }

                    if ((!bestCostDict.ContainsKey(neighborHexPoint)) ||
                        (newCost < bestCostDict[neighborHexPoint]))
                    {
                        bestCostDict[neighborHexPoint]  = newCost;
                        bestPrevPoint[neighborHexPoint] = ptWithCost;

                        if ((neighborHexPoint.Equals(snappedGoalPoint)) ||
                            ((neighborWorldPoint - worldGoalPoint).magnitude < targetRadius))
                        {
                            if (newCost < bestPathCost)
                            {
                                anyPathFound  = true;
                                bestPathCost  = newCost;
                                bestGoalPoint = new PointWithCost(neighborHexPoint, newCost, 0.0f);
                            }
                        }
                        else
                        {
                            Vector3 remainingDistance = (worldGoalPoint - neighborWorldPoint);
                            float   estRemainingCost  = remainingDistance.magnitude;

                            openHeap.Push(new PointWithCost(neighborHexPoint, newCost, newCost + estRemainingCost));
                        }
                    }
                }
            }

            if (anyPathFound)
            {
                PointWithCost p = bestGoalPoint;
                path.Add(p);
                while (bestPrevPoint.ContainsKey(p.point))
                {
                    PointWithCost prevPoint = bestPrevPoint[p.point];
                    if ((prevPoint == null) || (path.Contains(prevPoint)))
                    {
                        break;
                    }
                    path.Insert(0, prevPoint);
                    p = prevPoint;
                }
            }
            else
            {
                // draw the failed path data
                const int   SIDES  = 3;
                const float RADIUS = 12;

                foreach (PointWithCost startPoint in startPointList)
                {
                    Vector3 worldStartPoint = HexPoint3ToWorldPoint(startPoint.point, hexGrid);
                    for (int i = 0; i < SIDES; ++i)
                    {
                        float dx0 = RADIUS * Mathf.Cos(i * Mathf.PI * 2 / SIDES);
                        float dz0 = RADIUS * Mathf.Sin(i * Mathf.PI * 2 / SIDES);
                        float dx1 = RADIUS * Mathf.Cos((i + 1) * Mathf.PI * 2 / SIDES);
                        float dz1 = RADIUS * Mathf.Sin((i + 1) * Mathf.PI * 2 / SIDES);

                        Vector3 wp0 = new Vector3(worldStartPoint.x + dx0, 0, worldStartPoint.z + dz0);
                        Vector3 wp1 = new Vector3(worldStartPoint.x + dx1, 0, worldStartPoint.z + dz1);
                        Debug.DrawLine(wp0, wp1, Color.magenta, 15.0f);
                    }
                }

                Vector3 worldEndPoint = HexPoint3ToWorldPoint(snappedGoalPoint, hexGrid);
                Color   orangeColor   = new Color(1.0f, 0.5f, 0.0f);
                for (int i = 0; i < SIDES; ++i)
                {
                    float dx0 = RADIUS * Mathf.Cos(i * Mathf.PI * 2 / SIDES);
                    float dz0 = RADIUS * Mathf.Sin(i * Mathf.PI * 2 / SIDES);
                    float dx1 = RADIUS * Mathf.Cos((i + 1) * Mathf.PI * 2 / SIDES);
                    float dz1 = RADIUS * Mathf.Sin((i + 1) * Mathf.PI * 2 / SIDES);

                    Vector3 wp0 = new Vector3(worldEndPoint.x + dx0, 0, worldEndPoint.z + dz0);
                    Vector3 wp1 = new Vector3(worldEndPoint.x + dx1, 0, worldEndPoint.z + dz1);
                    Debug.DrawLine(wp0, wp1, orangeColor, 15.0f);
                }
            }

            int removedCount = 0;

            // Now, check to see if the end of the path is in "danger". If it is, prune until it's not, which might lead to an empty path.
            while (path.Count > 0)
            {
                PointWithCost      lastHexPoint   = path[path.Count - 1];
                Vector3            lastWorldPoint = HexPoint3ToWorldPoint(lastHexPoint.point, hexGrid);
                MapTerrainDataCell dataCell       = unit.Combat.MapMetaData.GetCellAt(lastWorldPoint);

                if (SplatMapInfo.IsDropshipLandingZone(dataCell.terrainMask) || SplatMapInfo.IsDangerousLocation(dataCell.terrainMask) || SplatMapInfo.IsDropPodLandingZone(dataCell.terrainMask))
                {
                    path.RemoveAt(path.Count - 1);
                    ++removedCount;
                }
                else
                {
                    break;
                }
            }

            if (removedCount > 0)
            {
                if (path.Count == 0)
                {
                    BehaviorNode.LogAI(unit, string.Format("DANGER TRIM: removed all {0} points, bracing", removedCount));
                }
                else
                {
                    BehaviorNode.LogAI(unit, string.Format("DANGER TRIM: removed {0} points, moving to {1}", removedCount, path[path.Count - 1]));
                }
            }

            return(path);
        }
        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;
        }
Esempio n. 14
0
 public DistCell(float dist, MapTerrainDataCell cell)
 {
     this.dist = dist;
     this.cell = cell;
 }
Esempio n. 15
0
        public bool IsSpawnValid(Vector3 position, Vector3 validityPosition, UnitType type, string identifier)
        {
            CombatGameState    combatState        = UnityGameInstance.BattleTechGame.Combat;
            EncounterLayerData encounterLayerData = MissionControl.Instance.EncounterLayerData;
            MapTerrainDataCell cellData           = combatState.MapMetaData.GetCellAt(position);

            Main.LogDebug($"");
            Main.LogDebug($"-------- [PathFinderManager.IsSpawnValid] [{identifier}] --------");

            if (cellData.cachedSteepness > MAX_SLOPE_FOR_PATHFINDING)
            {
                Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Spawn point of '{cellData.cachedSteepness}' is too steep (> {MAX_SLOPE_FOR_PATHFINDING}). Not a valid spawn");
                return(false);
            }

            if (IsCellImpassableOrDeepWater(cellData))
            {
                return(false);
            }
            if (!encounterLayerData.IsInEncounterBounds(position))
            {
                return(false);
            }
            if (cellData.cachedHeight > (cellData.terrainHeight + 50f))
            {
                return(false);
            }

            float         pathFindingZoneRadius = 25f;
            AbstractActor pathfindingActor      = GetPathFindingActor(type);

            SetupPathfindingActor(position, pathfindingActor);

            try {
                PathNodeGrid pathfinderPathGrid = pathfindingActor.Pathing.CurrentGrid;
                PathNode     positionPathNode   = pathfinderPathGrid.GetValidPathNodeAt(position, pathfindingActor.Pathing.MaxCost);
                if (positionPathNode == null)
                {
                    Reset();
                    return(false);
                }

                DynamicLongRangePathfinder.PointWithCost pointWithCost = new DynamicLongRangePathfinder.PointWithCost(combatState.HexGrid.GetClosestHexPoint3OnGrid(positionPathNode.Position), 0, (validityPosition - positionPathNode.Position).magnitude)
                {
                    pathNode = positionPathNode
                };
                List <Vector3> path = DynamicLongRangePathfinder.GetDynamicPathToDestination(new List <DynamicLongRangePathfinder.PointWithCost>()
                {
                    pointWithCost
                }, validityPosition, float.MaxValue, pathfindingActor, false, new List <AbstractActor>(), pathfindingActor.Pathing.CurrentGrid, pathFindingZoneRadius);

                // List<Vector3> path = DynamicLongRangePathfinder.GetPathToDestination(position, float.MaxValue, pathfindingActor, true, pathFindingZoneRadius);
                // List<Vector3> path = DynamicLongRangePathfinder.GetDynamicPathToDestination(position, float.MaxValue, pathfindingActor, true, new List<AbstractActor>(), pathfindingActor.Pathing.CurrentGrid, pathFindingZoneRadius);

                Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Path count is: '{path.Count}', Current position is: '{position}'");

                // GUARD: Against deep water and other impassables that have slipped through
                if (HasPathImpassableOrDeepWaterTiles(combatState, path))
                {
                    return(false);
                }

                if (path != null && path.Count > 1 && (path[path.Count - 1].DistanceFlat(validityPosition) <= pathFindingZoneRadius))
                {
                    Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Path count is: '{path.Count}', Current position is: '{position}'");
                    Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Last point is '{path[path.Count - 1]}', Validity position is '{validityPosition}'");
                    Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Distance from last path to valdity position is: '{(path[path.Count - 1].DistanceFlat(validityPosition))}' and is it within zone radius? '{(path[path.Count - 1].DistanceFlat(validityPosition) <= pathFindingZoneRadius)}'");
                    Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Has valid long range path finding");
                    if (HasValidNeighbours(positionPathNode, validityPosition, type))
                    {
                        Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Has at least two valid neighbours");

                        if (HasValidLocalPathfinding(positionPathNode, validityPosition, type))
                        {
                            Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Has a valid path");
                            Reset();
                            Main.LogDebug($"-------- END [PathFinderManager.IsSpawnValid] [{identifier}] END --------");
                            Main.LogDebug($"");
                            return(true);
                        }
                        else
                        {
                            Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Does NOT have a valid path");
                        }
                    }
                    else
                    {
                        Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Does not have two valid neighbours");
                    }
                }

                /* // Failed attempt to improve spawn checks
                 * List<Vector3> path = DynamicLongRangePathfinder.GetDynamicPathToDestination(validityPosition, float.MaxValue, pathfindingActor, true, new List<AbstractActor>(), pathfindingActor.Pathing.CurrentGrid, pathFindingZoneRadius);
                 * if (path != null && (path[path.Count - 1].DistanceFlat(validityPosition) <= pathFindingZoneRadius)) {
                 * if (path.Count > 4) { // very strong pathfinding location
                 *  return true;
                 * } else {
                 *  Main.Logger.Log($"[PathFinderManager] Spawn point is valid due to proximity but is not strong enough success for pathing. Attempting to confirm.");
                 *  CombatGameState combatState = UnityGameInstance.BattleTechGame.Combat;
                 *  List<Vector3> pointsAroundPosition = combatState.HexGrid.GetGridPointsAroundPointWithinRadius(position, 3, 5);
                 *
                 *  foreach (Vector3 point in pointsAroundPosition) {
                 *    List<Vector3> secondaryPath = DynamicLongRangePathfinder.GetDynamicPathToDestination(point, float.MaxValue, pathfindingActor, true, new List<AbstractActor>(), pathfindingActor.Pathing.CurrentGrid, 2);
                 *    if (path != null && path.Count > 2) {
                 *      Main.Logger.Log($"[PathFinderManager] Spawn point is valid. It is close to the validation point but can be moved away from. Success.");
                 *      return true;
                 *    }
                 *  }
                 * }
                 * }
                 */
            } catch (Exception e) {
                // TODO: Sometimes this gets triggered in very large amounts. It's usually because the SpawnLogic.GetClosestValidPathFindingHex is increasing
                // the radius larger and larger and the checks keep going off the map
                // I need a way to hard abort out of this and either use the original origin of the focus or trigger the rule logic again (random, around a position etc)
                Main.LogDebug($"[PathFinderManager.IsSpawnValid] [{identifier}] Array out of bounds detected in the path finding code. Flagging as invalid spawn. Select a new spawn point. {e.Message}, {e.StackTrace}");
            }

            Main.LogDebug($"-------- END [PathFinderManager.IsSpawnValid] [{identifier}] END --------");
            Main.LogDebug($"");
            Reset();
            return(false);
        }