Ejemplo n.º 1
0
 // Translate the strings in the config to enum types
 private void BuildWeightTable()
 {
     foreach (string weightS in this.Ambush.AmbushWeights)
     {
         AmbushType enumVal = (AmbushType)Enum.Parse(typeof(AmbushType), weightS);
         this.Ambush.AmbushTypes.Add(enumVal);
     }
 }
Ejemplo n.º 2
0
        static void Prefix(TurnDirector __instance)
        {
            if (!__instance.IsInterleaved && !__instance.IsInterleavePending &&
                __instance.CurrentRound >= Mod.Config.Ambush.EnableOnRound &&
                ModState.PotentialAmbushOrigins.Count != 0 &&
                __instance.ActiveTurnActor is Team activeTeam && activeTeam.IsLocalPlayer)
            {
                // Re-Filter the candidates to try to catch buildings that were marked contract objectives
                CandidateBuildingsHelper.FilterOnTurnActorIncrement(__instance.Combat);


                // Determine potential ambush sites based upon the origins
                Dictionary <Vector3, List <BattleTech.Building> > ambushSites = new Dictionary <Vector3, List <BattleTech.Building> >();
                foreach (Vector3 origin in ModState.PotentialAmbushOrigins)
                {
                    // For the given origin, find how many potential buildings there are.
                    List <BattleTech.Building> originCandidates = CandidateBuildingsHelper.ClosestCandidatesToPosition(origin, Mod.Config.Ambush.SearchRadius);
                    Mod.Log.Debug?.Write($" Found {originCandidates.Count} candidate buildings for originPos: {origin}");
                    ambushSites.Add(origin, originCandidates);
                }
                ModState.PotentialAmbushOrigins.Clear(); // reset potential origins for next round

                // Determine if the unit was ambushed this turn
                bool wasAmbushed = false;
                if (ModState.Ambushes < Mod.Config.Ambush.MaxPerMap && ambushSites.Count > 0)
                {
                    float roll      = Mod.Random.Next(1, 100);
                    int   threshold = (int)Math.Ceiling(ModState.CurrentAmbushChance * 100f);
                    if (roll <= threshold)
                    {
                        Mod.Log.Info?.Write($" Roll: {roll} is under current threshold: {threshold}, enabling possible ambush");
                        wasAmbushed = true;
                    }
                    else
                    {
                        ModState.CurrentAmbushChance += Mod.Config.Ambush.ChancePerActor;
                        Mod.Log.Info?.Write($" Roll: {roll} was over threshold: {threshold}, increasing ambush chance to: {ModState.CurrentAmbushChance} for next position.");
                    }
                }

                if (wasAmbushed)
                {
                    // Sort the ambushSites by number of buildings to maximize ambush success
                    Mod.Log.Debug?.Write("Sorting sites by potential buildings.");
                    List <KeyValuePair <Vector3, List <BattleTech.Building> > > sortedSites = ambushSites.ToList();
                    sortedSites.Sort((x, y) => x.Value.Count.CompareTo(y.Value.Count));

                    Vector3 ambushOrigin = sortedSites[0].Key;
                    Mod.Log.Debug?.Write($"Spawning an ambush at position: {ambushOrigin}");

                    // Randomly determine an ambush type by weight
                    List <AmbushType> shuffledTypes = new List <AmbushType>();
                    shuffledTypes.AddRange(Mod.Config.Ambush.AmbushTypes);
                    shuffledTypes.Shuffle <AmbushType>();
                    AmbushType ambushType = shuffledTypes[0];
                    Mod.Log.Info?.Write($"Ambush type of: {ambushType} will be applied at position: {ambushOrigin}");

                    switch (ambushType)
                    {
                    case AmbushType.Explosion:
                        ExplosionAmbushHelper.SpawnAmbush(ambushOrigin);
                        break;

                    case AmbushType.Infantry:
                        InfantryAmbushHelper.SpawnAmbush(ambushOrigin);
                        break;

                    case AmbushType.BattleArmor:
                        SpawnAmbushHelper.SpawnAmbush(ambushOrigin, AmbushType.BattleArmor);
                        break;

                    case AmbushType.Mech:
                        SpawnAmbushHelper.SpawnAmbush(ambushOrigin, AmbushType.Mech);
                        break;

                    case AmbushType.Vehicle:
                        SpawnAmbushHelper.SpawnAmbush(ambushOrigin, AmbushType.Vehicle);
                        break;

                    default:
                        Mod.Log.Error?.Write($"UNKNOWN AMBUSH TYPE: {ambushType} - CANNOT PROCEED!");
                        break;
                    }

                    // Record a successful ambush and reset the weighting
                    ModState.AmbushOrigins.Add(ambushOrigin);
                    ModState.Ambushes++;
                    ModState.CurrentAmbushChance = Mod.Config.Ambush.BaseChance;
                }
            }
        }
        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!");
            }
        }