예제 #1
0
  // return quality-of-life bonus
  public static double Bonus(Vessel v)
  {
    // deduce crew count and capacity
    int crew_count = Lib.CrewCount(v);
    int crew_capacity = Lib.CrewCapacity(v);

    // deduce entertainment bonus, multiplying all entertainment factors
    double entertainment = 1.0;
    if (v.loaded)
    {
      foreach(Entertainment m in v.FindPartModulesImplementing<Entertainment>())
      {
        entertainment *= m.rate;
      }
    }
    else
    {
      foreach(ProtoPartSnapshot part in v.protoVessel.protoPartSnapshots)
      {
        foreach(ProtoPartModuleSnapshot m in part.modules)
        {
          if (m.moduleName == "Entertainment") entertainment *= Lib.GetProtoValue<double>(m, "rate");
        }
      }
    }

    // calculate quality of life bonus
    return Bonus((uint)crew_count, (uint)crew_capacity, entertainment, Lib.Landed(v), Signal.Link(v).linked);
  }
예제 #2
0
        /// <summary> This ctor is to be used for newly created vessels </summary>
        public VesselData(Vessel vessel)
        {
            UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.VesselData.Ctor");

            ExistsInFlight = true;              // vessel exists
            IsSimulated    = false;             // will be evaluated in next fixedupdate

            Vessel   = vessel;
            VesselId = Vessel.id;

            parts = new Dictionary <uint, PartData>();
            if (Vessel.loaded)
            {
                foreach (Part part in Vessel.Parts)
                {
                    parts.Add(part.flightID, new PartData(part));
                }
            }
            else
            {
                // vessels can be created unloaded, asteroids for example
                foreach (ProtoPartSnapshot protopart in Vessel.protoVessel.protoPartSnapshots)
                {
                    parts.Add(protopart.flightID, new PartData(protopart));
                }
            }


            FieldsDefaultInit();
            cfg_storm |= Features.SpaceWeather && Lib.CrewCount(vessel) > 0;

            Lib.LogDebug("VesselData ctor (new vessel) : id '" + VesselId + "' (" + Vessel.vesselName + "), part count : " + parts.Count);
            UnityEngine.Profiling.Profiler.EndSample();
        }
예제 #3
0
        /// <summary>
        /// This ctor is meant to be used in OnLoad only, but can be used as a fallback
        /// with a null ConfigNode to create VesselData from a protovessel.
        /// The Vessel reference will be acquired in the next fixedupdate
        /// </summary>
        public VesselData(ProtoVessel protoVessel, ConfigNode node)
        {
            UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.VesselData.Ctor");
            ExistsInFlight = false;
            IsSimulated    = false;

            VesselId = protoVessel.vesselID;

            parts = new Dictionary <uint, PartData>();
            foreach (ProtoPartSnapshot protopart in protoVessel.protoPartSnapshots)
            {
                parts.Add(protopart.flightID, new PartData(protopart));
            }

            if (node == null)
            {
                FieldsDefaultInit();
                cfg_storm |= Features.SpaceWeather && Lib.CrewCount(protoVessel.vesselRef) > 0;
                Lib.LogDebug("VesselData ctor (created from protovessel) : id '" + VesselId + "' (" + protoVessel.vesselName + "), part count : " + parts.Count);
            }
            else
            {
                Load(node);
                Lib.LogDebug("VesselData ctor (loaded from database) : id '" + VesselId + "' (" + protoVessel.vesselName + "), part count : " + parts.Count);
            }
            UnityEngine.Profiling.Profiler.EndSample();
        }
예제 #4
0
 GUIContent indicator_supplies(Vessel v, vessel_info vi)
 {
   GUIContent state = new GUIContent();
   List<string> tooltips = new List<string>();
   uint max_severity = 0;
   if (Lib.CrewCount(v) > 0)
   {
     foreach(Rule r in Kerbalism.supply_rules)
     {
       var vmon = vi.vmon[r.name];
       string deplete_str = vmon.depletion <= double.Epsilon
         ? ", depleted"
         : double.IsNaN(vmon.depletion)
         ? ""
         : ", deplete in <b>" + Lib.HumanReadableDuration(vmon.depletion) + "</b>";
       tooltips.Add(r.resource_name + ": <b>" + (vmon.level * 100.0).ToString("F0") + "%</b>" + deplete_str);
       uint severity = vmon.level <= double.Epsilon ? 2u : vmon.level <= r.low_threshold ? 1u : 0;
       max_severity = Math.Max(max_severity, severity);
     }
   }
   switch(max_severity)
   {
     case 0: state.image = icon_supplies_nominal; break;
     case 1: state.image = icon_supplies_warning; break;
     case 2: state.image = icon_supplies_danger; break;
   }
   state.tooltip = string.Join("\n", tooltips.ToArray());
   return state;
 }
예제 #5
0
        void ToEVA(GameEvents.FromToAction <Part, Part> data)
        {
            OnVesselModified(data.from.vessel);
            OnVesselModified(data.to.vessel);

            // get total crew in the origin vessel
            double tot_crew = Lib.CrewCount(data.from.vessel) + 1.0;

            // get vessel resources handler
            VesselResources resources = ResourceCache.Get(data.from.vessel);

            // setup supply resources capacity in the eva kerbal
            Profile.SetupEva(data.to);

            string evaPropName = Lib.EvaPropellantName();

            // for each resource in the kerbal
            for (int i = 0; i < data.to.Resources.Count; ++i)
            {
                // get the resource
                PartResource res = data.to.Resources[i];

                // eva prop is handled differently
                if (res.resourceName == evaPropName)
                {
                    continue;
                }

                double quantity = Math.Min(resources.GetResource(data.from.vessel, res.resourceName).Amount / tot_crew, res.maxAmount);
                // remove resource from vessel
                quantity = data.from.RequestResource(res.resourceName, quantity);

                // add resource to eva kerbal
                data.to.RequestResource(res.resourceName, -quantity);
            }

            // Airlock loss
            resources.Consume(data.from.vessel, "Nitrogen", Settings.LifeSupportAtmoLoss, ResourceBroker.Generic);

            KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>();


            // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva
            EVA.HeadLamps(kerbal, false);

            // execute script
            data.from.vessel.KerbalismData().computer.Execute(data.from.vessel, ScriptType.eva_out);

            // Start a coroutine for doing eva propellant resource transfers once the kerbal EVA is started (this is too early here)
            data.to.StartCoroutine(PostEVATweaks(data.from, data.to, evaPropName));
        }
예제 #6
0
  // estimate lifetime for the rule resource
  // note: this function must be called only once for simulation step
  // return 0 if no resource left, NaN if rate of change is positive, or seconds left in other cases
  public double EstimateLifetime(Vessel v)
  {
    // get prev amount for the vessel
    double prev_amount = 0.0;
    if (!prev_amounts.TryGetValue(v.id, out prev_amount)) prev_amount = 0.0;

    // calculate delta
    double amount = Lib.GetResourceAmount(v, this.resource_name);
    double meal_rate = this.interval > double.Epsilon ? this.rate / this.interval : 0.0;
    double delta = (amount - prev_amount) / TimeWarp.fixedDeltaTime - meal_rate * Lib.CrewCount(v);

    // remember prev amount
    prev_amounts[v.id] = amount;

    // return lifetime in seconds
    return amount <= double.Epsilon ? 0.0 : delta >= -double.Epsilon ? double.NaN : amount / -delta;
  }
예제 #7
0
        private void EvaluateStatus()
        {
            UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.VesselData.EvaluateStatus");
            // determine if there is enough EC for a powered state
            powered = Lib.IsPowered(Vessel);

            // calculate crew info for the vessel
            crewCount    = Lib.CrewCount(Vessel);
            crewCapacity = Lib.CrewCapacity(Vessel);

            // malfunction stuff
            malfunction = Reliability.HasMalfunction(Vessel);
            critical    = Reliability.HasCriticalFailure(Vessel);

            // communications info
            connection = ConnectionInfo.Update(Vessel, powered, EnvBlackout);

            // habitat data
            habitatInfo.Update(Vessel);
            volume   = Habitat.Tot_volume(Vessel);
            surface  = Habitat.Tot_surface(Vessel);
            pressure = Math.Min(Habitat.Pressure(Vessel), habitatInfo.MaxPressure);

            evas          = (uint)(Math.Max(0, ResourceCache.GetResource(Vessel, "Nitrogen").Amount - 330) / Settings.LifeSupportAtmoLoss);
            poisoning     = Habitat.Poisoning(Vessel);
            shielding     = Habitat.Shielding(Vessel);
            livingSpace   = Habitat.Living_space(Vessel);
            volumePerCrew = Habitat.Volume_per_crew(Vessel);
            comforts      = new Comforts(Vessel, EnvLanded, crewCount > 1, connection.linked && connection.rate > double.Epsilon);

            // data about greenhouses
            greenhouses = Greenhouse.Greenhouses(Vessel);

            Drive.GetCapacity(this, out drivesFreeSpace, out drivesCapacity);

            // solar panels data
            if (Vessel.loaded)
            {
                solarPanelsAverageExposure = SolarPanelFixer.GetSolarPanelsAverageExposure(solarPanelsExposure);
                solarPanelsExposure.Clear();
            }
            UnityEngine.Profiling.Profiler.EndSample();
        }
예제 #8
0
        void ToEVA(GameEvents.FromToAction <Part, Part> data)
        {
            // get total crew in the origin vessel
            double tot_crew = (double)Lib.CrewCount(data.from.vessel) + 1.0;

            // get vessel resources handler
            Vessel_resources resources = ResourceCache.Get(data.from.vessel);

            // setup supply resources capacity in the eva kerbal
            Profile.SetupEva(data.to);

            // for each resource in the kerbal
            for (int i = 0; i < data.to.Resources.Count; ++i)
            {
                // get the resource
                PartResource res = data.to.Resources[i];

                // determine quantity to take
                double quantity = Math.Min(resources.Info(data.from.vessel, res.resourceName).amount / tot_crew, res.maxAmount);

                // remove resource from vessel
                quantity = data.from.RequestResource(res.resourceName, quantity);

                // add resource to eva kerbal
                data.to.RequestResource(res.resourceName, -quantity);
            }

            // show warning if there isn't monoprop in the eva suit
            string prop_name = Lib.EvaPropellantName();

            if (Lib.Amount(data.to, prop_name) <= double.Epsilon && !Lib.Landed(data.from.vessel))
            {
                Message.Post(Severity.danger, Lib.BuildString("There isn't any <b>", prop_name, "</b> in the EVA suit"), "Don't let the ladder go!");
            }

            // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva
            KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>();

            EVA.HeadLamps(kerbal, false);

            // execute script
            DB.Vessel(data.from.vessel).computer.Execute(data.from.vessel, ScriptType.eva_out);
        }
예제 #9
0
  // return seconds until depletion, 0.0 if there is no resource and NaN if it will never deplete
  public double Depletion(Vessel v, resource_info res)
  {
    // [unused] Newton–Raphson, 1 step
    // C = amount
    // R = delta
    // A = change in delta
    // t0 = C / -R              // eyeball estimate
    // F' = 0.5 * t0 * t0       // differential of F
    // F = C + R * t0 + A * F'  // equation of motion, constant acceleration
    // t1 = t0 - F / F'         // time until depletion

    // calculate rate of change from interval-based rule
    double meal_rate = interval > double.Epsilon ? rate / interval : 0.0;

    // calculate total rate of change
    double delta = res.rate - meal_rate * Lib.CrewCount(v);

    // return depletion
    return res.amount <= double.Epsilon ? 0.0 : delta >= -0.000001 ? double.NaN : res.amount / -delta;
  }
예제 #10
0
        public void Update()
        {
            // set lamps emissive object
            if (lamps_rdr != null)
            {
                float intensity = Lib.IsFlight() ? (active ? (float)(artificial / light_tolerance) : 0.0f) : (active ? 1.0f : 0.0f);
                lamps_rdr.material.SetColor("_EmissiveColor", new Color(intensity, intensity, intensity, 1.0f));
            }

            // in flight
            if (Lib.IsFlight())
            {
                // still-play plants animation
                if (plants_anim != null)
                {
                    plants_anim.Still(growth);
                }

                // update ui
                string status = issue.Length > 0 ? Lib.BuildString("<color=yellow>", issue, "</color>") : growth > 0.99 ? "ready to harvest" : "growing";
                Events["Toggle"].guiName              = Lib.StatusToggle("Greenhouse", active ? status : "disabled");
                Fields["status_natural"].guiActive    = active && growth < 0.99;
                Fields["status_artificial"].guiActive = active && growth < 0.99;
                Fields["status_tta"].guiActive        = active && growth < 0.99;
                status_natural    = Lib.HumanReadableFlux(natural);
                status_artificial = Lib.HumanReadableFlux(artificial);
                status_tta        = Lib.HumanReadableDuration(tta);

                // show/hide harvest buttons
                bool manned = FlightGlobals.ActiveVessel.isEVA || Lib.CrewCount(vessel) > 0;
                Events["Harvest"].active          = manned && growth >= 0.99;
                Events["EmergencyHarvest"].active = manned && growth >= 0.5 && growth < 0.99;
            }
            // in editor
            else
            {
                // update ui
                Events["Toggle"].guiName = Lib.StatusToggle("Greenhouse", active ? "enabled" : "disabled");
            }
        }
예제 #11
0
  void Breakdown(Vessel v, ProtoCrewMember c)
  {
    // constants
    const double food_penality = 0.2;        // proportion of food lost on 'depressed'
    const double oxygen_penality = 0.2;      // proportion of oxygen lost on 'wrong_valve'

    // get info
    double food_amount = Lib.GetResourceAmount(v, "Food");
    double oxygen_amount = Lib.GetResourceAmount(v, "Oxygen");

    // compile list of events with condition satisfied
    List<KerbalBreakdown> events = new List<KerbalBreakdown>();
    events.Add(KerbalBreakdown.mumbling); //< do nothing, here so there is always something that can happen
    if (Lib.CrewCount(v) > 1) events.Add(KerbalBreakdown.argument); //< do nothing, add some variation to messages
    if (Lib.HasData(v)) events.Add(KerbalBreakdown.fat_finger);
    if (Malfunction.CanMalfunction(v)) events.Add(KerbalBreakdown.rage);
    if (food_amount > double.Epsilon) events.Add(KerbalBreakdown.depressed);
    if (oxygen_amount > double.Epsilon) events.Add(KerbalBreakdown.wrong_valve);

    // choose a breakdown event
    KerbalBreakdown breakdown = events[Lib.RandomInt(events.Count)];

    // post message first so this one is shown before malfunction message
    Message.Post(Severity.breakdown, KerbalEvent.stress, v, c, breakdown);

    // trigger the event
    switch(breakdown)
    {
      case KerbalBreakdown.mumbling: break; // do nothing
      case KerbalBreakdown.argument: break; // do nothing
      case KerbalBreakdown.fat_finger: Lib.RemoveData(v); break;
      case KerbalBreakdown.rage: Malfunction.CauseMalfunction(v); break;
      case KerbalBreakdown.depressed: Lib.RequestResource(v, "Food", food_amount * food_penality); break;
      case KerbalBreakdown.wrong_valve: Lib.RequestResource(v, "Oxygen", oxygen_amount * oxygen_penality); break;
    }
  }
예제 #12
0
        public void TransferData()
        {
            var hardDrives = vessel.FindPartModulesImplementing <HardDrive>();

            foreach (var hardDrive in hardDrives)
            {
                if (hardDrive == this)
                {
                    continue;
                }
                hardDrive.drive.Move(drive, PreferencesScience.Instance.sampleTransfer || Lib.CrewCount(vessel) > 0);
            }
        }
예제 #13
0
        private static string TestForIssues(Vessel v, Resource_info ec, Experiment experiment, uint hdId, bool broken,
                                            double remainingSampleMass, bool didPrepare, bool isShrouded, string last_subject_id)
        {
            var subject_id = Science.Generate_subject_id(experiment.experiment_id, v);

            if (broken)
            {
                return("broken");
            }

            if (isShrouded && !experiment.allow_shrouded)
            {
                return("shrouded");
            }

            bool needsReset = experiment.crew_reset.Length > 0 &&
                              !string.IsNullOrEmpty(last_subject_id) && subject_id != last_subject_id;

            if (needsReset)
            {
                return("reset required");
            }

            if (ec.amount < double.Epsilon && experiment.ec_rate > double.Epsilon)
            {
                return("no Electricity");
            }

            if (!string.IsNullOrEmpty(experiment.crew_operate))
            {
                var cs = new CrewSpecs(experiment.crew_operate);
                if (!cs && Lib.CrewCount(v) > 0)
                {
                    return("crew on board");
                }
                else if (cs && !cs.Check(v))
                {
                    return(cs.Warning());
                }
            }

            if (!experiment.sample_collecting && remainingSampleMass < double.Epsilon && experiment.sample_mass > double.Epsilon)
            {
                return("depleted");
            }

            if (!didPrepare && !string.IsNullOrEmpty(experiment.crew_prepare))
            {
                return("not prepared");
            }

            string situationIssue = Science.TestRequirements(experiment.experiment_id, experiment.requires, v);

            if (situationIssue.Length > 0)
            {
                return(Science.RequirementText(situationIssue));
            }

            var    experimentSize = Science.Experiment(subject_id).max_amount;
            double chunkSize      = Math.Min(experiment.data_rate * Kerbalism.elapsed_s, experimentSize);
            Drive  drive          = GetDrive(experiment, v, hdId, chunkSize, subject_id);

            var    isFile    = experiment.sample_mass < double.Epsilon;
            double available = 0;

            if (isFile)
            {
                available  = drive.FileCapacityAvailable();
                available += Cache.WarpCache(v).FileCapacityAvailable();
            }
            else
            {
                available = drive.SampleCapacityAvailable(subject_id);
            }

            if (Math.Min(experiment.data_rate * Kerbalism.elapsed_s, experimentSize) > available)
            {
                return(insufficient_storage);
            }

            return(string.Empty);
        }
예제 #14
0
  GUIContent indicator_supplies(Vessel v, List<Scrubber> scrubbers, List<Greenhouse> greenhouses)
  {
    // get food & oxygen info
    double food_amount = Lib.GetResourceAmount(v, "Food");
    double food_capacity = Lib.GetResourceCapacity(v, "Food");
    double food_level = food_capacity > 0.0 ? food_amount / food_capacity : 1.0;
    double oxygen_amount = Lib.GetResourceAmount(v, "Oxygen");
    double oxygen_capacity = Lib.GetResourceCapacity(v, "Oxygen");
    double oxygen_level = oxygen_capacity > 0.0 ? oxygen_amount / oxygen_capacity : 1.0;
    double level = Math.Min(food_level, oxygen_level);

    // store the icon and tooltip
    GUIContent state = new GUIContent();

    // choose an icon
    if (level <= Settings.ResourceDangerThreshold) state.image = icon_supplies_danger;
    else if (level <= Settings.ResourceWarningThreshold) state.image = icon_supplies_warning;
    else state.image = icon_supplies_nominal;

    // if there is someone on board
    List<string> tooltips = new List<string>();
    int crew_count = Lib.CrewCount(v);
    if (crew_count > 0)
    {
      // get oxygen recycled by scrubbers
      double oxygen_recycled = 0.0;
      double ec_left = Lib.GetResourceAmount(v, "ElectricCharge");
      double co2_left = Lib.GetResourceAmount(v, "CO2");
      foreach(Scrubber scrubber in scrubbers)
      {
        if (scrubber.is_enabled)
        {
          double co2_consumed = Math.Max(co2_left, scrubber.co2_rate);
          ec_left -= scrubber.ec_rate;
          co2_left -= co2_consumed;
          if (ec_left > -double.Epsilon && co2_left > -double.Epsilon) oxygen_recycled += co2_consumed * scrubber.efficiency;
          else break;
        }
      }

      // calculate time until depletion for food
      double food_consumption = (double)crew_count * Settings.FoodPerMeal / Settings.MealFrequency;
      if (food_capacity > double.Epsilon && food_consumption > double.Epsilon)
      {
        double food_depletion = food_amount / food_consumption;
        tooltips.Add(food_amount / food_capacity > Settings.ResourceDangerThreshold
          ? "Food: <b>" + (food_level * 100.0).ToString("F0") + "%, </b>deplete in <b>" + Lib.HumanReadableDuration(food_depletion) + "</b>"
          : "Food: <b>depleted</b>");
      }

      // calculate time until depletion for oxygen
      double oxygen_consumption = !LifeSupport.BreathableAtmosphere(v) ? (double)crew_count * Settings.OxygenPerSecond - oxygen_recycled : 0.0;
      if (oxygen_capacity > double.Epsilon && oxygen_consumption > double.Epsilon)
      {
        double oxygen_depletion = oxygen_amount / oxygen_consumption;
        tooltips.Add(oxygen_amount / oxygen_capacity > Settings.ResourceDangerThreshold
          ? "Oxygen: <b>" + (oxygen_level * 100.0).ToString("F0") + "%, </b>deplete in <b>" + Lib.HumanReadableDuration(oxygen_depletion) + "</b>"
          : "Oxygen: <b>depleted</b>");
      }
    }
    state.tooltip = string.Join("\n", tooltips.ToArray());
    return state;
  }
예제 #15
0
        void ToEVA(GameEvents.FromToAction <Part, Part> data)
        {
            OnVesselModified(data.from.vessel);
            OnVesselModified(data.to.vessel);

            // get total crew in the origin vessel
            double tot_crew = Lib.CrewCount(data.from.vessel) + 1.0;

            // get vessel resources handler
            VesselResources resources = ResourceCache.Get(data.from.vessel);

            // setup supply resources capacity in the eva kerbal
            Profile.SetupEva(data.to);

            String prop_name = Lib.EvaPropellantName();

            // for each resource in the kerbal
            for (int i = 0; i < data.to.Resources.Count; ++i)
            {
                // get the resource
                PartResource res = data.to.Resources[i];

                // eva prop is handled differently
                if (res.resourceName == prop_name)
                {
                    continue;
                }

                double quantity = Math.Min(resources.GetResource(data.from.vessel, res.resourceName).Amount / tot_crew, res.maxAmount);
                // remove resource from vessel
                quantity = data.from.RequestResource(res.resourceName, quantity);

                // add resource to eva kerbal
                data.to.RequestResource(res.resourceName, -quantity);
            }

            // take as much of the propellant as possible. just imagine: there are 1.3 units left, and 12 occupants
            // in the ship. you want to send out an engineer to fix the chemical plant that produces monoprop,
            // and have to get from one end of the station to the other with just 0.1 units in the tank...
            // nope.
            double evaPropQuantity = data.from.RequestResource(prop_name, Lib.EvaPropellantCapacity());

            // We can't just add the monoprop here, because that doesn't always work. It might be related
            // to the fact that stock KSP wants to add 5 units of monoprop to new EVAs. Instead of fighting KSP here,
            // we just let it do it's thing and set our amount later in EVA.cs - which seems to work just fine.
            // don't put that into Cache.VesselInfo because that can be deleted before we get there
            Cache.SetVesselObjectsCache(data.to.vessel, "eva_prop", evaPropQuantity);

            // Airlock loss
            resources.Consume(data.from.vessel, "Nitrogen", Settings.LifeSupportAtmoLoss, ResourceBroker.Generic);

            // show warning if there is little or no EVA propellant in the suit
            if (evaPropQuantity <= 0.05 && !Lib.Landed(data.from.vessel))
            {
                Message.Post(Severity.danger,
                             Local.CallBackMsg_EvaNoMP.Format("<b>" + prop_name + "</b>"), Local.CallBackMsg_EvaNoMP2);       //Lib.BuildString("There isn't any <<1>> in the EVA suit")"Don't let the ladder go!"
            }

            // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva
            KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>();

            EVA.HeadLamps(kerbal, false);

            // execute script
            data.from.vessel.KerbalismData().computer.Execute(data.from.vessel, ScriptType.eva_out);
        }
예제 #16
0
        public Vessel_info(Vessel v, UInt64 vessel_id, UInt64 inc)
        {
            // NOTE: anything used here can't in turn use cache, unless you know what you are doing

            // NOTE: you can't cache vessel position
            // at any point in time all vessel/body positions are relative to a different frame of reference
            // so comparing the current position of a vessel, with the cached one of another make no sense

            // associate with an unique incremental id
            this.inc = inc;

            // determine if this is a valid vessel
            is_vessel = Lib.IsVessel(v);
            if (!is_vessel)
            {
                return;
            }

            // determine if this is a rescue mission vessel
            is_rescue = Misc.IsRescueMission(v);
            if (is_rescue)
            {
                return;
            }

            // dead EVA are not valid vessels
            if (EVA.IsDead(v))
            {
                return;
            }

            // shortcut for common tests
            is_valid = true;

            // generate id once
            id = vessel_id;

            // calculate crew info for the vessel
            crew_count    = Lib.CrewCount(v);
            crew_capacity = Lib.CrewCapacity(v);

            // get vessel position
            Vector3d position = Lib.VesselPosition(v);

            // this should never happen again
            if (Vector3d.Distance(position, v.mainBody.position) < 1.0)
            {
                throw new Exception("Shit hit the fan for vessel " + v.vesselName);
            }

            // determine if there is enough EC for a powered state
            powered = ResourceCache.Info(v, "ElectricCharge").amount > double.Epsilon;

            // determine if in sunlight, calculate sun direction and distance
            sunlight = Sim.RaytraceBody(v, position, FlightGlobals.Bodies[0], out sun_dir, out sun_dist) ? 1.0 : 0.0;

            // environment stuff
            atmo_factor        = Sim.AtmosphereFactor(v.mainBody, position, sun_dir);
            gamma_transparency = Sim.GammaTransparency(v.mainBody, v.altitude);
            underwater         = Sim.Underwater(v);
            breathable         = Sim.Breathable(v, underwater);
            landed             = Lib.Landed(v);
            zerog = !landed && (!v.mainBody.atmosphere || v.mainBody.atmosphereDepth < v.altitude);

            if (v.mainBody.flightGlobalsIndex != 0 && TimeWarp.CurrentRate > 1000.0f)
            {
                highspeedWarp(v);
            }

            // temperature at vessel position
            temperature = Sim.Temperature(v, position, sunlight, atmo_factor, out solar_flux, out albedo_flux, out body_flux, out total_flux);
            temp_diff   = Sim.TempDiff(temperature, v.mainBody, landed);

            // radiation
            radiation = Radiation.Compute(v, position, gamma_transparency, sunlight, out blackout, out magnetosphere, out inner_belt, out outer_belt, out interstellar);

            // extended atmosphere
            thermosphere = Sim.InsideThermosphere(v);
            exosphere    = Sim.InsideExosphere(v);

            // malfunction stuff
            malfunction = Reliability.HasMalfunction(v);
            critical    = Reliability.HasCriticalFailure(v);

            // communications info
            connection   = new ConnectionInfo(v, powered, blackout);
            transmitting = Science.Transmitting(v, connection.linked && connection.rate > double.Epsilon);

            // habitat data
            volume          = Habitat.Tot_volume(v);
            surface         = Habitat.Tot_surface(v);
            pressure        = Habitat.Pressure(v);
            evas            = (uint)(Math.Max(0, ResourceCache.Info(v, "Nitrogen").amount - 330) / PreferencesLifeSupport.Instance.evaAtmoLoss);
            poisoning       = Habitat.Poisoning(v);
            humidity        = Habitat.Humidity(v);
            shielding       = Habitat.Shielding(v);
            living_space    = Habitat.Living_space(v);
            volume_per_crew = Habitat.Volume_per_crew(v);
            comforts        = new Comforts(v, landed, crew_count > 1, connection.linked && connection.rate > double.Epsilon);

            // data about greenhouses
            greenhouses = Greenhouse.Greenhouses(v);

            // other stuff
            gravioli = Sim.Graviolis(v);
        }
예제 #17
0
        private void FieldsDefaultInit(ProtoVessel pv)
        {
            msg_signal      = false;
            msg_belt        = false;
            cfg_ec          = PreferencesMessages.Instance.ec;
            cfg_supply      = PreferencesMessages.Instance.supply;
            cfg_signal      = PreferencesMessages.Instance.signal;
            cfg_malfunction = PreferencesMessages.Instance.malfunction;
            cfg_storm       = Features.SpaceWeather && PreferencesMessages.Instance.storm && Lib.CrewCount(pv) > 0;
            cfg_script      = PreferencesMessages.Instance.script;
            cfg_highlights  = PreferencesReliability.Instance.highlights;
            cfg_showlink    = true;
            cfg_show        = true;
            deviceTransmit  = true;

            stormData        = new StormData(null);
            habitatInfo      = new VesselHabitatInfo(null);
            computer         = new Computer(null);
            supplies         = new Dictionary <string, SupplyData>();
            scansat_id       = new List <uint>();
            filesTransmitted = new List <File>();
            vesselSituations = new VesselSituations(this);
        }
예제 #18
0
 public static double Volume_per_crew(Vessel v)
 {
     // living space is the volume per-capita normalized against an 'ideal living space' and clamped in an acceptable range
     return(Tot_volume(v) / Math.Max(1, Lib.CrewCount(v)));
 }
예제 #19
0
 // return living space factor in a vessel
 public static double Living_space(Vessel v)
 {
     // living space is the volume per-capita normalized against an 'ideal living space' and clamped in an acceptable range
     return(Lib.Clamp((Tot_volume(v) / Lib.CrewCount(v)) / PreferencesComfort.Instance.livingSpace, 0.1, 1.0));
 }
예제 #20
0
        public void StoreData()
        {
            // disable for dead eva kerbals
            Vessel v = FlightGlobals.ActiveVessel;

            if (v == null || EVA.IsDead(v))
            {
                return;
            }

            // transfer data
            if (!Drive.Transfer(v, drive, PreferencesScience.Instance.sampleTransfer || Lib.CrewCount(v) > 0))
            {
                Message.Post
                (
                    Lib.Color(Lib.BuildString("WARNING: not evering copied"), Lib.Kolor.Red, true),
                    Lib.BuildString("Storage is at capacity")
                );
            }
        }
예제 #21
0
파일: Cache.cs 프로젝트: zajc3w/Kerbalism
  public vessel_info(Vessel v, uint vessel_id, UInt64 inc)
  {
    // NOTE: anything used here can't in turn use cache, unless you know what you are doing

    // associate with an unique incremental id
    this.inc = inc;

    // determine if this is a valid vessel
    is_vessel = Lib.IsVessel(v);
    if (!is_vessel) return;

    // determine if this is a resque mission vessel
    is_resque = Lib.IsResqueMission(v);
    if (is_resque) return;

    // dead EVA are not valid vessels
    if (v.isEVA && EVA.KerbalData(v).eva_dead) return;

    // shortcut for common tests
    is_valid = true;

    // generate id once
    id = vessel_id;

    // calculate crew info for the vessel
    crew_count = Lib.CrewCount(v);
    crew_capacity = Lib.CrewCapacity(v);

    // get vessel position once
    position = Lib.VesselPosition(v);

    // determine if in sunlight, calculate sun direction and distance
    sunlight = Sim.RaytraceBody(v, position, FlightGlobals.Bodies[0], out sun_dir, out sun_dist) ? 1.0 : 0.0;

    // if the orbit length vs simulation step is lower than an acceptable threshold, use discrete sun visibility
    if (v.mainBody.flightGlobalsIndex != 0)
    {
      double orbit_period = Sim.OrbitalPeriod(v);
      if (orbit_period / Kerbalism.elapsed_s < 16.0) sunlight = 1.0 - Sim.ShadowPeriod(v) / orbit_period;
    }

    // calculate environment stuff
    atmo_factor = Sim.AtmosphereFactor(v.mainBody, position, sun_dir);
    gamma_transparency = Sim.GammaTransparency(v.mainBody, v.altitude);
    breathable = Sim.Breathable(v);
    landed = Lib.Landed(v);

    // calculate temperature at vessel position
    temperature = Sim.Temperature(v, position, sunlight, atmo_factor, out solar_flux, out albedo_flux, out body_flux, out total_flux);

    // calculate radiation
    radiation = Radiation.Compute(v, position, gamma_transparency, sunlight, out blackout, out inside_pause, out inside_belt);

    // calculate malfunction stuff
    max_malfunction = Reliability.MaxMalfunction(v);
    avg_quality = Reliability.AverageQuality(v);

    // calculate signal info
    antenna = new antenna_data(v);
    avoid_inf_recursion.Add(v.id);
    link = Signal.Link(v, position, antenna, blackout, avoid_inf_recursion);
    avoid_inf_recursion.Remove(v.id);

    // partial data about modules, used by vessel info/monitor
    scrubbers = Scrubber.PartialData(v);
    recyclers = Recycler.PartialData(v);
    greenhouses = Greenhouse.PartialData(v);

    // woot relativity
    time_dilation = Sim.TimeDilation(v);
  }
예제 #22
0
        // trigger a random breakdown event
        public static void Breakdown(Vessel v, ProtoCrewMember c)
        {
            // constants
            const double res_penalty = 0.1; // proportion of food lost on 'depressed' and 'wrong_valve'

            // get info
            Rule          supply = supply_rules.Count > 0 ? supply_rules[Lib.RandomInt(supply_rules.Count)] : null;
            resource_info res    = supply != null?ResourceCache.Info(v, supply.resource_name) : null;

            // compile list of events with condition satisfied
            List <KerbalBreakdown> events = new List <KerbalBreakdown>();

            events.Add(KerbalBreakdown.mumbling); //< do nothing, here so there is always something that can happen
            if (Lib.CrewCount(v) > 1)
            {
                events.Add(KerbalBreakdown.argument);               //< do nothing, add some variation to messages
            }
            if (Lib.HasData(v))
            {
                events.Add(KerbalBreakdown.fat_finger);
            }
            if (Reliability.CanMalfunction(v))
            {
                events.Add(KerbalBreakdown.rage);
            }
            if (supply != null && res.amount > double.Epsilon)
            {
                events.Add(KerbalBreakdown.depressed);
                events.Add(KerbalBreakdown.wrong_valve);
            }

            // choose a breakdown event
            KerbalBreakdown breakdown = events[Lib.RandomInt(events.Count)];

            // generate message
            string text    = "";
            string subtext = "";

            switch (breakdown)
            {
            case KerbalBreakdown.mumbling:    text = "$ON_VESSEL$KERBAL has been in space for too long"; subtext = "Mumbling incoherently"; break;

            case KerbalBreakdown.argument:    text = "$ON_VESSEL$KERBAL had an argument with the rest of the crew"; subtext = "Morale is degenerating at an alarming rate"; break;

            case KerbalBreakdown.fat_finger:  text = "$ON_VESSEL$KERBAL is pressing buttons at random on the control panel"; subtext = "Science data has been lost"; break;

            case KerbalBreakdown.rage:        text = "$ON_VESSEL$KERBAL is possessed by a blind rage"; subtext = "A component has been damaged"; break;

            case KerbalBreakdown.depressed:   text = "$ON_VESSEL$KERBAL is not respecting the rationing guidelines"; subtext = supply.resource_name + " has been lost"; break;

            case KerbalBreakdown.wrong_valve: text = "$ON_VESSEL$KERBAL opened the wrong valve"; subtext = supply.resource_name + " has been lost"; break;
            }

            // post message first so this one is shown before malfunction message
            Message.Post(Severity.breakdown, Lib.ExpandMsg(text, v, c), subtext);

            // trigger the event
            switch (breakdown)
            {
            case KerbalBreakdown.mumbling: break; // do nothing

            case KerbalBreakdown.argument: break; // do nothing

            case KerbalBreakdown.fat_finger: Lib.RemoveData(v); break;

            case KerbalBreakdown.rage: Reliability.CauseMalfunction(v); break;

            case KerbalBreakdown.depressed:
            case KerbalBreakdown.wrong_valve: res.Consume(res.amount * res_penalty); break;
            }

            // remove reputation
            if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER)
            {
                Reputation.Instance.AddReputation(-Settings.BreakdownReputationPenalty, TransactionReasons.Any);
            }
        }
예제 #23
0
        // implement radiation mechanics
        public void FixedUpdate()
        {
            // avoid case when DB isn't ready for whatever reason
            if (!DB.Ready())
            {
                return;
            }

            // do nothing in the editors and the menus
            if (!Lib.SceneIsGame())
            {
                return;
            }

            // do nothing if paused
            if (Lib.IsPaused())
            {
                return;
            }

            // get time elapsed from last update
            double elapsed_s = TimeWarp.fixedDeltaTime;

            // for each vessel
            foreach (Vessel v in FlightGlobals.Vessels)
            {
                // skip invalid vessels
                if (!Lib.IsVessel(v))
                {
                    continue;
                }

                // skip dead eva kerbals
                if (EVA.IsDead(v))
                {
                    continue;
                }

                // get crew
                List <ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew();

                // get crew count
                int crew_count = Lib.CrewCount(v);

                // get vessel info from the cache
                vessel_info info = Cache.VesselInfo(v);

                // get vessel data
                vessel_data vd = DB.VesselData(v.id);


                // belt warnings
                // note: we only show it for manned vesssels, but the first time we also show it for probes
                if (crew_count > 0 || DB.NotificationData().first_belt_crossing == 0)
                {
                    if (InsideBelt(v) && vd.msg_belt < 1)
                    {
                        Message.Post("<b>" + v.vesselName + "</b> is crossing <i>" + v.mainBody.bodyName + " radiation belt</i>", "Exposed to extreme radiation");
                        vd.msg_belt = 1;
                        DB.NotificationData().first_belt_crossing = 1; //< record first belt crossing
                    }
                    else if (!InsideBelt(v) && vd.msg_belt > 0)
                    {
                        // no message after crossing the belt
                        vd.msg_belt = 0;
                    }
                }


                // for each crew
                foreach (ProtoCrewMember c in crew)
                {
                    // get kerbal data
                    kerbal_data kd = DB.KerbalData(c.name);

                    // skip resque kerbals
                    if (kd.resque == 1)
                    {
                        continue;
                    }

                    // skip disabled kerbals
                    if (kd.disabled == 1)
                    {
                        continue;
                    }

                    // accumulate radiation
                    kd.radiation += info.radiation * elapsed_s;

                    // kill kerbal if necessary
                    if (kd.radiation >= Settings.RadiationFatalThreshold)
                    {
                        Message.Post(Severity.fatality, KerbalEvent.radiation, v, c);
                        Kerbalism.Kill(v, c);
                    }
                    // show warnings
                    else if (kd.radiation >= Settings.RadiationDangerThreshold && kd.msg_radiation < 2)
                    {
                        Message.Post(Severity.danger, KerbalEvent.radiation, v, c);
                        kd.msg_radiation = 2;
                    }
                    else if (kd.radiation >= Settings.RadiationWarningThreshold && kd.msg_radiation < 1)
                    {
                        Message.Post(Severity.danger, KerbalEvent.radiation, v, c);
                        kd.msg_radiation = 1;
                    }
                    // note: no recovery from radiations
                }
            }
        }
예제 #24
0
  public vessel_info(Vessel v, uint vessel_id, UInt64 inc)
  {
    // NOTE: anything used here can't in turn use cache, unless you know what you are doing

    // NOTE: you can't cache vessel position
    // at any point in time all vessel/body positions are relative to a different frame of reference
    // so comparing the current position of a vessel, with the cached one of another make no sense

    // associate with an unique incremental id
    this.inc = inc;

    // determine if this is a valid vessel
    is_vessel = Lib.IsVessel(v);
    if (!is_vessel) return;

    // determine if this is a rescue mission vessel
    is_rescue = Misc.IsRescueMission(v);
    if (is_rescue) return;

    // dead EVA are not valid vessels
    if (EVA.IsDead(v)) return;

    // shortcut for common tests
    is_valid = true;

    // generate id once
    id = vessel_id;

    // calculate crew info for the vessel
    crew_count = Lib.CrewCount(v);
    crew_capacity = Lib.CrewCapacity(v);

    // get vessel position
    Vector3d position = Lib.VesselPosition(v);

    // this should never happen again
    if (Vector3d.Distance(position, v.mainBody.position) < 1.0)
    {
      throw new Exception("Shit hit the fan for vessel " + v.vesselName);
    }

    // determine if in sunlight, calculate sun direction and distance
    sunlight = Sim.RaytraceBody(v, position, FlightGlobals.Bodies[0], out sun_dir, out sun_dist) ? 1.0 : 0.0;

    // at the two highest timewarp speed, the number of sun visibility samples drop to the point that
    // the quantization error first became noticeable, and then exceed 100%
    // to solve this, we switch to an analytical estimation of the portion of orbit that was in sunlight
    // - we check against timewarp rate, instead of index, to avoid issues during timewarp blending
    if (v.mainBody.flightGlobalsIndex != 0 && TimeWarp.CurrentRate > 1000.0f)
    {
      sunlight = 1.0 - Sim.ShadowPeriod(v) / Sim.OrbitalPeriod(v);
    }

    // environment stuff
    atmo_factor = Sim.AtmosphereFactor(v.mainBody, position, sun_dir);
    gamma_transparency = Sim.GammaTransparency(v.mainBody, v.altitude);
    underwater = Sim.Underwater(v);
    breathable = Sim.Breathable(v, underwater);
    landed = Lib.Landed(v);

    // temperature at vessel position
    temperature = Sim.Temperature(v, position, sunlight, atmo_factor, out solar_flux, out albedo_flux, out body_flux, out total_flux);
    temp_diff = Sim.TempDiff(temperature, v.mainBody, landed);

    // radiation
    radiation = Radiation.Compute(v, position, gamma_transparency, sunlight, out blackout, out magnetosphere, out inner_belt, out outer_belt, out interstellar);

    // extended atmosphere
    thermosphere = Sim.InsideThermosphere(v);
    exosphere = Sim.InsideExosphere(v);

    // malfunction stuff
    malfunction = Reliability.HasMalfunction(v);
    critical = Reliability.HasCriticalFailure(v);

    // signal info
    antenna = new AntennaInfo(v);
    avoid_inf_recursion.Add(v.id);
    connection = Signal.connection(v, position, antenna, blackout, avoid_inf_recursion);
    transmitting = Science.transmitting(v, connection.linked);
    relaying = Signal.relaying(v, avoid_inf_recursion);
    avoid_inf_recursion.Remove(v.id);

    // habitat data
    volume = Habitat.tot_volume(v);
    surface = Habitat.tot_surface(v);
    pressure = Habitat.pressure(v);
    poisoning = Habitat.poisoning(v);
    shielding = Habitat.shielding(v);
    living_space = Habitat.living_space(v);
    comforts = new Comforts(v, landed, crew_count > 1, connection.linked);

    // data about greenhouses
    greenhouses = Greenhouse.Greenhouses(v);

    // other stuff
    gravioli = Sim.Graviolis(v);
  }
예제 #25
0
		// return living space factor in a vessel
		public static double living_space(Vessel v)
		{
			// living space is the volume per-capita normalized against an 'ideal living space' and clamped in an acceptable range
			return Lib.Clamp((tot_volume(v) / Lib.CrewCount(v)) / Settings.IdealLivingSpace, 0.1, 1.0);
		}
예제 #26
0
  // return quality-of-life bonus
  public static double Bonus(Vessel v, string k_name)
  {
    // get QoL data from db
    kerbal_data kd = DB.KerbalData(k_name);

    // calculate quality of life bonus
    return Bonus(kd.living_space, kd.entertainment, Lib.Landed(v), Signal.Link(v).linked, Lib.CrewCount(v) < 2u);
  }