public static SalaryConfig FromModConfig(CrewOpts config)
 {
     return(new SalaryConfig()
     {
         Multi = config.SalaryMulti,
         Exponent = config.SalaryExponent,
         Variance = config.SalaryVariance,
         BonusVariance = config.BonusVariance
     });
 }
        public static int RandomContractLength(CrewOpts opts)
        {
            int rand   = Mod.Random.Next(opts.MinContractDaysMulti, opts.MaxContractDaysMulti);
            int length = rand * opts.BaseDaysInContract;

            Mod.Log.Debug?.Write($"Generated random contract length of: {length} days from baseDays: {opts.BaseDaysInContract} " +
                                 $"x rand: {rand} from low: {opts.MinContractDaysMulti} to high: {opts.MaxContractDaysMulti}");

            return(length);
        }
        public void UpdateOnRehire(PilotDef pilotDef)
        {
            // Calculate value and set required tags
            CrewOpts config = null;

            if (IsAerospaceCrew)
            {
                Value  = Mod.Config.HiringHall.PointsBySkillAndSize.Aerospace[this.Skill - 1][this.Size - 1];
                config = Mod.Config.HiringHall.AerospaceWings;
            }
            else if (IsMechTechCrew)
            {
                Value  = Mod.Config.HiringHall.PointsBySkillAndSize.MechTech[this.Skill - 1][this.Size - 1];
                config = Mod.Config.HiringHall.MechTechCrews;
            }
            else if (IsMedTechCrew)
            {
                Value  = Mod.Config.HiringHall.PointsBySkillAndSize.MedTech[this.Skill - 1][this.Size - 1];
                config = Mod.Config.HiringHall.MedTechCrews;
            }
            else if (IsMechWarrior)
            {
                Value  = pilotDef.BaseGunnery + pilotDef.BasePiloting + pilotDef.BaseGuts + pilotDef.BaseTactics;
                config = Mod.Config.HiringHall.MechWarriors;
            }
            else if (IsVehicleCrew)
            {
                Value  = pilotDef.BaseGunnery + pilotDef.BasePiloting + pilotDef.BaseGuts + pilotDef.BaseTactics;
                config = Mod.Config.HiringHall.VehicleCrews;
            }

            // Calculate salary and bonus
            SalaryHelper.CalcSalary(Value, this.SalaryConfig, out int salary, out int bonus);
            this.Salary      = salary;
            this.HiringBonus = bonus;

            this.Attitude     += Mod.Config.Attitude.RehireBonusMod;
            this.ExpirationDay = ModState.SimGameState.DaysPassed + ContractTerm;
        }
        public static bool CanHireMoreCrewOfType(CrewDetails details)
        {
            CrewOpts  crewOpt  = null;
            Statistic crewStat = null;

            if (details.IsAerospaceCrew)
            {
                crewOpt  = Mod.Config.HiringHall.AerospaceWings;
                crewStat = ModState.SimGameState.CompanyStats.GetStatistic(ModStats.CrewCount_Aerospace);
            }
            if (details.IsMechTechCrew)
            {
                crewOpt  = Mod.Config.HiringHall.MechTechCrews;
                crewStat = ModState.SimGameState.CompanyStats.GetStatistic(ModStats.CrewCount_MechTechs);
            }
            if (details.IsMechWarrior)
            {
                crewOpt  = Mod.Config.HiringHall.MechWarriors;
                crewStat = ModState.SimGameState.CompanyStats.GetStatistic(ModStats.CrewCount_MechWarriors);
            }
            if (details.IsMedTechCrew)
            {
                crewOpt  = Mod.Config.HiringHall.MedTechCrews;
                crewStat = ModState.SimGameState.CompanyStats.GetStatistic(ModStats.CrewCount_MedTechs);
            }
            if (details.IsVehicleCrew)
            {
                crewOpt  = Mod.Config.HiringHall.VehicleCrews;
                crewStat = ModState.SimGameState.CompanyStats.GetStatistic(ModStats.CrewCount_VehicleCrews);
            }

            int countOfType = crewStat == null ? 0 : crewStat.Value <int>();

            Mod.Log.Debug?.Write($"Comparing countOfType: {countOfType} vs. limit: {crewOpt.MaxOfType}");

            return(crewOpt.MaxOfType == -1 || countOfType < crewOpt.MaxOfType);
        }
        private static void GenerateAbilityDefs(PilotDef pilotDef, Dictionary <string, int> skillLevels, CrewOpts crewOpts)
        {
            //Mod.Log.Info?.Write($" Pilot skills => gunnery: {skillLevels[ModConsts.Skill_Gunnery]}  guts: {skillLevels[ModConsts.Skill_Guts]}  " +
            //    $"piloting: {skillLevels[ModConsts.Skill_Piloting]}  tactics: {skillLevels[ModConsts.Skill_Tactics]}");
            //Mod.Log.Info?.Write($" Pilot base skills => gunnery: {pilotDef.BaseGunnery}  guts: {pilotDef.BaseGuts}  " +
            //    $"piloting: {pilotDef.BasePiloting}  tactics: {pilotDef.BaseTactics}");
            //Mod.Log.Info?.Write($"Initial abilityDef names are: {string.Join(",", pilotDef.abilityDefNames)}");

            //Mod.Log.Info?.Write($"-- ITERATING ABILITY TREE");
            //foreach (KeyValuePair<string, Dictionary<int, List<AbilityDef>>> kvp in ModState.SimGameState.AbilityTree)
            //{
            //    Mod.Log.Info?.Write($"-- {kvp.Key}");
            //    foreach (KeyValuePair<int, List<AbilityDef>> kvp2 in kvp.Value)
            //    {
            //        Mod.Log.Info?.Write($" ---- {string.Join(",", kvp2.Value.Select(x => x.Id))}");
            //    }
            //}

            // Sort by skill skill
            string primarySkill = SelectHighestSkill(skillLevels);

            Dictionary <string, int> secondarySkills = new Dictionary <string, int>(skillLevels);

            secondarySkills.Remove(primarySkill);
            string secondarySkill = SelectHighestSkill(secondarySkills);

            try
            {
                Mod.Log.Info?.Write($"Generating pilot with skills => primary: {primarySkill}@{skillLevels[primarySkill]} " +
                                    $"secondary: {secondarySkill}@{skillLevels[secondarySkill]}");

                // Filter for primary defs - sort by weight
                if (SetPilotAbilitiesT == null)
                {
                    SetPilotAbilitiesT = Traverse
                                         .Create(ModState.CrewCreateState.HBSPilotGenerator)
                                         .Method("SetPilotAbilities", new Type[] { typeof(PilotDef), typeof(string), typeof(int) });
                }

                // invokeType - PilotDef pilot, string type, int value
                Mod.Log.Info?.Write($" -- setting primary skill: {primarySkill}");
                for (int i = 1; i < skillLevels[primarySkill] + 1; i++)
                {
                    SetPilotAbilitiesT.GetValue(new object[] { pilotDef, primarySkill, i });
                }

                Mod.Log.Info?.Write($" -- setting secondary skill: {secondarySkill}");
                for (int i = 1; i < skillLevels[secondarySkill] + 1; i++)
                {
                    SetPilotAbilitiesT.GetValue(new object[] { pilotDef, secondarySkill, i });
                }
            }
            catch (Exception e)
            {
                Mod.Log.Warn?.Write(e, " Failed to set skill defs!");
            }

            Mod.Log.Info?.Write($"Final ability def names are: {string.Join(",", pilotDef.abilityDefNames)}");
            Mod.Log.Info?.Write($"Final abilityDefs are:");
            foreach (AbilityDef abilityDef in pilotDef.AbilityDefs)
            {
                Mod.Log.Info?.Write($" -- {abilityDef.Description.Id}");
            }
        }
        public CrewDetails(PilotDef pilotDef, CrewType type,
                           FactionValue favoredFaction, FactionValue hatedFaction,
                           int sizeIdx            = 0, int skillIdx = 0, bool isFounder = false,
                           SalaryConfig salaryCfg = null)
        {
            this.Type  = type;
            this.Size  = sizeIdx + 1;
            this.Skill = skillIdx + 1;

            this.Attitude = 0;

            // Calculate value and set required tags
            CrewOpts config = null;

            if (IsAerospaceCrew)
            {
                Value  = Mod.Config.HiringHall.PointsBySkillAndSize.Aerospace[skillIdx][sizeIdx];
                config = Mod.Config.HiringHall.AerospaceWings;
                pilotDef.PilotTags.Add(ModTags.Tag_CrewType_Aerospace);
            }
            else if (IsMechTechCrew)
            {
                Value  = Mod.Config.HiringHall.PointsBySkillAndSize.MechTech[skillIdx][sizeIdx];
                config = Mod.Config.HiringHall.MechTechCrews;
                pilotDef.PilotTags.Add(ModTags.Tag_CrewType_MechTech);
            }
            else if (IsMedTechCrew)
            {
                Value  = Mod.Config.HiringHall.PointsBySkillAndSize.MedTech[skillIdx][sizeIdx];
                config = Mod.Config.HiringHall.MedTechCrews;
                pilotDef.PilotTags.Add(ModTags.Tag_CrewType_MedTech);
            }
            else if (IsMechWarrior)
            {
                Value  = pilotDef.BaseGunnery + pilotDef.BasePiloting + pilotDef.BaseGuts + pilotDef.BaseTactics;
                config = Mod.Config.HiringHall.MechWarriors;
            }
            else if (IsVehicleCrew)
            {
                Value  = pilotDef.BaseGunnery + pilotDef.BasePiloting + pilotDef.BaseGuts + pilotDef.BaseTactics;
                config = Mod.Config.HiringHall.VehicleCrews;

                // Required tags
                pilotDef.PilotTags.Add(ModTags.Tag_CU_NoMech_Crew);
                pilotDef.PilotTags.Add(ModTags.Tag_CU_Vehicle_Crew);
            }

            if (salaryCfg == null)
            {
                this.SalaryConfig = SalaryConfig.FromModConfig(config);
            }
            else
            {
                this.SalaryConfig = salaryCfg;
            }


            // Calculate salary and bonus
            SalaryHelper.CalcSalary(Value, this.SalaryConfig, out int salary, out int bonus);
            this.Salary      = salary;
            this.HiringBonus = bonus;

            // Determine contract length
            if (pilotDef.IsFree && pilotDef.IsImmortal)
            {
                IsPlayer = true;
                Mod.Log.Debug?.Write("Setting expiration and contract term to 0 for player character.");
                this.ContractTerm  = 0; // Free and Immortal = player character
                this.ExpirationDay = 0;
            }
            else if (isFounder)
            {
                IsFounder = true;
                Mod.Log.Debug?.Write("Setting expiration and contract term to 0 for founding member.");
                this.ContractTerm  = 0; // Free overhead, not immortal
                this.ExpirationDay = 0;
            }
            else
            {
                IsPlayer = false;
                Mod.Log.Debug?.Write("Generating contract length, new expiration day");
                this.ContractTerm  = CrewHelper.RandomContractLength(config);
                this.ExpirationDay = ModState.SimGameState.DaysPassed + ContractTerm;
            }

            if (favoredFaction != null)
            {
                this.FavoredFactionId = (int)favoredFaction.FactionID;
            }
            if (hatedFaction != null)
            {
                this.HatedFactionId = (int)hatedFaction.FactionID;
            }

            this.NextHeadHuntingDay = ModState.SimGameState.DaysPassed;
        }