Beispiel #1
0
        public override void Run(RunPayload payload)
        {
            Main.Logger.Log($"[AddExtraLanceSpawnPoints] Adding lance spawn points to match contract override data");
            Contract           contract           = MissionControl.Instance.CurrentContract;
            EncounterLayerData encounterLayerData = MissionControl.Instance.EncounterLayerData;
            ContractOverride   contractOverride   = contract.Override;

            lanceSpawners = new List <LanceSpawnerGameLogic>(encounterLayerData.gameObject.GetComponentsInChildren <LanceSpawnerGameLogic>());

            TeamOverride targetTeamOverride   = contractOverride.targetTeam;
            TeamOverride employerTeamOverride = contractOverride.employerTeam;

            IncreaseLanceSpawnPoints(contractOverride, targetTeamOverride);
            IncreaseLanceSpawnPoints(contractOverride, employerTeamOverride);

            state.Set("ExtraLanceSpawnKeys", spawnKeys);
        }
Beispiel #2
0
        public override void Run(RunPayload payload)
        {
            Main.LogDebug("[DialogTrigger] Setting up trigger");
            EncounterLayerData   encounterData   = MissionControl.Instance.EncounterLayerData;
            SmartTriggerResponse triggerDialogue = new SmartTriggerResponse();

            triggerDialogue.inputMessage   = onMessage;
            triggerDialogue.designName     = $"Initiate dialogue on {triggerDialogue}";
            triggerDialogue.conditionalbox = new EncounterConditionalBox(conditional);

            DialogResult dialogueResult = ScriptableObject.CreateInstance <DialogResult>();

            dialogueResult.dialogueRef.EncounterObjectGuid = dialogueGuid;
            dialogueResult.isInterrupt = this.isInterrupt;

            triggerDialogue.resultList.contentsBox.Add(new EncounterResultBox(dialogueResult));
            encounterData.responseGroup.triggerList.Add(triggerDialogue);
        }
        public override void Run(RunPayload payload)
        {
            Main.LogDebug("[ShowObjectiveTrigger] Setting up trigger");
            EncounterLayerData   encounterData = MissionControl.Instance.EncounterLayerData;
            SmartTriggerResponse trigger       = new SmartTriggerResponse();

            trigger.inputMessage   = onMessage;
            trigger.designName     = $"Show objective on {onMessage}";
            trigger.conditionalbox = new EncounterConditionalBox(conditional);

            ShowObjectiveResult showObjectiveResult = ScriptableObject.CreateInstance <ShowObjectiveResult>();

            showObjectiveResult.ObjectiveGuid       = this.objectiveGuid;
            showObjectiveResult.IsContractObjective = this.isContractObjective;

            trigger.resultList.contentsBox.Add(new EncounterResultBox(showObjectiveResult));
            encounterData.responseGroup.triggerList.Add(trigger);
        }
        public override void Run(RunPayload payload)
        {
            Main.Logger.Log($"[AddCustomPlayerLanceSpawnChunk] Adding encounter structure");
            EncounterLayerData        encounterLayerData = MissionControl.Instance.EncounterLayerData;
            EmptyCustomChunkGameLogic emptyCustomChunk   = ChunkFactory.CreateEmptyCustomChunk("Chunk_Lance");

            emptyCustomChunk.encounterObjectGuid = System.Guid.NewGuid().ToString();
            emptyCustomChunk.notes = debugDescription;

            // Auto select Player or Employer based on the lance configurator
            if (teamGuid == null)
            {
                SpawnableUnit[] lanceUnits = MissionControl.Instance.CurrentContract.Lances.GetLanceUnits(EncounterRules.PLAYER_TEAM_ID);
                if (lanceUnits.Length > 4)
                {
                    teamGuid = EncounterRules.PLAYER_TEAM_ID;
                }
                else
                {
                    teamGuid = EncounterRules.EMPLOYER_TEAM_ID;
                }
            }

            // Guard: Don't do anything if there are no employer units and employer mode is on
            SpawnableUnit[] employerLanceUnits = MissionControl.Instance.CurrentContract.Lances.GetLanceUnits(EncounterRules.EMPLOYER_TEAM_ID);
            Main.Logger.Log($"[AddCustomPlayerLanceExtraSpawnPoints] '{employerLanceUnits.Length}' employer lance units are being sent to Mission Control by Bigger Drops.");
            if (employerLanceUnits.Length <= 0)
            {
                return;
            }

            bool spawnOnActivation = true;
            CustomPlayerLanceSpawnerGameLogic lanceSpawner = LanceSpawnerFactory.CreateCustomPlayerLanceSpawner(
                emptyCustomChunk.gameObject,
                spawnerName,
                lanceGuid,
                teamGuid,
                spawnOnActivation,
                SpawnUnitMethodType.ViaLeopardDropship,
                unitGuids
                );

            lanceSpawner.transform.position = Vector3.zero;
        }
        public void InitSceneData()
        {
            CombatGameState combat = UnityGameInstance.BattleTechGame.Combat;

            if (!EncounterLayerParentGameObject)
            {
                EncounterLayerParentGameObject = GameObject.Find("EncounterLayerParent");
            }
            EncounterLayerParent = EncounterLayerParentGameObject.GetComponent <EncounterLayerParent>();

            EncounterLayerData       = GetActiveEncounter();
            EncounterLayerGameObject = EncounterLayerData.gameObject;
            EncounterLayerData.CalculateEncounterBoundary();

            if (HexGrid == null)
            {
                HexGrid = ReflectionHelper.GetPrivateStaticField(typeof(WorldPointGameLogic), "_hexGrid") as HexGrid;
            }
        }
Beispiel #6
0
        protected List <GameObject> GetInvalidLanceMemberSpawns(GameObject lance, Vector3 checkTarget)
        {
            CombatGameState    combatState        = UnityGameInstance.BattleTechGame.Combat;
            EncounterLayerData encounterLayerData = MissionControl.Instance.EncounterLayerData;

            List <GameObject> invalidLanceSpawns  = new List <GameObject>();
            List <GameObject> spawnPoints         = lance.FindAllContains("SpawnPoint");
            Vector3           checkTargetPosition = checkTarget.GetClosestHexLerpedPointOnGrid();

            foreach (GameObject spawnPoint in spawnPoints)
            {
                // Vector3 spawnPointPosition = spawnPoint.transform.position.GetClosestHexLerpedPointOnGrid();
                Vector3 spawnPointPosition = GetClosestValidPathFindingHex(spawnPoint, spawnPoint.transform.position, $"GetInvalidLanceMemberSpawns.{spawnPoint.name}", IsLancePlayerLance(lance.name) ? checkTargetPosition : Vector3.zero, 2);
                Main.LogDebug($"[SpawnLanceLogic.GetInvalidLanceMemberSpawns] Spawn point's closest hex lerped point on grid for '{spawnPoint.name}' is '{spawnPointPosition}'");

                if (!encounterLayerData.IsInEncounterBounds(spawnPointPosition))
                {
                    Main.LogDebugWarning("[SpawnLanceLogic.GetInvalidLanceMemberSpawns] Lance member spawn is outside of the boundary. Select a new lance spawn point.");
                    invalidLanceSpawns.Add(spawnPoint);
                    continue;
                }

                // Ensure the lance member's spawn's closest valid point isn't on another spawn point's closest valid point
                if (IsPointTooCloseToOtherPointsClosestPointOnGrid(spawnPointPosition, spawnPoints.Where(sp => spawnPoint.name != sp.name).ToList()))
                {
                    Main.LogDebugWarning("[SpawnLanceLogic.GetInvalidLanceMemberSpawns] Lance member spawn is too close to the other spawns when snapped to the grid");
                    invalidLanceSpawns.Add(spawnPoint);
                    continue;
                }

                if (!PathFinderManager.Instance.IsSpawnValid(spawnPoint, spawnPointPosition, checkTargetPosition, UnitType.Mech, spawnPoint.name))
                {
                    Main.LogDebugWarning($"[SpawnLanceLogic.GetInvalidLanceMemberSpawns] Lance member spawn '{spawnPoint.name}' path to check target '{checkTarget}' is blocked. Select a new lance spawn point");
                    invalidLanceSpawns.Add(spawnPoint);
                    continue;
                }

                spawnPoint.transform.position = spawnPointPosition;
            }

            return(invalidLanceSpawns);
        }
Beispiel #7
0
        public static EncounterLayerData CreateEncounterLayer(Contract contract)
        {
            EncounterLayer_MDD encounterLayerMDD = MissionControl.Instance.EncounterLayerMDD;

            GameObject         encounterLayerGo = new GameObject(encounterLayerMDD.Name);
            EncounterLayerData encounterLayer   = encounterLayerGo.AddComponent <EncounterLayerData>();

            encounterLayer.encounterObjectGuid  = contract.encounterObjectGuid;
            encounterLayer.encounterName        = MissionControl.Instance.CurrentContractTypeValue.Name;
            encounterLayer.encounterDescription = MissionControl.Instance.EncounterLayerMDD.Description;
            encounterLayer.EDITOR_SetSupportedContractTypeID(MissionControl.Instance.CurrentContractTypeValue.Name);
            encounterLayer.version = MissionControl.Instance.CurrentContractTypeValue.Version;

            // Need to dump the serialised binary data into a mock EncounterLayerData then throw it away to get the important bits
            GameObject         mockGo    = new GameObject("MockGo");
            EncounterLayerData mockLayer = mockGo.AddComponent <EncounterLayerData>();
            List <EncounterLayerIdentifier> layerIdList = UnityGameInstance.BattleTechGame.Combat.MapMetaData.encounterLayerIdentifierList;

            mockLayer.LoadMapData(layerIdList[0], UnityGameInstance.BattleTechGame.DataManager);
            encounterLayer.mapEncounterLayerDataCells = mockLayer.mapEncounterLayerDataCells;
            encounterLayer.inclineMeshData            = mockLayer.inclineMeshData;
            MonoBehaviour.Destroy(mockLayer);

            // Clear out all old regions from copied encounter layer data
            for (int j = 0; j < encounterLayer.mapEncounterLayerDataCells.GetLength(1); j++)
            {
                for (int k = 0; k < encounterLayer.mapEncounterLayerDataCells.GetLength(0); k++)
                {
                    List <string> regionGuidList = encounterLayer.mapEncounterLayerDataCells[j, k].regionGuidList;
                    if (regionGuidList != null)
                    {
                        regionGuidList.Clear();
                    }
                }
            }

            CreateContractObjectives(encounterLayerGo);

            encounterLayerGo.AddComponent <PlotOverride>();

            return(encounterLayer);
        }
Beispiel #8
0
    public static MapEncounterLayerDataCell[,] GetMapEncounterLayerDataCells(this EncounterLayerData layerData)
    {
        if (layerData.mapEncounterLayerDataCells != null)
        {
            return(layerData.mapEncounterLayerDataCells);
        }

        Transform parent = layerData.gameObject.transform.parent;

        foreach (Transform t in parent)
        {
            EncounterLayerData siblingEncounterLayer = t.GetComponent <EncounterLayerData>();
            if (siblingEncounterLayer.mapEncounterLayerDataCells != null)
            {
                return(siblingEncounterLayer.mapEncounterLayerDataCells);
            }
        }

        return(null);
    }
Beispiel #9
0
        public override void Run(RunPayload payload)
        {
            Main.Logger.Log($"[AddObjectiveToAutocompleteTrigger] Adding objective '{objectiveId}' to AutocompleteGameLogic");
            EncounterLayerData    encounterLayerData    = MissionControl.Instance.EncounterLayerData;
            AutoCompleteGameLogic autoCompleteGameLogic = encounterLayerData.GetComponent <AutoCompleteGameLogic>();

            if (autoCompleteGameLogic != null)
            {
                TriggeringObjectiveStatus triggeringObjectiveStatus = new TriggeringObjectiveStatus();
                ObjectiveRef objectiveRef = new ObjectiveRef();
                objectiveRef.EncounterObjectGuid          = objectiveId;
                triggeringObjectiveStatus.objective       = objectiveRef;
                triggeringObjectiveStatus.objectiveStatus = ObjectiveStatusEvaluationType.Complete;

                autoCompleteGameLogic.triggeringObjectiveList.Add(triggeringObjectiveStatus);
            }
            else
            {
                Main.LogDebugWarning($"[AddObjectiveToAutocompleteTrigger] Contract type '{MissionControl.Instance.CurrentContractType}' has no Autocomplete logic. Unable to add objective '{objectiveId}' to triggers");
            }
        }
Beispiel #10
0
    public static List <MapEncounterLayerDataCell> GetMapEncounterLayerDataCellsWithinCollider(GameObject regionGo)
    {
        MeshCollider    collider               = regionGo.GetComponent <MeshCollider>();
        RegionGameLogic regionGameLogic        = regionGo.GetComponent <RegionGameLogic>();
        List <MapEncounterLayerDataCell> cells = new List <MapEncounterLayerDataCell>();
        Vector3 colliderExtents = collider.bounds.extents;
        Vector3 colliderCenter  = collider.bounds.center;

        EncounterLayerData encounterLayerData = MissionControl.MissionControl.Instance.EncounterLayerData;
        int cellX = encounterLayerData.GetXIndex(colliderCenter.x);
        int cellZ = encounterLayerData.GetZIndex(colliderCenter.z);

        // Add center
        MapEncounterLayerDataCell layerDataCell = GetOrCreateEncounterLayerDataCell(cellX, cellZ);

        cells.Add(layerDataCell);

        float bottom = colliderCenter.x - colliderExtents.x;
        float top    = colliderCenter.x + colliderExtents.x;
        float left   = colliderCenter.z - colliderExtents.z;
        float right  = colliderCenter.z + colliderExtents.z;

        for (float i = bottom; i < top; i += 0.5f)
        {
            for (float j = left; j < right; j += 0.5f)
            {
                cellX = encounterLayerData.GetXIndex(i);
                cellZ = encounterLayerData.GetZIndex(j);

                layerDataCell = GetOrCreateEncounterLayerDataCell(cellX, cellZ);

                if (layerDataCell != null)
                {
                    cells.Add(layerDataCell);
                }
            }
        }

        return(cells);
    }
Beispiel #11
0
        public override void Run(RunPayload payload)
        {
            Main.Logger.Log($"[AddLanceSpawnChunk] Adding encounter structure");
            EncounterLayerData        encounterLayerData = MissionControl.Instance.EncounterLayerData;
            EmptyCustomChunkGameLogic emptyCustomChunk   = ChunkFactory.CreateEmptyCustomChunk("Chunk_Lance");

            emptyCustomChunk.encounterObjectGuid = System.Guid.NewGuid().ToString();
            emptyCustomChunk.notes = debugDescription;

            bool spawnOnActivation             = true;
            LanceSpawnerGameLogic lanceSpawner = LanceSpawnerFactory.CreateLanceSpawner(
                emptyCustomChunk.gameObject,
                spawnerName,
                lanceGuid,
                teamGuid,
                spawnOnActivation,
                SpawnUnitMethodType.InstantlyAtSpawnPoint,
                unitGuids
                );

            lanceSpawner.transform.position = Vector3.zero;
        }
Beispiel #12
0
        public override void Run(RunPayload payload)
        {
            Main.LogDebug("[IssueFollowLanceOrderTrigger] Setting up trigger");
            EncounterLayerData   encounterData             = MissionControl.Instance.EncounterLayerData;
            SmartTriggerResponse onEncounterLoadIssueOrder = new SmartTriggerResponse();

            onEncounterLoadIssueOrder.inputMessage   = MessageCenterMessageType.OnEncounterBegin;
            onEncounterLoadIssueOrder.designName     = "Issue Follow Lance AI order on Encounter Start";
            onEncounterLoadIssueOrder.conditionalbox = new EncounterConditionalBox(ScriptableObject.CreateInstance <AlwaysTrueConditional>());

            FollowLanceOrder followOrder = new FollowLanceOrder();

            followOrder.TargetEncounterTags.AddRange(this.targetTags);

            IssueCustomAIOrderResult issueOrder = ScriptableObject.CreateInstance <IssueCustomAIOrderResult>();

            issueOrder.issueAIOrderTo = this.receiverType;
            issueOrder.requiredReceiverTags.AddRange(this.receiverTags);
            issueOrder.aiOrder = followOrder;

            onEncounterLoadIssueOrder.resultList.contentsBox.Add(new EncounterResultBox(issueOrder));
            encounterData.responseGroup.triggerList.Add(onEncounterLoadIssueOrder);
        }
Beispiel #13
0
        public override void Run(RunPayload payload)
        {
            Main.Logger.Log($"[AddExtraLanceSpawnPoints] Adding lance spawn points to match contract override data");
            Contract           contract           = MissionControl.Instance.CurrentContract;
            EncounterLayerData encounterLayerData = MissionControl.Instance.EncounterLayerData;
            ContractOverride   contractOverride   = contract.Override;

            lancesToSkip  = (List <string>)state.GetObject("LancesToSkip");
            lanceSpawners = new List <LanceSpawnerGameLogic>(encounterLayerData.gameObject.GetComponentsInChildren <LanceSpawnerGameLogic>());

            TeamOverride targetTeamOverride   = contractOverride.targetTeam;
            TeamOverride employerTeamOverride = contractOverride.employerTeam;

            if (Main.Settings.ActiveContractSettings.Has(ContractSettingsOverrides.ExtendedLances_EnemyLanceSizeOverride))
            {
                int lanceSizeOverride = Main.Settings.ActiveContractSettings.GetInt(ContractSettingsOverrides.ExtendedLances_EnemyLanceSizeOverride);
                Main.Logger.Log($"[AddExtraLanceSpawnPoints] Using contract-specific settings override for contract '{MissionControl.Instance.CurrentContract.Name}'. Enemy lance size will be '{lanceSizeOverride}'.");
                IncreaseLanceSpawnPoints(contractOverride, targetTeamOverride, lanceSizeOverride);
            }
            else
            {
                IncreaseLanceSpawnPoints(contractOverride, targetTeamOverride);
            }

            if (Main.Settings.ActiveContractSettings.Has(ContractSettingsOverrides.ExtendedLances_AllyLanceSizeOverride))
            {
                int lanceSizeOverride = Main.Settings.ActiveContractSettings.GetInt(ContractSettingsOverrides.ExtendedLances_AllyLanceSizeOverride);
                Main.Logger.Log($"[AddExtraLanceSpawnPoints] Using contract-specific settings override for contract '{MissionControl.Instance.CurrentContract.Name}'. Ally lance size will be '{lanceSizeOverride}'.");
                IncreaseLanceSpawnPoints(contractOverride, employerTeamOverride, lanceSizeOverride);
            }
            else
            {
                IncreaseLanceSpawnPoints(contractOverride, employerTeamOverride);
            }

            state.Set("ExtraLanceSpawnKeys", spawnKeys);
        }
Beispiel #14
0
        private GameObject CheckAllPlotsForValidPlot(GameObject plotsParent, Vector3 origin, string name)
        {
            GameObject closestPlot     = null;
            float      closestDistance = -1;

            foreach (Transform t in plotsParent.transform)
            {
                Vector3 plotPosition = t.position;
                if (EncounterLayerData.IsInEncounterBounds(plotPosition))
                {
                    if (IsPlotValidForEncounter(t, name))
                    {
                        float distance = Vector3.Distance(t.position, origin);
                        if (closestDistance == -1 || closestDistance < distance)
                        {
                            closestDistance = distance;
                            closestPlot     = t.gameObject;
                        }
                    }
                }
            }

            return(closestPlot);
        }
        public static void SpawnAmbush(Vector3 ambushOrigin, AmbushType ambushType)
        {
            // Determine how many units we're spawning
            int minSpawns = 0, maxSpawns = 0;

            if (ambushType == AmbushType.BattleArmor)
            {
                if (!Mod.Config.BattleArmorAmbush.Enabled)
                {
                    return;
                }
                minSpawns = ModState.BattleArmorAmbushDefForContract.MinSpawns;
                maxSpawns = ModState.BattleArmorAmbushDefForContract.MaxSpawns;
            }
            else if (ambushType == AmbushType.Mech)
            {
                if (!Mod.Config.MechAmbush.Enabled)
                {
                    return;
                }
                minSpawns = ModState.MechAmbushDefForContract.MinSpawns;
                maxSpawns = ModState.MechAmbushDefForContract.MaxSpawns;
            }
            else if (ambushType == AmbushType.Vehicle)
            {
                if (!Mod.Config.VehicleAmbush.Enabled)
                {
                    return;
                }
                minSpawns = ModState.VehicleAmbushDefForContract.MinSpawns;
                maxSpawns = ModState.VehicleAmbushDefForContract.MaxSpawns;
            }

            int actorsToSpawn = Mod.Random.Next(minSpawns, maxSpawns);

            Mod.Log.Debug?.Write($"Spawning {actorsToSpawn} actors as part of this ambush.");

            // Starting with the closest building, look through the buildings and determine how many locations can support a unit.
            List <BattleTech.Building> candidates = CandidateBuildingsHelper.ClosestCandidatesToPosition(ambushOrigin, Mod.Config.Ambush.SearchRadius);

            if (candidates.Count < minSpawns)
            {
                Mod.Log.Debug?.Write($"Insufficient candidate buildings for a spawn ambush. Skipping.");
                return;
            }

            // Create a new lance in the target team
            Lance ambushLance = TeamHelper.CreateAmbushLance(ModState.AmbushTeam);

            ModState.CurrentSpawningLance = ambushLance;

            EncounterLayerData         encounterLayerData = ModState.Combat.EncounterLayerData;
            List <BattleTech.Building> buildingsToLevel   = new List <BattleTech.Building>();
            List <AbstractActor>       spawnedActors      = new List <AbstractActor>();

            foreach (BattleTech.Building building in candidates)
            {
                if (actorsToSpawn == 0)
                {
                    break;                     // nothing more to do, end processing.
                }
                // Spawn one unit at the origin of the building
                buildingsToLevel.Add(building);
                Mod.Log.Debug?.Write("Spawning actor at building origin.");

                AbstractActor spawnedActor = null;
                if (ambushType == AmbushType.BattleArmor)
                {
                    spawnedActor = SpawnAmbushMech(ModState.AmbushTeam, ambushLance, ambushOrigin, building.CurrentPosition, building.CurrentRotation, ModState.BattleArmorAmbushDefForContract.SpawnPool);
                }
                else if (ambushType == AmbushType.Mech)
                {
                    spawnedActor = SpawnAmbushMech(ModState.AmbushTeam, ambushLance, ambushOrigin, building.CurrentPosition, building.CurrentRotation, ModState.MechAmbushDefForContract.SpawnPool);
                }
                else if (ambushType == AmbushType.Vehicle)
                {
                    spawnedActor = SpawnAmbushVehicle(ModState.AmbushTeam, ambushLance, ambushOrigin, building.CurrentPosition, building.CurrentRotation);
                }

                spawnedActors.Add(spawnedActor);
                actorsToSpawn--;

                // Iterate through adjacent hexes to see if we can spawn more units in the building
                List <Vector3> adjacentHexes = ModState.Combat.HexGrid.GetGridPointsAroundPointWithinRadius(ambushOrigin, 3); // 3 hexes should cover most large buidlings
                foreach (Vector3 adjacentHex in adjacentHexes)
                {
                    if (actorsToSpawn == 0)
                    {
                        break;
                    }

                    Point cellPoint = new Point(ModState.Combat.MapMetaData.GetXIndex(adjacentHex.x), ModState.Combat.MapMetaData.GetZIndex(adjacentHex.z));
                    Mod.Log.Debug?.Write($" Evaluating point {cellPoint.X}, {cellPoint.Z} for potential ambush.");
                    if (encounterLayerData.mapEncounterLayerDataCells[cellPoint.Z, cellPoint.X].HasSpecifiedBuilding(building.GUID))
                    {
                        Mod.Log.Debug?.Write($"Spawning actor at adjacent hex at position: {adjacentHex}");
                        AbstractActor additionalSpawn = null;
                        if (ambushType == AmbushType.BattleArmor)
                        {
                            additionalSpawn = SpawnAmbushMech(ModState.AmbushTeam, ambushLance, ambushOrigin, adjacentHex, building.CurrentRotation, ModState.BattleArmorAmbushDefForContract.SpawnPool);
                        }
                        else if (ambushType == AmbushType.Mech)
                        {
                            additionalSpawn = SpawnAmbushMech(ModState.AmbushTeam, ambushLance, ambushOrigin, adjacentHex, building.CurrentRotation, ModState.MechAmbushDefForContract.SpawnPool);
                        }
                        else if (ambushType == AmbushType.Vehicle)
                        {
                            additionalSpawn = SpawnAmbushVehicle(ModState.AmbushTeam, ambushLance, ambushOrigin, adjacentHex, building.CurrentRotation);
                        }

                        spawnedActors.Add(additionalSpawn);
                        actorsToSpawn--;
                    }
                    else
                    {
                        Mod.Log.Debug?.Write($" Hex {adjacentHex} is outside of the main building, skipping.");
                    }
                }
            }

            // Remove any buildings that are part of this ambush from candidates
            ModState.CandidateBuildings.RemoveAll(x => buildingsToLevel.Contains(x));

            // Determine the targets that should be prioritized by the enemies
            List <ICombatant> targets = new List <ICombatant>();

            foreach (ICombatant combatant in ModState.Combat.GetAllCombatants())
            {
                if (!combatant.IsDead && !combatant.IsFlaggedForDeath &&
                    combatant.team != null &&
                    ModState.Combat.HostilityMatrix.IsLocalPlayerFriendly(combatant.team))
                {
                    if (Vector3.Distance(ambushOrigin, combatant.CurrentPosition) <= Mod.Config.Ambush.SearchRadius)
                    {
                        targets.Add(combatant);
                    }
                }
            }

            bool applyAttacks = false;

            if (ambushType == AmbushType.BattleArmor && Mod.Config.BattleArmorAmbush.FreeAttackEnabled)
            {
                applyAttacks = true;
            }
            if (ambushType == AmbushType.Mech && Mod.Config.MechAmbush.FreeAttackEnabled)
            {
                applyAttacks = true;
            }
            if (ambushType == AmbushType.Vehicle && Mod.Config.VehicleAmbush.FreeAttackEnabled)
            {
                applyAttacks = true;
            }

            Mod.Log.Info?.Write($"Adding SpawnAmbushSequence for {spawnedActors.Count} actors and {buildingsToLevel.Count} buildings to be leveled.");
            try
            {
                SpawnAmbushSequence ambushSequence = new SpawnAmbushSequence(ModState.Combat, ambushOrigin, spawnedActors, buildingsToLevel, targets, applyAttacks);
                ModState.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(ambushSequence));
            }
            catch (Exception e)
            {
                Mod.Log.Error?.Write(e, "Failed to create AES sequence due to error!");
            }
        }
        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);
        }
Beispiel #17
0
        private Vector3 PathfindFromPointToSpawn(GameObject originGo, Vector3 origin, int radius, string identifier, Vector3 pathfindingTarget)
        {
            EncounterLayerData encounterLayerData = MissionControl.Instance.EncounterLayerData;
            CombatGameState    combatState        = UnityGameInstance.BattleTechGame.Combat;
            Vector3            originOnGrid       = origin.GetClosestHexLerpedPointOnGrid();

            // TODO: If the SpawnerPlayerLanceGo's closest hex point is in an inaccessible location - this will cause infinite loading issues
            // TODO: Need to find a reliably accessible location (hard to do in a proc-genned setup)
            Vector3 pathfindingPoint = (pathfindingTarget == Vector3.zero) ?
                                       EncounterRules.SpawnerPlayerLanceGo.transform.position.GetClosestHexLerpedPointOnGrid()
      : pathfindingTarget.GetClosestHexLerpedPointOnGrid();

            Main.LogDebug($"[PathfindFromPointToPlayerSpawn] Using pathfinding point '{pathfindingPoint}'");

            if (!PathFinderManager.Instance.IsSpawnValid(originGo, originOnGrid, pathfindingPoint, UnitType.Mech, identifier))
            {
                List <Vector3> adjacentPointsOnGrid = combatState.HexGrid.GetGridPointsAroundPointWithinRadius(originOnGrid, radius);
                Main.LogDebug($"[PathfindFromPointToPlayerSpawn] Adjacent point count is '{adjacentPointsOnGrid.Count}'");

                adjacentPointsOnGrid = adjacentPointsOnGrid.Where(point => {
                    return(!checkedAdjacentPoints.Contains(point) && encounterLayerData.IsInEncounterBounds(point));
                }).ToList();

                Main.LogDebug($"[PathfindFromPointToPlayerSpawn] Removed already checked points & out of bounds points. Adjacent point count is now '{adjacentPointsOnGrid.Count}'");

                adjacentPointsOnGrid.Shuffle();

                int count = 0;
                foreach (Vector3 point in adjacentPointsOnGrid)
                {
                    if (count > ADJACENT_NODE_LIMITED)
                    {
                        Main.LogDebug($"[PathfindFromPointToPlayerSpawn] Adjacent point count limited exceeded (random selection of {ADJACENT_NODE_LIMITED} / {adjacentPointsOnGrid.Count}). Bailing.");
                        break;
                    }

                    Vector3 validPoint = point.GetClosestHexLerpedPointOnGrid();

                    Main.LogDebug($"[PathfindFromPointToPlayerSpawn] Testing an adjacent point of '{validPoint}'");
                    if (PathFinderManager.Instance.IsSpawnValid(originGo, validPoint, pathfindingPoint, UnitType.Mech, identifier))
                    {
                        return(validPoint);
                    }

                    bool isBadPathfindTest = PathFinderManager.Instance.IsProbablyABadPathfindTest(pathfindingPoint);
                    if (isBadPathfindTest)
                    {
                        Main.LogDebug($"[PathfindFromPointToPlayerSpawn] Estimated this is a bad pathfind setup so trying something new.");
                        radius = 100;
                        count  = ADJACENT_NODE_LIMITED;
                    }

                    checkedAdjacentPoints.Add(point);
                    count++;
                }

                return(Vector3.zero);
            }
            else
            {
                Main.LogDebug($"[PathfindFromPointToPlayerSpawn] Spawn has been found valid by pathfinding '{originOnGrid}'");
            }

            return(originOnGrid);
        }
        public static AbstractActor SpawnAmbushTurret(Team team, Lance ambushLance, BattleTech.Building building, Vector3 ambushOrigin)
        {
            // Randomly determine one of the spawnpairs from the current ambushdef
            List <TurretAndPilotDef> shuffledSpawns = new List <TurretAndPilotDef>();

            shuffledSpawns.AddRange(ModState.InfantryAmbushDefForContract.SpawnPool);
            shuffledSpawns.Shuffle();
            TurretAndPilotDef ambushDef = shuffledSpawns[0];

            PilotDef  pilotDef  = ModState.Combat.DataManager.PilotDefs.Get(ambushDef.PilotDefId);
            TurretDef turretDef = ModState.Combat.DataManager.TurretDefs.GetOrCreate(ambushDef.TurretDefId);

            turretDef.Refresh();

            // determine a position somewhere up the building's axis
            EncounterLayerData encounterLayerData = ModState.Combat.EncounterLayerData;
            Point cellPoint = new Point(
                ModState.Combat.MapMetaData.GetXIndex(building.CurrentPosition.x),
                ModState.Combat.MapMetaData.GetZIndex(building.CurrentPosition.z));
            MapEncounterLayerDataCell melDataCell =
                encounterLayerData.mapEncounterLayerDataCells[cellPoint.Z, cellPoint.X];
            float buildingHeight = melDataCell.GetBuildingHeight();

            float terrainHeight = ModState.Combat.MapMetaData.GetLerpedHeightAt(building.CurrentPosition, true);
            float heightDelta   = (buildingHeight - terrainHeight) * 0.7f;
            float adjustedY     = terrainHeight + heightDelta;

            Mod.Log.Debug?.Write($"At building position, terrain height is: {terrainHeight} while buildingHeight is: {buildingHeight}. " +
                                 $" Calculated 70% of building height + terrain as {adjustedY}.");

            Vector3 newPosition = building.GameRep.transform.position;

            newPosition.y = adjustedY;
            Mod.Log.Debug?.Write($"Changing transform position from: {building.GameRep.transform.position} to {newPosition}");

            /// Rotate to face the ambush origin
            Vector3    spawnDirection = Vector3.RotateTowards(building.CurrentRotation.eulerAngles, ambushOrigin, 1f, 0f);
            Quaternion spawnRotation  = Quaternion.LookRotation(spawnDirection);

            // Create the turret
            Turret turret = ActorFactory.CreateTurret(turretDef, pilotDef, team.EncounterTags, ModState.Combat, team.GetNextSupportUnitGuid(), "", null);

            turret.Init(newPosition, spawnRotation.eulerAngles.y, true);
            turret.InitGameRep(null);

            if (turret == null)
            {
                Mod.Log.Error?.Write($"Failed to spawn turretDefId: {ambushDef.TurretDefId} + pilotDefId: {ambushDef.PilotDefId} !");
            }

            Mod.Log.Debug?.Write($" Spawned trap turret, adding to team.");
            team.AddUnit(turret);
            turret.AddToTeam(team);
            turret.AddToLance(ambushLance);

            turret.BehaviorTree = BehaviorTreeFactory.MakeBehaviorTree(ModState.Combat.BattleTechGame, turret, BehaviorTreeIDEnum.CoreAITree);
            Mod.Log.Debug?.Write("Updated turret behaviorTree");

            ModState.AmbushBuildingGUIDToTurrets.Add(building.GUID, turret);
            ModState.AmbushTurretGUIDtoBuilding.Add(turret.GUID, building);

            // Associate the building withe the team
            building.AddToTeam(team);
            building.BuildingRep.IsTargetable = true;
            building.BuildingRep.SetHighlightColor(ModState.Combat, team);
            building.BuildingRep.RefreshEdgeCache();

            // Increase the building's health to the current value + turret structure
            float combinedStructure = (float)Math.Ceiling(building.CurrentStructure + turret.GetCurrentStructure(BuildingLocation.Structure));

            Mod.Log.Debug?.Write($"Setting ambush structure to: {combinedStructure} = building.currentStructure: {building.CurrentStructure} + " +
                                 $"turret.currentStructure: {turret.GetCurrentStructure(BuildingLocation.Structure)}");
            building.StatCollection.Set <float>("Structure", combinedStructure);

            // Finally notify others
            UnitSpawnedMessage message = new UnitSpawnedMessage("CJ_TRAP", turret.GUID);

            ModState.Combat.MessageCenter.PublishMessage(message);

            // Finally force the turret to be fully visible
            turret.OnPlayerVisibilityChanged(VisibilityLevel.LOSFull);

            return(turret);
        }
Beispiel #19
0
 static void Postfix(EncounterLayerData __instance)
 {
     Main.LogDebug($"[EncounterLayerData.Postfix] Running...");
     EncounterDataManager.Instance.HandleCustomContractType();
 }
        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;
        }
        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;
        }
        public bool IsGoodFaithEffort()
        {
            EncounterLayerData encounterLayerData = UnityEngine.Object.FindObjectOfType <EncounterLayerData>();

            return(encounterLayerData.IsGoodFaithEffort);
        }
Beispiel #23
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);
        }
 public static void EncounterLayerData_Constructor(EncounterLayerData __instance)
 {
     eld_cache = eld_cache.Where(c => c != null).ToList();
     eld_cache.Add(__instance);
 }
        private void SetBoundarySizeToCustom(EncounterLayerData encounterLayerData, float size)
        {
            EncounterBoundaryChunkGameLogic encounterBoundaryChunk = encounterLayerData.GetComponentInChildren <EncounterBoundaryChunkGameLogic>();

            if (encounterBoundaryChunk != null)
            {
                Main.Logger.Log($"[MaximiseBoundarySize.SetBoundarySizeToCustom] Increasing Boundary Size by '{size * 100}%'");
                List <RectHolder>          encounterBoundaryRectList         = new List <RectHolder>();
                EncounterObjectGameLogic[] childEncounterObjectGameLogicList = encounterBoundaryChunk.childEncounterObjectGameLogicList;

                float mapBorderSize = 50f;
                float mapSize       = 2048f;
                int   mapSide       = (int)(mapSize - mapBorderSize);

                for (int i = 0; i < childEncounterObjectGameLogicList.Length; i++)
                {
                    EncounterBoundaryRectGameLogic encounterBoundaryRectGameLogic = childEncounterObjectGameLogicList[i] as EncounterBoundaryRectGameLogic;

                    int sizeFactor     = (int)(encounterBoundaryRectGameLogic.width * (1f + size));
                    int movementFactor = (int)(encounterBoundaryRectGameLogic.width * size);

                    if (sizeFactor > mapSide)
                    {
                        Main.Logger.Log($"[MaximiseBoundarySize.SetBoundarySizeToCustom] Custom size would be greater than map size. Using map size.'");
                        MatchBoundarySizeToMapSize(encounterLayerData);
                    }
                    else
                    {
                        if (encounterBoundaryRectGameLogic != null)
                        {
                            Vector3 position = encounterBoundaryRectGameLogic.transform.position;

                            Main.Logger.Log($"[MaximiseBoundarySize.SetBoundarySizeToCustom] Boundary [X,Z] originally was [{position.x}, {position.z}]");

                            float xPosition = 0;
                            if (position.x > 0)
                            {
                                xPosition = position.x - movementFactor; // (movementFactor / 2f);
                                if (xPosition < 0)
                                {
                                    xPosition = 0;
                                }
                            }
                            else if (position.x < 0)
                            {
                                xPosition = position.x + movementFactor; // (movementFactor / 2f);
                                if (xPosition > 0)
                                {
                                    xPosition = 0;
                                }
                            }

                            float zPosition = 0;
                            if (position.z > 0)
                            {
                                zPosition = position.z - movementFactor; // (movementFactor / 2f);
                                if (zPosition < 0)
                                {
                                    zPosition = 0;
                                }
                            }
                            else if (position.z < 0)
                            {
                                zPosition = position.z + movementFactor; // (movementFactor / 2f);
                                if (zPosition > 0)
                                {
                                    zPosition = 0;
                                }
                            }

                            encounterBoundaryRectGameLogic.width  = (int)sizeFactor;
                            encounterBoundaryRectGameLogic.height = (int)sizeFactor;

                            encounterBoundaryRectGameLogic.transform.position = new Vector3(xPosition, encounterBoundaryRectGameLogic.transform.position.y, zPosition);

                            Main.Logger.Log($"[MaximiseBoundarySize.SetBoundarySizeToCustom] Boundary [X,Z] is now [{xPosition}, {zPosition}]");

                            encounterLayerData.CalculateEncounterBoundary();
                        }
                        else
                        {
                            Main.Logger.Log($"[MaximiseBoundarySize] This encounter has no boundary to maximise.");
                        }
                    }
                }
            }
        }
        public void GenerateEncounterLayerBuildingData()
        {
            Main.Logger.LogDebug($"[EncounterDataManager.GenerateEncounterLayerBuildingData] Generating building data");
            Terrain terrain            = Terrain.activeTerrain;
            float   terrainZSize       = terrain.terrainData.size.z;
            float   terrainXSize       = terrain.terrainData.size.x;
            Vector3 terrainPosition    = terrain.transform.position;
            float   halfMapCellSize    = (float)MapMetaDataExporter.cellSize / 2f;
            int     halfMapCellSizeInt = MapMetaDataExporter.cellSize / 2;
            EncounterLayerParent encounterLayerParent = MissionControl.Instance.EncounterLayerParent;
            EncounterLayerData   encounterLayerData   = MissionControl.Instance.EncounterLayerData;
            MapMetaDataExporter  mapMetaExporter      = encounterLayerParent.GetComponent <MapMetaDataExporter>();
            MapMetaData          mapMetaData          = UnityGameInstance.BattleTechGame.Combat.MapMetaData;
            Vector3 raycastOrigin = new Vector3(0f, 1000f, 0f);

            // Lookups
            Dictionary <string, int>    regionRaycastHits         = new Dictionary <string, int>();
            List <RegionGameLogic>      regionGameObjectList      = new List <RegionGameLogic>();
            List <ObstructionGameLogic> obstructionGameObjectList = new List <ObstructionGameLogic>();

            // Marks only the ObstructionGameLogic objects for ray tracing for performance reasons
            AccessTools.Method(typeof(MapMetaDataExporter), "MarkCellsForRaycasting").Invoke(mapMetaExporter, new object[] { mapMetaData.mapTerrainDataCells, (int)terrain.transform.position.x, (int)terrain.transform.position.z });

            // TODO: Maybe wipe region building lists. Not sure if I really need/want this yet
            RegionGameLogic[] componentsInChildren = encounterLayerData.GetComponentsInChildren <RegionGameLogic>();
            for (int i = 0; i < componentsInChildren.Length; i++)
            {
                componentsInChildren[i].InitRegionForRayCasting();
            }

            // Iterate over the Z cell range
            for (float i = halfMapCellSize; i < terrainZSize; i += (float)MapMetaDataExporter.cellSize)
            {
                int zCellIndex = (int)i / MapMetaDataExporter.cellSize;

                // Iterate over the X cell range
                for (float j = halfMapCellSize; j < terrainXSize; j += (float)MapMetaDataExporter.cellSize)
                {
                    int xCellIndex = (int)j / MapMetaDataExporter.cellSize;

                    MapEncounterLayerDataCell mapEncounterLayerDataCell = new MapEncounterLayerDataCell();
                    if (mapMetaData.mapTerrainDataCells[zCellIndex, xCellIndex].doRayCast)
                    {
                        int hitIndex = 0;
                        regionRaycastHits.Clear();
                        regionGameObjectList.Clear();
                        obstructionGameObjectList.Clear();

                        for (int k = -halfMapCellSizeInt; k < halfMapCellSizeInt; k++)
                        {
                            raycastOrigin.z = i + (float)k + terrainPosition.z;

                            for (int l = -halfMapCellSizeInt; l < halfMapCellSizeInt; l++)
                            {
                                raycastOrigin.x = j + (float)l + terrainPosition.x;

                                RaycastHit[] raycastHits          = Physics.RaycastAll(raycastOrigin, Vector3.down);
                                List <ObstructionGameLogic> list3 = new List <ObstructionGameLogic>();

                                // Go through all the raycasts at Z,X of the terrain by cell size (middle of cell)
                                // Then find any regions hit, record the number of hits/cells the region has
                                for (int m = 0; m < raycastHits.Length; m++)
                                {
                                    RegionGameLogic regionGameLogic = raycastHits[m].transform.GetComponent <RegionGameLogic>();
                                    if (regionGameLogic != null)
                                    {
                                        if (!regionRaycastHits.ContainsKey(regionGameLogic.encounterObjectGuid))
                                        {
                                            regionRaycastHits[regionGameLogic.encounterObjectGuid] = 1;
                                        }
                                        else
                                        {
                                            string encounterObjectGuid = regionGameLogic.encounterObjectGuid;
                                            regionRaycastHits[encounterObjectGuid]++;
                                        }

                                        // Cache the region in the lookup
                                        if (!regionGameObjectList.Contains(regionGameLogic))
                                        {
                                            regionGameObjectList.Add(regionGameLogic);
                                        }
                                    }

                                    ObstructionGameLogic obstructionGameLogicInParent = raycastHits[m].transform.GetComponentInParent <ObstructionGameLogic>();
                                    if (obstructionGameLogicInParent != null && raycastHits[m].point.y > mapMetaData.mapTerrainDataCells[zCellIndex, xCellIndex].terrainHeight)
                                    {
                                        if (obstructionGameLogicInParent.IsBuildingHitAddWorthy)
                                        {
                                            if (!obstructionGameObjectList.Contains(obstructionGameLogicInParent))
                                            {
                                                obstructionGameObjectList.Add(obstructionGameLogicInParent);
                                            }
                                        }

                                        Vector3            normal             = raycastHits[m].normal;
                                        BuildingRaycastHit buildingRaycastHit = new BuildingRaycastHit {
                                            buildingSteepness = 90f - 57.29578f * Mathf.Atan2(normal.y, Mathf.Sqrt(normal.x * normal.x + normal.z * normal.z)),
                                            buildingHeight    = raycastHits[m].point.y,
                                            buildingGuid      = obstructionGameLogicInParent.encounterObjectGuid,
                                            hitIndex          = hitIndex
                                        };
                                        mapEncounterLayerDataCell.AddBuildingHit(buildingRaycastHit);
                                    }
                                }
                                hitIndex++;
                            }
                        }

                        // For all the regions detected, if it exists in 10 or more cells - add the region to the map encounter layer data (this is vanilla... why?!)
                        // And all all obstruction games logics to the region
                        foreach (RegionGameLogic regionGameLogic in regionGameObjectList)
                        {
                            if (regionRaycastHits[regionGameLogic.encounterObjectGuid] >= 10)
                            {
                                mapEncounterLayerDataCell.AddRegion(regionGameLogic);
                                foreach (ObstructionGameLogic obstructionGameLogic in obstructionGameObjectList)
                                {
                                    regionGameLogic.AddBuildingGuidToRegion(obstructionGameLogic.encounterObjectGuid);
                                }
                            }
                        }
                        mapEncounterLayerDataCell.AverageTheBuildingHits();
                        mapEncounterLayerDataCell.SortBuildingListByHeight();
                    }

                    encounterLayerData.mapEncounterLayerDataCells[zCellIndex, xCellIndex] = mapEncounterLayerDataCell;
                    encounterLayerData.mapEncounterLayerDataCells[zCellIndex, xCellIndex].relatedTerrainCell = mapMetaData.mapTerrainDataCells[zCellIndex, xCellIndex];
                    mapMetaData.mapTerrainDataCells[zCellIndex, xCellIndex].MapEncounterLayerDataCell        = mapEncounterLayerDataCell;
                }
            }
        }