Ejemplo n.º 1
0
        private static List <DebugMenuOption> Options_AddAffix()
        {
            List <DebugMenuOption> debugMenuOptionList = new List <DebugMenuOption>();

            foreach (LootAffixDef affixDef in DefDatabase <LootAffixDef> .AllDefs.OrderBy(lad => lad.affixCost))
            {
                LootAffixDef localDef = affixDef;
                debugMenuOptionList.Add(new DebugMenuOption(localDef.defName, DebugMenuOptionMode.Tool, () => {
                    CompLootAffixableThing comp = Find.CurrentMap.thingGrid.
                                                  ThingsAt(UI.MouseCell()).
                                                  Where(t => t is ThingWithComps).Cast <ThingWithComps>().
                                                  Select(twc => twc.TryGetComp <CompLootAffixableThing>()).
                                                  Where(c => c is CompLootAffixableThing).
                                                  FirstOrDefault()
                    ;

                    var lads = comp.AllAffixDefs;
                    if (lads.Contains(localDef) || lads.Count >= 4)
                    {
                        return;
                    }
                    lads.Add(localDef);
                    comp.PostAffixCleanup();
                }));
            }
            return(debugMenuOptionList);
        }
Ejemplo n.º 2
0
        static public void AddNewAffixes(this CompLootAffixableThing comp, float affixPoints = 0, int ttlAffixes = 0)     // options for debug only
        {
            List <LootAffixDef> affixes = comp.affixes;
            ThingWithComps      thing   = comp.parent;

            affixes.Clear();
            if (affixPoints == 0)
            {
                affixPoints = CalculateTotalLootAffixPoints(thing);
            }

            if (ttlAffixes == 0)
            {
                for (int i = 1; i <= 4; i++)
                {
                    // FIXME: Add config sliders for percentages here
                    // 25% chance for each affix (compounded)
                    if (0.25f < Random.Range(0.0f, 1.0f))
                    {
                        break;
                    }
                    ttlAffixes = i;
                }
            }

            if (ttlAffixes == 0)
            {
                return;
            }

            // Baseline of affixes that can be used (since affixPoints could change upward or downward)
            List <LootAffixDef> baseAffixDefs =
                DefDatabase <LootAffixDef> .AllDefsListForReading.
                FindAll(lad => lad.CanBeAppliedToThing(thing))
            ;

            // Affix picking loop
            for (int curAffixes = affixes.Count + 1; curAffixes <= 4; curAffixes++)
            {
                LootAffixDef newAffix = PickAffix(thing, baseAffixDefs, curAffixes, ttlAffixes, affixPoints);
                if (newAffix == null)
                {
                    return;
                }

                affixes.Add(newAffix);
                affixPoints  -= newAffix.GetRealAffixCost(thing);
                baseAffixDefs = baseAffixDefs.FindAll(lad => lad.groupName != newAffix.groupName);
            }
        }
Ejemplo n.º 3
0
        private static List <DebugMenuOption> Options_RemoveAffix(CompLootAffixableThing comp)
        {
            List <DebugMenuOption> debugMenuOptionList = new List <DebugMenuOption>();

            foreach (LootAffixDef affixDef in comp.AllAffixDefs)
            {
                LootAffixDef localDef = affixDef;
                debugMenuOptionList.Add(new DebugMenuOption(comp.AllAffixesByAffixDefs[localDef], DebugMenuOptionMode.Action, () => {
                    int i = comp.AllAffixDefs.IndexOf(localDef);
                    comp.AllAffixDefs.RemoveAt(i);
                    comp.PostAffixCleanup();
                }));
            }
            return(debugMenuOptionList);
        }
Ejemplo n.º 4
0
        static public void PostAffixCleanup(this CompLootAffixableThing comp, bool fixLabel = true)
        {
            ThingWithComps thing = comp.parent;

            comp.ClearAffixCaches();

            if (fixLabel)
            {
                comp.affixRules.Clear();
                comp.fullStuffLabel = null;
                string name = thing.LabelNoCount;
                name = comp.TransformLabel(name);
            }

            thing.def.SpecialDisplayStats(StatRequest.For(thing));

            foreach (LootAffixDef affix in comp.affixes)
            {
                affix.PostApplyAffix(thing);
            }
        }
Ejemplo n.º 5
0
            static IEnumerable <StatDrawEntry> Postfix(IEnumerable <StatDrawEntry> values, ThingDef __instance, StatRequest req)
            {
                CompLootAffixableThing comp = null;

                if (req.Thing is ThingWithComps thing)
                {
                    comp = thing.TryGetComp <CompLootAffixableThing>();
                }

                // Cycle through the entries
                foreach (StatDrawEntry value in values)
                {
                    // Give it to the comp to meddle with
                    if (comp != null)
                    {
                        comp.SpecialDisplayStatsInjectors(value);
                    }

                    yield return(value);
                }

                // Go back to the old set.  Iterators cannot have refs, so we have to replace this with reflection.
                if (!Base.origVerbPropertiesCache.ContainsKey(__instance.defName))
                {
                    if (comp != null && !req.Thing.def.Verbs.NullOrEmpty())
                    {
                        Log.Error("Old VerbProperties lost from SpecialDisplayStats swap!");
                    }
                    yield break;
                }

                // [Reflection] thing.verbs = Base.origVerbPropertiesCache[__instance.defName];
                FieldInfo verbsField = AccessTools.Field(typeof(ThingDef), "verbs");

                verbsField.SetValue(__instance, Base.origVerbPropertiesCache[__instance.defName]);
            }
Ejemplo n.º 6
0
 static public void InitializeAffixes(this CompLootAffixableThing comp, float affixPoints = 0, int ttlAffixes = 0)     // options for debug only
 {
     comp.affixes.Clear();
     comp.AddNewAffixes(affixPoints, ttlAffixes);
     comp.PostAffixCleanup();
 }
Ejemplo n.º 7
0
        static public string GetSetFullStuffLabel(this CompLootAffixableThing comp, string label)
        {
            List <LootAffixDef> affixes    = comp.affixes;
            List <Rule>         affixRules = comp.affixRules;
            ThingWithComps      thing      = comp.parent;

            // Make sure we're not saving color tag information
            label = label.StripTags();

            // Short-circuit: No affixes
            if (comp.AffixCount == 0)
            {
                return(label);
            }

            // Short-circuit: Already calculated the full label and no replacement required
            string stuffLabel = GenLabel.ThingLabel(thing.def, thing.Stuff, 1).StripTags();

            if (comp.fullStuffLabel != null && stuffLabel == label)
            {
                return(comp.fullStuffLabel);
            }

            // Short-circuit: Still have the calculated full label
            string preExtra  = "";
            string postExtra = "";
            int    pos       = label.IndexOf(stuffLabel);

            if (pos >= 0)
            {
                preExtra  = label.Substring(0, pos);
                postExtra = label.Substring(pos + stuffLabel.Length);
            }

            if (comp.fullStuffLabel != null)
            {
                return(preExtra + comp.fullStuffLabel + postExtra);
            }

            // Need the calculate the label then...
            var namerDef = DefDatabase <LootAffixNamerRulePackDef> .GetNamed("RimLoot_LootAffixNamer");

            GrammarRequest request = new GrammarRequest();

            request.Includes.Add(namerDef);

            // Word class counter set-up
            Dictionary <string, int> maxWordClasses = namerDef.maxWordClasses;
            Dictionary <string, int> curWordClasses = new Dictionary <string, int> ();

            for (int i = 0; i < 5; i++)
            {
                curWordClasses = maxWordClasses.ToDictionary(k => k.Key, v => 0);    // shallow clone to k=0

                // Add in the affixes
                foreach (LootAffixDef affix in affixes)
                {
                    foreach (Rule rule in affix.PickAffixRulesForLabeling(curWordClasses, maxWordClasses))
                    {
                        if (rule.keyword.StartsWith("AFFIX_"))
                        {
                            comp.affixRules.Add(rule);
                        }
                        request.Rules.Add(rule);
                        if (rule.keyword.StartsWith("AFFIXPROP_"))
                        {
                            request.Constants.Add(rule.keyword, rule.Generate());
                        }
                    }
                }

                // Double-check we didn't hit one of those disallowed combinations
                if (namerDef.IsWordClassComboAllowed(affixRules))
                {
                    break;
                }
                else
                {
                    affixRules.Clear();
                    continue;
                }
            }

            if (affixRules.Count != comp.AffixCount)
            {
                Log.Error("Chosen affix words for " + thing + " don't match the number of affixes:\n" + string.Join(
                              "\nvs.\n",
                              string.Join(" | ", affixRules),
                              string.Join(", ", affixes)
                              ));
            }

            // Add a few types of labels for maximum language flexibility
            request.Rules.Add(new Rule_String("STUFF_label", thing.Stuff != null ? thing.Stuff.LabelAsStuff : ""));
            request.Rules.Add(new Rule_String("THING_defLabel", thing.def.label));
            request.Rules.Add(new Rule_String("THING_stuffLabel", stuffLabel));

            string rootKeyword = "r_affix" + comp.AffixCount;

            comp.fullStuffLabel = NameGenerator.GenerateName(request, null, false, rootKeyword, rootKeyword);

            // It's possible we might end up hitting this later than we expected, and run into affixes/word
            // desyncs, so clear the cache, just in case.
            comp.ClearAffixCaches();

            return(preExtra + comp.fullStuffLabel + postExtra);
        }
Ejemplo n.º 8
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);
        }
Ejemplo n.º 9
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);
        }