public static void AppendDangerousTerrainText(MoveStatusPreview __instance, AbstractActor actor, Vector3 worldPos)
 {
     try {
         MapTerrainDataCell cell = Combat.EncounterLayerData.GetCellAt(worldPos).relatedTerrainCell;
         bool isLandingZone      = SplatMapInfo.IsDropshipLandingZone(cell.terrainMask),
              isDangerous        = SplatMapInfo.IsDangerousLocation(cell.terrainMask);
         if (!isLandingZone && !isDangerous)
         {
             return;
         }
         DesignMaskDef mask = Combat.MapMetaData.GetPriorityDesignMask(cell);
         if (mask == null)
         {
             return;
         }
         string title = mask.Description.Name, text = mask.Description.Details;
         CombatUIConstantsDef desc = Combat.Constants.CombatUIConstants;
         if (isDangerous)
         {
             title += " <#FF0000>(" + desc.DangerousLocationDesc.Name + ")";
             text  += " <#FF0000>" + desc.DangerousLocationDesc.Details;
             if (isLandingZone)
             {
                 text += " " + desc.DrophipLocationDesc.Details;
             }
         }
         else
         {
             title += " <#FF0000>(" + desc.DrophipLocationDesc.Name + ")";
             text  += " <#FF0000>" + desc.DrophipLocationDesc.Details;
         }
         CombatHUDInfoSidePanel sidePanel = (CombatHUDInfoSidePanel)SidePanelProp?.GetValue(__instance, null);
         sidePanel?.ForceShowSingleFrame(new Text(title), new Text(text), null, false);
     }                 catch (Exception ex) { Error(ex); }
 }
        // WARNING: DUPLICATE OF HBS CODE. THIS IS LIKELY TO BREAK IF HBS CHANGES THE SOURCE FUNCTIONS
        public static float GetTargetSignature(ICombatant target, EWState sourceState)
        {
            if (target == null || (target as AbstractActor) == null)
            {
                return(1f);
            }

            AbstractActor targetActor = target as AbstractActor;
            float         allTargetSignatureModifiers = GetAllTargetSignatureModifiers(targetActor, sourceState);
            float         staticSignature             = 1f * allTargetSignatureModifiers;

            // Add in any design mask boosts
            DesignMaskDef occupiedDesignMask = targetActor.occupiedDesignMask;

            if (occupiedDesignMask != null)
            {
                staticSignature *= occupiedDesignMask.signatureMultiplier;
            }

            if (staticSignature < Mod.Config.Sensors.MinSignature)
            {
                staticSignature = Mod.Config.Sensors.MinSignature;
            }

            return(staticSignature);
        }
Beispiel #3
0
        public AttackDetails(AbstractActor attacker, ICombatant target, Vector3 attackPos, Vector3 targetPos, bool useRevengeBonus)
        {
            this.Attacker = attacker;
            this.Target   = target;

            this.AttackPosition = attackPos;
            this.TargetPosition = targetPos;

            this.UseRevengeBonus = useRevengeBonus;

            // Precalculate some values heavily used by the prediction engine

            // Impact quality for any melee attack is always solid
            this.BaseRangedImpactQuality = SharedState.Combat.ToHit.GetBlowQuality(attacker, attackPos,
                                                                                   null, target, MeleeAttackType.Punch, false);

            this.AttackerDesignMask = SharedState.Combat.MapMetaData.GetPriorityDesignMaskAtPos(attackPos);
            if (this.AttackerDesignMask == null)
            {
                AttackerDesignMask = new DesignMaskDef();
            }
            this.TargetDesignMask = SharedState.Combat.MapMetaData.GetPriorityDesignMaskAtPos(targetPos);
            if (this.TargetDesignMask == null)
            {
                TargetDesignMask = new DesignMaskDef();
            }

            // Calculate the total damage multiplier for attacks by weaponType
            this.DamageMultipliers.Add(DamageMultiType.Ballistic, CalculateDamageMulti(DamageMultiType.Ballistic, target));
            this.DamageMultipliers.Add(DamageMultiType.Energy, CalculateDamageMulti(DamageMultiType.Energy, target));
            this.DamageMultipliers.Add(DamageMultiType.Missile, CalculateDamageMulti(DamageMultiType.Missile, target));
            this.DamageMultipliers.Add(DamageMultiType.Support, CalculateDamageMulti(DamageMultiType.Support, target));
            this.DamageMultipliers.Add(DamageMultiType.Generic, CalculateDamageMulti(DamageMultiType.Generic, target));
        }
        // This method is used by the IL generators in IntermediateLanguageFuckery
        public static float VariantDamage(WeaponEffect weaponEffect, DesignMaskDef designMask)
        {
            var weapon = weaponEffect.weapon;
            var key    = ShotMemoKey(weaponEffect);

            if (DamageWasAlreadyCalculated(key, out var variantDamage))
            {
                return(variantDamage);
            }
            if (IsNonVariantWeapon(key, weapon, out var damageVariance, out var normalDamage))
            {
                return(normalDamage);
            }

            // the following damage calcs should match with Weapon.DamagePerShotAdjusted(DesignMaskDef), with
            // the addition of the variance computations
            var damagePerShot = weapon.DamagePerShotAdjusted();

            Logger.Debug(
                $"some damage numbers:\n" +
                $"weapon damage: {weapon.DamagePerShot}\n" +
                $"weapon damage adjusted: {weapon.DamagePerShotAdjusted()}\n" +
                $"stats based: {weapon.StatCollection.GetValue<float>("DamagePerShot")}"
                );

            var bounds = new VarianceBounds(
                min: damagePerShot - damageVariance,
                max: damagePerShot + damageVariance,
                standardDeviation: ModSettings.StandardDeviationVarianceMultiplier * damageVariance
                );
            var damage        = Utility.NormalDistibutionRandom(bounds);
            var combat        = Traverse.Create(weapon).Field("combat").GetValue <CombatGameState>();
            var damageWDesign = damage * weapon.GetMaskDamageMultiplier(weapon.parent.occupiedDesignMask);
            var result        = damageWDesign * weapon.GetMaskDamageMultiplier(combat.MapMetaData.biomeDesignMask);

            Logger.Debug(
                $"effect id: {key.weaponEffectId}\n" +
                $"hit index: {key.hitIndex}\n" +
                $"damage and variance: {damagePerShot}+-{damageVariance}\n" +
                $"damage range: {bounds.min}-{bounds.max} (std. dev. {bounds.standardDeviation}\n" +
                $"computed damage: {damage}\n" +
                $"damage w/ design mask: {damageWDesign}\n" +
                $"damage w/ env: {result}"
                );
            WeaponDamageMemo[key] = result;
            return(WeaponDamageMemo[key]);
        }
        // WARNING: DUPLICATE OF HBS CODE. THIS IS LIKELY TO BREAK IF HBS CHANGES THE SOURCE FUNCTIONS
        public static float GetAllSensorRangeMultipliers(AbstractActor source)
        {
            if (source == null)
            {
                return(1f);
            }
            float sensorMulti = source.SensorDistanceMultiplier;

            DesignMaskDef occupiedDesignMask = source.occupiedDesignMask;

            if (occupiedDesignMask != null)
            {
                sensorMulti *= occupiedDesignMask.sensorRangeMultiplier;
            }

            return(sensorMulti);
        }
        // Replicates logic from Mech::AdjustedHeatSinkCapacity to allow displaying multiplier
        public static float DesignMaskHeatMulti(this Mech mech, bool isProjectedHeat)
        {
            float capacityMulti = 1f;

            try {
                // Check for currently occupied, or future
                if (isProjectedHeat)
                {
                    Mod.HeatLog.Trace?.Write("Calculating projected position heat.");
                    if (mech.Pathing != null && mech.Pathing.CurrentPath != null && mech.Pathing.CurrentPath.Count > 0)
                    {
                        // Determine the destination designMask
                        Mod.HeatLog.Trace?.Write($"CurrentPath has: {mech.Pathing.CurrentPath.Count} nodes, using destination path: {mech.Pathing.ResultDestination}");
                        DesignMaskDef destinationDesignMaskDef = mech?.Combat?.MapMetaData?.GetPriorityDesignMaskAtPos(mech.Pathing.ResultDestination);
                        if (destinationDesignMaskDef != null && !Mathf.Approximately(destinationDesignMaskDef.heatSinkMultiplier, 1f))
                        {
                            Mod.HeatLog.Trace?.Write($"Destination design mask: {destinationDesignMaskDef?.Description?.Name} has heatSinkMulti: x{destinationDesignMaskDef?.heatSinkMultiplier} ");
                            capacityMulti *= destinationDesignMaskDef.heatSinkMultiplier;
                        }

                        // Check for any cells along the way that will apply the burning sticky effect.
                        //   See CustomAmmoCategories\designmask\DesignMaskBurningForest
                        List <WayPoint> waypointsFromPath = ActorMovementSequence.ExtractWaypointsFromPath(
                            mech, mech.Pathing.CurrentPath, mech.Pathing.ResultDestination, (ICombatant)mech.Pathing.CurrentMeleeTarget, mech.Pathing.MoveType
                            );
                        List <MapTerrainCellWaypoint> terrainWaypoints = DynamicMapHelper.getVisitedWaypoints(mech.Combat, waypointsFromPath);
                        Mod.HeatLog.Trace?.Write($"  Count of waypointsFromPath: {waypointsFromPath?.Count}  terrainWaypoints: {terrainWaypoints?.Count}");

                        // This assumes 1) only KMission is using stickyEffects that modify HeatSinkCapacity and 2) it has a stackLimit of 1. Anything else will break this.
                        float stickyModifier = 1f;
                        foreach (MapTerrainCellWaypoint cell in terrainWaypoints)
                        {
                            if (cell != null && cell?.cell?.BurningStrength > 0 && cell?.cell?.mapMetaData?.designMaskDefs != null)
                            {
                                Mod.HeatLog.Trace?.Write($"  checking burningCell for designMask.");
                                foreach (DesignMaskDef cellDesignMaskDef in cell?.cell?.mapMetaData?.designMaskDefs?.Values)
                                {
                                    Mod.HeatLog.Trace?.Write($"    checking designMask for stickyEffects.");
                                    if (cellDesignMaskDef.stickyEffect != null && cellDesignMaskDef.stickyEffect?.statisticData != null &&
                                        cellDesignMaskDef.stickyEffect.statisticData.statName == ModStats.HBS_HeatSinkCapacity)
                                    {
                                        Mod.HeatLog.Trace?.Write($"      found stickyEffects.");
                                        stickyModifier = Single.Parse(cellDesignMaskDef.stickyEffect.statisticData.modValue);
                                    }
                                }
                            }
                        }
                        if (!Mathf.Approximately(stickyModifier, 1f))
                        {
                            capacityMulti *= stickyModifier;
                            Mod.HeatLog.Trace?.Write($"  capacityMulti: {capacityMulti} after stickyModifier: {stickyModifier}");
                        }
                    }
                    else
                    {
                        Mod.HeatLog.Trace?.Write($"Current path is null or has 0 count, skipping.");
                    }
                }
                else
                {
                    Mod.HeatLog.Trace?.Write("Calculating current position heat.");
                    if (mech.occupiedDesignMask != null && !Mathf.Approximately(mech.occupiedDesignMask.heatSinkMultiplier, 1f))
                    {
                        Mod.HeatLog.Trace?.Write($"Multi for currentPos is: {mech?.occupiedDesignMask?.heatSinkMultiplier}");
                        capacityMulti *= mech.occupiedDesignMask.heatSinkMultiplier;
                    }
                }

                if (mech?.Combat?.MapMetaData?.biomeDesignMask != null && !Mathf.Approximately(mech.Combat.MapMetaData.biomeDesignMask.heatSinkMultiplier, 1f))
                {
                    Mod.HeatLog.Trace?.Write($"Biome: {mech.Combat.MapMetaData.biomeDesignMask.Id} has heatSinkMulti: x{mech.Combat.MapMetaData.biomeDesignMask.heatSinkMultiplier} ");
                    capacityMulti *= mech.Combat.MapMetaData.biomeDesignMask.heatSinkMultiplier;
                }
            } catch (Exception e) {
                Mod.HeatLog.Error?.Write(e, $"Failed to calculate designMaskHeatMulti due to error: {e}");
            }

            Mod.HeatLog.Trace?.Write($"Calculated capacityMulti: {capacityMulti} x globalHeatSinkMulti: {mech.Combat.Constants.Heat.GlobalHeatSinkMultiplier} ");
            capacityMulti *= mech.Combat.Constants.Heat.GlobalHeatSinkMultiplier;
            return(capacityMulti);
        }