Esempio n. 1
0
        //If we still have fuel, don't drain through the parent unless the parent node is a stack node.
        //For example, a fuel tank radial mounted to a parent fuel tank will drain itself before starting to drain its parent.
        //This is in contrast to the stacked case, where a fuel tank will drain the parent it is stacked under before draining itself.
        //This just seems to be an idiosyncracy of the KSP fuel flow system, which we faithfully simulate.
        bool drainFromSourceBeforeSelf(int type, FuelNode source)
        {
            if (this.resources.Amount(type) < 1.0F)
            {
                return(true);                                      //if we have no fuel, then obviously we are allowed to drain any source node before this one
            }
            if (source.part != this.part.parent)
            {
                return(true);                                 //we have no qualms about draining non-parents before ourselves
            }
            if (this.part.parent == null)
            {
                return(true);                          //so that the next line doesn't cause an error
            }
            foreach (AttachNode attachNode in this.part.parent.attachNodes)
            {
                //if we are attached to the parent by a non-stack node, it's not cool to draw fuel from them
                //(unless we have no fuel of our own. that case was handled above.)
                if (attachNode.attachedPart == this.part && attachNode.nodeType != AttachNode.NodeType.Stack)
                {
                    return(false);
                }
            }

            //we only reach here if we have fuel, and source == parent, and parent != null, and we are not attached to the parent by a non-stack node
            //that is, we only reach here if we have fuel, and source == parent, and we are attached to the parent by a stack node
            //In this case it's OK to draw fuel from the parent.
            return(true);
        }
Esempio n. 2
0
        void AssignFuelDrainRateStagePriorityFlow(int type, float amount, List <FuelNode> vessel)
        {
            int maxInverseStage = -1;

            using (var dispoSources = ListPool <FuelNode> .Instance.BorrowDisposable())
            {
                var sources = dispoSources.value;
                for (int i = 0; i < vessel.Count; i++)
                {
                    FuelNode n = vessel[i];
                    if (n.resources[type] > DRAINED)
                    {
                        if (n.inverseStage > maxInverseStage)
                        {
                            maxInverseStage = n.inverseStage;
                            sources.Clear();
                            sources.Add(n);
                        }
                        else if (n.inverseStage == maxInverseStage)
                        {
                            sources.Add(n);
                        }
                    }
                }
                for (int i = 0; i < sources.Count; i++)
                {
                    if (!freeResources[type])
                    {
                        sources[i].resourceDrains[type] += amount / sources.Count;
                    }
                }
            }
        }
Esempio n. 3
0
 //call this when a node no longer exists, so that this node knows that it's no longer a valid source
 public void RemoveSourceNode(FuelNode n)
 {
     if (sourceNodes.Contains(n))
     {
         sourceNodes.Remove(n);
     }
 }
Esempio n. 4
0
        public static FuelNode Borrow(Part part, bool dVLinearThrust)
        {
            FuelNode node = pool.Borrow();

            node.Init(part, dVLinearThrust);
            return(node);
        }
Esempio n. 5
0
        // Find the set of nodes from which we can draw resources according to the STACK_PRIORITY_SEARCH flow scheme.
        // This gets called after all the FuelNodes have been constructed in order to set up the fuel flow graph.
        // Note that fuel flow through fuel lines and docked docking nodes is set up separately in
        // SetupFuelLineSources*()
        public void SetupRegularSources(Part part, Dictionary <Part, FuelNode> nodeLookup)
        {
            // When fuelCrossFeed is enabled we can draw fuel through stack and surface attachment
            if (part.fuelCrossFeed)
            {
                // Stack nodes:
                for (int i = 0; i < part.attachNodes.Count; i++)
                {
                    AttachNode attachNode = part.attachNodes[i];
                    if (attachNode.attachedPart != null)
                    {
                        // For stack nodes, we can draw fuel unless this node is specifically
                        // labeled as having crossfeed disabled (Kashua rule #4)
                        FuelNode fuelnode;
                        if (attachNode.id != "Strut" &&
                            attachNode.ResourceXFeed &&
                            !(part.NoCrossFeedNodeKey.Length > 0 &&
                              attachNode.id.Contains(part.NoCrossFeedNodeKey)) &&
                            nodeLookup.TryGetValue(attachNode.attachedPart, out fuelnode))
                        {
                            stackNodeSources.Add(fuelnode);
                        }
                    }
                }

                // If we are surface-mounted to our parent we can draw fuel from it (Kashua rule #7)
                if (part.attachMode == AttachModes.SRF_ATTACH && part.parent != null)
                {
                    surfaceMountParent = nodeLookup[part.parent];
                }
            }
        }
        //If we still have fuel, don't drain through the parent unless the parent node is a stack node.
        //This just seems to be an idiosyncracy of the KSP fuel flow system, which we faithfully simulate.
        bool drainFromSourceBeforeSelf(FuelNode source)
        {
            if (this.fuel == 0)
            {
                return(true);
            }
            if (source.part != this.part.parent)
            {
                return(true);
            }
            if (this.part.parent == null)
            {
                return(true);
            }

            foreach (AttachNode attachNode in this.part.parent.attachNodes)
            {
                if (attachNode.attachedPart == this.part && attachNode.nodeType != AttachNode.NodeType.Stack)
                {
                    return(false);
                }
            }

            return(true);
        }
 public void addSource(FuelNode source)
 {
     if (!sources.Contains(source))
     {
         sources.Add(source);
     }
 }
Esempio n. 8
0
        void AssignFuelDrainRateStagePriorityFlow(int type, float amount, List <FuelNode> vessel)
        {
            int             maxInverseStage = -1;
            List <FuelNode> sources         = new List <FuelNode>();

            for (int i = 0; i < vessel.Count; i++)
            {
                FuelNode n = vessel[i];
                if (n.resources[type] > DRAINED)
                {
                    if (n.inverseStage > maxInverseStage)
                    {
                        maxInverseStage = n.inverseStage;
                        sources.Clear();
                        sources.Add(n);
                    }
                    else if (n.inverseStage == maxInverseStage)
                    {
                        sources.Add(n);
                    }
                }
            }
            for (int i = 0; i < sources.Count; i++)
            {
                sources[i].resourceDrains[type] += amount / sources.Count;
            }
        }
Esempio n. 9
0
        //Takes a list of parts so that the simulation can be run in the editor as well as the flight scene
        public void Init(List <Part> parts, bool dVLinearThrust)
        {
            KpaToAtmospheres = PhysicsGlobals.KpaToAtmospheres;

            // Create FuelNodes corresponding to each Part
            nodes.Clear();
            nodeLookup.Clear();
            //Dictionary<Part, FuelNode> nodeLookup = parts.ToDictionary(p => p, p => FuelNode.Borrow(p, dVLinearThrust));
            for (int index = 0; index < parts.Count; index++)
            {
                Part     part = parts[index];
                FuelNode node = FuelNode.Borrow(part, dVLinearThrust);
                nodeLookup[part] = node;
                nodes.Add(node);
            }
            // Determine when each part will be decoupled
            Part rootPart = parts[0]; // hopefully always correct

            nodeLookup[rootPart].AssignDecoupledInStage(rootPart, nodeLookup, -1);

            // Set up the fuel flow graph
            if (HighLogic.LoadedSceneIsFlight)
            {
                for (int i = 0; i < parts.Count; i++)
                {
                    Part p = parts[i];
                    nodeLookup[p].SetupFuelLineSourcesFlight(p, nodeLookup);
                }
            }
            else
            {
                for (int i = 0; i < parts.Count; i++)
                {
                    Part p = parts[i];
                    nodeLookup[p].SetupFuelLineSourcesFlight(p, nodeLookup);
                    nodeLookup[p].SetupFuelLineSourcesEditor(p, nodeLookup);
                }
            }
            for (int i = 0; i < parts.Count; i++)
            {
                Part p = parts[i];
                nodeLookup[p].SetupRegularSources(p, nodeLookup);
                nodeLookup[p].SetupSurfaceMountSources(p, nodeLookup);
            }


            simStage = Staging.lastStage + 1;

            // Add a fake stage if we are beyond the first one
            // Mostly usefull for the Node Executor who use the last stage info
            // and fail to get proper info when the ship was never staged and
            // some engine were activated manually
            if (Staging.CurrentStage > Staging.lastStage)
            {
                simStage++;
            }
        }
        //assemble the representation of the ship in terms of a set of FuelNodes and fuel source relationships
        public void rebuildFuelFlowGraph(Environment enviro)
        {
            nodes = new List <FuelNode>();

            //a useful tool to let us look up the fuel node corresponding to a given part:
            Dictionary <Part, FuelNode> nodeLookup = new Dictionary <Part, FuelNode>();

            //create a FuelNode for each part
            foreach (Part p in parts)
            {
                FuelNode n = new FuelNode(p, enviro);
                nodes.Add(n);
                nodeLookup[p] = n;
            }

            //figure out where each FuelNode can draw fuel from
            foreach (FuelNode n in nodes)
            {
                //solid rockets can only draw on internal fuel
                if (n.part is SolidRocket)
                {
                    n.addSource(n);
                    continue; //skip all the code below, which applies only to liquid fuel
                }

                //first draw fuel from any fuel lines that point to this part
                foreach (FuelNode m in nodes)
                {
                    if (m.part is FuelLine && ((FuelLine)m.part).target == n.part)
                    {
                        n.addSource(m);
                    }
                }

                //then draw fuel from stacked parts
                foreach (AttachNode attachNode in n.part.attachNodes)
                {
                    //decide if it's possible to draw fuel through this node:
                    if (attachNode.attachedPart != null &&                              //if there is a part attached here
                        attachNode.nodeType == AttachNode.NodeType.Stack &&             //and the attached part is stacked (rather than surface mounted)
                        (attachNode.attachedPart.fuelCrossFeed ||                       //and the attached part allows fuel flow
                         attachNode.attachedPart is FuelTank) &&                        //    or the attached part is a fuel tank
                        !(n.part.NoCrossFeedNodeKey.Length > 0 &&                       //and this part does not forbid fuel flow
                          attachNode.id.Contains(n.part.NoCrossFeedNodeKey)))           //    through this particular node
                    {
                        n.addSource(nodeLookup[attachNode.attachedPart]);
                    }
                }

                //then draw fuel from the parent part. For example, fuel tanks can draw
                //fuel from a fuel tank to which they are surface mounted.
                if (n.part.parent != null && n.part.parent.fuelCrossFeed)
                {
                    n.addSource(nodeLookup[n.part.parent]);
                }
            }
        }
Esempio n. 11
0
 //call this when a node no longer exists, so that this node knows that it's no longer a valid source
 public void RemoveSourceNode(FuelNode n)
 {
     if (fuelLineSources.Contains(n))
     {
         fuelLineSources.Remove(n);
     }
     if (stackNodeSources.Contains(n))
     {
         stackNodeSources.Remove(n);
     }
     if (surfaceMountParent == n)
     {
         surfaceMountParent = null;
     }
 }
Esempio n. 12
0
        //Build up the representation of the vessel in terms of FuelNodes, and let them
        //figure out how to draw fuel from each other
        void rebuildVesselRepresenation(List <Part> parts, float atmospheres, bool current_thrust)
        {
            nodes = new List <FuelNode>();
            Dictionary <Part, FuelNode> nodeLookup = new Dictionary <Part, FuelNode>();

            foreach (Part p in parts)
            {
                FuelNode node = new FuelNode(p, atmospheres, current_thrust);
                nodes.Add(node);
                nodeLookup[p] = node;
            }
            foreach (FuelNode n in nodes)
            {
                n.findSourceNodes(nodeLookup);
            }
        }
Esempio n. 13
0
        //Find the set of nodes from which we can draw resources according to the STACK_PRIORITY_SEARCH flow scheme.
        //This gets called after all the FuelNodes have been constructed in order to set up the fuel flow graph
        public void FindSourceNodes(Part part, Dictionary <Part, FuelNode> nodeLookup)
        {
            //we can draw fuel from any fuel lines that point to this part
            foreach (Part p in nodeLookup.Keys)
            {
                if (p is FuelLine && ((FuelLine)p).target == part)
                {
                    sourceNodes.Add(nodeLookup[p]);
                }
            }

            surfaceMounted = true;
            if (part.parent != null)
            {
                this.parent = nodeLookup[part.parent];
            }

            //we can (sometimes) draw fuel from stacked parts
            foreach (AttachNode attachNode in part.attachNodes)
            {
                //decide if it's possible to draw fuel through this node:
                if (attachNode.attachedPart != null &&                         //if there is a part attached here
                    attachNode.nodeType == AttachNode.NodeType.Stack &&        //and the attached part is stacked (rather than surface mounted)
                    !(part.NoCrossFeedNodeKey.Length > 0 &&                    //and this part does not forbid fuel flow
                      attachNode.id.Contains(part.NoCrossFeedNodeKey)))        //    through this particular node
                {
                    if (part.fuelCrossFeed)
                    {
                        sourceNodes.Add(nodeLookup[attachNode.attachedPart]);
                    }
                    if (attachNode.attachedPart == part.parent)
                    {
                        surfaceMounted = false;
                    }
                }
            }

            //Parts can draw resources from their parents
            //(exception: surface mounted fuel tanks cannot)
            if (part.parent != null && part.fuelCrossFeed)
            {
                sourceNodes.Add(nodeLookup[part.parent]);
            }

            //Debug.Log("source nodes for part " + partName);
            //foreach (FuelNode n in sourceNodes) Debug.Log("    " + n.partName);
        }
        /*
         * //Find fuel nodes that are expected to supply fuel but can't. These nodes
         * //have run out of fuel and will never be able to supply fuel again. Remove them
         * //as possible sources for other fuel nodes. This way, when an engine no longer
         * //has any sources, we know it has run out of fuel.
         * void killEmptySources(List<FuelNode> engines)
         * {
         *  bool sourceKilled = true;
         *  while (sourceKilled)
         *  {
         *      //figure out where fuel is being drained from
         *      assignFuelDrainRates();
         *
         *      //check if any nodes are expected to supply fuel but can't. if so they have been
         *      //drained; remove them from the fuel flow graph
         *      sourceKilled = false;
         *      foreach (FuelNode n in nodes)
         *      {
         *          if (n.fuelDrainRate > 0 && n.fuel < 1.0F)
         *          {
         *              if (killSource(n)) sourceKilled = true;
         *          }
         *      }
         *
         *      //If we killed a source, it might have been at the end of a long fuel chain. We may now need
         *      //to kill some or all of the rest of the chain. Hence the while loop.
         *  }
         * }
         */

        //Remove the given FuelNode from any source lists in which it appears.
        //Should be called when the given FuelNode becomes no longer capable of supplying
        //fuel to anyone. Returns true if the given FuelNode was actually removed from
        //at least one source list.
        bool killSource(FuelNode source)
        {
            bool wasSource = false;

            source.fuel = 0;

            foreach (FuelNode n in nodes)
            {
                if (n.sources.Contains(source))
                {
                    n.sources.Remove(source);
                    wasSource = true;
                }
            }

            return(wasSource);
        }
Esempio n. 15
0
        void AssignFuelDrainRateStagePriorityFlow(int type, double amount, bool usePrio, List <FuelNode> vessel)
        {
            int maxPrio = int.MinValue;

            using (var dispoSources = ListPool <FuelNode> .Instance.BorrowDisposable())
            {
                var sources = dispoSources.value;
                //print("AssignFuelDrainRateStagePriorityFlow for " + partName + " searching for " + amount + " of " + PartResourceLibrary.Instance.GetDefinition(type).name + " in " + vessel.Count + " parts ");
                for (int i = 0; i < vessel.Count; i++)
                {
                    FuelNode n = vessel[i];
                    if (n.resources[type] > n.resourceRequestRemainingThreshold)
                    {
                        if (usePrio)
                        {
                            if (n.resourcePriority > maxPrio)
                            {
                                maxPrio = n.resourcePriority;
                                sources.Clear();
                                sources.Add(n);
                            }
                            else if (n.resourcePriority == maxPrio)
                            {
                                sources.Add(n);
                            }
                        }
                        else
                        {
                            sources.Add(n);
                        }
                    }
                }
                //print(partName + " drains resource from " + sources.Count + " parts ");
                for (int i = 0; i < sources.Count; i++)
                {
                    if (!freeResources[type])
                    {
                        sources[i].resourceDrains[type] += amount / sources.Count;
                    }
                }
            }
        }
Esempio n. 16
0
        void AssignFuelDrainRateAllVessel(int type, float amount, List <FuelNode> vessel)
        {
            //I don't know how this flow scheme actually works but I'm going to assume
            //that it drains from the part with the highest possible inverseStage
            FuelNode source = null;

            foreach (FuelNode n in vessel)
            {
                if (n.resources[type] > DRAINED)
                {
                    if (source == null || n.inverseStage > source.inverseStage)
                    {
                        source = n;
                    }
                }
            }
            if (source != null)
            {
                source.resourceDrains[type] += amount;
            }
        }
Esempio n. 17
0
        //Takes a list of parts so that the simulation can be run in the editor as well as the flight scene
        public void Init(List <Part> parts, bool dVLinearThrust)
        {
            KpaToAtmospheres = PhysicsGlobals.KpaToAtmospheres;

            // Create FuelNodes corresponding to each Part
            nodes.Clear();
            nodeLookup.Clear();

            for (int index = 0; index < parts.Count; index++)
            {
                Part     part = parts[index];
                FuelNode node = FuelNode.Borrow(part, dVLinearThrust);
                nodeLookup[part] = node;
                nodes.Add(node);
            }
            // Determine when each part will be decoupled
            Part rootPart = parts[0]; // hopefully always correct

            nodeLookup[rootPart].AssignDecoupledInStage(rootPart, nodeLookup, -1);

            // Set up the fuel flow graph
            for (int i = 0; i < parts.Count; i++)
            {
                Part p = parts[i];
                nodeLookup[p].AddCrossfeedSouces(p.crossfeedPartSet.GetParts(), nodeLookup);
            }

            simStage = StageManager.LastStage + 1;

            // Add a fake stage if we are beyond the first one
            // Mostly usefull for the Node Executor who use the last stage info
            // and fail to get proper info when the ship was never staged and
            // some engine were activated manually
            if (StageManager.CurrentStage > StageManager.LastStage)
            {
                simStage++;
            }
        }
Esempio n. 18
0
        void assignFuelDrainRateAllVessel(int type, float amount, List <FuelNode> vessel)
        {
            //print("Assigning " + amount + " drain of " + PartResourceLibrary.Instance.GetDefinition(type).name + " from " + part);
            //I don't know how this flow scheme actually works but I'm going to assume
            //that it drains from the part with the highest possible inverseStage
            FuelNode source = null;

            foreach (FuelNode n in vessel)
            {
                //print("  -- looking at " + n.part + " - amount = " + n.resources.Amount(type));
                if (n.resources.Amount(type) > 1.0F)
                {
                    if (source == null || n.part.inverseStage > source.part.inverseStage)
                    {
                        source = n;
                    }
                }
            }
            //print("draining from source = " + part);
            if (source != null)
            {
                source.resourceDrains.Add(type, amount);
            }
        }
Esempio n. 19
0
        /*
        //Find fuel nodes that are expected to supply fuel but can't. These nodes
        //have run out of fuel and will never be able to supply fuel again. Remove them
        //as possible sources for other fuel nodes. This way, when an engine no longer
        //has any sources, we know it has run out of fuel.
        void killEmptySources(List<FuelNode> engines)
        {
            bool sourceKilled = true;
            while (sourceKilled)
            {
                //figure out where fuel is being drained from
                assignFuelDrainRates();

                //check if any nodes are expected to supply fuel but can't. if so they have been
                //drained; remove them from the fuel flow graph
                sourceKilled = false;
                foreach (FuelNode n in nodes)
                {
                    if (n.fuelDrainRate > 0 && n.fuel < 1.0F)
                    {
                        if (killSource(n)) sourceKilled = true;
                    }
                }

                //If we killed a source, it might have been at the end of a long fuel chain. We may now need
                //to kill some or all of the rest of the chain. Hence the while loop.
            }
        }
        */
        //Remove the given FuelNode from any source lists in which it appears.
        //Should be called when the given FuelNode becomes no longer capable of supplying
        //fuel to anyone. Returns true if the given FuelNode was actually removed from
        //at least one source list.
        bool killSource(FuelNode source)
        {
            bool wasSource = false;

            source.fuel = 0;

            foreach (FuelNode n in nodes)
            {
                if (n.sources.Contains(source))
                {
                    n.sources.Remove(source);
                    wasSource = true;
                }
            }

            return wasSource;
        }
Esempio n. 20
0
        //assemble the representation of the ship in terms of a set of FuelNodes and fuel source relationships
        public void rebuildFuelFlowGraph(Environment enviro)
        {
            nodes = new List<FuelNode>();

            //a useful tool to let us look up the fuel node corresponding to a given part:
            Dictionary<Part, FuelNode> nodeLookup = new Dictionary<Part, FuelNode>();

            //create a FuelNode for each part
            foreach (Part p in parts)
            {
                FuelNode n = new FuelNode(p, enviro);
                nodes.Add(n);
                nodeLookup[p] = n;
            }

            //figure out where each FuelNode can draw fuel from
            foreach (FuelNode n in nodes)
            {
                //solid rockets can only draw on internal fuel
                if (n.part is SolidRocket)
                {
                    n.addSource(n);
                    continue; //skip all the code below, which applies only to liquid fuel
                }

                //first draw fuel from any fuel lines that point to this part
                foreach (FuelNode m in nodes)
                {
                    if (m.part is FuelLine && ((FuelLine)m.part).target == n.part)
                    {
                        n.addSource(m);
                    }
                }

                //then draw fuel from stacked parts
                foreach (AttachNode attachNode in n.part.attachNodes)
                {
                    //decide if it's possible to draw fuel through this node:
                    if (attachNode.attachedPart != null                                 //if there is a part attached here
                        && attachNode.nodeType == AttachNode.NodeType.Stack             //and the attached part is stacked (rather than surface mounted)
                        && (attachNode.attachedPart.fuelCrossFeed                       //and the attached part allows fuel flow
                            || attachNode.attachedPart is FuelTank)                     //    or the attached part is a fuel tank
                        && !(n.part.NoCrossFeedNodeKey.Length > 0                       //and this part does not forbid fuel flow
                             && attachNode.id.Contains(n.part.NoCrossFeedNodeKey)))     //    through this particular node
                    {
                        n.addSource(nodeLookup[attachNode.attachedPart]);
                    }
                }

                //then draw fuel from the parent part. For example, fuel tanks can draw
                //fuel from a fuel tank to which they are surface mounted.
                if (n.part.parent != null && n.part.parent.fuelCrossFeed)
                {
                    n.addSource(nodeLookup[n.part.parent]);
                }
            }
        }
Esempio n. 21
0
        //Whether we've used up the current stage
        public bool AllowedToStage()
        {
            //print("Checking whether allowed to stage at t = " + t);

            List <FuelNode> activeEngines = FindActiveEngines();

            //print("  activeEngines.Count = " + activeEngines.Count);

            //if no engines are active, we can always stage
            if (activeEngines.Count == 0)
            {
                //print("Allowed to stage because no active engines");
                return(true);
            }

            List <int> burnedResources = activeEngines.SelectMany(eng => eng.BurnedResources()).Distinct().ToList();

            //if staging would decouple an active engine or non-empty fuel tank, we're not allowed to stage
            for (int i = 0; i < nodes.Count; i++)
            {
                FuelNode n = nodes[i];
//print(n.partName + " is sepratron? " + n.isSepratron);
                if (n.decoupledInStage == (simStage - 1) && !n.isSepratron)
                {
                    if (activeEngines.Contains(n) || n.ContainsResources(burnedResources))
                    {
                        //print("Not allowed to stage because " + n.partName + " either contains resources or is an active engine");
                        return(false);
                    }
                }
            }

            // We are not allowed to stage if the stage does not decouple anything, and there is an active engine that still has access to resources
            {
                bool activeEnginesWorking     = false;
                bool partDecoupledInNextStage = false;

                for (int i = 0; i < nodes.Count; i++)
                {
                    FuelNode n = nodes[i];
                    if (activeEngines.Contains(n))
                    {
                        if (n.CanDrawNeededResources(nodes))
                        {
                            //print("Part " + n.partName + " is an active engine that still has resources to draw on.");
                            activeEnginesWorking = true;
                        }
                    }

                    if (n.decoupledInStage == (simStage - 1))
                    {
                        //print("Part " + n.partName + " is decoupled in the next stage.");

                        partDecoupledInNextStage = true;
                    }
                }

                if (!partDecoupledInNextStage && activeEnginesWorking)
                {
                    //print("Not allowed to stage because nothing is decoupled in the enst stage, and there are already other engines active.");
                    return(false);
                }
            }

            //if this isn't the last stage, we're allowed to stage because doing so wouldn't drop anything important
            if (simStage > 0)
            {
                //print("Allowed to stage because this isn't the last stage");
                return(true);
            }

            //print("Not allowed to stage because there are active engines and this is the last stage");

            //if this is the last stage, we're not allowed to stage while there are still active engines
            return(false);
        }
Esempio n. 22
0
        private void Init(Part part, bool dVLinearThrust)
        {
            resources.Clear();
            resourceConsumptions.Clear();
            resourceDrains.Clear();
            freeResources.Clear();

            propellantRatios.Clear();
            propellantFlows.Clear();

            fuelLineSources.Clear();
            stackNodeSources.Clear();
            surfaceMountSources.Clear();

            surfaceMountParent = null;
            isEngine = false;

            dryMass = 0;
            fairingMass = 0;

            moduleMass = 0;
            if (!part.IsLaunchClamp())
            {
                //print(part.partInfo.name.PadRight(25) + " " + part.mass.ToString("F4") + " " + part.GetPhysicslessChildMass().ToString("F4") + " " + part.GetModuleMass(part.partInfo.partPrefab.mass).ToString("F4"));
                dryMass = part.mass; // Intentionally ignore the physic flag.

                moduleMass = part.GetModuleMassNoAlloc(part.partInfo.partPrefab != null ? part.partInfo.partPrefab.mass : dryMass);
                if (part.HasModule<ModuleProceduralFairing>())
                {
                    fairingMass = moduleMass;
                }
            }

            inverseStage = part.inverseStage;
            partName = part.partInfo.name;

            //note which resources this part has stored
            for (int i = 0; i < part.Resources.Count; i++)
            {
                PartResource r = part.Resources[i];
                if (r.info.density > 0)
                {
                    if (r.flowState)
                    {
                        resources[r.info.id] = (float)r.amount;
                    }
                    else
                    {
                        dryMass += (float)(r.amount * r.info.density); // disabled resources are just dead weight
                    }
                }
                if (r.info.name == "IntakeAir")
                    freeResources[PartResourceLibrary.Instance.GetDefinition("IntakeAir").id] = true;
                // Those two are in the CRP.
                if (r.info.name == "IntakeLqd")
                    freeResources[PartResourceLibrary.Instance.GetDefinition("IntakeLqd").id] = true;
                if (r.info.name == "IntakeAtm")
                    freeResources[PartResourceLibrary.Instance.GetDefinition("IntakeAtm").id] = true;
            }

            // TODO : handle the multiple active ModuleEngine case ( SXT engines with integrated vernier )

            //record relevant engine stats
            //ModuleEngines engine = part.Modules.OfType<ModuleEngines>().FirstOrDefault(e => e.isEnabled);
            ModuleEngines engine = null;
            for (int i = 0; i < part.Modules.Count; i++)
            {
                PartModule pm = part.Modules[i];
                ModuleEngines e = pm as ModuleEngines;
                if (e != null && e.isEnabled)
                {
                    engine = e;
                    break;
                }
            }

            if (engine != null)
            {
                //Only count engines that either are ignited or will ignite in the future:
                if ((HighLogic.LoadedSceneIsEditor || inverseStage < StageManager.CurrentStage || engine.getIgnitionState) && (engine.thrustPercentage > 0 || engine.minThrust > 0))
                {
                    //if an engine has been activated early, pretend it is in the current stage:
                    if (engine.getIgnitionState && inverseStage < StageManager.CurrentStage)
                        inverseStage = StageManager.CurrentStage;

                    isEngine = true;

                    g = engine.g;

                    // If we take into account the engine rotation
                    if (dVLinearThrust)
                    {
                        Vector3 thrust = Vector3d.zero;
                        for (int i = 0; i < engine.thrustTransforms.Count; i++)
                        {
                            thrust -= engine.thrustTransforms[i].forward * engine.thrustTransformMultipliers[i];
                        }

                        Vector3d fwd = HighLogic.LoadedScene == GameScenes.EDITOR ? EditorLogic.VesselRotation * Vector3d.up : engine.part.vessel.GetTransform().up;
                        fwdThrustRatio = Vector3.Dot(fwd, thrust);
                    }
                    else
                    {
                        fwdThrustRatio = 1;
                    }

                    thrustPercentage = engine.thrustPercentage;

                    minFuelFlow = engine.minFuelFlow;
                    maxFuelFlow = engine.maxFuelFlow;

                    atmosphereCurve = new FloatCurve(engine.atmosphereCurve.Curve.keys);
                    atmChangeFlow = engine.atmChangeFlow;
                    useAtmCurve = engine.useAtmCurve;
                    if (useAtmCurve)
                        atmCurve = new FloatCurve(engine.atmCurve.Curve.keys);
                    useVelCurve = engine.useVelCurve;
                    if (useVelCurve)
                        velCurve = new FloatCurve(engine.velCurve.Curve.keys);

                    propellantSumRatioTimesDensity = engine.propellants.Slinq().Where(prop => !prop.ignoreForIsp).Select(prop => prop.ratio * MuUtils.ResourceDensity(prop.id)).Sum();
                    propellantRatios.Clear();
                    propellantFlows.Clear();
                    var dics = new Tuple<KeyableDictionary<int, float>, KeyableDictionary<int, ResourceFlowMode>>(propellantRatios, propellantFlows);
                    engine.propellants.Slinq()
                        .Where(prop => MuUtils.ResourceDensity(prop.id) > 0 && !prop.ignoreForIsp)
                        .ForEach((p, dic) =>
                        {
                            dic._1.Add(p.id, p.ratio);
                            dic._2.Add(p.id, p.GetFlowMode());
                        }, dics);
                }
            }
        }
Esempio n. 23
0
        //Find the set of nodes from which we can draw resources according to the STACK_PRIORITY_SEARCH flow scheme.
        //This gets called after all the FuelNodes have been constructed in order to set up the fuel flow graph
        public void FindSourceNodes(Part part, Dictionary<Part, FuelNode> nodeLookup)
        {
            //we can draw fuel from any fuel lines that point to this part
            foreach (Part p in nodeLookup.Keys)
            {
                if (p is FuelLine && ((FuelLine)p).target == part)
                {
                    sourceNodes.Add(nodeLookup[p]);
                }
            }

            surfaceMounted = true;
            if (part.parent != null) this.parent = nodeLookup[part.parent];

            //we can (sometimes) draw fuel from stacked parts
            foreach (AttachNode attachNode in part.attachNodes)
            {
                //decide if it's possible to draw fuel through this node:
                if (attachNode.attachedPart != null                            //if there is a part attached here
                    && attachNode.nodeType == AttachNode.NodeType.Stack        //and the attached part is stacked (rather than surface mounted)
                    && attachNode.attachedPart.fuelCrossFeed                   //and the attached part allows fuel flow
                    && !(part.NoCrossFeedNodeKey.Length > 0                    //and this part does not forbid fuel flow
                         && attachNode.id.Contains(part.NoCrossFeedNodeKey)))  //    through this particular node
                {
                    sourceNodes.Add(nodeLookup[attachNode.attachedPart]);
                    if (attachNode.attachedPart == part.parent) surfaceMounted = false;
                }
            }

            //Parts can draw resources from their parents
            //(exception: surface mounted fuel tanks cannot)
            if (part.parent != null && part.parent.fuelCrossFeed) sourceNodes.Add(nodeLookup[part.parent]);
        }
Esempio n. 24
0
 private static void Reset(FuelNode obj)
 {
 }
Esempio n. 25
0
 public void addSource(FuelNode source)
 {
     if (!sources.Contains(source)) sources.Add(source);
 }
Esempio n. 26
0
        // Find the set of nodes from which we can draw resources according to the STACK_PRIORITY_SEARCH flow scheme.
        // This gets called after all the FuelNodes have been constructed in order to set up the fuel flow graph.
        // Note that fuel flow through fuel lines and docked docking nodes is set up separately in
        // SetupFuelLineSources*()
        public void SetupRegularSources(Part part, Dictionary<Part, FuelNode> nodeLookup)
        {
            // When fuelCrossFeed is enabled we can draw fuel through stack and surface attachment
            if (part.fuelCrossFeed)
            {
                // Stack nodes:
                for (int i = 0; i < part.attachNodes.Count; i++)
                {
                    AttachNode attachNode = part.attachNodes[i];
                    if (attachNode.attachedPart != null)
                    {
                        // For stack nodes, we can draw fuel unless this node is specifically
                        // labeled as having crossfeed disabled (Kashua rule #4)
                        FuelNode fuelnode;
                        if (attachNode.id != "Strut"
                            && attachNode.ResourceXFeed
                            && !(part.NoCrossFeedNodeKey.Length > 0
                                 && attachNode.id.Contains(part.NoCrossFeedNodeKey))
                            && nodeLookup.TryGetValue(attachNode.attachedPart, out fuelnode))
                        {
                            stackNodeSources.Add(fuelnode);
                        }
                    }
                }

                // If we are surface-mounted to our parent we can draw fuel from it (Kashua rule #7)
                if (part.attachMode == AttachModes.SRF_ATTACH && part.parent != null)
                {
                    surfaceMountParent = nodeLookup[part.parent];
                }
            }
        }
Esempio n. 27
0
        //If we still have fuel, don't drain through the parent unless the parent node is a stack node.
        //This just seems to be an idiosyncracy of the KSP fuel flow system, which we faithfully simulate.
        bool drainFromSourceBeforeSelf(FuelNode source)
        {
            if (this.fuel == 0) return true;
            if (source.part != this.part.parent) return true;
            if (this.part.parent == null) return true;

            foreach (AttachNode attachNode in this.part.parent.attachNodes)
            {
                if (attachNode.attachedPart == this.part && attachNode.nodeType != AttachNode.NodeType.Stack) return false;
            }

            return true;
        }
Esempio n. 28
0
 //call this when a node no longer exists, so that this node knows that it's no longer a valid source
 public void RemoveSourceNode(FuelNode n)
 {
     if (fuelLineSources.Contains(n)) fuelLineSources.Remove(n);
     if (stackNodeSources.Contains(n)) stackNodeSources.Remove(n);
     if (surfaceMountParent == n) surfaceMountParent = null;
 }
Esempio n. 29
0
        //Find the set of nodes from which we can draw resources according to the STACK_PRIORITY_SEARCH flow scheme.
        //This gets called after all the FuelNodes have been constructed in order to set up the fuel flow graph
        public void FindSourceNodes(Part part, Dictionary<Part, FuelNode> nodeLookup)
        {
            //we can draw fuel from any fuel lines that point to this part
            foreach (Part p in nodeLookup.Keys)
            {
                if (p is FuelLine)
                    print("FuelLine ");
                if (p is FuelLine && ((FuelLine)p).target == part)
                {
                    sourceNodes.Add(nodeLookup[p]);
                    print("FuelLine " + nodeLookup[p].partName + "_" + p.uid);
                }
            }

            surfaceMounted = true;
            if (part.parent != null) this.parent = nodeLookup[part.parent];

            ModuleDockingNode dockNode;
            // In-flight docked ports attach only one way. This finds the docked port in the other direction.
            // However, this doesn't work in the VAB/SPH (as there is no vessel), and isn't needed there anyway.
            if (part.vessel && (dockNode = part.Modules.OfType<ModuleDockingNode>().FirstOrDefault()))
            {
                uint dockedPartUId = dockNode.dockedPartUId;
                Part p = part.vessel[dockedPartUId];
                print(String.Format("docking port {0} {1}", part, p));
                if (p)
                    sourceNodes.Add(nodeLookup[p]);
            }

            //we can (sometimes) draw fuel from stacked parts
            foreach (AttachNode attachNode in part.attachNodes)
            {
                //decide if it's possible to draw fuel through this node:
                if (attachNode.attachedPart != null                            //if there is a part attached here
                    && attachNode.nodeType == AttachNode.NodeType.Stack        //and the attached part is stacked (rather than surface mounted)
                    && attachNode.id != "Strut"                                //and it's not a Strut
                    && !(part.NoCrossFeedNodeKey.Length > 0                    //and this part does not forbid fuel flow
                         && attachNode.id.Contains(part.NoCrossFeedNodeKey)))  //    through this particular node
                {
                    print("attachNode.id " + attachNode.id);
                    if (part.fuelCrossFeed)
                    {
                        sourceNodes.Add(nodeLookup[attachNode.attachedPart]);
                        print("AttachedPart " + nodeLookup[attachNode.attachedPart].partName + "_" + attachNode.attachedPart.uid);
                    }
                    if (attachNode.attachedPart == part.parent) surfaceMounted = false;
                }
            }

            //Parts can draw resources from their parents
            //(exception: surface mounted fuel tanks cannot)
            if (part.parent != null && part.fuelCrossFeed)
            {
                sourceNodes.Add(nodeLookup[part.parent]);
                print("Parent Part " + nodeLookup[part.parent].partName + "_" + part.parent.uid);
            }

            print("source nodes for part " + partName);
            foreach (FuelNode n in sourceNodes) print("    " + n.partName);
        }
Esempio n. 30
0
        //Find the set of nodes from which we can draw resources according to the STACK_PRIORITY_SEARCH flow scheme.
        //This gets called after all the FuelNodes have been constructed in order to set up the fuel flow graph
        public void FindSourceNodes(Part part, Dictionary <Part, FuelNode> nodeLookup)
        {
            //we can draw fuel from any fuel lines that point to this part
            foreach (Part p in nodeLookup.Keys)
            {
                if (p is FuelLine)
                {
                    print("FuelLine ");
                }
                if (p is FuelLine && ((FuelLine)p).target == part)
                {
                    sourceNodes.Add(nodeLookup[p]);
                    print("FuelLine " + nodeLookup[p].partName + "_" + p.uid);
                }
            }

            surfaceMounted = true;
            if (part.parent != null)
            {
                this.parent = nodeLookup[part.parent];
            }

            ModuleDockingNode dockNode;

            // In-flight docked ports attach only one way. This finds the docked port in the other direction.
            // However, this doesn't work in the VAB/SPH (as there is no vessel), and isn't needed there anyway.
            if (part.vessel && (dockNode = part.Modules.OfType <ModuleDockingNode>().FirstOrDefault()))
            {
                uint dockedPartUId = dockNode.dockedPartUId;
                Part p             = part.vessel[dockedPartUId];
                print(String.Format("docking port {0} {1}", part, p));
                if (p)
                {
                    sourceNodes.Add(nodeLookup[p]);
                }
            }

            //we can (sometimes) draw fuel from stacked parts
            foreach (AttachNode attachNode in part.attachNodes)
            {
                //decide if it's possible to draw fuel through this node:
                if (attachNode.attachedPart != null &&                         //if there is a part attached here
                    attachNode.nodeType == AttachNode.NodeType.Stack &&        //and the attached part is stacked (rather than surface mounted)
                    attachNode.id != "Strut" &&                                //and it's not a Strut
                    !(part.NoCrossFeedNodeKey.Length > 0 &&                    //and this part does not forbid fuel flow
                      attachNode.id.Contains(part.NoCrossFeedNodeKey)))        //    through this particular node
                {
                    print("attachNode.id " + attachNode.id);
                    if (part.fuelCrossFeed)
                    {
                        sourceNodes.Add(nodeLookup[attachNode.attachedPart]);
                        print("AttachedPart " + nodeLookup[attachNode.attachedPart].partName + "_" + attachNode.attachedPart.uid);
                    }
                    if (attachNode.attachedPart == part.parent)
                    {
                        surfaceMounted = false;
                    }
                }
            }

            //Parts can draw resources from their parents
            //(exception: surface mounted fuel tanks cannot)
            if (part.parent != null && part.fuelCrossFeed)
            {
                sourceNodes.Add(nodeLookup[part.parent]);
                print("Parent Part " + nodeLookup[part.parent].partName + "_" + part.parent.uid);
            }

            print("source nodes for part " + partName);
            foreach (FuelNode n in sourceNodes)
            {
                print("    " + n.partName);
            }
        }
Esempio n. 31
0
 //call this when a node no longer exists, so that this node knows that it's no longer a valid source
 public void RemoveSourceNode(FuelNode n)
 {
     crossfeedSources.Remove(n);
 }
Esempio n. 32
0
        private void Init(Part part, bool dVLinearThrust)
        {
            resources.Clear();
            resourceConsumptions.Clear();
            resourceDrains.Clear();
            freeResources.Clear();

            propellantRatios.Clear();
            propellantFlows.Clear();

            fuelLineSources.Clear();
            stackNodeSources.Clear();
            surfaceMountSources.Clear();

            surfaceMountParent = null;
            isEngine           = false;

            dryMass     = 0;
            fairingMass = 0;

            moduleMass = 0;
            if (!part.IsLaunchClamp())
            {
                //print(part.partInfo.name.PadRight(25) + " " + part.mass.ToString("F4") + " " + part.GetPhysicslessChildMass().ToString("F4") + " " + part.GetModuleMass(part.partInfo.partPrefab.mass).ToString("F4"));
                dryMass = part.mass; // Intentionally ignore the physic flag.

                moduleMass = part.GetModuleMass(part.partInfo.partPrefab != null ? part.partInfo.partPrefab.mass : dryMass);
                if (part.HasModule <ModuleProceduralFairing>())
                {
                    fairingMass = moduleMass;
                }
            }

            inverseStage = part.inverseStage;
            partName     = part.partInfo.name;

            //note which resources this part has stored
            for (int i = 0; i < part.Resources.Count; i++)
            {
                PartResource r = part.Resources[i];
                if (r.info.density > 0)
                {
                    if (r.flowState)
                    {
                        resources[r.info.id] = (float)r.amount;
                    }
                    else
                    {
                        dryMass += (float)(r.amount * r.info.density); // disabled resources are just dead weight
                    }
                }
                if (r.info.name == "IntakeAir")
                {
                    freeResources[PartResourceLibrary.Instance.GetDefinition("IntakeAir").id] = true;
                }
                // Those two are in the CRP.
                if (r.info.name == "IntakeLqd")
                {
                    freeResources[PartResourceLibrary.Instance.GetDefinition("IntakeLqd").id] = true;
                }
                if (r.info.name == "IntakeAtm")
                {
                    freeResources[PartResourceLibrary.Instance.GetDefinition("IntakeAtm").id] = true;
                }
            }

            // TODO : handle the multiple active ModuleEngine case ( SXT engines with integrated vernier )

            //record relevant engine stats
            //ModuleEngines engine = part.Modules.OfType<ModuleEngines>().FirstOrDefault(e => e.isEnabled);
            ModuleEngines engine = null;

            for (int i = 0; i < part.Modules.Count; i++)
            {
                PartModule    pm = part.Modules[i];
                ModuleEngines e  = pm as ModuleEngines;
                if (e != null && e.isEnabled)
                {
                    engine = e;
                    break;
                }
            }

            if (engine != null)
            {
                //Only count engines that either are ignited or will ignite in the future:
                if ((HighLogic.LoadedSceneIsEditor || inverseStage < Staging.CurrentStage || engine.getIgnitionState) && (engine.thrustPercentage > 0 || engine.minThrust > 0))
                {
                    //if an engine has been activated early, pretend it is in the current stage:
                    if (engine.getIgnitionState && inverseStage < Staging.CurrentStage)
                    {
                        inverseStage = Staging.CurrentStage;
                    }

                    isEngine = true;

                    g = engine.g;

                    // If we take into account the engine rotation
                    if (dVLinearThrust)
                    {
                        Vector3 thrust = Vector3d.zero;
                        for (int i = 0; i < engine.thrustTransforms.Count; i++)
                        {
                            thrust -= engine.thrustTransforms[i].forward / engine.thrustTransforms.Count;
                        }

                        Vector3d fwd = HighLogic.LoadedScene == GameScenes.EDITOR ? EditorLogic.VesselRotation * Vector3d.up : engine.part.vessel.GetTransform().up;
                        fwdThrustRatio = Vector3.Dot(fwd, thrust);
                    }
                    else
                    {
                        fwdThrustRatio = 1;
                    }

                    thrustPercentage = engine.thrustPercentage;

                    minFuelFlow = engine.minFuelFlow;
                    maxFuelFlow = engine.maxFuelFlow;

                    atmosphereCurve = new FloatCurve(engine.atmosphereCurve.Curve.keys);
                    atmChangeFlow   = engine.atmChangeFlow;
                    useAtmCurve     = engine.useAtmCurve;
                    if (useAtmCurve)
                    {
                        atmCurve = new FloatCurve(engine.atmCurve.Curve.keys);
                    }
                    useVelCurve = engine.useVelCurve;
                    if (useVelCurve)
                    {
                        velCurve = new FloatCurve(engine.velCurve.Curve.keys);
                    }

                    propellantSumRatioTimesDensity = engine.propellants.Slinq().Where(prop => !prop.ignoreForIsp).Select(prop => prop.ratio * MuUtils.ResourceDensity(prop.id)).Sum();
                    propellantRatios.Clear();
                    propellantFlows.Clear();
                    var dics = new Tuple <KeyableDictionary <int, float>, KeyableDictionary <int, ResourceFlowMode> >(propellantRatios, propellantFlows);
                    engine.propellants.Slinq()
                    .Where(prop => MuUtils.ResourceDensity(prop.id) > 0 && !prop.ignoreForIsp)
                    .ForEach((p, dic) =>
                    {
                        dic._1.Add(p.id, p.ratio);
                        dic._2.Add(p.id, p.GetFlowMode());
                    }, dics);
                }
            }
        }
Esempio n. 33
0
 //call this when a node no longer exists, so that this node knows that it's no longer a valid source
 public void RemoveSourceNode(FuelNode n)
 {
     if (sourceNodes.Contains(n)) sourceNodes.Remove(n);
 }
Esempio n. 34
0
 private static void Reset(FuelNode obj)
 {
 }