Beispiel #1
0
        public static void update(Vessel v, vessel_info vi, VesselData vd, vessel_resources resources, double elapsed_s)
        {
            // get most used resource handlers
            resource_info ec = resources.Info(v, "ElectricCharge");

            // store data required to support multiple modules of same type in a part
            var PD = new Dictionary <string, Lib.module_prefab_data>();

            // for each part
            foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots)
            {
                // get part prefab (required for module properties)
                Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab;

                // get all module prefabs
                var module_prefabs = part_prefab.FindModulesImplementing <PartModule>();

                // clear module indexes
                PD.Clear();

                // for each module
                foreach (ProtoPartModuleSnapshot m in p.modules)
                {
                    // get module type
                    // if the type is unknown, skip it
                    module_type type = ModuleType(m.moduleName);
                    if (type == module_type.Unknown)
                    {
                        continue;
                    }

                    // get the module prefab
                    // if the prefab doesn't contain this module, skip it
                    PartModule module_prefab = Lib.ModulePrefab(module_prefabs, m.moduleName, PD);
                    if (!module_prefab)
                    {
                        continue;
                    }

                    // if the module is disabled, skip it
                    // note: this must be done after ModulePrefab is called, so that indexes are right
                    if (!Lib.Proto.GetBool(m, "isEnabled"))
                    {
                        continue;
                    }

                    // process modules
                    // note: this should be a fast switch, possibly compiled to a jump table
                    switch (type)
                    {
                    case module_type.Reliability: Reliability.BackgroundUpdate(v, p, m, module_prefab as Reliability); break;

                    case module_type.Experiment: Experiment.BackgroundUpdate(v, m, module_prefab as Experiment, ec, elapsed_s); break;

                    case module_type.Greenhouse: Greenhouse.BackgroundUpdate(v, m, module_prefab as Greenhouse, vi, resources, elapsed_s); break;

                    case module_type.GravityRing: GravityRing.BackgroundUpdate(v, p, m, module_prefab as GravityRing, ec, elapsed_s); break;

                    case module_type.Emitter: Emitter.BackgroundUpdate(v, p, m, module_prefab as Emitter, ec, elapsed_s); break;

                    case module_type.Harvester: Harvester.BackgroundUpdate(v, m, module_prefab as Harvester, elapsed_s); break;                             // Kerbalism ground and air harvester module

                    case module_type.Laboratory: Laboratory.BackgroundUpdate(v, p, m, module_prefab as Laboratory, ec, elapsed_s); break;

                    case module_type.Command: ProcessCommand(v, p, m, module_prefab as ModuleCommand, resources, elapsed_s); break;

                    case module_type.Panel: ProcessPanel(v, p, m, module_prefab as ModuleDeployableSolarPanel, vi, ec, elapsed_s); break;

                    case module_type.Generator: ProcessGenerator(v, p, m, module_prefab as ModuleGenerator, resources, elapsed_s); break;

                    case module_type.Converter: ProcessConverter(v, p, m, module_prefab as ModuleResourceConverter, resources, elapsed_s); break;

                    case module_type.Drill: ProcessDrill(v, p, m, module_prefab as ModuleResourceHarvester, resources, elapsed_s); break;                             // Stock ground harvester module

                    case module_type.AsteroidDrill: ProcessAsteroidDrill(v, p, m, module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break;                 // Stock asteriod harvester module

                    case module_type.StockLab: ProcessStockLab(v, p, m, module_prefab as ModuleScienceConverter, ec, elapsed_s); break;

                    case module_type.Light: ProcessLight(v, p, m, module_prefab as ModuleLight, ec, elapsed_s); break;

                    case module_type.Scanner: ProcessScanner(v, p, m, module_prefab, part_prefab, vd, ec, elapsed_s); break;

                    case module_type.CurvedPanel: ProcessCurvedPanel(v, p, m, module_prefab, part_prefab, vi, ec, elapsed_s); break;

                    case module_type.FissionGenerator: ProcessFissionGenerator(v, p, m, module_prefab, ec, elapsed_s); break;

                    case module_type.RadioisotopeGenerator: ProcessRadioisotopeGenerator(v, p, m, module_prefab, ec, elapsed_s); break;

                    //case module_type.CryoTank: ProcessCryoTank(v, p, m, module_prefab, resources, elapsed_s); break;
                    case module_type.FNGenerator: ProcessFNGenerator(v, p, m, module_prefab, ec, elapsed_s); break;
                    }
                }
            }
        }
Beispiel #2
0
        // consume EC for transmission, and transmit science data
        public static void update(Vessel v, vessel_info vi, VesselData vd, vessel_resources resources, double elapsed_s)
        {
            // hard-coded transmission buffer size in Mb
            const double buffer_capacity = 8.0;

            // do nothing if science system is disabled
            if (!Features.Science)
            {
                return;
            }

            // avoid corner-case when RnD isn't live during scene changes
            // - this avoid losing science if the buffer reach threshold during a scene change
            if (HighLogic.CurrentGame.Mode != Game.Modes.SANDBOX && ResearchAndDevelopment.Instance == null)
            {
                return;
            }

            // get connection info
            ConnectionInfo conn = vi.connection;

            if (conn == null || String.IsNullOrEmpty(vi.transmitting))
            {
                return;
            }

            // consume ec if data is transmitted or relayed
            if (vi.transmitting.Length > 0 || vi.relaying.Length > 0)
            {
                resources.Consume(v, "ElectricCharge", conn.cost * elapsed_s);
            }

            // get filename of data being downloaded
            string filename = vi.transmitting;

            // if some data is being downloaded
            // - avoid cornercase at scene changes
            if (filename.Length > 0 && vd.drive.files.ContainsKey(filename))
            {
                // get file
                File file = vd.drive.files[filename];

                // determine how much data is transmitted
                double transmitted = Math.Min(file.size, conn.rate * elapsed_s);

                // consume data in the file
                file.size -= transmitted;

                // accumulate in the buffer
                file.buff += transmitted;

                // if buffer is full, or file was transmitted completely
                if (file.size <= double.Epsilon || file.buff > buffer_capacity)
                {
                    // collect the science data
                    Science.credit(filename, file.buff, true, v.protoVessel);

                    // reset the buffer
                    file.buff = 0.0;
                }

                // if file was transmitted completely
                if (file.size <= double.Epsilon)
                {
                    // remove the file
                    vd.drive.files.Remove(filename);

                    // inform the user
                    Message.Post
                    (
                        Lib.BuildString("<color=cyan><b>DATA RECEIVED</b></color>\nTransmission of <b>", Science.experiment(filename).name, "</b> completed"),
                        Lib.TextVariant("Our researchers will jump on it right now", "The checksum is correct, data must be valid")
                    );
                }
            }
        }
Beispiel #3
0
        static void ProcessDrill(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceHarvester harvester, vessel_resources resources, double elapsed_s)
        {
            // note: ignore stock temperature mechanic of harvesters
            // note: ignore autoshutdown
            // note: ignore depletion (stock seem to do the same)
            // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)

            // if active
            if (Lib.Proto.GetBool(m, "IsActivated"))
            {
                // do nothing if full
                // note: comparing against previous amount
                if (resources.Info(v, harvester.ResourceName).level < harvester.FillAmount - double.Epsilon)
                {
                    // deduce crew bonus
                    int exp_level = -1;
                    if (harvester.UseSpecialistBonus)
                    {
                        foreach (ProtoCrewMember c in Lib.CrewList(v))
                        {
                            if (c.experienceTrait.Effects.Find(k => k.Name == harvester.ExperienceEffect) != null)
                            {
                                exp_level = Math.Max(exp_level, c.experienceLevel);
                            }
                        }
                    }
                    double exp_bonus = exp_level < 0
                                          ? harvester.EfficiencyBonus * harvester.SpecialistBonusBase
                                          : harvester.EfficiencyBonus * (harvester.SpecialistBonusBase + (harvester.SpecialistEfficiencyFactor * (exp_level + 1)));

                    // detect amount of ore in the ground
                    AbundanceRequest request = new AbundanceRequest
                    {
                        Altitude     = v.altitude,
                        BodyId       = v.mainBody.flightGlobalsIndex,
                        CheckForLock = false,
                        Latitude     = v.latitude,
                        Longitude    = v.longitude,
                        ResourceType = (HarvestTypes)harvester.HarvesterType,
                        ResourceName = harvester.ResourceName
                    };
                    double abundance = ResourceMap.Instance.GetAbundance(request);

                    // if there is actually something (should be if active when unloaded)
                    if (abundance > harvester.HarvestThreshold)
                    {
                        // create and commit recipe
                        resource_recipe recipe = new resource_recipe();
                        foreach (var ir in harvester.inputList)
                        {
                            recipe.Input(ir.ResourceName, ir.Ratio * elapsed_s);
                        }
                        recipe.Output(harvester.ResourceName, abundance * harvester.Efficiency * exp_bonus * elapsed_s, true);
                        resources.Transform(recipe);
                    }
                }

                // undo stock behaviour by forcing last_update_time to now
                Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime());
            }
        }
Beispiel #4
0
        static void ProcessAsteroidDrill(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleAsteroidDrill asteroid_drill, vessel_resources resources, double elapsed_s)
        {
            // note: untested
            // note: ignore stock temperature mechanic of asteroid drills
            // note: ignore autoshutdown
            // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)

            // if active
            if (Lib.Proto.GetBool(m, "IsActivated"))
            {
                // get asteroid data
                ProtoPartModuleSnapshot asteroid_info     = null;
                ProtoPartModuleSnapshot asteroid_resource = null;
                foreach (ProtoPartSnapshot pp in v.protoVessel.protoPartSnapshots)
                {
                    if (asteroid_info == null)
                    {
                        asteroid_info = pp.modules.Find(k => k.moduleName == "ModuleAsteroidInfo");
                    }
                    if (asteroid_resource == null)
                    {
                        asteroid_resource = pp.modules.Find(k => k.moduleName == "ModuleAsteroidResource");
                    }
                }

                // if there is actually an asteroid attached to this active asteroid drill (it should)
                if (asteroid_info != null && asteroid_resource != null)
                {
                    // get some data
                    double mass_threshold = Lib.Proto.GetDouble(asteroid_info, "massThresholdVal");
                    double mass           = Lib.Proto.GetDouble(asteroid_info, "currentMassVal");
                    double abundance      = Lib.Proto.GetDouble(asteroid_resource, "abundance");
                    string res_name       = Lib.Proto.GetString(asteroid_resource, "resourceName");
                    double res_density    = PartResourceLibrary.Instance.GetDefinition(res_name).density;

                    // if asteroid isn't depleted
                    if (mass > mass_threshold && abundance > double.Epsilon)
                    {
                        // deduce crew bonus
                        int exp_level = -1;
                        if (asteroid_drill.UseSpecialistBonus)
                        {
                            foreach (ProtoCrewMember c in Lib.CrewList(v))
                            {
                                if (c.experienceTrait.Effects.Find(k => k.Name == asteroid_drill.ExperienceEffect) != null)
                                {
                                    exp_level = Math.Max(exp_level, c.experienceLevel);
                                }
                            }
                        }
                        double exp_bonus = exp_level < 0
                                                ? asteroid_drill.EfficiencyBonus * asteroid_drill.SpecialistBonusBase
                                                : asteroid_drill.EfficiencyBonus * (asteroid_drill.SpecialistBonusBase + (asteroid_drill.SpecialistEfficiencyFactor * (exp_level + 1)));

                        // determine resource extracted
                        double res_amount = abundance * asteroid_drill.Efficiency * exp_bonus * elapsed_s;

                        // transform EC into mined resource
                        resource_recipe recipe = new resource_recipe();
                        recipe.Input("ElectricCharge", asteroid_drill.PowerConsumption * elapsed_s);
                        recipe.Output(res_name, res_amount, true);
                        resources.Transform(recipe);

                        // if there was ec
                        // note: comparing against amount in previous simulation step
                        if (resources.Info(v, "ElectricCharge").amount > double.Epsilon)
                        {
                            // consume asteroid mass
                            Lib.Proto.Set(asteroid_info, "currentMassVal", (mass - res_density * res_amount));
                        }
                    }
                }

                // undo stock behaviour by forcing last_update_time to now
                Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime());
            }
        }
Beispiel #5
0
        static void ProcessConverter(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceConverter converter, vessel_resources resources, double elapsed_s)
        {
            // note: ignore stock temperature mechanic of converters
            // note: ignore autoshutdown
            // note: non-mandatory resources 'dynamically scale the ratios', that is exactly what mandatory resources do too (DERP ALERT)
            // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)

            // if active
            if (Lib.Proto.GetBool(m, "IsActivated"))
            {
                // determine if vessel is full of all output resources
                // note: comparing against previous amount
                bool full = true;
                foreach (var or in converter.outputList)
                {
                    resource_info res = resources.Info(v, or.ResourceName);
                    full &= (res.level >= converter.FillAmount - double.Epsilon);
                }

                // if not full
                if (!full)
                {
                    // deduce crew bonus
                    int exp_level = -1;
                    if (converter.UseSpecialistBonus)
                    {
                        foreach (ProtoCrewMember c in Lib.CrewList(v))
                        {
                            if (c.experienceTrait.Effects.Find(k => k.Name == converter.ExperienceEffect) != null)
                            {
                                exp_level = Math.Max(exp_level, c.experienceLevel);
                            }
                        }
                    }
                    double exp_bonus = exp_level < 0
                                          ? converter.EfficiencyBonus * converter.SpecialistBonusBase
                                          : converter.EfficiencyBonus * (converter.SpecialistBonusBase + (converter.SpecialistEfficiencyFactor * (exp_level + 1)));

                    // create and commit recipe
                    resource_recipe recipe = new resource_recipe();
                    foreach (var ir in converter.inputList)
                    {
                        recipe.Input(ir.ResourceName, ir.Ratio * exp_bonus * elapsed_s);
                    }
                    foreach (var or in converter.outputList)
                    {
                        recipe.Output(or.ResourceName, or.Ratio * exp_bonus * elapsed_s, or.DumpExcess);
                    }
                    resources.Transform(recipe);
                }

                // undo stock behaviour by forcing last_update_time to now
                Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime());
            }
        }
Beispiel #6
0
 static void ProcessGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleGenerator generator, vessel_resources resources, double elapsed_s)
 {
     // if active
     if (Lib.Proto.GetBool(m, "generatorIsActive"))
     {
         // create and commit recipe
         resource_recipe recipe = new resource_recipe();
         foreach (ModuleResource ir in generator.resHandler.inputResources)
         {
             recipe.Input(ir.name, ir.rate * elapsed_s);
         }
         foreach (ModuleResource or in generator.resHandler.outputResources)
         {
             recipe.Output(or.name, or.rate * elapsed_s, true);
         }
         resources.Transform(recipe);
     }
 }
Beispiel #7
0
 static void ProcessCommand(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleCommand command, vessel_resources resources, double elapsed_s)
 {
     // do not consume if this is a MCM with no crew
     // rationale: for consistency, the game doesn't consume resources for MCM without crew in loaded vessels
     //            this make some sense: you left a vessel with some battery and nobody on board, you expect it to not consume EC
     if (command.minimumCrew == 0 || p.protoModuleCrew.Count > 0)
     {
         // for each input resource
         foreach (ModuleResource ir in command.resHandler.inputResources)
         {
             // consume the resource
             resources.Consume(v, ir.name, ir.rate * elapsed_s);
         }
     }
 }
Beispiel #8
0
  // called every simulation step
  void FixedUpdate()
  {
    // do nothing if paused
    if (Lib.IsPaused()) return;

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

    // do nothing if db isn't ready
    if (!DB.Ready()) return;

    // get elapsed time
    double elapsed_s = Kerbalism.elapsed_s;

    // evict oldest entry from vessel cache
    cache.update();

    // store info for oldest unloaded vessel
    double last_time = 0.0;
    Vessel last_v = null;
    vessel_info last_vi = null;
    vessel_data last_vd = null;
    vessel_resources last_resources = null;

    // for each vessel
    foreach(Vessel v in FlightGlobals.Vessels)
    {
      // get vessel info from the cache
      vessel_info vi = Cache.VesselInfo(v);

      // skip invalid vessels
      if (!vi.is_valid) continue;

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

      // get resource cache
      vessel_resources resources = ResourceCache.Get(v);

      // if loaded
      if (v.loaded)
      {
        // show belt warnings
        Radiation.beltWarnings(v, vi, vd);

        // update storm data
        storm.update(v, vi, vd, elapsed_s);

        // consume relay EC and show signal warnings
        signal.update(v, vi, vd, resources, elapsed_s * vi.time_dilation);

        // apply rules
        Rule.applyRules(v, vi, vd, resources, elapsed_s * vi.time_dilation);

        // apply deferred requests
        resources.Sync(v, elapsed_s);

        // update computer
        vd.computer.update(v, elapsed_s);

        // remove from unloaded data container
        unloaded.Remove(vi.id);
      }
      // if unloaded
      else
      {
        // get unloaded data, or create an empty one
        unloaded_data ud;
        if (!unloaded.TryGetValue(vi.id, out ud))
        {
          ud = new unloaded_data();
          unloaded.Add(vi.id, ud);
        }

        // accumulate time
        ud.time += elapsed_s;

        // maintain oldest entry
        if (ud.time > last_time)
        {
          last_time = ud.time;
          last_v = v;
          last_vi = vi;
          last_vd = vd;
          last_resources = resources;
        }
      }
    }


    // if the oldest unloaded vessel was selected
    if (last_v != null)
    {
      // show belt warnings
      Radiation.beltWarnings(last_v, last_vi, last_vd);

      // decay unloaded vessels inside atmosphere
      Kerbalism.atmosphereDecay(last_v, last_vi, last_time);

      // update storm data
      storm.update(last_v, last_vi, last_vd, last_time);

      // consume relay EC and show signal warnings
      signal.update(last_v, last_vi, last_vd, last_resources, last_time * last_vi.time_dilation);

      // apply rules
      Rule.applyRules(last_v, last_vi, last_vd, last_resources, last_time * last_vi.time_dilation);

      // simulate modules in background
      Background.update(last_v, last_vi, last_vd, last_resources, last_time * last_vi.time_dilation);

      // apply deferred requests
      last_resources.Sync(last_v, last_time);

      // update computer
      last_vd.computer.update(last_v, last_time);

      // remove from unloaded data container
      unloaded.Remove(last_vi.id);
    }


    // update storm data for one body per-step
    storm_bodies.ForEach(k => k.time += elapsed_s);
    storm_data sd = storm_bodies[storm_index];
    storm.update(sd.body, sd.time);
    sd.time = 0.0;
    storm_index = (storm_index + 1) % storm_bodies.Count;
  }
Beispiel #9
0
        void FixedUpdate()
        {
            // remove control locks in any case
            Misc.clearLocks();

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

            // maintain elapsed_s, converting to double only once
            // and detect warp blending
            double fixedDeltaTime = TimeWarp.fixedDeltaTime;

            if (Math.Abs(fixedDeltaTime - elapsed_s) > double.Epsilon)
            {
                warp_blending = 0;
            }
            else
            {
                ++warp_blending;
            }
            elapsed_s = fixedDeltaTime;

            // evict oldest entry from vessel cache
            Cache.update();

            // store info for oldest unloaded vessel
            double           last_time      = 0.0;
            Vessel           last_v         = null;
            vessel_info      last_vi        = null;
            VesselData       last_vd        = null;
            vessel_resources last_resources = null;

            // for each vessel
            foreach (Vessel v in FlightGlobals.Vessels)
            {
                // get vessel info from the cache
                vessel_info vi = Cache.VesselInfo(v);

                // set locks for active vessel
                if (v.isActiveVessel)
                {
                    Misc.setLocks(v, vi);
                }

                // maintain eva dead animation and helmet state
                if (v.loaded && v.isEVA)
                {
                    EVA.update(v);
                }

                // keep track of resque mission kerbals, and gift resources to their vessels on discovery
                if (v.loaded && vi.is_vessel)
                {
                    // manage resque mission mechanics
                    Misc.manageResqueMission(v);
                }

                // do nothing else for invalid vessels
                if (!vi.is_valid)
                {
                    continue;
                }

                // get vessel data from db
                VesselData vd = DB.Vessel(v);

                // get resource cache
                vessel_resources resources = ResourceCache.Get(v);

                // if loaded
                if (v.loaded)
                {
                    // show belt warnings
                    Radiation.beltWarnings(v, vi, vd);

                    // update storm data
                    Storm.update(v, vi, vd, elapsed_s);

                    // show signal warnings
                    Signal.update(v, vi, vd, elapsed_s);

                    // consume ec for transmission, and transmit science data
                    Science.update(v, vi, vd, resources, elapsed_s);

                    // apply rules
                    Profile.Execute(v, vi, vd, resources, elapsed_s);

                    // apply deferred requests
                    resources.Sync(v, elapsed_s);

                    // call automation scripts
                    vd.computer.automate(v, vi, resources);

                    // remove from unloaded data container
                    unloaded.Remove(vi.id);
                }
                // if unloaded
                else
                {
                    // get unloaded data, or create an empty one
                    unloaded_data ud;
                    if (!unloaded.TryGetValue(vi.id, out ud))
                    {
                        ud = new unloaded_data();
                        unloaded.Add(vi.id, ud);
                    }

                    // accumulate time
                    ud.time += elapsed_s;

                    // maintain oldest entry
                    if (ud.time > last_time)
                    {
                        last_time      = ud.time;
                        last_v         = v;
                        last_vi        = vi;
                        last_vd        = vd;
                        last_resources = resources;
                    }
                }
            }


            // if the oldest unloaded vessel was selected
            if (last_v != null)
            {
                // show belt warnings
                Radiation.beltWarnings(last_v, last_vi, last_vd);

                // update storm data
                Storm.update(last_v, last_vi, last_vd, last_time);

                // show signal warnings
                Signal.update(last_v, last_vi, last_vd, last_time);

                // consume ec for transmission, and transmit science
                Science.update(last_v, last_vi, last_vd, last_resources, last_time);

                // apply rules
                Profile.Execute(last_v, last_vi, last_vd, last_resources, last_time);

                // simulate modules in background
                Background.update(last_v, last_vi, last_vd, last_resources, last_time);

                // apply deferred requests
                last_resources.Sync(last_v, last_time);

                // call automation scripts
                last_vd.computer.automate(last_v, last_vi, last_resources);

                // remove from unloaded data container
                unloaded.Remove(last_vi.id);
            }

            // update storm data for one body per-step
            storm_bodies.ForEach(k => k.time += elapsed_s);
            storm_data sd = storm_bodies[storm_index];

            Storm.update(sd.body, sd.time);
            sd.time     = 0.0;
            storm_index = (storm_index + 1) % storm_bodies.Count;
        }