Exemplo n.º 1
0
        private static float GetAccuracy(Thing weapon, VerbProperties verb, ProjectileProperties projectile, float dist, Pawn pawn = null)
        {
            float forcedMissRadius = CalculateAdjustedForcedMissDist(verb.ForcedMissRadius, dist);
            float baseAimOn        = verb.GetHitChanceFactor(weapon, dist);

            if (pawn != null)
            {
                baseAimOn *= ShotReport.HitFactorFromShooter(pawn, dist);
            }
            int affectedCellCount = (verb.CausesExplosion) ? GenRadial.NumCellsInRadius(projectile.explosionRadius) : 1;

            float accuracy;

            if (forcedMissRadius > 0.5f)
            {
                int affectableCellCount = GenRadial.NumCellsInRadius(forcedMissRadius);
                accuracy = (float)affectedCellCount / affectableCellCount;
            }
            else
            {
                float medianToWildRadius = ShootTuning.MissDistanceFromAimOnChanceCurves.Evaluate(baseAimOn, 0.5f);
                float indirectHitChance  = (float)(affectedCellCount - 1) / GenRadial.NumCellsInRadius(medianToWildRadius);
                accuracy = baseAimOn + (1f - baseAimOn) * indirectHitChance;
            }
            return(Mathf.Clamp01(accuracy));
        }
Exemplo n.º 2
0
        public static float RangedDPS(ThingWithComps weapon, float speedBias, float averageSpeed, float range)
        {
            Verb           atkVerb  = (weapon.GetComp <CompEquippable>()).PrimaryVerb;
            VerbProperties atkProps = atkVerb.verbProps;

            if (atkProps.range * atkProps.range < range || atkProps.minRange * atkProps.minRange > range)
            {
                return(-1);
            }

            float hitChance       = atkProps.GetHitChanceFactor(weapon, range);
            float damage          = (atkProps.defaultProjectile == null) ? 0 : atkProps.defaultProjectile.projectile.GetDamageAmount(weapon);
            int   burstShot       = atkProps.burstShotCount;
            float speedFactor     = RangedSpeed(weapon);
            float speedFactorBase = speedFactor;

            float diffFromAverage = speedFactor - averageSpeed;

            diffFromAverage *= (speedBias - 1);
            speedFactor     += diffFromAverage;

            float rawDps = (damage * burstShot) / speedFactor;
            float Dps    = rawDps * hitChance;

            //Log.Message(weapon.LabelCap + " dps:" + rawDps + "dam:"+damage*burstShot + " spdfac:" + speedFactor + " spdFacBase:" + speedFactorBase);
            return(Dps);
        }
        /// <summary>
        /// Gets the adjusted hit chance factor of a shot.  This is equivalent to shootVerb.GetHitChanceFactor() unless
        /// a shooter is provided, in which case it will also be adjusted based on the shooter's hit chance.
        ///
        /// This value can be greater than 1.0 in the case of weapons with overcapped accuracy.
        /// </summary>
        /// <returns>The adjusted hit chance factor.</returns>
        /// <param name="range">The range of the shot.</param>
        /// <param name="shooter">(Optional) The turret or pawn shooting the weapon.</param>
        public float GetAdjustedHitChanceFactor(float range, Thing shooter = null)
        {
            float hitChance = shootVerb.GetHitChanceFactor(weapon, range);

            if (shooter != null)
            {
                hitChance *= ShotReport.HitFactorFromShooter(shooter, range);
            }

            return(hitChance);
        }
Exemplo n.º 4
0
        internal static float RangedDPS(ThingWithComps weapon, float speedBias, float range)
        {
            Verb           atkVerb  = (weapon.GetComp <CompEquippable>()).PrimaryVerb;
            VerbProperties atkProps = atkVerb.verbProps;

            if (atkProps.range * atkProps.range < range || atkProps.minRange * atkProps.minRange > range)
            {
                return(-1);
            }

            float hitChance         = atkProps.GetHitChanceFactor(weapon, range);
            float damage            = (atkProps.defaultProjectile == null) ? 0 : atkProps.defaultProjectile.projectile.GetDamageAmount(weapon);
            float warmup            = atkProps.warmupTime;
            float cooldown          = weapon.def.GetStatValueAbstract(StatDefOf.RangedWeapon_Cooldown, null);
            int   burstShot         = atkProps.burstShotCount;
            int   ticksBetweenShots = atkProps.ticksBetweenBurstShots;
            float rawDps            = (damage * burstShot) / (((warmup + cooldown)) + warmup * (speedBias - 1f) + (burstShot - 1) * (ticksBetweenShots / 60f));
            float Dps = rawDps * hitChance;

            return(Dps);
        }
Exemplo n.º 5
0
        public override float GetValueUnfinalized(StatRequest req, bool applyPostProcess = true)
        {
            if (!(req.Def is ThingDef def))
            {
                return(0.0f);
            }

            CompEquippable         equipComp = null;
            CompLootAffixableThing lootComp  = null;

            if (req.HasThing)
            {
                var thing = (ThingWithComps)req.Thing;
                equipComp = thing.TryGetComp <CompEquippable>();
                lootComp  = thing.TryGetComp <CompLootAffixableThing>();
            }

            Verb           verb      = equipComp?.AllVerbs.First(v => v.verbProps.isPrimary);
            VerbProperties verbProps = verb != null ? verb.verbProps : def.Verbs.First(vp => vp.isPrimary);
            Pawn           attacker  = req.HasThing ? GetCurrentWeaponUser(req.Thing) : null;
            var            projProps = verbProps.defaultProjectile.projectile;

            var projModifier = (LootAffixModifier_VerbPropertiesChange_Def)lootComp?.AllModifiers.FirstOrFallback(
                lam => lam.AppliesTo == ModifierTarget.VerbProperties && lam is LootAffixModifier_VerbPropertiesChange_Def lamVPCD && lamVPCD.affectedField == "defaultProjectile"
                );
            ThingDef modProjectile = projModifier != null ? (ThingDef)projModifier.resolvedDef : null;
            var      modProjProps  = modProjectile?.projectile;

            float chance = modProjProps != null ? 1f - projModifier.GetRealChance(lootComp.parent) : 1f;

            if (chance <= 0.05f)
            {
                chance = 1f;                   // already permanently set to "base" verbProps
            }
            float baseDamage =
                projProps.damageDef.harmsHealth == false ? 0f :
                req.HasThing         ? projProps.GetDamageAmount(req.Thing) :
                req.StuffDef != null?projProps.GetDamageAmount(def.GetStatValueAbstract(StatDefOf.RangedWeapon_DamageMultiplier, req.StuffDef)) :
                    projProps.GetDamageAmount(def.GetStatValueAbstract(StatDefOf.RangedWeapon_DamageMultiplier))
            ;

            float damage = baseDamage * verbProps.burstShotCount * chance;

            if (chance < 1f)
            {
                float modChance     = 1f - chance;
                float modBaseDamage =
                    modProjProps.damageDef.harmsHealth == false ? 0f :
                    modProjProps.GetDamageAmount(req.Thing)
                ;

                damage += modBaseDamage * verbProps.burstShotCount * modChance;
            }

            // FIXME: Confirm warmupTime (and AimingDelayFactor) is used in a full shot cycle
            // FIXME: warmupTime * this.CasterPawn.GetStatValue(StatDefOf.AimingDelayFactor, true)).SecondsToTicks()
            float secondsSpent = 0;

            if (verb != null)
            {
                secondsSpent = verbProps.AdjustedFullCycleTime(verb, attacker);
            }
            else
            {
                secondsSpent  = verbProps.warmupTime + ((verbProps.burstShotCount - 1) * verbProps.ticksBetweenBurstShots).TicksToSeconds();
                secondsSpent +=
                    req.HasThing         ? req.Thing.GetStatValue(StatDefOf.RangedWeapon_Cooldown, true) :
                    req.StuffDef != null?def.GetStatValueAbstract(StatDefOf.RangedWeapon_Cooldown, req.StuffDef) :
                        def.GetStatValueAbstract(StatDefOf.RangedWeapon_Cooldown)
                ;
            }

            // Every integer range possible as an average
            float avgAccuracy = 0;

            for (int i = 3; i <= verbProps.range; i++)
            {
                float rngAccuracy = verbProps.GetHitChanceFactor(req.Thing, i);
                if (attacker != null)
                {
                    rngAccuracy *= ShotReport.HitFactorFromShooter(attacker, i);
                }
                avgAccuracy += rngAccuracy;
            }
            if (verbProps.range >= 3)
            {
                avgAccuracy /= verbProps.range - 2;
            }

            return(secondsSpent == 0 ? 0.0f : damage / secondsSpent * avgAccuracy);
        }
Exemplo n.º 6
0
        public override string GetExplanationUnfinalized(StatRequest req, ToStringNumberSense numberSense)
        {
            if (!(req.Def is ThingDef def))
            {
                return(null);
            }

            /* Damage section */
            CompEquippable         equipComp = null;
            CompLootAffixableThing lootComp  = null;

            if (req.HasThing)
            {
                var thing = (ThingWithComps)req.Thing;
                equipComp = thing.TryGetComp <CompEquippable>();
                lootComp  = thing.TryGetComp <CompLootAffixableThing>();
            }

            Verb           verb      = equipComp?.AllVerbs.First(v => v.verbProps.isPrimary);
            VerbProperties verbProps = verb != null ? verb.verbProps : def.Verbs.First(vp => vp.isPrimary);
            Pawn           attacker  = req.HasThing ? GetCurrentWeaponUser(req.Thing) : null;
            var            projProps = verbProps.defaultProjectile.projectile;

            var projModifier = (LootAffixModifier_VerbPropertiesChange_Def)lootComp?.AllModifiers.FirstOrFallback(
                lam => lam.AppliesTo == ModifierTarget.VerbProperties && lam is LootAffixModifier_VerbPropertiesChange_Def lamVPCD && lamVPCD.affectedField == "defaultProjectile"
                );
            ThingDef modProjectile = projModifier != null ? (ThingDef)projModifier.resolvedDef : null;
            var      modProjProps  = modProjectile?.projectile;

            float chance = modProjProps != null ? 1f - projModifier.GetRealChance(lootComp.parent) : 1f;

            if (chance <= 0.05f)
            {
                chance = 1f;                   // already permanently set to "base" verbProps
            }
            string chanceStr = GenText.ToStringPercent(chance);

            float baseDamage =
                projProps.damageDef.harmsHealth == false ? 0f :
                req.HasThing         ? projProps.GetDamageAmount(req.Thing) :
                req.StuffDef != null?projProps.GetDamageAmount(def.GetStatValueAbstract(StatDefOf.RangedWeapon_DamageMultiplier, req.StuffDef)) :
                    projProps.GetDamageAmount(def.GetStatValueAbstract(StatDefOf.RangedWeapon_DamageMultiplier))
            ;

            float damage = baseDamage * verbProps.burstShotCount * chance;

            string reportText = "Damage".Translate() + ":\n";

            if (chance < 1f)
            {
                reportText += "    " + "RimLoot_StatsReport_ProjectileWithChance".Translate(
                    verbProps.defaultProjectile.Named("PROJECTILE"), chanceStr.Named("chance")
                    ) + "\n";
                reportText += string.Format("    {0}: {1} * {2} * {3} = {4}\n\n",
                                            "Damage".Translate(),
                                            baseDamage.ToStringDecimalIfSmall(),
                                            verbProps.burstShotCount,
                                            chanceStr,
                                            damage.ToStringDecimalIfSmall()
                                            );

                float  modChance    = 1f - chance;
                string modChanceStr = GenText.ToStringPercent(modChance);

                float modBaseDamage =
                    modProjProps.damageDef.harmsHealth == false ? 0f :
                    modProjProps.GetDamageAmount(req.Thing)
                ;
                float modDamage = modBaseDamage * verbProps.burstShotCount * modChance;

                reportText += "    " + "RimLoot_StatsReport_ProjectileWithChance".Translate(
                    modProjectile.Named("PROJECTILE"), modChanceStr.Named("chance")
                    ) + "\n";
                reportText += string.Format("    {0}: {1} * {2} * {3} = {4}\n\n",
                                            "Damage".Translate(),
                                            modBaseDamage.ToStringDecimalIfSmall(),
                                            verbProps.burstShotCount,
                                            modChanceStr,
                                            modDamage.ToStringDecimalIfSmall()
                                            );

                reportText += string.Format("{0}: {1}\n\n", "StatsReport_TotalValue".Translate(), (damage + modDamage).ToStringDecimalIfSmall());
            }
            else
            {
                reportText += "    " + "RimLoot_StatsReport_Projectile".Translate(verbProps.defaultProjectile.Named("PROJECTILE")) + "\n";
                reportText += string.Format("    {0}: {1} * {2} = {3}\n\n",
                                            "Damage".Translate(),
                                            baseDamage.ToStringDecimalIfSmall(),
                                            verbProps.burstShotCount,
                                            damage.ToStringDecimalIfSmall()
                                            );
            }

            /* Seconds per attack */
            float secondsSpent = 0;
            float cooldown     =
                req.HasThing         ? req.Thing.GetStatValue(StatDefOf.RangedWeapon_Cooldown, true) :
                req.StuffDef != null?def.GetStatValueAbstract(StatDefOf.RangedWeapon_Cooldown, req.StuffDef) :
                    def.GetStatValueAbstract(StatDefOf.RangedWeapon_Cooldown)
            ;

            float burstShotTime = ((verbProps.burstShotCount - 1) * verbProps.ticksBetweenBurstShots).TicksToSeconds();

            if (verb != null)
            {
                secondsSpent = verbProps.AdjustedFullCycleTime(verb, attacker);
            }
            else
            {
                secondsSpent = verbProps.warmupTime + cooldown + burstShotTime;
            }

            reportText += GenText.ToTitleCaseSmart("SecondsPerAttackLower".Translate()) + ":\n";
            reportText += string.Format("    {0}: {1}\n", "WarmupTime".Translate(), "PeriodSeconds".Translate(verbProps.warmupTime.ToStringDecimalIfSmall()));
            if (burstShotTime > 0)
            {
                reportText += string.Format("    {0}: {1}\n", "BurstShotFireRate".Translate(), "PeriodSeconds".Translate(burstShotTime.ToStringDecimalIfSmall()));
            }
            reportText += string.Format("    {0}: {1}\n", "CooldownTime".Translate(), "PeriodSeconds".Translate(cooldown.ToStringDecimalIfSmall()));
            reportText += string.Format("{0}: {1}\n\n", "StatsReport_TotalValue".Translate(), "PeriodSeconds".Translate(secondsSpent.ToStringDecimalIfSmall()));

            /* Average accuracy */

            // Every integer range possible as an average
            float wpnAccuracy  = 0;
            float pawnAccuracy = 0;

            for (int i = 3; i <= verbProps.range; i++)
            {
                wpnAccuracy += verbProps.GetHitChanceFactor(req.Thing, i);
                if (attacker != null)
                {
                    pawnAccuracy += ShotReport.HitFactorFromShooter(attacker, i);
                }
            }
            if (verbProps.range >= 3)
            {
                wpnAccuracy /= verbProps.range - 2;
                if (attacker != null)
                {
                    pawnAccuracy /= verbProps.range - 2;
                }
            }

            reportText += "AverageAccuracy".Translate() + ":\n";
            reportText += string.Format("    {0}: {1}\n", "ShootReportWeapon".Translate(), wpnAccuracy.ToStringPercent("F1"));
            if (pawnAccuracy > 0)
            {
                reportText += string.Format("    {0}: {1}\n", "ShootReportShooterAbility".Translate(), pawnAccuracy.ToStringPercent("F1"));
            }

            return(reportText);
        }