예제 #1
0
        /*
         * Take the BaseInitiative value from all components on the unit, remove the
         *  normal phase modifiers HBS applies from SimGameConstants (-2 for light, etc),
         *  then invert the value. Because HBS defines bonuses as negative modifiers,
         *  invert this value to have it make sense elsewhere in the code.
         */
        // TODO: Should this use the MechDef call below?
        public static int GetNormalizedComponentModifier(AbstractActor actor)
        {
            int unitInit = 0;

            WeightClass weightClass;

            if (actor.GetType() == typeof(Mech))
            {
                weightClass = ((Mech)actor).weightClass;
            }
            else if (actor.GetType() == typeof(Vehicle))
            {
                var vehicle = (Vehicle)actor;
                weightClass = ((Vehicle)actor).weightClass;
            }
            else     // turret
            {
                TagSet actorTags = actor.GetTags();
                if (actorTags != null && actorTags.Contains("unit_light"))
                {
                    weightClass = WeightClass.LIGHT;
                }
                else if (actorTags != null && actorTags.Contains("unit_medium"))
                {
                    weightClass = WeightClass.MEDIUM;
                }
                else if (actorTags != null && actorTags.Contains("unit_heavy"))
                {
                    weightClass = WeightClass.HEAVY;
                }
                else
                {
                    weightClass = WeightClass.ASSAULT;
                }
            }

            // TODO: Validate that vehicles are normalized properly - looks like HBS adjusts the phases, may not be working properly
            // TODO: Validate that turret are normalized properly - looks like HBS adjusts the phases, may not be working properly

            /*
             *  HBS VALUES
             *  "PhaseSpecial": 1,
             *  "PhaseLight": 2,
             *  "PhaseMedium": 3,
             *  "PhaseHeavy": 4,
             *  "PhaseAssault": 5,
             *  "PhaseLightVehicle": 3,
             *  "PhaseMediumVehicle": 4,
             *  "PhaseHeavyVehicle": 5,
             *  "PhaseAssaultVehicle": 5,
             *  "PhaseLightTurret": 5,
             *  "PhaseMediumTurret": 5,
             *  "PhaseHeavyTurret": 5,
             *  "PhaseAssaultTurret": 5
             *
             *  RT VALUE
             *  "PhaseSpecial": 1,
             *  "PhaseLight": 2,
             *  "PhaseMedium": 3,
             *  "PhaseHeavy": 4,
             *  "PhaseAssault": 5,
             *  "PhaseLightVehicle": 2,
             *  "PhaseMediumVehicle": 3,
             *  "PhaseHeavyVehicle": 4,
             *  "PhaseAssaultVehicle": 5,
             *  "PhaseLightTurret": 3,
             *  "PhaseMediumTurret": 4,
             *  "PhaseHeavyTurret": 5,
             *  "PhaseAssaultTurret": 5
             */

            if (actor.StatCollection != null && actor.StatCollection.ContainsStatistic("BaseInitiative"))
            {
                int baseMod = actor.StatCollection.GetValue <int>("BaseInitiative");

                // Normalize the value
                // TODO: These should come from CombatGameDef.PhaseConstantsDef, but I can't find a reference to pull
                //   those values. May have to load at mod start and cache them.
                switch (weightClass)
                {
                case WeightClass.LIGHT:
                    baseMod -= 2;
                    break;

                case WeightClass.MEDIUM:
                    baseMod -= 3;
                    break;

                case WeightClass.HEAVY:
                    baseMod -= 4;
                    break;

                case WeightClass.ASSAULT:
                    baseMod -= 5;
                    break;

                default:
                    Mod.Log.Debug($"Actor:{actor.DisplayName}_{actor.GetPilot().Name}" +
                                  $" has unknown or undefined weight class:{weightClass}!");
                    break;
                }

                // Because HBS init values were from 2-5, bonuses will be negative at this point and penalties positive. Invert these.
                unitInit = baseMod * -1;
                Mod.Log.Debug($"Normalized BaseInit for Actor:{actor.DisplayName}_{actor.GetPilot().Name}" +
                              $" from {actor.StatCollection.GetValue<int>("BaseInitiative")} to unitInit:{unitInit}");
            }

            return(unitInit);
        }
 public static void Postfix(CombatAuraReticle __instance, ref AbstractActor ___owner, ref float ___currentAuraRange, ref CombatHUD ___HUD, ref Transform ___thisTransform)
 {
     //if(___HUD.SelectedActor != null && ___HUD.SelectionHandler.ActiveState is SelectionStateMoveBase && ___owner.GUID == ___HUD.SelectedActor.GUID) {
     //AuraPreviewRecord preview =
     //}
 }
 public static bool isActivated(this AbstractActor unit)
 {
     return(activatedActors.Contains(unit));
 }
예제 #4
0
            public static void Postfix(ToHit __instance, ref string __result, CombatGameState ___combat, AbstractActor attacker, Weapon weapon, ICombatant target)
            {
                try
                {
                    if (DemandingJumps.Settings.ToHitSelfJumpedSpareAI && !___combat.LocalPlayerTeam.IsActive)
                    {
                        return;
                    }
                    Logger.Info("[ToHit_GetAllModifiersDescription_POSTFIX] CombatGameState.LocalPlayerTeam.IsActive: " + ___combat.LocalPlayerTeam.IsActive);



                    bool AttackerJumpedThisRound = attacker.HasMovedThisRound && attacker.JumpedLastRound;
                    Logger.Info("[ToHit_GetAllModifiersDescription_POSTFIX] Fields.JumpPreview: " + Fields.JumpPreview);
                    Logger.Info("[ToHit_GetAllModifiersDescription_POSTFIX] AttackerJumpedThisRound: " + AttackerJumpedThisRound);

                    if (AttackerJumpedThisRound)
                    {
                        int ToHitSelfJumpedModifier = Utilities.GetAttackerJumpedAccuracyModifier(attacker);
                        Logger.Debug("[ToHit_GetAllModifiersDescription_POSTFIX] Add description for ToHitSelfJumped: " + ToHitSelfJumpedModifier);
                        __result = string.Format("{0}JUMPED {1:+#;-#}; ", __result, ToHitSelfJumpedModifier);
                    }
                }
                catch (Exception e)
                {
                    Logger.Error(e);
                }
            }
 public static bool Prefix(CombatAuraReticle __instance, ref ButtonState __result, ref AbstractActor ___owner, ref CombatHUD ___HUD)
 {
     try {
         AuraBubble aura = __instance.AuraBubble();
         if (aura == null)
         {
             __result = ButtonState.Disabled;  return(false);
         }
         __result = __instance.isAuraVisible(aura, ___owner, ___HUD, false)? ButtonState.Enabled : ButtonState.Disabled;
         return(false);
     }catch (Exception e) {
         Log.LogWrite(e.ToString() + "\n");
     }
     return(true);
     //__instance.GameRep.PlayVFXAt(__instance.GameRep.thisTransform, Vector3.zero, "vfxPrfPrtl_ECM_loop", true, Vector3.zero, false, -1f);
 }
예제 #6
0
 public void SetPlayer(AbstractActor player)
 {
     _player = Objects.RequireNonNull(player);
 }
예제 #7
0
        public static void Postfix(CombatHUDActorNameDisplay __instance, VisibilityLevel visLevel, AbstractActor ___displayedActor)
        {
            if (___displayedActor != null && ModState.LastPlayerActorActivated != null && ModState.TurnDirectorStarted &&
                !___displayedActor.Combat.HostilityMatrix.IsLocalPlayerFriendly(___displayedActor.TeamId))
            {
                SensorScanType scanType = SensorLockHelper.CalculateSharedLock(___displayedActor, ModState.LastPlayerActorActivated);

                // TODO: Needs to be hidden or localized
                string nameText = scanType >= SensorScanType.AllInformation ? ___displayedActor.GetPilot().Name : "";
                __instance.PilotNameText.SetText(nameText);
            }
        }
 static void Prefix(AbstractActor __instance)
 {
     Mod.MoveLog.Info?.Write($"Resetting pathing for actor: {__instance.DistinctId()}");
 }
 static void Prefix(Pathing __instance, Vector3 origin, float beginAngle, AbstractActor actor, bool justStoodUp)
 {
     Mod.MoveLog.Info?.Write($"Resetting path grid for actor: {actor.DistinctId()} for origin: {origin} and beginAngle: {beginAngle}");
 }
예제 #10
0
 public static bool IsOnSecondAction(AbstractActor unit)
 {
     return(!IsOnFirstAction(unit));
 }
 static void Postfix(AbstractActor __instance)
 {
     Mod.Log.Debug?.Write($"AA:IES entered- setting CanShootAfterSprinting for actor:{__instance.DisplayName}");
     __instance.StatCollection.Set(ModStats.CanShootAfterSprinting, true);
 }
예제 #12
0
        public Vector3 GetDestination(List <PointWithDistance> startPointList, Vector3 goal, float movementBudget, float maxSlope, AbstractActor unit, bool shouldSprint, List <AbstractActor> lanceUnits, PathNodeGrid pathGrid, out Vector3 lookAtPoint)
        {
            if (shouldSprint && unit.CanSprint)
            {
                unit.Pathing.SetSprinting();
            }
            else
            {
                unit.Pathing.SetWalking();
            }

            InclineIndexPoint goalPoint = WorldPointToInclineIndices(goal);

            List <PointWithDistance> pathLatticePoints = FindPath(startPointList, goalPoint, maxSlope, maxSlope);

            if ((pathLatticePoints == null) || (pathLatticePoints.Count == 0))
            {
                // can't find a path(!)

                // set the lookAtPoint (out) variable to a point further out in the direction of start->goal
                lookAtPoint = goal * 2.0f - unit.CurrentPosition;
                return(goal);
            }


            List <PathNode> pathNodes = new List <PathNode>();
            PathNode        walkNode  = pathLatticePoints[0].pathNode;

            while (walkNode != null)
            {
                pathNodes.Insert(0, walkNode);
                walkNode = walkNode.Parent;
            }

            List <Vector3> longRangePathWorldPoints = pathLatticePoints.ConvertAll(x => InclineIndicesToWorldPoint(x.point));
            List <Vector3> pathWorldPoints          = pathNodes.ConvertAll(x => x.Position);

            pathWorldPoints.AddRange(longRangePathWorldPoints);

            Vector3 destination;

            if (longRangePathWorldPoints.Count == 1)
            {
                destination = longRangePathWorldPoints[0];
                if ((goal - destination).magnitude < 1.0f)
                {
                    lookAtPoint = destination * 2.0f - unit.CurrentPosition;
                }
                else
                {
                    lookAtPoint = goal;
                }

                return(destination);
            }

            // Debug Draw Path
            float scale = 1.0f;

            for (int pathIndex = 0; pathIndex < pathWorldPoints.Count - 1; ++pathIndex)
            {
                int     nextIndex = pathIndex + 1;
                Vector3 p0        = pathWorldPoints[pathIndex];
                Vector3 p1        = pathWorldPoints[nextIndex];

                scale = (p1 - p0).magnitude;

                for (int dx = -1; dx <= 1; ++dx)
                {
                    for (int dz = -1; dz <= 1; ++dz)
                    {
                        Vector3 offset = new Vector3(dx * scale * 0.1f, 0, dz * scale * 0.1f);
                        Debug.DrawLine(p0 + offset, p1 + offset, Color.red, 30.0f);
                    }
                }
            }

            drawCircle(unit.CurrentPosition, scale * 0.4f, Color.cyan, 30.0f);
            drawCircle(goal, scale * 0.4f, Color.magenta, 30.0f);
            drawCircle(pathWorldPoints[0], scale * 0.3f, Color.red, 30.0f);
            drawCircle(pathWorldPoints[0], scale * 0.35f, Color.white, 30.0f);
            drawCircle(pathWorldPoints[0], scale * 0.4f, Color.blue, 30.0f);

            // TODO/dlecompte push this up to filter our selection of path nodes, above.
            float spread = unit.BehaviorTree.GetBehaviorVariableValue(
                unit.Combat.TurnDirector.IsInterleaved ?
                BehaviorVariableName.Float_InterleavedLanceSpreadDistance :
                BehaviorVariableName.Float_NonInterleavedLanceSpreadDistance).FloatVal;

            drawCircle(unit.CurrentPosition, spread, Color.green, 30.0f);

            Debug.Assert(pathWorldPoints.Count >= 2);             // already tested this, above.
            float accumDistance = 0.0f;

            // if we can get to the goal, look at a point further away in the direction of start -> goal
            lookAtPoint = 2 * goal - unit.CurrentPosition;
            Vector3 clipPoint = goal;

            // Now we walk along the pathWorldPoints, snapping them to grid points.
            // We want to take the point furthest along the path that doesn't alias to an earlier point.
            // Also, we want to make sure that it's the furthest point within our movement budget and within our lance spread.
            // MUST BE : within movement budget, not an alias to an earlier point
            // IF any points exist inside lance spread, pick last point inside lance spread, else last point.

            List <Vector3> dedupedSnappedPointsList = new List <Vector3>();
            List <Vector3> snappedPointsInOrder     = new List <Vector3>();
            List <Vector3> nextPointsInOrder        = new List <Vector3>();
            List <bool>    pointsInSpreadRangeList  = new List <bool>();
            List <bool>    isNewGroundList          = new List <bool>();

            float ROUNDING_RADIUS = 1.0f;

            bool wasEverInside = false;

            for (int pointIndex = 0; (pointIndex < pathWorldPoints.Count) && (accumDistance <= movementBudget); ++pointIndex)
            {
                Vector3 thisPoint = pathWorldPoints[pointIndex];
                Vector3 nextPoint = goal;
                if (pointIndex + 1 < pathWorldPoints.Count)
                {
                    nextPoint = pathWorldPoints[pointIndex + 1];
                }

                Vector3 thisSnappedPoint = unit.Combat.HexGrid.GetClosestPointOnGrid(thisPoint);
                snappedPointsInOrder.Add(thisSnappedPoint);
                nextPointsInOrder.Add(nextPoint);

                bool pointIsInsideSpread = AIUtil.IsPositionWithinLanceSpread(unit, lanceUnits, thisSnappedPoint);
                wasEverInside |= pointIsInsideSpread;
                pointsInSpreadRangeList.Add(pointIsInsideSpread);

                bool alreadyVisited = isPointInList(thisSnappedPoint, dedupedSnappedPointsList, ROUNDING_RADIUS);

                isNewGroundList.Add(!alreadyVisited);

                if (!alreadyVisited)
                {
                    dedupedSnappedPointsList.Add(thisSnappedPoint);
                }

                if (pointIndex + 1 < pathWorldPoints.Count)
                {
                    accumDistance += (nextPoint - thisPoint).magnitude;
                }
            }

            if (wasEverInside)
            {
                // find the last point of our list that is "new ground" and inside
                for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i)
                {
                    if (isNewGroundList[i] && pointsInSpreadRangeList[i])
                    {
                        clipPoint   = snappedPointsInOrder[i];
                        lookAtPoint = nextPointsInOrder[i];
                        break;
                    }
                }
            }

            if ((!wasEverInside) || ((clipPoint - unit.CurrentPosition).magnitude < 1.0f))
            {
                // find the last point of our list that is "new ground"
                for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i)
                {
                    if (isNewGroundList[i])
                    {
                        clipPoint   = snappedPointsInOrder[i];
                        lookAtPoint = nextPointsInOrder[i];
                        break;
                    }
                }
            }

            for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i)
            {
                drawCircle(snappedPointsInOrder[i], scale * 0.2f, new Color(0.5f, 0.5f, 0.0f), 30.0f);
                if (pointsInSpreadRangeList[i])
                {
                    drawCircle(snappedPointsInOrder[i], scale * 0.25f, new Color(0.0f, 1.0f, 0.0f), 30.0f);
                }
            }

            //float lookAngle = PathingUtil.GetAngle(lookAtPoint - clipPoint);
            //Vector3 resultPos = clipPoint;
            drawCircle(clipPoint, scale * 0.4f, new Color(1.0f, 0.5f, 0.0f), 30.0f);
            return(clipPoint);
        }
예제 #13
0
        public Vector3 GetDestination(Vector3 goal, float movementBudget, float maxSlope, AbstractActor unit, bool shouldSprint, List <AbstractActor> lanceUnits, PathNodeGrid pathGrid, out Vector3 lookAtPoint)
        {
            List <PointWithDistance> startPointList = new List <PointWithDistance>();

            List <PathNode> pathNodes = pathGrid.GetSampledPathNodes();

            for (int pni = 0; pni < pathNodes.Count; ++pni)
            {
                PathNode          pn  = pathNodes[pni];
                PointWithDistance pwd = new PointWithDistance(WorldPointToInclineIndices(pn.Position), pn.DepthInPath * 24, (goal - pn.Position).magnitude);
                pwd.pathNode = pn;
                startPointList.Add(pwd);
            }

            return(GetDestination(startPointList, goal, movementBudget, maxSlope, unit, shouldSprint, lanceUnits, pathGrid, out lookAtPoint));
        }
예제 #14
0
        /// <summary>
        /// Finds a destination point along a path from start to goal, where the distance from the destination should be
        /// approximately movementBudget. The path from start to goal will not have inclines nor declines exceeding maxSlope.
        /// </summary>
        /// <returns>The destination.</returns>
        /// <param name="start">Start.</param>
        /// <param name="goal">Goal.</param>
        /// <param name="movementBudget">Movement budget.</param>
        /// <param name="maxSlope">Max slope.</param>
        /// <param name="unit">unit that is moving</param>
        /// <param name="shouldSprint">whether to sprint or not</param>
        /// <param name="lanceUnits">observe lance spread from these units when choosing destinations</param>
        /// <param name="pathGrid">the pathing grid that indicates where a unit can get to on this turn</param>
        /// <param name="lookAtPoint">point to look at</param>
        public Vector3 GetDestination(Vector3 start, Vector3 goal, float movementBudget, float maxSlope, AbstractActor unit, bool shouldSprint, List <AbstractActor> lanceUnits, PathNodeGrid pathGrid, out Vector3 lookAtPoint)
        {
            List <PointWithDistance> startPointList = new List <PointWithDistance>();

            startPointList.Add(new PointWithDistance(WorldPointToInclineIndices(start), 0, (goal - start).magnitude));
            return(GetDestination(startPointList, goal, movementBudget, maxSlope, unit, shouldSprint, lanceUnits, pathGrid, out lookAtPoint));
        }
예제 #15
0
        public static bool SavedVsPanic(AbstractActor actor, float savingThrow)
        {
            try
            {
                AbstractActor defender = null;
                if (actor is Vehicle vehicle)
                {
                    if (!modSettings.VehiclesCanPanic)
                    {
                        return(true);
                    }

                    defender = vehicle;
                }
                else if (actor is Mech mech)
                {
                    defender = mech;
                }

                if (defender == null)
                {
                    LogDebug($"defender null, passing save. actor {actor} is type {actor.GetType()}");
                    return(true);
                }

                if (modSettings.QuirksEnabled)
                {
                    if (defender is Mech m)
                    {
                        if (m.pilot.pilotDef.PilotTags.Contains("pilot_brave"))
                        {
                            savingThrow -= modSettings.BraveModifier;
                            LogReport($"{"Bravery",-20} | {modSettings.BraveModifier,10} | {savingThrow,10:F3}");
                        }
                    }
                }

                var   index         = GetActorIndex(defender);
                float panicModifier = GetPanicModifier(TrackedActors[index].PanicStatus);
                savingThrow *= panicModifier;
                LogReport($"{"Panic multiplier",-20} | {panicModifier,10} | {savingThrow,10:F3}");
                savingThrow = (float)Math.Max(0f, Math.Round(savingThrow));

                if (savingThrow < 1)
                {
                    LogReport(new string('-', 46));
                    LogReport("Negative saving throw| skipping");
                    return(true);
                }

                var roll = Random.Range(1, 100);
                LogReport(new string('-', 46));
                LogReport($"{"Saving throw",-20} | {savingThrow,-5}{roll,5} | {"Roll",10}");
                LogReport(new string('-', 46));
                SaySpamFloatie(defender, $"{$"{modSettings.PanicSpamSaveString}:{savingThrow}",-6} {$"{modSettings.PanicSpamRollString}:{roll}!",3}");

                // lower panic level on crit success
                if (roll == 100)
                {
                    LogReport("Critical success");
                    SaySpamFloatie(defender, $"{modSettings.PanicSpamCritSaveString}");
                    TrackedActors[index].PanicStatus--;
                    // just in case the status went down then back up on a crit save in the same round
                    TrackedActors[index].PanicWorsenedRecently = false;
                    return(true);
                }

                if (!modSettings.AlwaysPanic &&
                    roll >= savingThrow)
                {
                    LogReport("Successful panic save");
                    SaySpamFloatie(defender, $"{modSettings.PanicSpamSaveString}!");
                    return(true);
                }

                LogReport("Failed panic save");
                SaySpamFloatie(defender, $"{modSettings.PanicSpamFailString}!");

                var originalStatus = TrackedActors[index].PanicStatus;
                if (defender is Vehicle)
                {
                    TrackedActors[index].PanicStatus = PanicStatus.Panicked;
                }
                else
                {
                    TrackedActors[index].PanicStatus++;
                }

                TrackedActors[index].PanicWorsenedRecently = true;

                // check for panic crit
                if (roll == 1 ||
                    ActorHealth(defender) <= modSettings.MechHealthForCrit &&
                    roll < Convert.ToInt32(savingThrow) - modSettings.CritOver)
                {
                    LogReport("Critical failure on panic save");
                    defender.Combat.MessageCenter.PublishMessage(
                        new AddSequenceToStackMessage(
                            new ShowActorInfoSequence(defender, modSettings.PanicCritFailString, FloatieMessage.MessageNature.CriticalHit, true)));
                    // ejection can only occur from a stressed or panicked state where panicked requirement is achieved regardless
                    // no crit going from confident to panicked then ejection
                    TrackedActors[index].PanicStatus = PanicStatus.Panicked;
                }

                TrackedActors[index].PreventEjection = originalStatus < PanicStatus.Stressed;
            }
            catch (Exception ex)
            {
                LogDebug(ex);
            }

            return(false);
        }
예제 #16
0
    public static BehaviorNode InitRootNode(BehaviorTree behaviorTree, AbstractActor unit, GameInstance game)
    {
        LanceHasLOSNode lanceHasLOS0000 = new LanceHasLOSNode("lanceHasLOS0000", behaviorTree, unit);

        FindVisibleEnemiesNode findVisibleEnemies0000 = new FindVisibleEnemiesNode("findVisibleEnemies0000", behaviorTree, unit);

        IsMovementAvailableForUnitNode movementAvailable0000 = new IsMovementAvailableForUnitNode("movementAvailable0000", behaviorTree, unit);

        SortEnemiesByThreatNode sortEnemiesByThreat0000 = new SortEnemiesByThreatNode("sortEnemiesByThreat0000", behaviorTree, unit);

        MoveTowardsHighestPriorityEnemyNode moveTowardsHighestPriorityEnemy0000 = new MoveTowardsHighestPriorityEnemyNode("moveTowardsHighestPriorityEnemy0000", behaviorTree, unit);

        SequenceNode canMove = new SequenceNode("canMove", behaviorTree, unit);

        canMove.AddChild(movementAvailable0000);
        canMove.AddChild(sortEnemiesByThreat0000);
        canMove.AddChild(moveTowardsHighestPriorityEnemy0000);

        IsAttackAvailableForUnitNode attackAvailable0000 = new IsAttackAvailableForUnitNode("attackAvailable0000", behaviorTree, unit);

        SortEnemiesByEffectivenessNode sortEnemiesByEffectiveness0000 = new SortEnemiesByEffectivenessNode("sortEnemiesByEffectiveness0000", behaviorTree, unit);

        ShootAtHighestPriorityEnemyNode shootAtHighestPriorityEnemy0000 = new ShootAtHighestPriorityEnemyNode("shootAtHighestPriorityEnemy0000", behaviorTree, unit);

        SequenceNode canAttack = new SequenceNode("canAttack", behaviorTree, unit);

        canAttack.AddChild(attackAvailable0000);
        canAttack.AddChild(sortEnemiesByEffectiveness0000);
        canAttack.AddChild(shootAtHighestPriorityEnemy0000);

        SelectorNode selector0000 = new SelectorNode("selector0000", behaviorTree, unit);

        selector0000.AddChild(canMove);
        selector0000.AddChild(canAttack);

        SequenceNode free_engage = new SequenceNode("free_engage", behaviorTree, unit);

        free_engage.AddChild(lanceHasLOS0000);
        free_engage.AddChild(findVisibleEnemies0000);
        free_engage.AddChild(selector0000);

        IsMovementAvailableForUnitNode movementAvailable0001 = new IsMovementAvailableForUnitNode("movementAvailable0001", behaviorTree, unit);

        FindPreviouslySeenEnemiesNode findPreviouslySeenEnemies0000 = new FindPreviouslySeenEnemiesNode("findPreviouslySeenEnemies0000", behaviorTree, unit);

        SortEnemiesByProximityNode sortEnemiesByProximity0000 = new SortEnemiesByProximityNode("sortEnemiesByProximity0000", behaviorTree, unit);

        MoveTowardsHighestPriorityEnemyNode moveTowardsHighestPriorityEnemy0001 = new MoveTowardsHighestPriorityEnemyNode("moveTowardsHighestPriorityEnemy0001", behaviorTree, unit);

        SequenceNode hunt_previously_seen = new SequenceNode("hunt_previously_seen", behaviorTree, unit);

        hunt_previously_seen.AddChild(movementAvailable0001);
        hunt_previously_seen.AddChild(findPreviouslySeenEnemies0000);
        hunt_previously_seen.AddChild(sortEnemiesByProximity0000);
        hunt_previously_seen.AddChild(moveTowardsHighestPriorityEnemy0001);

        SelectorNode selector0001 = new SelectorNode("selector0001", behaviorTree, unit);

        selector0001.AddChild(hunt_previously_seen);

        FailNode fail0000 = new FailNode("fail0000", behaviorTree, unit);

        SelectorNode patrol = new SelectorNode("patrol", behaviorTree, unit);

        patrol.AddChild(fail0000);

        BraceNode brace0000 = new BraceNode("brace0000", behaviorTree, unit);

        SelectorNode dumb_AI_root = new SelectorNode("dumb_AI_root", behaviorTree, unit);

        dumb_AI_root.AddChild(free_engage);
        dumb_AI_root.AddChild(selector0001);
        dumb_AI_root.AddChild(patrol);
        dumb_AI_root.AddChild(brace0000);

        return(dumb_AI_root);
    }
예제 #17
0
        // false is punchin' out
        public static bool SavedVsEject(AbstractActor actor, float savingThrow)
        {
            LogReport("Panic save failure requires eject save");

            try
            {
                if (actor.IsPilotable && actor.GetPilot() != null && actor.GetPilot().StatCollection.GetValue <bool>("CanEject") == false)
                {
                    LogReport($"Pilot CanEject Stat false - {(modSettings.ObeyPilotCanEjectStat ? "":"NOT")} obeying");
                    LogActor(actor, true);
                    if (modSettings.ObeyPilotCanEjectStat)
                    {
                        return(true);
                    }
                }
                if (actor.IsPilotable && actor.GetPilot() != null && actor.GetPilot().pilotDef.PilotTags.Contains("pilot_cannot_eject"))
                {
                    LogReport($"Pilot pilot_cannot_eject Tag set - {(modSettings.ObeyPilotCannotEjectTag ? "" : "NOT")} obeying");
                    LogActor(actor, true);
                    if (modSettings.ObeyPilotCannotEjectTag)
                    {
                        return(true);
                    }
                }
            }
            catch (Exception ex)
            {
                LogDebug(ex);
            }

            var pilotTracker = TrackedActors.First(tracker => tracker.Guid == actor.GUID);

            if (pilotTracker.PreventEjection)
            {
                LogReport("Ejection forbidden after crit unless already stressed or panicked");
                pilotTracker.PreventEjection = false;
                return(true);
            }

            DrawHeader();

            if (actor is Mech mech && modSettings.QuirksEnabled)
            {
                if (mech.pilot.pilotDef.PilotTags.Contains("pilot_dependable"))
                {
                    savingThrow -= modSettings.DependableModifier;
                    LogReport($"{"Dependable",-20} | {modSettings.DependableModifier,10} | {savingThrow,10:F3}");
                }
            }

            // calculate result
            if (modSettings.VehiclesCanPanic &&
                actor is Vehicle)
            {
                savingThrow = Math.Max(0f, savingThrow - modSettings.BaseVehicleEjectionResist);
                LogReport($"{"Base ejection resist",-20} | {modSettings.BaseVehicleEjectionResist,10} | {savingThrow,10:F3}");
            }
            else if (actor is Mech)
            {
                savingThrow = Math.Max(0f, savingThrow - modSettings.BaseEjectionResist);
                LogReport($"{"Base ejection resist",-20} | {modSettings.BaseEjectionResist,10} | {savingThrow,10:F3}");
            }

            savingThrow = (float)Math.Round(savingThrow);
            LogReport($"{"Eject multiplier",-20} | {modSettings.EjectChanceFactor,10} | {savingThrow,10:F3}");
            var roll = Random.Range(1, 100);

            LogReport(new string('-', 46));
            LogReport($"{"Saving throw",-20} | {savingThrow,-5:###}{roll,5} | {"Roll",10}");
            LogReport(new string('-', 46));
            if (!modSettings.AlwaysPanic &&
                savingThrow < 1)
            {
                LogReport("Negative saving throw| skipping");
                SaySpamFloatie(actor, $"{modSettings.PanicSpamEjectResistString}");
                return(true);
            }

            // cap the saving throw by the setting
            savingThrow = (int)Math.Min(savingThrow, modSettings.MaxEjectChance);

            SaySpamFloatie(actor, $"{modSettings.PanicSpamSaveString}:{savingThrow}  {modSettings.PanicSpamRollString}:{roll}!");
            if (!modSettings.AlwaysPanic &&
                roll >= savingThrow)
            {
                LogReport("Successful ejection save");
                SaySpamFloatie(actor, $"{modSettings.PanicSpamSaveString}!  {ActorHealth(actor):#.#}%");
                return(true);
            }

            // TODO can it be written if (mech != null) ? I don't know and testing it is a PITA!
            if (actor is Mech m)
            {
                if (modSettings.QuirksEnabled && m.MechDef.Chassis.ChassisTags.Contains("mech_quirk_noeject"))
                {
                    LogReport("This mech can't eject (quirk)");
                    actor.Combat.MessageCenter.PublishMessage(
                        new AddSequenceToStackMessage(
                            new ShowActorInfoSequence(actor, "Mech quirk: Can't eject", FloatieMessage.MessageNature.PilotInjury, true)));
                    return(true);
                }

                if (modSettings.QuirksEnabled && m.pilot.pilotDef.PilotTags.Contains("pilot_drunk") &&
                    m.pilot.pilotDef.TimeoutRemaining > 0)
                {
                    LogReport("Drunkard - not ejecting");
                    actor.Combat.MessageCenter.PublishMessage(
                        new AddSequenceToStackMessage(
                            new ShowActorInfoSequence(actor, "Pilot quirk: Drunkard won't eject", FloatieMessage.MessageNature.PilotInjury, true)));
                    return(true);
                }
            }

            LogReport("Failed ejection save: Punchin\' Out!!");
            return(false);
        }
 public FilterNonLOSOrLOFMovesNode(string name, BehaviorTree tree, AbstractActor unit) : base(name, tree, unit)
 {
 }
 public static void Prefix(AbstractActor __instance)
 {
     DamageHelper.completedTurnFor(__instance);
 }
 public static void Prefix(AbstractActor __instance)
 {
     VisibilityCacheGate.EnterGate();
     GateActive = true;
     counter    = VisibilityCacheGate.GetCounter;
 }
예제 #21
0
            public static void Postfix(ToHit __instance, ref float __result, CombatGameState ___combat, AbstractActor attacker, Weapon weapon, ICombatant target)
            {
                try
                {
                    if (DemandingJumps.Settings.ToHitSelfJumpedSpareAI && !___combat.LocalPlayerTeam.IsActive)
                    {
                        return;
                    }
                    Logger.Info("[ToHit_GetAllModifiers_POSTFIX] CombatGameState.LocalPlayerTeam.IsActive: " + ___combat.LocalPlayerTeam.IsActive);



                    bool AttackerJumpedThisRound = attacker.HasMovedThisRound && attacker.JumpedLastRound;
                    Logger.Info("[ToHit_GetAllModifiers_POSTFIX] Fields.JumpPreview: " + Fields.JumpPreview);
                    Logger.Info("[ToHit_GetAllModifiers_POSTFIX] AttackerJumpedThisRound: " + AttackerJumpedThisRound);

                    if (Fields.JumpPreview || AttackerJumpedThisRound)
                    {
                        int ToHitSelfJumpedModifier = Utilities.GetAttackerJumpedAccuracyModifier(attacker);
                        Logger.Info("[ToHit_GetAllModifiers_POSTFIX] Unit previews jump or already jumped. Applying ToHit penalty.");
                        __result = __result + (float)ToHitSelfJumpedModifier;
                    }
                }
                catch (Exception e)
                {
                    Logger.Error(e);
                }
            }
예제 #22
0
 public static bool IsBeatingDeadMech(AbstractActor __instance)
 {
     return(__instance.IsFlaggedForDeath || __instance.IsDead);
 }
예제 #23
0
        private static bool Prefix(ref float __result, AbstractActor unit, AbstractActor target, Vector3 targetPosition, int pipsRemoved)
        {
            Mod.Log.Trace($"  ---- AE_ADFP: Building list.");
            List <AbstractActor> list = new List <AbstractActor>();
            Dictionary <int, List <AbstractActor> > dictionary = new Dictionary <int, List <AbstractActor> >();
            int i;

            for (i = Mod.MinPhase; i <= Mod.MaxPhase; i++)
            {
                dictionary[i] = new List <AbstractActor>();
            }

            Mod.Log.Trace($"  ---- AE_ADFP: Mapping lance to init.");
            for (int j = 0; j < unit.lance.unitGuids.Count; j++)
            {
                string text = unit.lance.unitGuids[j];
                if (!(text == unit.GUID))
                {
                    AbstractActor itemByGUID = unit.Combat.ItemRegistry.GetItemByGUID <AbstractActor>(text);
                    int           initiative = itemByGUID.Initiative;
                    dictionary[initiative].Add(itemByGUID);
                }
            }

            Mod.Log.Trace($"  ---- AE_ADFP: Mapping all actors.");
            int currentPhase = unit.Combat.TurnDirector.CurrentPhase;

            for (int k = 0; k < dictionary[currentPhase].Count; k++)
            {
                AbstractActor abstractActor = dictionary[currentPhase][k];
                if (!abstractActor.HasActivatedThisRound)
                {
                    list.Add(abstractActor);
                }
            }

            Mod.Log.Trace($"  ---- AE_ADFP: actors for init");
            i = currentPhase;
            while (target.Initiative != i)
            {
                i++;
                if (i > Mod.MaxPhase)
                {
                    i = Mod.MinPhase;
                }
                for (int l = 0; l < dictionary[i].Count; l++)
                {
                    AbstractActor item = dictionary[i][l];
                    list.Add(item);
                }
            }

            Mod.Log.Trace($"  ---- AE_ADFP: Calulating");
            float num  = 0f;
            float num2 = 0f;
            int   evasivePipsCurrent  = target.EvasivePipsCurrent;
            int   evasivePipsCurrent2 = Mathf.Max(0, evasivePipsCurrent - pipsRemoved);

            for (int m = 0; m < list.Count; m++)
            {
                AbstractActor abstractActor2 = list[m];
                for (int n = 0; n < abstractActor2.Weapons.Count; n++)
                {
                    Weapon weapon = abstractActor2.Weapons[n];
                    if (weapon.CanFire && abstractActor2.HasLOFToTargetUnit(target, weapon))
                    {
                        target.EvasivePipsCurrent = evasivePipsCurrent;
                        float toHitFromPosition = weapon.GetToHitFromPosition(target, 1, abstractActor2.CurrentPosition, target.CurrentPosition, true, true, false);
                        num2 += toHitFromPosition * weapon.DamagePerShot * (float)weapon.ShotsWhenFired;
                        target.EvasivePipsCurrent = evasivePipsCurrent2;
                        toHitFromPosition         = weapon.GetToHitFromPosition(target, 1, abstractActor2.CurrentPosition, target.CurrentPosition, true, true, false);
                        num += toHitFromPosition * weapon.DamagePerShot * (float)weapon.ShotsWhenFired;
                    }
                }
            }
            target.EvasivePipsCurrent = evasivePipsCurrent;
            __result = num - num2;
            return(false);
        }
예제 #24
0
 public AIMCritInfo(AbstractActor target, WeaponHitInfo hitInfo, Weapon weapon)
 {
     this.target  = target;
     this.hitInfo = hitInfo;
     this.weapon  = weapon;
 }
 public static bool Prefix(CombatAuraReticle __instance, bool showActiveProbe, ref AbstractActor ___owner, ref float ___currentAPRange)
 {
     try {
         GameObject activeProbeRangeScaledObject = __instance.activeProbeRangeScaledObject();
         if (showActiveProbe == false)
         {
             activeProbeRangeScaledObject.SetActive(false);
             return(false);
         }
         //AuraBubble mainSensorsBubble = __instance.MainSensors();
         //Log.LogWrite("CombatAuraReticle.RefreshAuraRange " + (mainSensorsBubble == null ? "null" : mainSensorsBubble.collider.radius.ToString()) + "\n");
         //if (mainSensorsBubble != null) {
         //auraRangeScaledObject.SetActive(true);
         //float b = mainSensorsBubble.collider.radius;
         //if (!Mathf.Approximately(___currentAuraRange, b)) {
         //auraRangeScaledObject.transform.localScale = new Vector3(b * 2f, 1f, b * 2f);
         //}
         //___currentAuraRange = b;
         //return false;
         //}
         AuraBubble auraBubble = __instance.AuraBubble();
         if (auraBubble != null)
         {
             activeProbeRangeScaledObject.SetActive(true);
             float b = auraBubble.collider.radius;
             if (!Mathf.Approximately(___currentAPRange, b))
             {
                 activeProbeRangeScaledObject.transform.localScale = new Vector3(b * 2f, 1f, b * 2f);
             }
             ___currentAPRange = b;
             return(false);
         }
     } catch (Exception e) {
         Log.LogWrite(e.ToString() + "\n", true);
     }
     return(true);
 }
예제 #26
0
 private static bool Prefix(AbstractActor __instance)
 {
     return(false);
 }
 public static bool isAuraVisible(this CombatAuraReticle __instance, AuraBubble aura, AbstractActor ___owner, CombatHUD ___HUD, bool spinning)
 {
     if (CombatHUD_Update_HideReticlesHotKey.hideReticles == AuraShowState.HideAll)
     {
         return(false);
     }
     ;
     if ((___owner.IsVisibleToPlayer() == false) || (___owner.IsOperational == false))
     {
         return(false);
     }
     if (aura != null)
     {
         if (aura.Def.isSpining != spinning)
         {
             return(false);
         }
         if (aura.isMainSensors)
         {
             if (CombatHUD_Update_HideReticlesHotKey.hideReticles == AuraShowState.ShowAll)
             {
                 return(true);
             }
             if (aura.Def.NotShowOnSelected)
             {
                 return(false);
             }
             ;
             if (aura.Def.HideOnNotSelected)
             {
                 if (___HUD.SelectedActor != null)
                 {
                     if (___HUD.SelectedActor.GUID == ___owner.GUID)
                     {
                         return(true);
                     }
                 }
                 return(false);
             }
             else
             {
                 return(true);
             }
         }
         if (aura.source != null)
         {
             if (aura.Def.isSpining != spinning)
             {
                 return(false);
             }
             Weapon weapon = aura.source as Weapon;
             if ((weapon == null) || (aura.Def.Id != "AMS"))
             {
                 if (CombatHUD_Update_HideReticlesHotKey.hideReticles == AuraShowState.ShowAll)
                 {
                     return(true);
                 }
                 if (aura.Def.NotShowOnSelected)
                 {
                     return(false);
                 }
                 ;
                 if (aura.Def.HideOnNotSelected)
                 {
                     if (___HUD.SelectedActor != null)
                     {
                         if (___HUD.SelectedActor.GUID == ___owner.GUID)
                         {
                             return(true);
                         }
                     }
                     return(false);
                 }
                 else
                 {
                     return(true);
                 }
             }
             else
             {
                 if (weapon.isAMS() == false)
                 {
                     return(false);
                 }
                 if (weapon.IsEnabled == false)
                 {
                     return(false);
                 }
                 if (CombatHUD_Update_HideReticlesHotKey.hideReticles == AuraShowState.ShowAll)
                 {
                     return(true);
                 }
                 if (aura.Def.NotShowOnSelected)
                 {
                     return(false);
                 }
                 ;
                 if (aura.Def.HideOnNotSelected)
                 {
                     if (___HUD.SelectedActor != null)
                     {
                         if (___HUD.SelectedActor.GUID == ___owner.GUID)
                         {
                             return(true);
                         }
                     }
                     return(false);
                 }
                 else
                 {
                     return(true);
                 }
             }
         }
         else
         {
             return(false);
         }
     }
     return(false);
 }
예제 #28
0
        public static float GetSavingThrow(AbstractActor defender, AbstractActor attacker, int heatDamage, float damageIncludingHeatDamage)
        {
            var pilot             = defender.GetPilot();
            var weapons           = defender.Weapons;
            var gutsAndTacticsSum = defender.SkillGuts * modSettings.GutsEjectionResistPerPoint +
                                    defender.SkillTactics * modSettings.TacticsEjectionResistPerPoint;
            float totalMultiplier = 0;

            DrawHeader();
            LogReport($"{$"Unit health {ActorHealth(defender):F2}%",-20} | {"",10} |");

            if (defender is Mech defendingMech)
            {
                try
                {
                    if (modSettings.QuirksEnabled && attacker != null &&
                        attacker is Mech mech &&
                        mech.MechDef.Chassis.ChassisTags.Contains("mech_quirk_distracting"))
                    {
                        totalMultiplier += modSettings.DistractingModifier;
                        LogReport($"{"Distracting mech",-20} | {modSettings.DistractingModifier,10:F3} | {totalMultiplier,10:F3}");
                    }
#if NO_CAC
                    if (modSettings.HeatDamageFactor > 0)
                    {
#else
                    if (modSettings.HeatDamageFactor > 0 && defender.isHasHeat())
                    {
#endif
                        totalMultiplier += modSettings.HeatDamageFactor * heatDamage;
                        LogReport($"{$"Heat damage {heatDamage}",-20} | {modSettings.HeatDamageFactor * heatDamage,10:F3} | {totalMultiplier,10:F3}");
                    }

                    float percentPilot = PercentPilot(pilot);
                    if (percentPilot < 1)
                    {
                        totalMultiplier += modSettings.PilotHealthMaxModifier * percentPilot;
                        LogReport($"{"Pilot injuries",-20} | {modSettings.PilotHealthMaxModifier * percentPilot,10:F3} | {totalMultiplier,10:F3}");
                    }

                    if (defendingMech.IsUnsteady)
                    {
                        totalMultiplier += modSettings.UnsteadyModifier;
                        LogReport($"{"Unsteady",-20} | {modSettings.UnsteadyModifier,10} | {totalMultiplier,10:F3}");
                    }

                    if (defendingMech.IsFlaggedForKnockdown)
                    {
                        totalMultiplier += modSettings.UnsteadyModifier;
                        LogReport($"{"Knockdown",-20} | {modSettings.UnsteadyModifier,10} | {totalMultiplier,10:F3}");
                    }

                    if (modSettings.OverheatedModifier > 0 && defendingMech.OverheatLevel < defendingMech.CurrentHeat)
                    {
                        totalMultiplier += modSettings.OverheatedModifier;
                        LogReport($"{"Heat",-20} | {modSettings.OverheatedModifier,10:F3} | {totalMultiplier,10:F3}");
                    }

                    if (modSettings.ShutdownModifier > 0 && defendingMech.IsShutDown)
                    {
                        totalMultiplier += modSettings.ShutdownModifier;
                        LogReport($"{"Shutdown",-20} | {modSettings.ShutdownModifier,10:F3} | {totalMultiplier,10:F3}");
                    }

                    float percentHead = PercentHead(defendingMech);
                    if (percentHead < 1)
                    {
                        totalMultiplier += modSettings.HeadMaxModifier * (1 - percentHead);
                        LogReport($"{"Head",-20} | {modSettings.HeadMaxModifier * (1 - percentHead),10:F3} | {totalMultiplier,10:F3}");
                    }

                    float percentCenterTorso = PercentCenterTorso(defendingMech);
                    if (percentCenterTorso < 1)
                    {
                        totalMultiplier += modSettings.CenterTorsoMaxModifier * (1 - percentCenterTorso);
                        LogReport($"{"CT",-20} | {modSettings.CenterTorsoMaxModifier * (1 - percentCenterTorso),10:F3} | {totalMultiplier,10:F3}");
                    }

                    float percentLeftTorso = PercentLeftTorso(defendingMech);
                    if (percentLeftTorso < 1)
                    {
                        totalMultiplier += modSettings.SideTorsoMaxModifier * (1 - percentLeftTorso);
                        LogReport($"{"LT",-20} | {modSettings.SideTorsoMaxModifier * (1 - percentLeftTorso),10:F3} | {totalMultiplier,10:F3}");
                    }

                    float percentRightTorso = PercentRightTorso(defendingMech);
                    if (percentRightTorso < 1)
                    {
                        totalMultiplier += modSettings.SideTorsoMaxModifier * (1 - percentRightTorso);
                        LogReport($"{"RT",-20} | {modSettings.SideTorsoMaxModifier * (1 - percentRightTorso),10:F3} | {totalMultiplier,10:F3}");
                    }

                    float percentLeftLeg = PercentLeftLeg(defendingMech);
                    if (percentLeftLeg < 1)
                    {
                        totalMultiplier += modSettings.LeggedMaxModifier * (1 - percentLeftLeg);
                        LogReport($"{"LL",-20} | {modSettings.LeggedMaxModifier * (1 - percentLeftLeg),10:F3} | {totalMultiplier,10:F3}");
                    }

                    float percentRightLeg = PercentRightLeg(defendingMech);
                    if (percentRightLeg < 1)
                    {
                        totalMultiplier += modSettings.LeggedMaxModifier * (1 - percentRightLeg);
                        LogReport($"{"RL",-20} | {modSettings.LeggedMaxModifier * (1 - percentRightLeg),10:F3} | {totalMultiplier,10:F3}");
                    }

                    // alone
                    if (defendingMech.Combat.GetAllAlliesOf(defendingMech).TrueForAll(m => m.IsDead || m == defendingMech))
                    {
                        if (Random.Range(1, 5) == 0) // 20% chance of appearing
                        {
                            SaySpamFloatie(defendingMech, $"{modSettings.PanicSpamAloneString}");
                        }

                        totalMultiplier += modSettings.AloneModifier;
                        LogReport($"{"Alone",-20} | {modSettings.AloneModifier,10} | {totalMultiplier,10:F3}");
                    }
                    else if (defendingMech.Combat.GetAllAlliesOf(defendingMech).Count() > 0)
                    {
                        int alliesdead  = defendingMech.Combat.GetAllAlliesOf(defendingMech).Where(m => m.IsDead).Count();
                        int alliestotal = defendingMech.Combat.GetAllAlliesOf(defendingMech).Count();

                        totalMultiplier += modSettings.AloneModifier * alliesdead / alliestotal;
                        LogReport($"{$"Alone {alliesdead}/{alliestotal}",-20} | {modSettings.AloneModifier * alliesdead / alliestotal,10:F3} | {totalMultiplier,10:F3}");
                    }
                }
                catch (Exception ex)
                {
                    // BOMB
                    LogReport(ex);
                    return(-1f);
                }
            }

            // weaponless
            if (weapons.TrueForAll(w => w.DamageLevel != ComponentDamageLevel.Functional || !w.HasAmmo)) // only fully unusable
            {
                if (Random.Range(1, 5) == 1)                                                             // 20% chance of appearing
                {
                    SaySpamFloatie(defender, $"{modSettings.PanicSpamNoWeaponsString}");
                }

                totalMultiplier += modSettings.WeaponlessModifier;
                LogReport($"{"Weaponless",-20} | {modSettings.WeaponlessModifier,10} | {totalMultiplier,10:F3}");
            }

            // directly override the multiplier for vehicles
            if (modSettings.VehiclesCanPanic &&
                defender is Vehicle defendingVehicle)
            {
                float percentTurret = PercentTurret(defendingVehicle);
                if (percentTurret < 1)
                {
                    totalMultiplier += modSettings.VehicleDamageFactor * (1 - percentTurret);
                    LogReport($"{"T",-20} | {modSettings.VehicleDamageFactor * (1 - percentTurret),10:F3} | {totalMultiplier,10:F3}");
                }
                float percentLeft = PercentLeft(defendingVehicle);
                if (percentLeft < 1)
                {
                    totalMultiplier += modSettings.VehicleDamageFactor * (1 - percentLeft);
                    LogReport($"{"L",-20} | {modSettings.VehicleDamageFactor * (1 - percentLeft),10:F3} | {totalMultiplier,10:F3}");
                }
                float percentRight = PercentRight(defendingVehicle);
                if (percentRight < 1)
                {
                    totalMultiplier += modSettings.VehicleDamageFactor * (1 - percentRight);
                    LogReport($"{"R",-20} | {modSettings.VehicleDamageFactor * (1 - percentRight),10:F3} | {totalMultiplier,10:F3}");
                }
                float percentFront = PercentFront(defendingVehicle);
                if (percentFront < 1)
                {
                    totalMultiplier += modSettings.VehicleDamageFactor * (1 - percentFront);
                    LogReport($"{"F",-20} | {modSettings.VehicleDamageFactor * (1 - percentFront),10:F3} | {totalMultiplier,10:F3}");
                }
                float percentRear = PercentRear(defendingVehicle);
                if (percentRear < 1)
                {
                    totalMultiplier += modSettings.VehicleDamageFactor * (1 - percentRear);
                    LogReport($"{"B",-20} | {modSettings.VehicleDamageFactor * (1 - percentRear),10:F3} | {totalMultiplier,10:F3}");
                }
                LogReport($"{"Vehicle state",-20} | {modSettings.VehicleDamageFactor,10} | {totalMultiplier,10:F3}");

                // alone
                if (defendingVehicle.Combat.GetAllAlliesOf(defendingVehicle).TrueForAll(m => m.IsDead || m == defendingVehicle))
                {
                    if (Random.Range(1, 5) == 0) // 20% chance of appearing
                    {
                        SaySpamFloatie(defendingVehicle, $"{modSettings.PanicSpamAloneString}");
                    }

                    totalMultiplier += modSettings.AloneModifier;
                    LogReport($"{"Alone",-20} | {modSettings.AloneModifier,10} | {totalMultiplier,10:F3}");
                }
                else if (defendingVehicle.Combat.GetAllAlliesOf(defendingVehicle).Count() > 0)
                {
                    int alliesdead  = defendingVehicle.Combat.GetAllAlliesOf(defendingVehicle).Where(m => m.IsDead).Count();
                    int alliestotal = defendingVehicle.Combat.GetAllAlliesOf(defendingVehicle).Count();

                    totalMultiplier += modSettings.AloneModifier * alliesdead / alliestotal;
                    LogReport($"{$"Alone {alliesdead}/{alliestotal}",-20} | {modSettings.AloneModifier * alliesdead / alliestotal,10:F3} | {totalMultiplier,10:F3}");
                }
            }

            var resolveModifier = modSettings.ResolveMaxModifier *
                                  (defender.Combat.LocalPlayerTeam.Morale - modSettings.MedianResolve) / modSettings.MedianResolve;

            if (modSettings.VehiclesCanPanic &&
                defender is Vehicle)
            {
                resolveModifier *= modSettings.VehicleResolveFactor;
            }

            totalMultiplier -= resolveModifier;
            LogReport($"{$"Resolve {defender.Combat.LocalPlayerTeam.Morale}",-20} | {resolveModifier * -1,10:F3} | {totalMultiplier,10:F3}");

            if (modSettings.VehiclesCanPanic &&
                defender is Vehicle)
            {
                gutsAndTacticsSum *= modSettings.VehicleGutAndTacticsFactor;
            }

            totalMultiplier -= gutsAndTacticsSum;

            LogReport($"{"Guts and Tactics",-20} | {$"-{gutsAndTacticsSum}",10} | {totalMultiplier,10:F3}");
            return(totalMultiplier);
        }
예제 #29
0
 public static void AbstractActor_InitEffectStats_Postfix(AbstractActor __instance)
 {
     Mod.Log.Trace("AA:IES entered.");
     __instance.StatCollection.AddStatistic <Int32>(ModStats.CalledShotMod, 0);
     __instance.StatCollection.AddStatistic <bool>(ModStats.CalledShowAlwaysAllow, false);
 }
예제 #30
0
 internal static void PublishFloatieMessage(this AbstractActor actor, string text, FloatieMessage.MessageNature nature = FloatieMessage.MessageNature.CriticalHit)
 {
     actor.Combat.MessageCenter.PublishMessage(new FloatieMessage(actor.GUID, actor.GUID, text, nature));
 }