/// <summary> /// Initializer for detected ship event. FactionContact is the detector side of what is detected, while ShipTN itself stores the detectee side. /// multiple of these can exist, but only 1 per faction hopefully. /// </summary> /// <param name="DetectedShip">Ship detected.</param> /// <param name="Thermal">Was the detection thermal based?</param> /// <param name="em">Detection via EM?</param> /// <param name="Active">Active detection?</param> /// <param name="tick">What tick did this detection event occur on?</param> public FactionContact(Faction CurrentFaction, ShipTN DetectedShip, bool Thermal, bool em, int EMSig, bool Active, uint tick) { ship = DetectedShip; missileGroup = null; pop = null; thermal = Thermal; EM = em; EMSignature = EMSig; active = Active; String Contact = "New contact detected:"; if (thermal == true) { thermalTick = tick; Contact = String.Format("{0} Thermal Signature {1}", Contact, DetectedShip.CurrentThermalSignature); } if (EM == true) { EMTick = tick; Contact = String.Format("{0} EM Signature {1}", Contact, EMSignature); } if (active == true) { activeTick = tick; Contact = String.Format("{0} TCS {1}", Contact, DetectedShip.TotalCrossSection); } /// <summary> /// Print to the message log. /// </summary> MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ContactNew, DetectedShip.ShipsTaskGroup.Contact.Position.System, DetectedShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Contact); CurrentFaction.MessageLog.Add(NMsg); /// <summary> /// Inform SimEntity. /// </summary> GameState.SE.SetInterrupt(InterruptType.NewSensorContact); }
/// <summary> /// Populations can be detected as well, handle that constructor here. /// </summary> /// <param name="CurrentFaction">Faction that has detected this contact</param> /// <param name="DetectedPopulation">Population detected by faction.</param> /// <param name="Thermal">Whether detection was by thermal characteristics</param> /// <param name="em">Was it detected via em?</param> /// <param name="Active">was this population detected via actives?</param> /// <param name="tick">Tick this happened on.</param> public FactionContact(Faction CurrentFaction, Population DetectedPopulation, bool Thermal, bool em, bool Active, uint tick) { pop = DetectedPopulation; ship = null; missileGroup = null; thermal = Thermal; EM = em; EMSignature = pop.EMSignature; active = Active; String Contact = "New contact detected:"; if (thermal == true) { thermalTick = tick; Contact = String.Format("{0} Thermal Signature {1}", Contact, pop.ThermalSignature); } if (EM == true) { EMTick = tick; Contact = String.Format("{0} EM Signature {1}", Contact, EMSignature); } if (active == true) { activeTick = tick; Contact = String.Format("{0} Active Ping", Contact); } /// <summary> /// Print to the message log. /// </summary> MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ContactNew, pop.Position.System, pop.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Contact); CurrentFaction.MessageLog.Add(NMsg); /// <summary> /// Inform SimEntity. /// </summary> GameState.SE.SetInterrupt(InterruptType.NewSensorContact); }
/// <summary> /// Galaxy wide sensor sweep of all systems in which this faction has a presence. Here is where things start to get complicated. /// </summary> /// <param name="CurrentSecond">The second count for the current year.</param> public void SensorSweep() { /// <summary> /// clear the fleet preempt list. /// </summary> GameState.SE.ClearFleetPreemptList(); /// <summary> /// Loop through all population centers with deep space tracking arrays. /// </summary> for (int loop = 0; loop < Populations.Count; loop++) { /// <summary> /// No point in running the detection statistics if this population does not have a DSTS. /// </summary> int DSTS = (int)Math.Floor(Populations[loop].Installations[(int)Installation.InstallationType.DeepSpaceTrackingStation].Number); if (DSTS != 0) { Population CurrentPopulation = Populations[loop]; StarSystem System = CurrentPopulation.Planet.Position.System; int SensorTech = FactionTechLevel[(int)Faction.FactionTechnology.DSTSSensorStrength]; if (SensorTech > Constants.Colony.DeepSpaceMax) SensorTech = Constants.Colony.DeepSpaceMax; /// <summary> /// iterate through every contact, thermal.Count will be equal to systemContacts.Count /// </summary> for(int detListIterator = 0; detListIterator < System.FactionDetectionLists[FactionID].Thermal.Count; detListIterator++) { /// <summary> /// This System contact is not owned by my faction, and it isn't fully detected yet. Populations only have thermal and EM detection characteristics here. /// </summary> if (this != System.SystemContactList[detListIterator].faction && (System.FactionDetectionLists[FactionID].Thermal[detListIterator] != GameState.Instance.CurrentSecond || System.FactionDetectionLists[FactionID].EM[detListIterator] != GameState.Instance.CurrentSecond)) { /// <summary> /// Get the distance from the current population to systemContactList[detListIterator] and store it for this tick. /// </summary> float dist = -1.0f; CurrentPopulation.Contact.DistTable.GetDistance(System.SystemContactList[detListIterator], out dist); /// <summary> /// Now to find the biggest thermal signature in the contact. The biggest for planets is just the planetary pop itself since /// multiple colonies really shouldn't happen. /// </summary> int sig = -1; int detection = -1; /// <summary> /// Planetary Detection. /// </summary> #region Population to Population Detection if (System.SystemContactList[detListIterator].SSEntity == StarSystemEntityType.Population) { Population DetectedPopulation = System.SystemContactList[detListIterator].Entity as Population; /// <summary> /// If this population has already been detected via thermals do not attempt to recalculate whether it has been detected or not. /// </summary> if (System.FactionDetectionLists[FactionID].Thermal[detListIterator] != GameState.Instance.CurrentSecond) { sig = DetectedPopulation.ThermalSignature; double rangeAdj = (((double)sig) / (double)Constants.SensorTN.DefaultPassiveSignature); int ThermalScanStrength = DSTS * Constants.Colony.ThermalDeepSpaceStrength[SensorTech]; /// <summary> /// Detection is ScanStrength multiplied by the range adjustment, multiplied by 100. These numbers come from the overall scan /// strength of a population's DSTS arrays, the signature adjustment(signatures larger than 1000 are easier to detect, while /// signatures smaller than 1000 are harder to detect, planets will often be much much larger), and the 100, for the adjustment /// to 1M km. it is only 100 though because units are assumed to be in 10k lots, so 100 * 10,000 = 1,000,000 /// </summary> detection = (int)Math.Floor(ThermalScanStrength * rangeAdj * 100.0); /// <summary> /// LargeDetection handles determining if dist or detection go beyond INTMAX and acts accordingly. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// Mark this contact as detected for this time slice via thermal for both the contact, and for the faction as a whole. /// </summary> if (det == true) { DetectedPopulation.ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; System.FactionDetectionLists[FactionID].Thermal[detListIterator] = GameState.Instance.CurrentSecond; if (DetPopList.Contains(DetectedPopulation) == false) DetPopList.Add(DetectedPopulation); } } /// <summary> /// if this population has already been detected in EM then obviously there is no need to attempt to find it again. /// </summary> if (System.FactionDetectionLists[FactionID].EM[detListIterator] != GameState.Instance.CurrentSecond) { /// <summary> /// As signature can be different, detection should be recalculated. passive population based detection can be put into a table if need be, I am not /// sure how useful that would be however. /// </summary> sig = DetectedPopulation.EMSignature; double rangeAdj = (((double)sig) / (double)Constants.SensorTN.DefaultPassiveSignature); int EMScanStrength = DSTS * Constants.Colony.EMDeepSpaceStrength[SensorTech]; detection = (int)Math.Floor(EMScanStrength * rangeAdj * 100.0); bool det = LargeDetection(dist, detection); if (det == true) { DetectedPopulation.EMDetection[FactionID] = GameState.Instance.CurrentSecond; System.FactionDetectionLists[FactionID].EM[detListIterator] = GameState.Instance.CurrentSecond; if (DetPopList.Contains(DetectedPopulation) == false) DetPopList.Add(DetectedPopulation); } } } #endregion /// <summary> /// Taskgroup Detection. /// </summary> #region Population To TaskGroup Detection if (System.SystemContactList[detListIterator].SSEntity == StarSystemEntityType.TaskGroup && (System.SystemContactList[detListIterator].Entity as TaskGroupTN).Ships.Count != 0) { TaskGroupTN DetectedTaskGroup = System.SystemContactList[detListIterator].Entity as TaskGroupTN; bool noDetection = false; bool allDetection = false; #region Population to Taskgroup thermal detection if (System.FactionDetectionLists[FactionID].Thermal[detListIterator] != GameState.Instance.CurrentSecond) { /// <summary> /// Get the best detection range for thermal signatures in loop. /// </summary> int ShipID = DetectedTaskGroup.ThermalSortList.Last(); ShipTN scratch = DetectedTaskGroup.Ships[ShipID]; sig = scratch.CurrentThermalSignature; double rangeAdj = (((double)sig) / (double)Constants.SensorTN.DefaultPassiveSignature); int ThermalScanStrength = DSTS * Constants.Colony.ThermalDeepSpaceStrength[SensorTech]; /// <summary> /// Detection is ScanStrength multiplied by the range adjustment, multiplied by 100. These numbers come from the overall scan strength of a population's /// DSTS arrays, the signature adjustment(signatures larger than 1000 are easier to detect, while signatures smaller than 1000 are harder to detect, planets will /// often be much much larger), and the 100, for the adjustment to 1M km. it is only 100 though because units are assumed to be in 10k lots, so 100 * 10,000 = 1,000,000 /// </summary> detection = (int)Math.Floor(ThermalScanStrength * rangeAdj * 100.0); /// <summary> /// LargeDetection handles determining if dist or detection go beyond INTMAX and acts accordingly. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// Good case, none of the ships are detected. /// </summary> if (det == false) { noDetection = true; } /// <summary> /// Atleast the biggest ship is detected. /// </summary if (noDetection == false) { ShipID = DetectedTaskGroup.ThermalSortList.First(); scratch = DetectedTaskGroup.Ships[ShipID]; sig = scratch.CurrentThermalSignature; rangeAdj = (((double)sig) / (double)Constants.SensorTN.DefaultPassiveSignature); detection = (int)Math.Floor(ThermalScanStrength * rangeAdj * 100.0); det = LargeDetection(dist, detection); /// <summary> /// Best case, everything is detected. /// </summary> if (det == true) { allDetection = true; for (int loop3 = 0; loop3 < DetectedTaskGroup.Ships.Count; loop3++) { DetectedTaskGroup.Ships[loop3].ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(DetectedTaskGroup.Ships[loop3]) == false) { DetShipList.Add(DetectedTaskGroup.Ships[loop3]); } } System.FactionDetectionLists[FactionID].Thermal[detListIterator] = GameState.Instance.CurrentSecond; } else if (noDetection == false && allDetection == false) { /// <summary> /// Worst case. some are detected, some aren't. /// </summary> for (int loop3 = 0; loop3 < DetectedTaskGroup.Ships.Count; loop3++) { LinkedListNode<int> node = DetectedTaskGroup.ThermalSortList.Last; bool done = false; while (!done) { scratch = DetectedTaskGroup.Ships[node.Value]; if (scratch.ThermalDetection[FactionID] != GameState.Instance.CurrentSecond) { sig = scratch.CurrentThermalSignature; rangeAdj = (((double)sig) / 1000.0); detection = (int)Math.Floor(ThermalScanStrength * rangeAdj * 100.0); /// <summary> /// Test each ship until I get to one I don't see. /// </summary> det = LargeDetection(dist, detection); if (det == true) { scratch.ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(scratch) == false) { DetShipList.Add(scratch); } } else { done = true; break; } } if (node == DetectedTaskGroup.ThermalSortList.First) { /// <summary> /// This should not happen. /// </summary> String ErrorMessage = string.Format("Partial Thermal detect for Pops looped through every ship. {0} {1} {2} {3}", dist, detection, noDetection, allDetection); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.Error, Populations[loop].Position.System, Populations[loop].Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, ErrorMessage); MessageLog.Add(NMsg); done = true; break; } node = node.Previous; } } } } } #endregion #region Population to taskgroup EM detection if (System.FactionDetectionLists[FactionID].EM[detListIterator] != GameState.Instance.CurrentSecond) { noDetection = false; allDetection = false; /// <summary> /// Get the best detection range for EM signatures in loop. /// </summary> int ShipID = DetectedTaskGroup.EMSortList.Last(); ShipTN scratch = DetectedTaskGroup.Ships[ShipID]; sig = scratch.CurrentEMSignature; int EMScanStrength = DSTS * Constants.Colony.EMDeepSpaceStrength[SensorTech]; double rangeAdj = (((double)sig) / (double)Constants.SensorTN.DefaultPassiveSignature); detection = (int)Math.Floor(EMScanStrength * rangeAdj * 100.0); /// <summary> /// LargeDetection handles determining if dist or detection go beyond INTMAX and acts accordingly. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// Good case, none of the ships are detected. /// </summary> if (det == false) { noDetection = true; } /// <summary> /// Atleast the biggest ship is detected. /// </summary if (noDetection == false) { ShipID = DetectedTaskGroup.EMSortList.First(); scratch = DetectedTaskGroup.Ships[ShipID]; sig = scratch.CurrentEMSignature; rangeAdj = (((double)sig) / (double)Constants.SensorTN.DefaultPassiveSignature); detection = (int)Math.Floor(EMScanStrength * rangeAdj * 100.0); /// <summary> /// LargeDetection handles determining if dist or detection go beyond INTMAX and acts accordingly. /// </summary> det = LargeDetection(dist, detection); /// <summary> /// Best case, everything is detected. /// </summary> if (det == true) { allDetection = true; for (int loop3 = 0; loop3 < DetectedTaskGroup.Ships.Count; loop3++) { DetectedTaskGroup.Ships[loop3].EMDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(DetectedTaskGroup.Ships[loop3]) == false) { DetShipList.Add(DetectedTaskGroup.Ships[loop3]); } } System.FactionDetectionLists[FactionID].EM[detListIterator] = GameState.Instance.CurrentSecond; } else if (noDetection == false && allDetection == false) { /// <summary> /// Worst case. some are detected, some aren't. /// </summary> for (int loop3 = 0; loop3 < DetectedTaskGroup.Ships.Count; loop3++) { LinkedListNode<int> node = DetectedTaskGroup.EMSortList.Last; bool done = false; while (!done) { scratch = DetectedTaskGroup.Ships[node.Value]; if (scratch.EMDetection[FactionID] != GameState.Instance.CurrentSecond) { sig = scratch.CurrentEMSignature; /// <summary> /// here is where EM detection differs from Thermal detection: /// If a ship has a signature of 0 by this point(and we didn't already hit noDetection above, /// it means that one ship is emitting a signature, but that no other ships are. /// Mark the group as totally detected, but not the ships, this serves to tell me that the ships are undetectable /// in this case. /// </summary> if (sig == 0) { /// <summary> /// The last signature we looked at was the ship emitting an EM sig, and this one is not. /// Mark the entire group as "spotted" because no other detection will occur. /// </summary> if (DetectedTaskGroup.Ships[node.Next.Value].EMDetection[FactionID] == GameState.Instance.CurrentSecond) { System.FactionDetectionLists[FactionID].EM[detListIterator] = GameState.Instance.CurrentSecond; //since the ships aren't actually detected, don't add them to the detected ships list. } break; } rangeAdj = (((double)sig) / (double)Constants.SensorTN.DefaultPassiveSignature); detection = (int)Math.Floor(EMScanStrength * rangeAdj * 100.0); det = LargeDetection(dist, detection); if (det == true) { scratch.EMDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(scratch) == false) { DetShipList.Add(scratch); } } else { done = true; break; } } if (node == DetectedTaskGroup.EMSortList.First) { /// <summary> /// This should not happen. /// </summary> String ErrorMessage = string.Format("Partial EM detect for Pops looped through every ship. {0} {1} {2} {3}", dist, detection, noDetection, allDetection); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.Error, Populations[loop].Position.System, Populations[loop].Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, ErrorMessage); MessageLog.Add(NMsg); done = true; break; } node = node.Previous; } } } } } #endregion } #endregion /// <summary> /// Missile Detection. /// </summary> #region Population To Missile Detection if (System.SystemContactList[detListIterator].SSEntity == StarSystemEntityType.Missile) { OrdnanceGroupTN MissileGroup = (System.SystemContactList[detListIterator].Entity as OrdnanceGroupTN); OrdnanceTN Missile = MissileGroup.missiles[0]; if (System.FactionDetectionLists[FactionID].Thermal[detListIterator] != GameState.Instance.CurrentSecond) { int ThermalScanStrength = DSTS * Constants.Colony.ThermalDeepSpaceStrength[SensorTech]; sig = (int)Math.Ceiling(Missile.missileDef.totalThermalSignature); double rangeAdj = (((double)sig) / (double)Constants.SensorTN.DefaultPassiveSignature); /// <summary> /// Detection is ScanStrength multiplied by the range adjustment, multiplied by 100. These numbers come from the overall scan strength of a population's /// DSTS arrays, the signature adjustment(signatures larger than 1000 are easier to detect, while signatures smaller than 1000 are harder to detect, planets will /// often be much much larger), and the 100, for the adjustment to 1M km. it is only 100 though because units are assumed to be in 10k lots, so 100 * 10,000 = 1,000,000 /// </summary> detection = (int)Math.Floor(ThermalScanStrength * rangeAdj * 100.0); /// <summary> /// LargeDetection handles determining if dist or detection go beyond INTMAX and acts accordingly. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// Mark this contact as detected for this time slice via thermal for both the contact, and for the faction as a whole. /// </summary> if (det == true) { for (int loop3 = 0; loop3 < MissileGroup.missiles.Count; loop3++) { MissileGroup.missiles[loop3].ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; } if (DetMissileList.Contains(MissileGroup) == false) { DetMissileList.Add(MissileGroup); } System.FactionDetectionLists[FactionID].Thermal[detListIterator] = GameState.Instance.CurrentSecond; } } if (System.FactionDetectionLists[FactionID].EM[detListIterator] != GameState.Instance.CurrentSecond) { int EMSignature = 0; if (Missile.missileDef.activeStr != 0.0f) { EMSignature = Missile.missileDef.aSD.gps; } if (EMSignature != 0) { /// <summary> /// As signature can be different, detection should be recalculated. passive population based detection can be put into a table if need be, I am not /// sure how useful that would be however. /// </summary> sig = EMSignature; int EMScanStrength = DSTS * Constants.Colony.EMDeepSpaceStrength[SensorTech]; double rangeAdj = (((double)sig) / (double)Constants.SensorTN.DefaultPassiveSignature); detection = (int)Math.Floor(EMScanStrength * rangeAdj * 100.0); bool det = LargeDetection(dist, detection); if (det == true) { for (int loop3 = 0; loop3 < MissileGroup.missiles.Count; loop3++) { MissileGroup.missiles[loop3].EMDetection[FactionID] = GameState.Instance.CurrentSecond; } if (DetMissileList.Contains(MissileGroup) == false) { DetMissileList.Add(MissileGroup); } System.FactionDetectionLists[FactionID].EM[detListIterator] = GameState.Instance.CurrentSecond; } } } } #endregion }//end if not owned and not detected }//end for thermal.Count }//end if DSTS != 0 }//end for populations /// <summary> /// Loop through all faction taskgroups. /// </summary> #region Faction Taskgroup Loop foreach(TaskGroupTN CurrentTaskGroup in TaskGroups) { /// <summary> /// This Taskgroup can't perform a sensor sweep until jump sickness is gone. It stands to reason that if ship[0] is sick they all will be, but clever taskgroup reorganizing /// may thwart that. I'd recommend just banning taskgroup reorganization while jumpsick. /// </summary> if (CurrentTaskGroup.IsJumpSick()) continue; /// <summary> /// A taskgroup with no ships cannot detect anything. /// </summary> if (CurrentTaskGroup.Ships.Count == 0) continue; StarSystem System = CurrentTaskGroup.Contact.Position.System; /// <summary> /// Loop through the global contacts list for the system. thermal.Count is equal to SystemContacts.Count. or should be. /// </summary> for (int detListIterator = 0; detListIterator < System.FactionDetectionLists[FactionID].Thermal.Count; detListIterator++) { /// <summary> /// Check if System.SystemContactList[detListIterator] is in the same faction, and it hasn't been fully detected yet. /// </summary> if (this != System.SystemContactList[detListIterator].faction && (System.FactionDetectionLists[FactionID].Thermal[detListIterator] != GameState.Instance.CurrentSecond || System.FactionDetectionLists[FactionID].EM[detListIterator] != GameState.Instance.CurrentSecond || System.FactionDetectionLists[FactionID].Active[detListIterator] != GameState.Instance.CurrentSecond)) { float dist; // Check to see if our distance table is updated for this contact. if (!CurrentTaskGroup.Contact.DistTable.GetDistance(System.SystemContactList[detListIterator], out dist)) { /// <summary> /// Handle fleet interception check here. /// </summary> if (GameState.SE.FleetInterceptionPreemptTick != GameState.Instance.CurrentSecond && System.SystemContactList[detListIterator].SSEntity == StarSystemEntityType.TaskGroup) { TaskGroupTN TaskGroup = System.SystemContactList[detListIterator].Entity as TaskGroupTN; /// <summary> /// player created empty taskgroups should not get checked here. /// </summary> if (TaskGroup.Ships.Count != 0) { /// <summary> /// how far could this TG travel within a single day? /// </summary> float TaskGroupDistance = (float)Distance.ToAU(CurrentTaskGroup.CurrentSpeed) * Constants.TimeInSeconds.Day; #warning fleet intercept preemption magic number here, if less than 5 days travel time currently. #warning fleet intercept needs to only process for hostile or unknown taskgroups, not all taskgroups. int ShipID = TaskGroup.ActiveSortList.Last(); ShipTN LargestContactTCS = TaskGroup.Ships[ShipID]; /// <summary> /// If this Taskgroup isn't already detected, and the distance is short enough, put it in the fleet intercept preempt list. /// </summary> if (TaskGroupDistance >= (dist / 5.0) && (DetectedContactLists.ContainsKey(System) == false || (DetectedContactLists.ContainsKey(System) == true && (!DetectedContactLists[System].DetectedContacts.ContainsKey(LargestContactTCS) || DetectedContactLists[System].DetectedContacts[LargestContactTCS].active == false)))) { #warning Update this fleet intercept list for planets/populations GameState.SE.FleetInterceptionPreemptTick = GameState.Instance.CurrentSecond; GameState.SE.AddFleetToPreemptList(CurrentTaskGroup); if (System.SystemContactList[detListIterator].SSEntity == StarSystemEntityType.TaskGroup) { GameState.SE.AddFleetToPreemptList(TaskGroup); } } } else { /// <summary> /// TaskGroupToTest is going to be empty so it can't be detected. /// </summary> continue; } } // /Fleet Interception Check } // distance Table Update. /// <summary> /// Now to find the biggest signature in the contact. The biggest for planets is just the planetary pop itself since /// multiple colonies really shouldn't happen. /// </summary> int sig = -1; int detection = -1; /// <summary> /// Handle population detection /// </summary> if (System.SystemContactList[detListIterator].SSEntity == StarSystemEntityType.Population) { #region TaskGroup to Population detection Population Pop = System.SystemContactList[detListIterator].Entity as Population; if (System.FactionDetectionLists[FactionID].Thermal[detListIterator] != GameState.Instance.CurrentSecond) { sig = Pop.ThermalSignature; detection = CurrentTaskGroup.BestThermal.pSensorDef.GetPassiveDetectionRange(sig); /// <summary> /// LargeDetection handles determining if dist or detection go beyond INTMAX and acts accordingly. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// Mark this contact as detected for this time slice via thermal for both the contact, and for the faction as a whole. /// </summary> if (det == true) { Pop.ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; System.FactionDetectionLists[FactionID].Thermal[detListIterator] = GameState.Instance.CurrentSecond; if (DetPopList.Contains(Pop) == false) DetPopList.Add(Pop); } } if (System.FactionDetectionLists[FactionID].EM[detListIterator] != GameState.Instance.CurrentSecond) { sig = Pop.EMSignature; detection = CurrentTaskGroup.BestEM.pSensorDef.GetPassiveDetectionRange(sig); bool det = LargeDetection(dist, detection); if (det == true) { Pop.EMDetection[FactionID] = GameState.Instance.CurrentSecond; System.FactionDetectionLists[FactionID].EM[detListIterator] = GameState.Instance.CurrentSecond; if (DetPopList.Contains(Pop) == false) DetPopList.Add(Pop); } } if (System.FactionDetectionLists[FactionID].Active[detListIterator] != GameState.Instance.CurrentSecond && CurrentTaskGroup.ActiveSensorQue.Count > 0) { sig = Constants.ShipTN.ResolutionMax - 1; /// <summary> /// The -1 is because a planet is most certainly not a missile. /// </summary> detection = CurrentTaskGroup.ActiveSensorQue[CurrentTaskGroup.TaskGroupLookUpST[sig]].aSensorDef.GetActiveDetectionRange(sig, -1); /// <summary> /// Do detection calculations here. /// </summary> bool det = LargeDetection(dist, detection); if (det == true) { Pop.ActiveDetection[FactionID] = GameState.Instance.CurrentSecond; System.FactionDetectionLists[FactionID].Active[detListIterator] = GameState.Instance.CurrentSecond; if (DetPopList.Contains(Pop) == false) DetPopList.Add(Pop); } } #endregion } else if (System.SystemContactList[detListIterator].SSEntity == StarSystemEntityType.TaskGroup) { TaskGroupTN TaskGroupToTest = System.SystemContactList[detListIterator].Entity as TaskGroupTN; if (TaskGroupToTest.Ships.Count != 0) { /// <summary> /// Taskgroups have multiple signatures, so noDetection and allDetection become important. /// </summary> /// <summary> /// Thermal detection code. /// </summary> TaskGroupThermalDetection(System, CurrentTaskGroup, TaskGroupToTest, dist, detListIterator); /// <summary> /// EM detection is done in this function, this only differs from the above in the various datatypes used as well as sensor types. /// </summary> TaskGroupEMDetection(System, CurrentTaskGroup, TaskGroupToTest, dist, detListIterator); /// <summary> /// Active detection, this is different from passive detection in that resolution of the sensor as well as size of the target ship matter, /// rather than any signature. /// </summary> TaskGroupActiveDetection(System, CurrentTaskGroup, TaskGroupToTest, dist, detListIterator); } } else if (System.SystemContactList[detListIterator].SSEntity == StarSystemEntityType.Missile) { OrdnanceGroupTN MissileGroup = System.SystemContactList[detListIterator].Entity as OrdnanceGroupTN; if (MissileGroup.missiles.Count != 0) { /// <summary> /// Do Missile Detection here: /// </summary> #region Missile Detection OrdnanceTN Missile = MissileGroup.missiles[0]; if (System.FactionDetectionLists[FactionID].Thermal[detListIterator] != GameState.Instance.CurrentSecond) { int ThermalSignature = (int)Math.Ceiling(Missile.missileDef.totalThermalSignature); detection = -1; /// <summary> /// Check to make sure the taskgroup has a thermal sensor available, otherwise use the default. /// </summary> if (CurrentTaskGroup.BestThermalCount != 0) { detection = CurrentTaskGroup.BestThermal.pSensorDef.GetPassiveDetectionRange(ThermalSignature); } else { detection = ComponentList.DefaultPassives.GetPassiveDetectionRange(ThermalSignature); } /// <summary> /// Test the biggest signature against the best sensor. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// If one missile is detected, all are. /// </summary> if (det == true) { for (int loop3 = 0; loop3 < MissileGroup.missiles.Count; loop3++) { MissileGroup.missiles[loop3].ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; } if (DetMissileList.Contains(MissileGroup) == false) { DetMissileList.Add(MissileGroup); } System.FactionDetectionLists[FactionID].Thermal[detListIterator] = GameState.Instance.CurrentSecond; } } if (System.FactionDetectionLists[FactionID].EM[detListIterator] != GameState.Instance.CurrentSecond) { int EMSignature = 0; if (Missile.missileDef.activeStr != 0.0f) { EMSignature = Missile.missileDef.aSD.gps; } if (EMSignature != 0) { /// <summary> /// Check to see if the taskgroup has an em sensor, and that said em sensor is not destroyed. /// otherwise use the default passive detection range. /// </summary> if (CurrentTaskGroup.BestEMCount > 0) { detection = CurrentTaskGroup.BestEM.pSensorDef.GetPassiveDetectionRange(EMSignature); } else { detection = ComponentList.DefaultPassives.GetPassiveDetectionRange(EMSignature); } bool det = LargeDetection(dist, detection); /// <summary> /// If one missile is detected, all are. /// </summary> if (det == true) { for (int loop3 = 0; loop3 < MissileGroup.missiles.Count; loop3++) { MissileGroup.missiles[loop3].EMDetection[FactionID] = GameState.Instance.CurrentSecond; } if (DetMissileList.Contains(MissileGroup) == false) { DetMissileList.Add(MissileGroup); } System.FactionDetectionLists[FactionID].EM[detListIterator] = GameState.Instance.CurrentSecond; } } } if (System.FactionDetectionLists[FactionID].Active[detListIterator] != GameState.Instance.CurrentSecond && CurrentTaskGroup.ActiveSensorQue.Count > 0) { int TotalCrossSection_MSP = (int)Math.Ceiling(Missile.missileDef.size); sig = -1; detection = -1; if (TotalCrossSection_MSP < ((Constants.OrdnanceTN.MissileResolutionMaximum + 6) + 1)) { if (TotalCrossSection_MSP <= (Constants.OrdnanceTN.MissileResolutionMinimum + 6)) { sig = Constants.OrdnanceTN.MissileResolutionMinimum; } else if (TotalCrossSection_MSP <= (Constants.OrdnanceTN.MissileResolutionMaximum + 6)) { sig = TotalCrossSection_MSP - 6; } detection = CurrentTaskGroup.ActiveSensorQue[CurrentTaskGroup.TaskGroupLookUpST[sig]].aSensorDef.GetActiveDetectionRange(0, sig); } else { /// <summary> /// Big missiles will be treated in HS terms: 21-40 MSP = 2 HS, 41-60 = 3 HS, 61-80 = 4 HS, 81-100 = 5 HS. The same should hold true for greater than 100 sized missiles. /// but those are impossible to build. /// </summary> sig = (int)Math.Ceiling((float)TotalCrossSection_MSP / 20.0f); detection = CurrentTaskGroup.ActiveSensorQue[CurrentTaskGroup.TaskGroupLookUpST[sig]].aSensorDef.GetActiveDetectionRange(sig, -1); } bool det = LargeDetection(dist, detection); if (det == true) { for (int loop3 = 0; loop3 < MissileGroup.missiles.Count; loop3++) { MissileGroup.missiles[loop3].ActiveDetection[FactionID] = GameState.Instance.CurrentSecond; } if (DetMissileList.Contains(MissileGroup) == false) { DetMissileList.Add(MissileGroup); } System.FactionDetectionLists[FactionID].Active[detListIterator] = GameState.Instance.CurrentSecond; } } #endregion } } /// <summary> /// End if planet,Task Group, or Missile /// </sumamry> } /// <summary> /// End if not globally detected. /// </summary> } /// <summary> /// End for Faction Contact Lists /// </summary> } #endregion /// <summary> /// End for Faction TaskGroups. /// </summary> /// <summary> /// Loop through all missile groups. /// </summary> #region Faction Missile Loop for (int loop = 0; loop < MissileGroups.Count; loop++) { /// <summary> /// Hopefully I won't get into this situation ever. /// </summary> if (MissileGroups[loop].missiles.Count == 0) { String ErrorMessage = string.Format("Missile group {0} has no missiles and is still in the list of missile groups.", MissileGroups[loop].Name); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.Error, MissileGroups[loop].contact.Position.System, MissileGroups[loop].contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, ErrorMessage); MessageLog.Add(NMsg); continue; } /// <summary> /// Missile groups have homogeneous missile complements, so will only have 1 of each sensor to check against. /// </summary> StarSystem System = MissileGroups[loop].contact.Position.System; OrdnanceTN Missile = MissileGroups[loop].missiles[0]; /// <summary> /// If this missile has no sensors don't process all this. /// </summary> if (Missile.missileDef.tHD == null && Missile.missileDef.eMD == null && Missile.missileDef.aSD == null) continue; /// <summary> /// Loop through the global contacts list for the system. thermal.Count is equal to SystemContacts.Count. or should be. /// </summary> for (int loop2 = 0; loop2 < System.FactionDetectionLists[FactionID].Thermal.Count; loop2++) { /// <summary> /// I don't own loop2, and it hasn't been fully detected yet. And this missile can actually detect things. /// </summary> if (this != System.SystemContactList[loop2].faction && (System.FactionDetectionLists[FactionID].Thermal[loop2] != GameState.Instance.CurrentSecond || System.FactionDetectionLists[FactionID].EM[loop2] != GameState.Instance.CurrentSecond || System.FactionDetectionLists[FactionID].Active[loop2] != GameState.Instance.CurrentSecond )&& (Missile.missileDef.thermalStr != 0.0f || Missile.missileDef.eMStr != 0.0f || Missile.missileDef.activeStr != 0.0f)) { float dist; MissileGroups[loop].contact.DistTable.GetDistance(System.SystemContactList[loop2], out dist); /// <summary> /// Now to find the biggest thermal signature in the contact. The biggest for planets is just the planetary pop itself since /// multiple colonies really shouldn't happen. /// </summary> int sig = -1; int detection = -1; /// <summary> /// Check for each detection method whether or not this missile can actually detect things, then copy the taskgroup detection code here. /// Update detected contacts as needed: /// </summary> if (System.SystemContactList[loop2].SSEntity == StarSystemEntityType.Population) { #region Planetary detection by missiles Population Pop = System.SystemContactList[loop2].Entity as Population; /// <summary> /// Does this missile have a thermal sensor suite? by default the answer is no, this is different from Aurora which gives them the basic ship suite. /// </summary> if (Missile.missileDef.tHD != null && System.FactionDetectionLists[FactionID].Thermal[loop2] != GameState.Instance.CurrentSecond) { sig = Pop.ThermalSignature; detection = Missile.missileDef.tHD.GetPassiveDetectionRange(sig); bool det = LargeDetection(dist, detection); /// <summary> /// Mark this contact as detected for this time slice via thermal for both the contact, and for the faction as a whole. /// </summary> if (det == true) { Pop.ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; System.FactionDetectionLists[FactionID].Thermal[loop2] = GameState.Instance.CurrentSecond; if (DetPopList.Contains(Pop) == false) DetPopList.Add(Pop); } } /// <summary> /// Does this missile have an EM sensor suite? by default again the answer is no, which is again different from Aurora. /// </summary> if (Missile.missileDef.eMD != null && System.FactionDetectionLists[FactionID].EM[loop2] != GameState.Instance.CurrentSecond) { sig = Pop.EMSignature; detection = Missile.missileDef.eMD.GetPassiveDetectionRange(sig); bool det = LargeDetection(dist, detection); /// <summary> /// Mark this contact as detected for this time slice via EM for both the contact, and for the faction as a whole. /// </summary> if (det == true) { Pop.EMDetection[FactionID] = GameState.Instance.CurrentSecond; System.FactionDetectionLists[FactionID].EM[loop2] = GameState.Instance.CurrentSecond; if (DetPopList.Contains(Pop) == false) DetPopList.Add(Pop); } } /// <summary> /// Lastly does this missile have an active sensor? /// </summary> if (Missile.missileDef.aSD != null && System.FactionDetectionLists[FactionID].Active[loop2] != GameState.Instance.CurrentSecond) { sig = Constants.ShipTN.ResolutionMax - 1; detection = Missile.missileDef.aSD.GetActiveDetectionRange(sig, -1); bool det = LargeDetection(dist, detection); /// <summary> /// Mark this contact as detected for this time slice via Active for both the contact, and for the faction as a whole. /// </summary> if (det == true) { Pop.ActiveDetection[FactionID] = GameState.Instance.CurrentSecond; System.FactionDetectionLists[FactionID].Active[loop2] = GameState.Instance.CurrentSecond; if (DetPopList.Contains(Pop) == false) DetPopList.Add(Pop); } } #endregion } else if (System.SystemContactList[loop2].SSEntity == StarSystemEntityType.TaskGroup) { TaskGroupTN TaskGroup = System.SystemContactList[loop2].Entity as TaskGroupTN; if (TaskGroup.Ships.Count != 0) { #region Taskgroup Detection by Missiles /// <summary> /// Taskgroups have multiple signatures, so noDetection and allDetection become important. /// </summary> bool noDetection = false; bool allDetection = false; #region Ship Thermal Detection Code if (System.FactionDetectionLists[FactionID].Thermal[loop2] != GameState.Instance.CurrentSecond && Missile.missileDef.tHD != null) { int ShipID = TaskGroup.ThermalSortList.Last(); ShipTN scratch = TaskGroup.Ships[ShipID]; sig = scratch.CurrentThermalSignature; detection = Missile.missileDef.tHD.GetPassiveDetectionRange(sig); /// <summary> /// Test the biggest signature against the best sensor. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// Good case, none of the ships are detected. /// </summary> if (det == false) { noDetection = true; } /// <summary> /// Atleast the biggest ship is detected. /// </summary if (noDetection == false) { ShipID = TaskGroup.ThermalSortList.First(); scratch = TaskGroup.Ships[ShipID]; sig = scratch.CurrentThermalSignature; /// <summary> /// Now for the smallest vs the best. /// </summary> detection = Missile.missileDef.tHD.GetPassiveDetectionRange(sig); det = LargeDetection(dist, detection); /// <summary> /// Best case, everything is detected. /// </summary> if (det == true) { allDetection = true; for (int loop3 = 0; loop3 < TaskGroup.Ships.Count; loop3++) { TaskGroup.Ships[loop3].ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(TaskGroup.Ships[loop3]) == false) { DetShipList.Add(TaskGroup.Ships[loop3]); } } System.FactionDetectionLists[FactionID].Thermal[loop2] = GameState.Instance.CurrentSecond; } else if (noDetection == false && allDetection == false) { /// <summary> /// Worst case. some are detected, some aren't. /// </summary> for (int loop3 = 0; loop3 < TaskGroup.Ships.Count; loop3++) { LinkedListNode<int> node = TaskGroup.ThermalSortList.Last; bool done = false; while (!done) { scratch = TaskGroup.Ships[node.Value]; if (scratch.ThermalDetection[FactionID] != GameState.Instance.CurrentSecond) { sig = scratch.CurrentThermalSignature; detection = Missile.missileDef.tHD.GetPassiveDetectionRange(sig); /// <summary> /// Test each ship until I get to one I don't see. /// </summary> det = LargeDetection(dist, detection); if (det == true) { scratch.ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(scratch) == false) { DetShipList.Add(scratch); } } else { done = true; break; } } if (node == TaskGroup.ThermalSortList.First) { /// <summary> /// This should not happen. /// </summary> String ErrorMessage = string.Format("Partial Thermal detect for missiles looped through every ship. {0} {1} {2} {3}", dist, detection, noDetection, allDetection); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.Error, MissileGroups[loop].contact.Position.System, MissileGroups[loop].contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, ErrorMessage); MessageLog.Add(NMsg); done = true; break; } node = node.Previous; }//end while }//end for }//end else if (noDetection == false && allDetection == false) }//end if noDetection == false }//end if not detected and missile can detect thermally #endregion #region Ship EM Detection Code if (System.FactionDetectionLists[FactionID].EM[loop2] != GameState.Instance.CurrentSecond && Missile.missileDef.eMD != null) { int ShipID = TaskGroup.EMSortList.Last(); ShipTN scratch = TaskGroup.Ships[ShipID]; sig = scratch.CurrentEMSignature; detection = Missile.missileDef.eMD.GetPassiveDetectionRange(sig); /// <summary> /// Test the biggest signature against the best sensor. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// Good case, none of the ships are detected. /// </summary> if (det == false) { noDetection = true; } /// <summary> /// Atleast the biggest ship is detected. /// </summary if (noDetection == false) { ShipID = TaskGroup.EMSortList.First(); scratch = TaskGroup.Ships[ShipID]; sig = scratch.CurrentEMSignature; /// <summary> /// Now for the smallest vs the best. /// </summary> detection = Missile.missileDef.eMD.GetPassiveDetectionRange(sig); det = LargeDetection(dist, detection); /// <summary> /// Best case, everything is detected. /// </summary> if (det == true) { allDetection = true; for (int loop3 = 0; loop3 < TaskGroup.Ships.Count; loop3++) { TaskGroup.Ships[loop3].EMDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(TaskGroup.Ships[loop3]) == false) { DetShipList.Add(TaskGroup.Ships[loop3]); } } System.FactionDetectionLists[FactionID].EM[loop2] = GameState.Instance.CurrentSecond; } else if (noDetection == false && allDetection == false) { /// <summary> /// Worst case. some are detected, some aren't. /// </summary> for (int loop3 = 0; loop3 < TaskGroup.Ships.Count; loop3++) { LinkedListNode<int> node = TaskGroup.EMSortList.Last; bool done = false; while (!done) { scratch = TaskGroup.Ships[node.Value]; if (scratch.EMDetection[FactionID] != GameState.Instance.CurrentSecond) { sig = scratch.CurrentEMSignature; /// <summary> /// here is where EM detection differs from Thermal detection: /// If a ship has a signature of 0 by this point(and we didn't already hit noDetection above, /// it means that one ship is emitting a signature, but that no other ships are. /// Mark the group as totally detected, but not the ships, this serves to tell me that the ships are undetectable /// in this case. /// Also, sig will never be 0 for the first iteration of the loop, that has already been tested, so I don't need to worry about /// node.Next.Value blowing up on me. /// </summary> if (sig == 0) { /// <summary> /// The last signature we looked at was the ship emitting an EM sig, and this one is not. /// Mark the entire group as "spotted" because no other detection will occur. /// </summary> if (TaskGroup.Ships[node.Next.Value].EMDetection[FactionID] == GameState.Instance.CurrentSecond) { System.FactionDetectionLists[FactionID].EM[loop2] = GameState.Instance.CurrentSecond; } break; } detection = Missile.missileDef.eMD.GetPassiveDetectionRange(sig); /// <summary> /// Test each ship until I get to one I don't see. /// </summary> det = LargeDetection(dist, detection); if (det == true) { scratch.EMDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(scratch) == false) { DetShipList.Add(scratch); } } else { done = true; break; } } if (node == TaskGroup.EMSortList.First) { /// <summary> /// This should not happen. /// </summary> String ErrorMessage = string.Format("Partial EM detect for missiles looped through every ship. {0} {1} {2} {3}", dist, detection, noDetection, allDetection); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.Error, MissileGroups[loop].contact.Position.System, MissileGroups[loop].contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, ErrorMessage); MessageLog.Add(NMsg); done = true; break; } node = node.Previous; }//end while }//end for }//end else if (noDetection == false && allDetection == false) }//end if noDetection == false }//end if not detected and missile can detect EM #endregion #region Ship Active Detection Code if (System.FactionDetectionLists[FactionID].Active[loop2] != GameState.Instance.CurrentSecond && Missile.missileDef.aSD != null) { int ShipID = TaskGroup.ActiveSortList.Last(); ShipTN scratch = TaskGroup.Ships[ShipID]; sig = scratch.TotalCrossSection; detection = Missile.missileDef.aSD.GetActiveDetectionRange(sig, -1); /// <summary> /// Test the biggest signature against the best sensor. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// Good case, none of the ships are detected. /// </summary> if (det == false) { noDetection = true; } /// <summary> /// Atleast the biggest ship is detected. /// </summary if (noDetection == false) { ShipID = TaskGroup.ActiveSortList.First(); scratch = TaskGroup.Ships[ShipID]; sig = scratch.TotalCrossSection; /// <summary> /// Now for the smallest vs the best. /// </summary> detection = Missile.missileDef.aSD.GetActiveDetectionRange(sig, -1); det = LargeDetection(dist, detection); /// <summary> /// Best case, everything is detected. /// </summary> if (det == true) { allDetection = true; for (int loop3 = 0; loop3 < TaskGroup.Ships.Count; loop3++) { TaskGroup.Ships[loop3].ActiveDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(TaskGroup.Ships[loop3]) == false) { DetShipList.Add(TaskGroup.Ships[loop3]); } } System.FactionDetectionLists[FactionID].Active[loop2] = GameState.Instance.CurrentSecond; } else if (noDetection == false && allDetection == false) { /// <summary> /// Worst case. some are detected, some aren't. /// </summary> for (int loop3 = 0; loop3 < TaskGroup.Ships.Count; loop3++) { LinkedListNode<int> node = TaskGroup.ActiveSortList.Last; bool done = false; while (!done) { scratch = TaskGroup.Ships[node.Value]; if (scratch.ActiveDetection[FactionID] != GameState.Instance.CurrentSecond) { sig = scratch.TotalCrossSection; detection = Missile.missileDef.aSD.GetActiveDetectionRange(sig, -1); /// <summary> /// Test each ship until I get to one I don't see. /// </summary> det = LargeDetection(dist, detection); if (det == true) { scratch.ActiveDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(scratch) == false) { DetShipList.Add(scratch); } } else { done = true; break; } } if (node == TaskGroup.ActiveSortList.First) { /// <summary> /// This should not happen. /// </summary> String ErrorMessage = string.Format("Partial Active detect for missiles looped through every ship. {0} {1} {2} {3}", dist, detection, noDetection, allDetection); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.Error, MissileGroups[loop].contact.Position.System, MissileGroups[loop].contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, ErrorMessage); MessageLog.Add(NMsg); done = true; break; } node = node.Previous; }//end while }//end for }//end else if (noDetection == false && allDetection == false) }//end if noDetection == false }//end if not detected and missile can detect TCS #endregion #endregion } } else if (System.SystemContactList[loop2].SSEntity == StarSystemEntityType.Missile) { OrdnanceGroupTN MissileGroup = System.SystemContactList[loop2].Entity as OrdnanceGroupTN; if (MissileGroup.missiles.Count != 0) { /// <summary> /// Do Missile Detection here: /// </summary> #region Missile Detection OrdnanceTN MissileTarget = MissileGroup.missiles[0]; if (System.FactionDetectionLists[FactionID].Thermal[loop2] != GameState.Instance.CurrentSecond && Missile.missileDef.tHD != null) { int ThermalSignature = (int)Math.Ceiling(MissileTarget.missileDef.totalThermalSignature); detection = Missile.missileDef.tHD.GetPassiveDetectionRange(ThermalSignature); /// <summary> /// Test the biggest signature against the best sensor. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// If one missile is detected, all are. /// </summary> if (det == true) { for (int loop3 = 0; loop3 < MissileGroup.missiles.Count; loop3++) { MissileGroup.missiles[loop3].ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; } if (DetMissileList.Contains(MissileGroup) == false) { DetMissileList.Add(MissileGroup); } System.FactionDetectionLists[FactionID].Thermal[loop2] = GameState.Instance.CurrentSecond; } } if (System.FactionDetectionLists[FactionID].EM[loop2] != GameState.Instance.CurrentSecond && Missile.missileDef.eMD != null) { int EMSignature = 0; if (MissileTarget.missileDef.activeStr != 0.0f && MissileTarget.missileDef.aSD != null) { EMSignature = MissileTarget.missileDef.aSD.gps; } if (EMSignature != 0) { detection = Missile.missileDef.eMD.GetPassiveDetectionRange(EMSignature); bool det = LargeDetection(dist, detection); /// <summary> /// If one missile is detected, all are. /// </summary> if (det == true) { for (int loop3 = 0; loop3 < MissileGroup.missiles.Count; loop3++) { MissileGroup.missiles[loop3].EMDetection[FactionID] = GameState.Instance.CurrentSecond; } if (DetMissileList.Contains(MissileGroup) == false) { DetMissileList.Add(MissileGroup); } System.FactionDetectionLists[FactionID].EM[loop2] = GameState.Instance.CurrentSecond; } } } if (System.FactionDetectionLists[FactionID].Active[loop2] != GameState.Instance.CurrentSecond && Missile.missileDef.aSD != null) { int TotalCrossSection_MSP = (int)Math.Ceiling(MissileTarget.missileDef.size); sig = -1; detection = -1; if (TotalCrossSection_MSP < ((Constants.OrdnanceTN.MissileResolutionMaximum + 6) + 1)) { if (TotalCrossSection_MSP <= (Constants.OrdnanceTN.MissileResolutionMinimum + 6)) { sig = Constants.OrdnanceTN.MissileResolutionMinimum; } if (TotalCrossSection_MSP <= (Constants.OrdnanceTN.MissileResolutionMaximum + 6)) { sig = TotalCrossSection_MSP - 6; } detection = Missile.missileDef.aSD.GetActiveDetectionRange(0, sig); } else { /// <summary> /// Big missiles will be treated in HS terms: 21-40 MSP = 2 HS, 41-60 = 3 HS, 61-80 = 4 HS, 81-100 = 5 HS. The same should hold true for greater than 100 sized missiles. /// but those are impossible to build. /// </summary> sig = (int)Math.Ceiling((float)TotalCrossSection_MSP / 20.0f); detection = Missile.missileDef.aSD.GetActiveDetectionRange(sig, -1); } bool det = LargeDetection(dist, detection); if (det == true) { for (int loop3 = 0; loop3 < MissileGroup.missiles.Count; loop3++) { MissileGroup.missiles[loop3].ActiveDetection[FactionID] = GameState.Instance.CurrentSecond; } if (DetMissileList.Contains(MissileGroup) == false) { DetMissileList.Add(MissileGroup); } System.FactionDetectionLists[FactionID].Active[loop2] = GameState.Instance.CurrentSecond; } } #endregion } }//end else if SSE = missile && ordnance group has missiles in it }//end if contact not detected and can be detected }//end for faction detection list contacts }//end for missile groups #endregion /// <summary> /// Detected contacts logic. If a ship has been newly detected this tick, create a contact entry for it. /// Otherwise update the existing one. Messages to the message log should be handled there(at the top of this very file. /// if a ship is no longer detected this tick then remove it from the detected contacts list. /// /// This can actually be improved by turning detectedContact into a linked list, and putting updated contacts in front. /// this way unupdated contacts would be at the end, and I would not have to loop through all ships here. /// </summary> for (int loop3 = 0; loop3 < DetShipList.Count; loop3++) { ShipTN detectedShip = DetShipList[loop3]; StarSystem System = detectedShip.ShipsTaskGroup.Contact.Position.System; /// <summary> /// Sanity check to keep allied ships out of the DetectedContacts list. /// </summary> if (detectedShip.ShipsFaction != this) { bool inDict = DetectedContactLists.ContainsKey(System); if (inDict == false) { DetectedContactsList newDCL = new DetectedContactsList(); DetectedContactLists.Add(System, newDCL); } inDict = DetectedContactLists[System].DetectedContacts.ContainsKey(detectedShip); bool th = (detectedShip.ThermalDetection[FactionID] == GameState.Instance.CurrentSecond); bool em = (detectedShip.EMDetection[FactionID] == GameState.Instance.CurrentSecond); bool ac = (detectedShip.ActiveDetection[FactionID] == GameState.Instance.CurrentSecond); if (inDict == true) { int EMSig = -1; if (em == true) { EMSig = detectedShip.CurrentEMSignature; } DetectedContactLists[System].DetectedContacts[detectedShip].updateFactionContact(this, th, em, EMSig, ac, (uint)GameState.Instance.CurrentSecond); if (th == false && em == false && ac == false) { DetectedContactLists[System].DetectedContacts.Remove(detectedShip); } } else if (inDict == false && (th == true || em == true || ac == true)) { int EMSig = -1; if (em == true) { EMSig = detectedShip.CurrentEMSignature; } FactionContact newContact = new FactionContact(this, detectedShip, th, em, EMSig, ac, (uint)GameState.Instance.CurrentSecond); DetectedContactLists[System].DetectedContacts.Add(detectedShip, newContact); } } } for (int loop3 = 0; loop3 < DetMissileList.Count; loop3++) { OrdnanceTN Missile = DetMissileList[loop3].missiles[0]; StarSystem System = DetMissileList[loop3].contact.Position.System; /// <summary> /// Sanity check to keep allied missiles out of the DetectedContacts list. /// </summary> if (Missile.missileGroup.ordnanceGroupFaction != this) { bool inDict = DetectedContactLists.ContainsKey(System); if (inDict == false) { DetectedContactsList newDCL = new DetectedContactsList(); DetectedContactLists.Add(System, newDCL); } inDict = DetectedContactLists[System].DetectedMissileContacts.ContainsKey(Missile.missileGroup); bool th = (Missile.ThermalDetection[FactionID] == GameState.Instance.CurrentSecond); bool em = (Missile.EMDetection[FactionID] == GameState.Instance.CurrentSecond); bool ac = (Missile.ActiveDetection[FactionID] == GameState.Instance.CurrentSecond); if (inDict == true) { int EMSig = 0; if (em == true) { EMSig = Missile.missileDef.aSD.gps; } DetectedContactLists[System].DetectedMissileContacts[Missile.missileGroup].updateFactionContact(this, th, em, EMSig, ac, (uint)GameState.Instance.CurrentSecond); if (th == false && em == false && ac == false) { DetectedContactLists[System].DetectedMissileContacts.Remove(Missile.missileGroup); } } else if (inDict == false && (th == true || em == true || ac == true)) { FactionContact newContact = new FactionContact(this, Missile.missileGroup, th, em, ac, (uint)GameState.Instance.CurrentSecond); DetectedContactLists[System].DetectedMissileContacts.Add(Missile.missileGroup, newContact); } } } for (int loop3 = 0; loop3 < DetPopList.Count; loop3++) { Population CurrentPopulation = DetPopList[loop3]; StarSystem System = DetPopList[loop3].Contact.Position.System; if (CurrentPopulation.Faction != this) { bool inDict = DetectedContactLists.ContainsKey(System); if (inDict == false) { DetectedContactsList newDCL = new DetectedContactsList(); DetectedContactLists.Add(System, newDCL); } inDict = DetectedContactLists[System].DetectedPopContacts.ContainsKey(CurrentPopulation); bool th = (CurrentPopulation.ThermalDetection[FactionID] == GameState.Instance.CurrentSecond); bool em = (CurrentPopulation.EMDetection[FactionID] == GameState.Instance.CurrentSecond); bool ac = (CurrentPopulation.ActiveDetection[FactionID] == GameState.Instance.CurrentSecond); if (inDict == true) { DetectedContactLists[System].DetectedPopContacts[CurrentPopulation].updateFactionContact(this, th, em, CurrentPopulation.EMSignature, ac, (uint)GameState.Instance.CurrentSecond); if (th == false && em == false && ac == false) { DetectedContactLists[System].DetectedPopContacts.Remove(CurrentPopulation); } } else if (inDict == false && (th == true || em == true || ac == true)) { FactionContact newContact = new FactionContact(this, CurrentPopulation, th, em, ac, (uint)GameState.Instance.CurrentSecond); DetectedContactLists[System].DetectedPopContacts.Add(CurrentPopulation, newContact); } } } }
/// <summary> /// Shipyard modifications are done here. /// </summary> /// <param name="CurrentFaction">Current faction this shipyard belongs to.</param> /// <param name="CurrentPopulation">Current population this shipyard is on.</param> /// <param name="SYInfo">The shipyard itself.</param> private static void PerformShipyardActivity(Faction CurrentFaction, Population CurrentPopulation, Installation.ShipyardInformation SYInfo) { if (SYInfo.CurrentActivity.Activity != Constants.ShipyardInfo.ShipyardActivity.NoActivity && SYInfo.CurrentActivity.Paused == false) { float SYBP = SYInfo.CalcAnnualSYProduction() * Constants.Colony.ConstructionCycleFraction; int Adjustment = 1; if (SYInfo.ShipyardType == Constants.ShipyardInfo.SYType.Naval) { Adjustment = Constants.ShipyardInfo.NavalToCommercialRatio; } /// <summary> /// Don't bother with completion date, or progress, just add the capacity. /// </summary> if (SYInfo.CurrentActivity.Activity == Constants.ShipyardInfo.ShipyardActivity.CapExpansion || SYInfo.CurrentActivity.Activity == Constants.ShipyardInfo.ShipyardActivity.CapExpansionUntilX) { /// <summary> /// How many tons could this shipyard expand capacity by in this time increment? SYBP is the number of BP produced this cycle. BaseTotalCostOfExpansion is the cost for 500 tons. /// Adjustment is the factor that accounts for whether or not this is a commercial yard or a naval yard. /// </summary> float BaseCostIncrement = SYBP / (float)(Constants.ShipyardInfo.BaseTotalCostOfExpansion * Adjustment); float TonsPerCycle = BaseCostIncrement * (float)Constants.ShipyardInfo.TonnageDenominator; /// <summary> /// Don't build more than this many tons if the activity is CapX. /// </summary> if (SYInfo.CurrentActivity.Activity == Constants.ShipyardInfo.ShipyardActivity.CapExpansionUntilX) { if (SYInfo.Tonnage + (int)Math.Floor(TonsPerCycle) > SYInfo.CurrentActivity.CapExpansionLimit) { TonsPerCycle = SYInfo.CurrentActivity.CapExpansionLimit - SYInfo.Tonnage; } SYInfo.CurrentActivity.Progress = 1.0m - (((decimal)SYInfo.CurrentActivity.CapExpansionLimit - (decimal)(SYInfo.Tonnage + TonsPerCycle)) / (decimal)SYInfo.CurrentActivity.CapExpansionLimit); } decimal Cost = (Constants.ShipyardInfo.BaseTotalCostOfExpansion*Adjustment) * ((decimal)TonsPerCycle / (decimal)Constants.ShipyardInfo.TonnageDenominator) * SYInfo.Slipways * Adjustment; decimal[] mCost = new decimal[(int)Constants.Minerals.MinerialNames.MinerialCount]; mCost[(int)Constants.Minerals.MinerialNames.Duranium] = Cost / 2.0m; mCost[(int)Constants.Minerals.MinerialNames.Neutronium] = Cost / 2.0m; /// <summary> /// Can I build this tick's worth of production? /// </summary> bool CanBuild = CurrentPopulation.MineralRequirement(mCost, 1.0f); if (CanBuild == true) { CurrentPopulation.HandleShipyardCost(Cost, mCost, 1.0f); SYInfo.AddTonnage(CurrentFaction, (int)Math.Floor(TonsPerCycle)); } else { String Entry = String.Format("Not enough minerals to finish task {0} at Shipyard {1} on Population {2}", SYInfo.CurrentActivity.Activity, SYInfo, CurrentPopulation); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Position.System, CurrentPopulation, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); GameState.Instance.Factions[0].MessageLog.Add(NMsg); } } else { /// <summary> /// BP produced this construction cycle / the total cost of the activity. /// </summary> float CurrentProgress = SYBP / (float)SYInfo.CurrentActivity.CostOfActivity; if ((SYInfo.CurrentActivity.Progress + (decimal)CurrentProgress) > 1.0m) { CurrentProgress = (float)(1.0m - SYInfo.CurrentActivity.Progress); } bool CanBuild = CurrentPopulation.MineralRequirement(SYInfo.CurrentActivity.minerialsCost, CurrentProgress); if (CanBuild == true) { CurrentPopulation.HandleShipyardCost(SYInfo.CurrentActivity.CostOfActivity, SYInfo.CurrentActivity.minerialsCost, CurrentProgress); SYInfo.CurrentActivity.Progress = SYInfo.CurrentActivity.Progress + (decimal)CurrentProgress; } else { String Entry = String.Format("Not enough minerals to finish task {0} at Shipyard {1} on Population {2}", SYInfo.CurrentActivity.Activity, SYInfo, CurrentPopulation); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Position.System, CurrentPopulation, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); GameState.Instance.Factions[0].MessageLog.Add(NMsg); } /// <summary> /// handle standard task completion here. /// </summary> if (SYInfo.CurrentActivity.Progress >= 1.0m) { switch (SYInfo.CurrentActivity.Activity) { case Constants.ShipyardInfo.ShipyardActivity.AddSlipway: SYInfo.Slipways = SYInfo.Slipways + 1; break; case Constants.ShipyardInfo.ShipyardActivity.Add500Tons: SYInfo.AddTonnage(CurrentFaction, Constants.ShipyardInfo.TonnageDenominator); break; case Constants.ShipyardInfo.ShipyardActivity.Add1000Tons: SYInfo.AddTonnage(CurrentFaction, (Constants.ShipyardInfo.TonnageDenominator * 2)); break; case Constants.ShipyardInfo.ShipyardActivity.Add2000Tons: SYInfo.AddTonnage(CurrentFaction, (Constants.ShipyardInfo.TonnageDenominator * 4)); break; case Constants.ShipyardInfo.ShipyardActivity.Add5000Tons: SYInfo.AddTonnage(CurrentFaction, (Constants.ShipyardInfo.TonnageDenominator * 10)); break; case Constants.ShipyardInfo.ShipyardActivity.Add10000Tons: SYInfo.AddTonnage(CurrentFaction, (Constants.ShipyardInfo.TonnageDenominator * 20)); break; case Constants.ShipyardInfo.ShipyardActivity.Retool: SYInfo.AssignedClass = SYInfo.CurrentActivity.TargetOfRetool; if (SYInfo.AssignedClass.IsLocked == false) SYInfo.AssignedClass.IsLocked = true; break; } } else { /// <summary> /// Update the timer since this project won't finish just yet. /// </summary> decimal CostLeft = SYInfo.CurrentActivity.CostOfActivity * (1.0m - SYInfo.CurrentActivity.Progress); float YearsOfProduction = (float)CostLeft / SYInfo.CalcAnnualSYProduction(); DateTime EstTime = GameState.Instance.GameDateTime; if (YearsOfProduction < Constants.Colony.TimerYearMax) { float DaysInYear = (float)Constants.TimeInSeconds.RealYear / (float)Constants.TimeInSeconds.Day; int TimeToBuild = (int)Math.Floor(YearsOfProduction * DaysInYear); TimeSpan TS = new TimeSpan(TimeToBuild, 0, 0, 0); EstTime = EstTime.Add(TS); } SYInfo.CurrentActivity.CompletionDate = EstTime; } } /// <summary> /// Lastly clean up any completed activities. /// </summary> if (SYInfo.CurrentActivity.Activity == Constants.ShipyardInfo.ShipyardActivity.CapExpansionUntilX) { if (SYInfo.Tonnage >= SYInfo.CurrentActivity.CapExpansionLimit) { /// <summary> /// This activity has completed so end it. /// </summary> SYInfo.CurrentActivity = new Installation.ShipyardInformation.ShipyardActivity(); } } else if (SYInfo.CurrentActivity.Activity != Constants.ShipyardInfo.ShipyardActivity.CapExpansion && SYInfo.CurrentActivity.Activity != Constants.ShipyardInfo.ShipyardActivity.NoActivity) { if (SYInfo.CurrentActivity.Progress >= 1.0m) { /// <summary> /// This activity has completed so end it. /// </summary> SYInfo.CurrentActivity = new Installation.ShipyardInformation.ShipyardActivity(); } } } }
/// <summary> /// What sub pulse time slice best serves the DesiredTime requirement? /// </summary> /// <param name="DesiredTime">Time I want to advance. This should be in seconds, but will always be divisible by 5. It should already be slightly less than missile impact time.</param> /// <param name="Advance">subpulse length</param> /// <param name="Pulse">subpulses</param> /// <returns>If we handled DesiredTime correctly or not.</returns> public bool PreemptCheck(int DesiredTime, out int Advance, out int Pulse) { int FiveSecondIncrements = DesiredTime / 5; if (DesiredTime == Constants.TimeInSeconds.FiveSeconds) { Advance = (int)Constants.TimeInSeconds.FiveSeconds; Pulse = FiveSecondIncrements; } else if (DesiredTime <= Constants.TimeInSeconds.ThirtySeconds) { Advance = (int)Constants.TimeInSeconds.FiveSeconds; Pulse = FiveSecondIncrements; } else if (DesiredTime <= Constants.TimeInSeconds.TwoMinutes) { Advance = (int)Constants.TimeInSeconds.ThirtySeconds; Pulse = (int)Math.Floor((float)DesiredTime / (int)Constants.TimeInSeconds.ThirtySeconds); } else if (DesiredTime <= Constants.TimeInSeconds.FiveMinutes) { Advance = (int)Constants.TimeInSeconds.Minute; Pulse = (int)Math.Floor((float)DesiredTime / (int)Constants.TimeInSeconds.Minute); } else if (DesiredTime <= Constants.TimeInSeconds.TwentyMinutes) { Advance = (int)Constants.TimeInSeconds.FiveMinutes; Pulse = (int)Math.Floor((float)DesiredTime / (int)Constants.TimeInSeconds.FiveMinutes); } else if (DesiredTime <= Constants.TimeInSeconds.Hour) { Advance = (int)Constants.TimeInSeconds.TwentyMinutes; Pulse = (int)Math.Floor((float)DesiredTime / (int)Constants.TimeInSeconds.TwentyMinutes); } else if (DesiredTime <= Constants.TimeInSeconds.ThreeHours) { Advance = (int)Constants.TimeInSeconds.Hour; Pulse = (int)Math.Floor((float)DesiredTime / (int)Constants.TimeInSeconds.Hour); } else if (DesiredTime <= Constants.TimeInSeconds.EightHours) { Advance = (int)(Constants.TimeInSeconds.Hour * 2); Pulse = (int)Math.Floor((float)DesiredTime / (int)(Constants.TimeInSeconds.Hour * 2)); } else if (DesiredTime <= Constants.TimeInSeconds.Day) { Advance = (int)(Constants.TimeInSeconds.EightHours); Pulse = (int)Math.Floor((float)DesiredTime / (int)Constants.TimeInSeconds.EightHours); } else if (DesiredTime <= Constants.TimeInSeconds.FiveDays) { Advance = (int)(Constants.TimeInSeconds.Day); Pulse = (int)Math.Floor((float)DesiredTime / (int)Constants.TimeInSeconds.Day); } else if (DesiredTime <= Constants.TimeInSeconds.Month) { Advance = (int)(Constants.TimeInSeconds.FiveDays); Pulse = (int)Math.Floor((float)DesiredTime / (int)Constants.TimeInSeconds.FiveDays); } else { /// <summary> /// this should not happen. /// </summary> #warning SM log this String Entry = String.Format("Subpulse Error with desiredTime {0}, sub pulse set to 5 seconds. This should go in the SM log eventually.", DesiredTime); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.Error, null, null, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); GameState.Instance.Factions[0].MessageLog.Add(Msg); Advance = (int)Constants.TimeInSeconds.FiveSeconds; Pulse = 1; return false; } return true; }
/// <summary> /// TaskGroupEM Detection runs the EM detection routine on TaskGroupToTest from CurrentTaskGroup. This differs from Thermal in that we use the ship EM linked list as well as the faction EM /// Detection list. /// </summary> /// <param name="System">Current System this is taking place in.</param> /// <param name="CurrentTaskGroup">TaskGroup that is performing the sensor sweep.</param> /// <param name="TaskGroupToTest">TaskGroup being tested against.</param> /// <param name="dist">Distance between these two taskgroups.</param> /// <param name="detListIterator">Iterator for where the TaskGroupToTest is in the various detection lists and the system contact list.</param> /// <param name="DetShipList">If a ship is detected it must be put into this list for the detectedContactsList later on.</param> private void TaskGroupEMDetection(StarSystem System, TaskGroupTN CurrentTaskGroup, TaskGroupTN TaskGroupToTest, float dist, int detListIterator) { if (System.FactionDetectionLists[FactionID].EM[detListIterator] != GameState.Instance.CurrentSecond) { int sig = -1; int detection = -1; bool noDetection = false; bool allDetection = false; /// <summary> /// Get the best detection range for EM signatures in loop. /// </summary> int ShipID = TaskGroupToTest.EMSortList.Last(); ShipTN scratch = TaskGroupToTest.Ships[ShipID]; sig = scratch.CurrentEMSignature; /// <summary> /// Check to see if the taskgroup has an em sensor, and that said em sensor is not destroyed. /// otherwise use the default passive detection range. /// </summary> if (CurrentTaskGroup.BestEMCount > 0) { detection = CurrentTaskGroup.BestEM.pSensorDef.GetPassiveDetectionRange(sig); } else { detection = ComponentList.DefaultPassives.GetPassiveDetectionRange(sig); } bool det = LargeDetection(dist, detection); /// <summary> /// Good case, none of the ships are detected. /// </summary> if (det == false) { noDetection = true; } /// <summary> /// Atleast the biggest ship is detected. /// </summary if (noDetection == false) { ShipID = TaskGroupToTest.EMSortList.First(); scratch = TaskGroupToTest.Ships[ShipID]; sig = scratch.CurrentEMSignature; /// <summary> /// once again we must check here to make sure that the taskgroup does have a passive suite, or else use the default one. /// </summary> if (CurrentTaskGroup.BestEMCount > 0) { detection = CurrentTaskGroup.BestEM.pSensorDef.GetPassiveDetectionRange(sig); } else { detection = ComponentList.DefaultPassives.GetPassiveDetectionRange(sig); } det = LargeDetection(dist, detection); /// <summary> /// Best case, everything is detected. /// </summary> if (det == true) { allDetection = true; for (int loop3 = 0; loop3 < TaskGroupToTest.Ships.Count; loop3++) { TaskGroupToTest.Ships[loop3].EMDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(TaskGroupToTest.Ships[loop3]) == false) { DetShipList.Add(TaskGroupToTest.Ships[loop3]); } } System.FactionDetectionLists[FactionID].EM[detListIterator] = GameState.Instance.CurrentSecond; } else if (noDetection == false && allDetection == false) { /// <summary> /// Worst case. some are detected, some aren't. /// </summary> for (int loop3 = 0; loop3 < TaskGroupToTest.Ships.Count; loop3++) { LinkedListNode<int> node = TaskGroupToTest.EMSortList.Last; bool done = false; while (!done) { scratch = TaskGroupToTest.Ships[node.Value]; if (scratch.EMDetection[FactionID] != GameState.Instance.CurrentSecond) { sig = scratch.CurrentEMSignature; /// <summary> /// here is where EM detection differs from Thermal detection: /// If a ship has a signature of 0 by this point(and we didn't already hit noDetection above, /// it means that one ship is emitting a signature, but that no other ships are. /// Mark the group as totally detected, but not the ships, this serves to tell me that the ships are undetectable /// in this case. /// </summary> if (sig == 0) { /// <summary> /// The last signature we looked at was the ship emitting an EM sig, and this one is not. /// Mark the entire group as "spotted" because no other detection will occur. /// </summary> if (TaskGroupToTest.Ships[node.Next.Value].EMDetection[FactionID] == GameState.Instance.CurrentSecond) { System.FactionDetectionLists[FactionID].EM[detListIterator] = GameState.Instance.CurrentSecond; } break; } if (CurrentTaskGroup.BestEMCount > 0) { detection = CurrentTaskGroup.BestEM.pSensorDef.GetPassiveDetectionRange(sig); } else { detection = ComponentList.DefaultPassives.GetPassiveDetectionRange(sig); } det = LargeDetection(dist, detection); if (det == true) { scratch.EMDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(scratch) == false) { DetShipList.Add(scratch); } } else { done = true; break; } } if (node == TaskGroupToTest.EMSortList.First) { /// <summary> /// This should not happen. /// </summary> String ErrorMessage = string.Format("Partial EM detect for TGs looped through every ship. {0} {1} {2} {3}", dist, detection, noDetection, allDetection); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.Error, CurrentTaskGroup.Contact.Position.System, CurrentTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, ErrorMessage); MessageLog.Add(NMsg); done = true; break; } node = node.Previous; } } } /// <summary> /// End else /// </summary> } } }
/// <summary> /// Do all of the tasks that this shipyard has assigned to it. /// </summary> /// <param name="CurrentFaction">Faction both the population and the shipyard belong to.</param> /// <param name="CurrentPopulation">Population the shipyard is on</param> /// <param name="SYInfo">Shipyard the tasks are happening at</param> private static void BuildShips(Faction CurrentFaction, Population CurrentPopulation, List <Installation.ShipyardInformation.ShipyardTask> SortedList) { BindingList <Installation.ShipyardInformation.ShipyardTask> TasksToRemove = new BindingList <Installation.ShipyardInformation.ShipyardTask>(); foreach (Installation.ShipyardInformation.ShipyardTask Task in SortedList) { if (Task.IsPaused() == true) { continue; } /// <summary> /// the Annual Build Rate(ABR) is the number of BP per year that will be devoted to this activity. this is the number of BP produced only this cycle. /// </summary> float CycleBuildRate = Task.ABR * Constants.Colony.ConstructionCycleFraction; /// <summary> /// How much of this task will be completed this construction cycle? /// </summary> float CurrentProgress = CycleBuildRate / (float)Task.Cost; if ((Task.Progress + (decimal)CurrentProgress) > 1.0m) { CurrentProgress = (float)(1.0m - Task.Progress); } /// <summary> /// Can this shipyard Task be built this construction cycle? /// </summary> bool CanBuild = CurrentPopulation.MineralRequirement(Task.minerialsCost, CurrentProgress); if (CanBuild == true && Task.CurrentTask != Constants.ShipyardInfo.Task.Scrap) { CurrentPopulation.HandleShipyardCost(Task.Cost, Task.minerialsCost, CurrentProgress); Task.Progress = Task.Progress + (decimal)CurrentProgress; } else if (Task.CurrentTask == Constants.ShipyardInfo.Task.Scrap) { /// <summary> /// Return money to the population from the scrap. /// </summary> CurrentPopulation.HandleShipyardCost(Task.Cost, Task.minerialsCost, (CurrentProgress * -1.0f)); Task.Progress = Task.Progress + (decimal)CurrentProgress; } else { String Entry = String.Format("Not enough minerals to finish task {0} at Shipyard {1} on Population {2}", Task.CurrentTask, CurrentPopulation.ShipyardTasks[Task], CurrentPopulation); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Position.System, CurrentPopulation, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); GameState.Instance.Factions[0].MessageLog.Add(NMsg); } /// <summary> /// handle standard task completion here. /// </summary> if (Task.Progress >= 1.0m) { TasksToRemove.Add(Task); switch (Task.CurrentTask) { case Constants.ShipyardInfo.Task.Construction: Task.AssignedTaskGroup.AddShip(Task.ConstructRefitTarget, Task.Title); CurrentPopulation.FuelStockpile = Task.AssignedTaskGroup.Ships[Task.AssignedTaskGroup.Ships.Count - 1].Refuel(CurrentPopulation.FuelStockpile); break; case Constants.ShipyardInfo.Task.Repair: /// <summary> /// Set the Armor to fully repaired, set all components as not destroyed, and reduce the maintenance clock by a certain amount. /// </summary> #warning maintenance clock work should be performed here and in refit as well. Task.CurrentShip.ShipArmor.RepairAllArmor(); foreach (ComponentTN CurComp in Task.CurrentShip.ShipComponents) { CurComp.isDestroyed = false; } break; case Constants.ShipyardInfo.Task.Refit: /// <summary> /// need to remove the old ship, put in the new ship, copy over important information, adjust refueling,MSP,etc? /// </summary> /// <summary> /// Credit the population with the fuel and ordnance on the ship. the old ships MSP will be considered where the new ship got its MSP from. /// </summary> CurrentPopulation.FuelStockpile = CurrentPopulation.FuelStockpile + Task.CurrentShip.CurrentFuel; foreach (KeyValuePair <OrdnanceDefTN, int> OrdnancePair in Task.CurrentShip.ShipOrdnance) { CurrentPopulation.LoadMissileToStockpile(OrdnancePair.Key, (float)OrdnancePair.Value); } /// <summary> /// Destroy the ship. just use the existing code to remove the ship from the simulation, no point in reduplicating all of it. /// </summary> Task.CurrentShip.IsDestroyed = true; if (CurrentFaction.RechargeList.ContainsKey(Task.CurrentShip) == true) { if ((CurrentFaction.RechargeList[Task.CurrentShip] & (int)Faction.RechargeStatus.Destroyed) != (int)Faction.RechargeStatus.Destroyed) { CurrentFaction.RechargeList[Task.CurrentShip] = CurrentFaction.RechargeList[Task.CurrentShip] + (int)Faction.RechargeStatus.Destroyed; } } else { CurrentFaction.RechargeList.Add(Task.CurrentShip, (int)Faction.RechargeStatus.Destroyed); } /// <summary> /// Add in the "new" ship. /// </summary> Task.AssignedTaskGroup.AddShip(Task.ConstructRefitTarget, Task.CurrentShip.Name); Task.AssignedTaskGroup.Ships[Task.AssignedTaskGroup.Ships.Count - 1].TFTraining = Task.CurrentShip.TFTraining; Task.AssignedTaskGroup.Ships[Task.AssignedTaskGroup.Ships.Count - 1].ShipGrade = Task.CurrentShip.ShipGrade; CurrentPopulation.FuelStockpile = Task.AssignedTaskGroup.Ships[Task.AssignedTaskGroup.Ships.Count - 1].Refuel(CurrentPopulation.FuelStockpile); break; case Constants.ShipyardInfo.Task.Scrap: /// <summary> /// All non-destroyed components from the ship need to be put into the population stockpile. /// This further includes fuel, MSP, and ordnance as well as eventually officers and crew. /// </summary> #warning Handle officers and crew on ship scrapping. BindingList <ComponentDefTN> CompDefList = Task.CurrentShip.ShipClass.ListOfComponentDefs; BindingList <short> CompDefCount = Task.CurrentShip.ShipClass.ListOfComponentDefsCount; BindingList <ComponentTN> ShipCompList = Task.CurrentShip.ShipComponents; BindingList <ushort> ComponentDefIndex = Task.CurrentShip.ComponentDefIndex; int DefCount = Task.CurrentShip.ShipClass.ListOfComponentDefs.Count; for (int CompDefIndex = 0; CompDefIndex < DefCount; CompDefIndex++) { ComponentDefTN CurrentCompDef = CompDefList[CompDefIndex]; short CurrentCompCount = CompDefCount[CompDefIndex]; int destCount = 0; for (int CompIndex = 0; CompIndex < CurrentCompCount; CompIndex++) { if (ShipCompList[ComponentDefIndex[CompDefIndex] + CompIndex].isDestroyed == true) { destCount++; } } if (destCount != CurrentCompCount) { CurrentPopulation.AddComponentsToStockpile(CurrentCompDef, (float)(CurrentCompCount - destCount)); } } CurrentPopulation.FuelStockpile = CurrentPopulation.FuelStockpile + Task.CurrentShip.CurrentFuel; CurrentPopulation.MaintenanceSupplies = CurrentPopulation.MaintenanceSupplies + Task.CurrentShip.CurrentMSP; foreach (KeyValuePair <OrdnanceDefTN, int> OrdnancePair in Task.CurrentShip.ShipOrdnance) { CurrentPopulation.LoadMissileToStockpile(OrdnancePair.Key, (float)OrdnancePair.Value); } /// <summary> /// Finally destroy the ship. just use the existing code to remove the ship from the simulation, no point in reduplicating all of it. /// </summary> Task.CurrentShip.IsDestroyed = true; if (CurrentFaction.RechargeList.ContainsKey(Task.CurrentShip) == true) { if ((CurrentFaction.RechargeList[Task.CurrentShip] & (int)Faction.RechargeStatus.Destroyed) != (int)Faction.RechargeStatus.Destroyed) { CurrentFaction.RechargeList[Task.CurrentShip] = CurrentFaction.RechargeList[Task.CurrentShip] + (int)Faction.RechargeStatus.Destroyed; } } else { CurrentFaction.RechargeList.Add(Task.CurrentShip, (int)Faction.RechargeStatus.Destroyed); } break; } } else { /// <summary> /// Update the timer since this project won't finish just yet. /// </summary> decimal CostLeft = Task.Cost * (1.0m - Task.Progress); float YearsOfProduction = (float)CostLeft / Task.ABR; DateTime EstTime = GameState.Instance.GameDateTime; if (YearsOfProduction < Constants.Colony.TimerYearMax) { float DaysInYear = (float)Constants.TimeInSeconds.RealYear / (float)Constants.TimeInSeconds.Day; int TimeToBuild = (int)Math.Floor(YearsOfProduction * DaysInYear); TimeSpan TS = new TimeSpan(TimeToBuild, 0, 0, 0); EstTime = EstTime.Add(TS); } Task.CompletionDate = EstTime; } } /// <summary> /// Remove all the tasks that are now completed. /// </summary> foreach (Installation.ShipyardInformation.ShipyardTask Task in TasksToRemove) { /// <summary> /// Sanity check here. /// </summary> if (Task.Progress >= 1.0m) { Installation.ShipyardInformation SYI = CurrentPopulation.ShipyardTasks[Task]; SYI.BuildingShips.Remove(Task); CurrentPopulation.ShipyardTasks.Remove(Task); } } TasksToRemove.Clear(); }
/// <summary> /// any friendly ships that have orders to do anything at this taskgroup need to have those orders canceled as well. this is separate from the /// removal of ship specific ordering. /// </summary> /// <param name="pair"></param> private void RemoveFriendlyTaskGroupsOrdered(KeyValuePair<ShipTN, int> pair) { ShipTN Ship = pair.Key; foreach (TaskGroupTN TaskGroupOrdered in Ship.TaskGroupsOrdered) { for (int orderIterator = 0; orderIterator < TaskGroupOrdered.TaskGroupOrders.Count; orderIterator++) { Order TaskGroupOrder = TaskGroupOrdered.TaskGroupOrders[orderIterator]; if (TaskGroupOrder.target.SSEntity == StarSystemEntityType.TaskGroup) { if (TaskGroupOrder.taskGroup == Ship.ShipsTaskGroup) { /// <summary> /// At this point it has been established that the destroyed TG has TGs ordered to it, friendly TGs. /// That the ordered TG has multiple TG orders /// That the current order target is a taskgroup, and in fact this taskgroup. /// </summary> String Entry = String.Format("Taskgroup {0} cannot find target, orders canceled.", TaskGroupOrdered.Name); MessageEntry Entry2 = new MessageEntry(MessageEntry.MessageType.OrdersNotCompleted, TaskGroupOrdered.Contact.Position.System, TaskGroupOrdered.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); TaskGroupOrdered.TaskGroupFaction.MessageLog.Add(Entry2); int lastOrder = TaskGroupOrdered.TaskGroupOrders.Count - 1; for (int orderListIterator = lastOrder; orderListIterator >= orderIterator; orderListIterator--) { TaskGroupOrdered.TaskGroupOrders.RemoveAt(orderListIterator); } break; } } } } Ship.ShipsTaskGroup.clearAllOrders(); Ship.ShipsTaskGroup.Contact.Position.System.SystemContactList.Remove(Ship.ShipsTaskGroup.Contact); Ship.ShipsFaction.TaskGroups.Remove(Ship.ShipsTaskGroup); }
/// <summary> /// Reloads missile tubes, function is based on time alone for the most part. Launch tubes with no ordnance selected will "reload" as well. not going to demand absolutely meticulous /// ordnance management from the player. /// </summary> /// <param name="tick">time increment that the sim is advanced by. 1 day = 86400 seconds, smallest practical value is 5.</param> /// <returns> Whether all tubes have been loaded or not.</returns> public bool ReloadLaunchTubes(uint tick) { bool allTubesLoaded = true; for (int loop = 0; loop < ShipMLaunchers.Count; loop++) { if (ShipMLaunchers[loop].loadTime > 0) { allTubesLoaded = false; ShipMLaunchers[loop].loadTime = ShipMLaunchers[loop].loadTime - (int)tick; if (ShipMLaunchers[loop].loadTime < 0) { ShipMLaunchers[loop].loadTime = 0; } if (ShipMLaunchers[loop].loadedOrdnance != null) { if (ShipOrdnance.ContainsKey(ShipMLaunchers[loop].loadedOrdnance) == false) { String NoOrdnance = String.Format("No ordnance of type {0} remains on {1}. This will adversely affect turn processing(very slightly) if left unfixed.", ShipMLaunchers[loop].loadedOrdnance.Name, Name); MessageEntry NewMessage = new MessageEntry(MessageEntry.MessageType.LaunchTubeNoOrdnanceToReload, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, NoOrdnance); ShipsFaction.MessageLog.Add(NewMessage); ShipMLaunchers[loop].loadTime = -1; } } } /// <summary> /// Ships with no loaded ordnance will remain in the weapon recharge list due to this, either the AI or the player should fix this. /// </summary> else if (ShipMLaunchers[loop].loadTime == -1) { allTubesLoaded = false; if (ShipMLaunchers[loop].loadedOrdnance == null) { ShipMLaunchers[loop].loadTime = 0; } else { if (ShipOrdnance.ContainsKey(ShipMLaunchers[loop].loadedOrdnance) == false && ShipMLaunchers[loop].mFC.openFire == true) { String NoOrdnance = String.Format("No ordnance of type {0} remains on {1}, cannot fire. This will adversely affect turn processing(very slightly) if left unfixed.", ShipMLaunchers[loop].loadedOrdnance.Name, Name); MessageEntry NewMessage = new MessageEntry(MessageEntry.MessageType.LaunchTubeNoOrdnanceToReload, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, NoOrdnance); ShipsFaction.MessageLog.Add(NewMessage); } else if (ShipOrdnance.ContainsKey(ShipMLaunchers[loop].loadedOrdnance) == true) { ShipMLaunchers[loop].loadTime = 0; } } } } return allTubesLoaded; }
/// <summary> /// Processes construction factory work for every faction. /// </summary> /// <param name="P">List of factions.</param> public static void ConstructionFactoryBuild(BindingList <Faction> P) { /// <summary> /// Subtract the construction cycle from the construction tick. /// </summary> foreach (Faction CurrentFaction in P) { foreach (Population CurrentPopulation in CurrentFaction.Populations) { /// <summary> /// How much construction work per day does this colony do? default should be 5 day construction cycle. /// </summary> float CurrentIndustry = CurrentPopulation.CalcTotalIndustry() * Constants.Colony.ConstructionCycleFraction; float BuildPercentage = 0.0f; foreach (ConstructionBuildQueueItem CurrentConstruction in CurrentPopulation.ConstructionBuildQueue) { /// <summary> /// Check to see if this item is in the current build queue, or has to wait for capacity to free up. /// </summary> if ((BuildPercentage + CurrentConstruction.buildCapacity) <= 100.0f) { BuildPercentage = BuildPercentage + CurrentConstruction.buildCapacity; } else { break; } /// <summary> /// Calculate Completion Estimate: /// </summary> float BPRequirement = (float)Math.Floor(CurrentConstruction.numToBuild) * (float)CurrentConstruction.costPerItem; float DaysInYear = (float)Constants.TimeInSeconds.RealYear / (float)Constants.TimeInSeconds.Day; float YearsOfProduction = (BPRequirement / CurrentConstruction.buildCapacity); if (CurrentConstruction.buildCapacity != 0.0f && YearsOfProduction < Constants.Colony.TimerYearMax) { int TimeToBuild = (int)Math.Floor(YearsOfProduction * DaysInYear); DateTime EstTime = GameState.Instance.GameDateTime; TimeSpan TS = new TimeSpan(TimeToBuild, 0, 0, 0); EstTime = EstTime.Add(TS); CurrentConstruction.completionDate = EstTime; } /// <summary> /// This construction project is paused right now. /// </summary> if (CurrentConstruction.inProduction == false) { continue; } /// <summary> /// how much of total industry does this build project use? /// </summary> float DevotedIndustry = (CurrentConstruction.buildCapacity / 100.0f) * CurrentIndustry; float Completion = DevotedIndustry / (float)CurrentConstruction.costPerItem; bool CIRequired = false; bool MineRequired = false; bool CanBuild = false; /// <summary> /// Conventional industry must also be used. /// </summary> if (CurrentConstruction.buildType == ConstructionBuildQueueItem.CBType.PlanetaryInstallation) { if ((int)CurrentConstruction.installationBuild.Type >= (int)Installation.InstallationType.ConvertCIToConstructionFactory && (int)CurrentConstruction.installationBuild.Type <= (int)Installation.InstallationType.ConvertCIToOrdnanceFactory) { CIRequired = true; CanBuild = CurrentPopulation.CIRequirement(Completion); if (CanBuild == false) { String Entry = String.Format("Insufficent Conventional Industry to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksCI, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } } if ((int)CurrentConstruction.installationBuild.Type == (int)Installation.InstallationType.ConvertMineToAutomated) { MineRequired = true; CanBuild = CurrentPopulation.MineRequirement(Completion); if (CanBuild == false) { String Entry = String.Format("Insufficent Mines to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksCI, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } } } /// <summary> /// Check mineral costs to see if this can be built. /// </summary> switch (CurrentConstruction.buildType) { #warning PDC construction needs to be implemented here and slightly further down. case ConstructionBuildQueueItem.CBType.PlanetaryInstallation: CanBuild = CurrentPopulation.MineralRequirement(CurrentConstruction.installationBuild.MinerialsCost, Completion); break; case ConstructionBuildQueueItem.CBType.ShipComponent: CanBuild = CurrentPopulation.MineralRequirement(CurrentConstruction.componentBuild.minerialsCost, Completion); break; case ConstructionBuildQueueItem.CBType.PDCConstruction: break; case ConstructionBuildQueueItem.CBType.PDCPrefab: break; case ConstructionBuildQueueItem.CBType.PDCAssembly: break; case ConstructionBuildQueueItem.CBType.PDCRefit: break; case ConstructionBuildQueueItem.CBType.MaintenanceSupplies: CanBuild = CurrentPopulation.MineralRequirement(Constants.Colony.MaintenanceMineralCost, Completion); break; } if (CanBuild == false) { String Entry = String.Format("Insufficent Minerals to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } /// <summary> /// Adjust number to build downward to reflect construction happening. /// </summary> CurrentConstruction.numToBuild = CurrentConstruction.numToBuild - Completion; /// <summary> /// Handle the cost and build the item. /// </summary> switch (CurrentConstruction.buildType) { case ConstructionBuildQueueItem.CBType.PlanetaryInstallation: if (CIRequired == false && MineRequired == false) { CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, CurrentConstruction.installationBuild.MinerialsCost, Completion); } else if (CIRequired == true && MineRequired == false) { CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, CurrentConstruction.installationBuild.MinerialsCost, Completion, CIRequired, MineRequired); } else if (CIRequired == false && MineRequired == true) { CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, CurrentConstruction.installationBuild.MinerialsCost, Completion, CIRequired, MineRequired); } CurrentPopulation.AddInstallation(CurrentConstruction.installationBuild, Completion); break; case ConstructionBuildQueueItem.CBType.ShipComponent: CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, CurrentConstruction.componentBuild.minerialsCost, Completion); CurrentPopulation.AddComponentsToStockpile(CurrentConstruction.componentBuild, Completion); break; case ConstructionBuildQueueItem.CBType.PDCConstruction: break; case ConstructionBuildQueueItem.CBType.PDCPrefab: break; case ConstructionBuildQueueItem.CBType.PDCAssembly: break; case ConstructionBuildQueueItem.CBType.PDCRefit: break; case ConstructionBuildQueueItem.CBType.MaintenanceSupplies: CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, Constants.Colony.MaintenanceMineralCost, Completion); CurrentPopulation.AddMSP(Completion); break; } } /// <summary> /// Cleanup the CBQ here. /// </summary> for (int CBQIterator = 0; CBQIterator < CurrentPopulation.ConstructionBuildQueue.Count; CBQIterator++) { if (CurrentPopulation.ConstructionBuildQueue[CBQIterator].numToBuild <= 0.0f) { CurrentPopulation.ConstructionBuildQueue.RemoveAt(CBQIterator); CBQIterator--; } } } } }
/// <summary> /// Do all of the tasks that this shipyard has assigned to it. /// </summary> /// <param name="CurrentFaction">Faction both the population and the shipyard belong to.</param> /// <param name="CurrentPopulation">Population the shipyard is on</param> /// <param name="SYInfo">Shipyard the tasks are happening at</param> private static void BuildShips(Faction CurrentFaction, Population CurrentPopulation, List<Installation.ShipyardInformation.ShipyardTask> SortedList) { BindingList<Installation.ShipyardInformation.ShipyardTask> TasksToRemove = new BindingList<Installation.ShipyardInformation.ShipyardTask>(); foreach(Installation.ShipyardInformation.ShipyardTask Task in SortedList) { if (Task.IsPaused() == true) continue; /// <summary> /// the Annual Build Rate(ABR) is the number of BP per year that will be devoted to this activity. this is the number of BP produced only this cycle. /// </summary> float CycleBuildRate = Task.ABR * Constants.Colony.ConstructionCycleFraction; /// <summary> /// How much of this task will be completed this construction cycle? /// </summary> float CurrentProgress = CycleBuildRate / (float)Task.Cost; if ((Task.Progress + (decimal)CurrentProgress) > 1.0m) { CurrentProgress = (float)(1.0m - Task.Progress); } /// <summary> /// Can this shipyard Task be built this construction cycle? /// </summary> bool CanBuild = CurrentPopulation.MineralRequirement(Task.minerialsCost, CurrentProgress); if (CanBuild == true && Task.CurrentTask != Constants.ShipyardInfo.Task.Scrap) { CurrentPopulation.HandleShipyardCost(Task.Cost, Task.minerialsCost, CurrentProgress); Task.Progress = Task.Progress + (decimal)CurrentProgress; } else if (Task.CurrentTask == Constants.ShipyardInfo.Task.Scrap) { /// <summary> /// Return money to the population from the scrap. /// </summary> CurrentPopulation.HandleShipyardCost(Task.Cost, Task.minerialsCost, (CurrentProgress * -1.0f)); Task.Progress = Task.Progress + (decimal)CurrentProgress; } else { String Entry = String.Format("Not enough minerals to finish task {0} at Shipyard {1} on Population {2}", Task.CurrentTask, CurrentPopulation.ShipyardTasks[Task], CurrentPopulation); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Position.System, CurrentPopulation, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); GameState.Instance.Factions[0].MessageLog.Add(NMsg); } /// <summary> /// handle standard task completion here. /// </summary> if (Task.Progress >= 1.0m) { TasksToRemove.Add(Task); switch (Task.CurrentTask) { case Constants.ShipyardInfo.Task.Construction: Task.AssignedTaskGroup.AddShip(Task.ConstructRefitTarget, Task.Title); CurrentPopulation.FuelStockpile = Task.AssignedTaskGroup.Ships[Task.AssignedTaskGroup.Ships.Count - 1].Refuel(CurrentPopulation.FuelStockpile); break; case Constants.ShipyardInfo.Task.Repair: /// <summary> /// Set the Armor to fully repaired, set all components as not destroyed, and reduce the maintenance clock by a certain amount. /// </summary> #warning maintenance clock work should be performed here and in refit as well. Task.CurrentShip.ShipArmor.RepairAllArmor(); foreach (ComponentTN CurComp in Task.CurrentShip.ShipComponents) { CurComp.isDestroyed = false; } break; case Constants.ShipyardInfo.Task.Refit: /// <summary> /// need to remove the old ship, put in the new ship, copy over important information, adjust refueling,MSP,etc? /// </summary> /// <summary> /// Credit the population with the fuel and ordnance on the ship. the old ships MSP will be considered where the new ship got its MSP from. /// </summary> CurrentPopulation.FuelStockpile = CurrentPopulation.FuelStockpile + Task.CurrentShip.CurrentFuel; foreach (KeyValuePair<OrdnanceDefTN, int> OrdnancePair in Task.CurrentShip.ShipOrdnance) { CurrentPopulation.LoadMissileToStockpile(OrdnancePair.Key, (float)OrdnancePair.Value); } /// <summary> /// Destroy the ship. just use the existing code to remove the ship from the simulation, no point in reduplicating all of it. /// </summary> Task.CurrentShip.IsDestroyed = true; if (CurrentFaction.RechargeList.ContainsKey(Task.CurrentShip) == true) { if ((CurrentFaction.RechargeList[Task.CurrentShip] & (int)Faction.RechargeStatus.Destroyed) != (int)Faction.RechargeStatus.Destroyed) { CurrentFaction.RechargeList[Task.CurrentShip] = CurrentFaction.RechargeList[Task.CurrentShip] + (int)Faction.RechargeStatus.Destroyed; } } else { CurrentFaction.RechargeList.Add(Task.CurrentShip, (int)Faction.RechargeStatus.Destroyed); } /// <summary> /// Add in the "new" ship. /// </summary> Task.AssignedTaskGroup.AddShip(Task.ConstructRefitTarget,Task.CurrentShip.Name); Task.AssignedTaskGroup.Ships[Task.AssignedTaskGroup.Ships.Count - 1].TFTraining = Task.CurrentShip.TFTraining; Task.AssignedTaskGroup.Ships[Task.AssignedTaskGroup.Ships.Count - 1].ShipGrade = Task.CurrentShip.ShipGrade; CurrentPopulation.FuelStockpile = Task.AssignedTaskGroup.Ships[Task.AssignedTaskGroup.Ships.Count - 1].Refuel(CurrentPopulation.FuelStockpile); break; case Constants.ShipyardInfo.Task.Scrap: /// <summary> /// All non-destroyed components from the ship need to be put into the population stockpile. /// This further includes fuel, MSP, and ordnance as well as eventually officers and crew. /// </summary> #warning Handle officers and crew on ship scrapping. BindingList<ComponentDefTN> CompDefList = Task.CurrentShip.ShipClass.ListOfComponentDefs; BindingList<short> CompDefCount = Task.CurrentShip.ShipClass.ListOfComponentDefsCount; BindingList<ComponentTN> ShipCompList = Task.CurrentShip.ShipComponents; BindingList<ushort> ComponentDefIndex = Task.CurrentShip.ComponentDefIndex; int DefCount = Task.CurrentShip.ShipClass.ListOfComponentDefs.Count; for (int CompDefIndex = 0; CompDefIndex < DefCount; CompDefIndex++) { ComponentDefTN CurrentCompDef = CompDefList[CompDefIndex]; short CurrentCompCount = CompDefCount[CompDefIndex]; int destCount = 0; for (int CompIndex = 0; CompIndex < CurrentCompCount; CompIndex++) { if (ShipCompList[ComponentDefIndex[CompDefIndex] + CompIndex].isDestroyed == true) { destCount++; } } if (destCount != CurrentCompCount) { CurrentPopulation.AddComponentsToStockpile(CurrentCompDef, (float)(CurrentCompCount - destCount)); } } CurrentPopulation.FuelStockpile = CurrentPopulation.FuelStockpile + Task.CurrentShip.CurrentFuel; CurrentPopulation.MaintenanceSupplies = CurrentPopulation.MaintenanceSupplies + Task.CurrentShip.CurrentMSP; foreach (KeyValuePair<OrdnanceDefTN, int> OrdnancePair in Task.CurrentShip.ShipOrdnance) { CurrentPopulation.LoadMissileToStockpile(OrdnancePair.Key, (float)OrdnancePair.Value); } /// <summary> /// Finally destroy the ship. just use the existing code to remove the ship from the simulation, no point in reduplicating all of it. /// </summary> Task.CurrentShip.IsDestroyed = true; if (CurrentFaction.RechargeList.ContainsKey(Task.CurrentShip) == true) { if ((CurrentFaction.RechargeList[Task.CurrentShip] & (int)Faction.RechargeStatus.Destroyed) != (int)Faction.RechargeStatus.Destroyed) { CurrentFaction.RechargeList[Task.CurrentShip] = CurrentFaction.RechargeList[Task.CurrentShip] + (int)Faction.RechargeStatus.Destroyed; } } else { CurrentFaction.RechargeList.Add(Task.CurrentShip, (int)Faction.RechargeStatus.Destroyed); } break; } } else { /// <summary> /// Update the timer since this project won't finish just yet. /// </summary> decimal CostLeft = Task.Cost * (1.0m - Task.Progress); float YearsOfProduction = (float)CostLeft / Task.ABR; DateTime EstTime = GameState.Instance.GameDateTime; if (YearsOfProduction < Constants.Colony.TimerYearMax) { float DaysInYear = (float)Constants.TimeInSeconds.RealYear / (float)Constants.TimeInSeconds.Day; int TimeToBuild = (int)Math.Floor(YearsOfProduction * DaysInYear); TimeSpan TS = new TimeSpan(TimeToBuild, 0, 0, 0); EstTime = EstTime.Add(TS); } Task.CompletionDate = EstTime; } } /// <summary> /// Remove all the tasks that are now completed. /// </summary> foreach (Installation.ShipyardInformation.ShipyardTask Task in TasksToRemove) { /// <summary> /// Sanity check here. /// </summary> if (Task.Progress >= 1.0m) { Installation.ShipyardInformation SYI = CurrentPopulation.ShipyardTasks[Task]; SYI.BuildingShips.Remove(Task); CurrentPopulation.ShipyardTasks.Remove(Task); } } TasksToRemove.Clear(); }
/// <summary> /// The Fire control itself must determine if the target is in range of both itself and its weapons. /// </summary> /// <param name="DistanceToTarget">distance in KM to target.</param> /// <param name="RNG">RNG passed to this function from source further up the chain.</param> /// <param name="track">Base empire tracking or ship speed ,whichever is higher. Turrets should set track to their tracking speed.</param> /// <returns>Whether or not a weapon was able to fire.</returns> public bool FireWeapons(float DistanceToTarget, Random RNG, int track, ShipTN FiringShip) { if (DistanceToTarget > m_oBeamFireControlDef.range || (m_lLinkedWeapons.Count == 0 && m_lLinkedTurrets.Count == 0) || isDestroyed == true) { if (DistanceToTarget > m_oBeamFireControlDef.range) { MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringZeroHitChance, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, (this.Name + " Zero % chance to hit.")); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } return false; } else { /// <summary> /// Range * 2 / 10000.0; /// </summary> int RangeIncrement = (int)Math.Floor(DistanceToTarget / 5000.0f); float FireAccuracy = GetFiringAccuracy(RangeIncrement, track); /// <summary> /// Fire Accuracy is the likelyhood of a shot hitting from this FC at this point. /// ECM vs ECCM needs to be done around here as well. /// </summary> bool weaponFired = false; if (m_oTarget.targetType == StarSystemEntityType.TaskGroup) { int toHit = (int)Math.Floor(FireAccuracy * 100.0f); ushort Columns = m_oTarget.ship.ShipArmor.armorDef.cNum; foreach (BeamTN LinkedWeapon in m_lLinkedWeapons) { if (LinkedWeapon.beamDef.range > DistanceToTarget && LinkedWeapon.readyToFire() == true) { RangeIncrement = (int)Math.Floor(DistanceToTarget / 10000.0f); weaponFired = LinkedWeapon.Fire(); if (weaponFired == true) { for (int BeamShotIterator = 0; BeamShotIterator < LinkedWeapon.beamDef.shotCount; BeamShotIterator++) { int Hit = RNG.Next(1, 100); if (toHit >= Hit) { String WeaponFireS = String.Format("{0} hit {1} damage at {2}% tohit", LinkedWeapon.Name, LinkedWeapon.beamDef.damage[RangeIncrement], toHit); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringHit, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); ushort location = (ushort)RNG.Next(0, Columns); bool ShipDest = m_oTarget.ship.OnDamaged(LinkedWeapon.beamDef.damageType, LinkedWeapon.beamDef.damage[RangeIncrement], location, FiringShip); if (ShipDest == true) { m_oTarget = null; m_oOpenFire = false; return weaponFired; } } else { String WeaponFireS = String.Format("{0} missed at {1}% tohit", LinkedWeapon.Name, toHit); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringMissed, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } } } else if (LinkedWeapon.isDestroyed == false) { String WeaponFireS = String.Format("{0} Recharging {1}/{2} Power", LinkedWeapon.Name, LinkedWeapon.currentCapacitor, LinkedWeapon.beamDef.weaponCapacitor); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringRecharging, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } } } foreach (TurretTN LinkedTurret in m_lLinkedTurrets) { /// <summary> /// turrets have changed tracking and therefore tohit from regular beams. /// </summary> FireAccuracy = GetFiringAccuracy(RangeIncrement, LinkedTurret.turretDef.tracking); toHit = (int)Math.Floor(FireAccuracy * 100.0f); if (LinkedTurret.turretDef.baseBeamWeapon.range > DistanceToTarget && LinkedTurret.readyToFire() == true) { RangeIncrement = (int)Math.Floor(DistanceToTarget / 10000.0f); weaponFired = LinkedTurret.Fire(); if (weaponFired == true) { for (int TurretShotIterator = 0; TurretShotIterator < LinkedTurret.turretDef.totalShotCount; TurretShotIterator++) { int Hit = RNG.Next(1, 100); if (toHit >= Hit) { String WeaponFireS = String.Format("{0} hit {1} damage at {2}% tohit", LinkedTurret.Name, LinkedTurret.turretDef.baseBeamWeapon.damage[RangeIncrement], toHit); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringHit, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); ushort location = (ushort)RNG.Next(0, Columns); bool ShipDest = m_oTarget.ship.OnDamaged(LinkedTurret.turretDef.baseBeamWeapon.damageType, LinkedTurret.turretDef.baseBeamWeapon.damage[RangeIncrement], location, FiringShip); if (ShipDest == true) { m_oTarget = null; m_oOpenFire = false; return weaponFired; } } else { String WeaponFireS = String.Format("{0} missed at {1}% tohit", LinkedTurret.Name, toHit); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringMissed, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } } } else if (LinkedTurret.isDestroyed == false) { String WeaponFireS = String.Format("{0} Recharging {1}/{2} Power", LinkedTurret.Name, LinkedTurret.currentCapacitor, (LinkedTurret.turretDef.baseBeamWeapon.weaponCapacitor * LinkedTurret.turretDef.multiplier)); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringRecharging, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } } } return weaponFired; } else if (m_oTarget.targetType == StarSystemEntityType.Missile) { /// <summary> /// this is for beam targetting on missiles. /// </summary> int toHit = (int)Math.Floor(FireAccuracy * 100.0f); /// <summary> /// have all targeted missiles been destroyed. /// </summary> bool noMissilesLeft = false; /// <summary> /// For all weapons linked to this BFC /// </summary> foreach (BeamTN LinkedWeapon in m_lLinkedWeapons) { /// <summary> /// if range > distance and the weapon is ready to fire. /// </summary> if (LinkedWeapon.beamDef.range > DistanceToTarget && LinkedWeapon.readyToFire() == true) { RangeIncrement = (int)Math.Floor(DistanceToTarget / 10000.0f); weaponFired = LinkedWeapon.Fire(); if (weaponFired == true) { /// <summary> /// Some weapons have multiple shots, but most will have just 1. /// </summary> for (int BeamShotIterator = 0; BeamShotIterator < LinkedWeapon.beamDef.shotCount; BeamShotIterator++) { int Hit = RNG.Next(1, 100); /// <summary> /// Did the weapon hit? /// </summary> if (toHit >= Hit) { ushort ToDestroy; if (m_oTarget.missileGroup.missiles[0].missileDef.armor == 0) ToDestroy = 100; else ToDestroy = (ushort)(Math.Round((LinkedWeapon.beamDef.damage[RangeIncrement] / (m_oTarget.missileGroup.missiles[0].missileDef.armor + LinkedWeapon.beamDef.damage[RangeIncrement]))) * 100.0f); ushort DestChance = (ushort)RNG.Next(1, 100); /// <summary> /// Does the weapon have the power to make a kill? /// </summary> if (ToDestroy >= DestChance) { String WeaponFireS = String.Format("{0} and destroyed a missile at {1}% tohit", LinkedWeapon.Name, toHit); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringHit, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); /// <summary> /// Set destruction of targeted missile here. This is used by sim entity to determine how to handle missile group cleanup. /// </summary> m_oTarget.missileGroup.missilesDestroyed = m_oTarget.missileGroup.missilesDestroyed + 1; if (m_oTarget.missileGroup.missilesDestroyed == m_oTarget.missileGroup.missiles.Count) { break; } } else { String WeaponFireS = String.Format("{0} and failed to destroyed a missile at {1}% tohit", LinkedWeapon.Name, toHit); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringHit, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } } else { String WeaponFireS = String.Format("{0} missed at {1}% tohit", LinkedWeapon.Name, toHit); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringMissed, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } }//end for shot count } else if (LinkedWeapon.isDestroyed == false) { String WeaponFireS = String.Format("{0} Recharging {1}/{2} Power", LinkedWeapon.Name, LinkedWeapon.currentCapacitor, LinkedWeapon.beamDef.weaponCapacitor); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringRecharging, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } }//end if in range and weapon can fire if (m_oTarget.missileGroup.missilesDestroyed == m_oTarget.missileGroup.missiles.Count) { noMissilesLeft = true; break; } }// end for linked weapons if (noMissilesLeft == false) { foreach (TurretTN LinkedTurret in m_lLinkedTurrets) //(int loop = 0; loop < LinkedTurrets.Count; loop++) { FireAccuracy = GetFiringAccuracy(RangeIncrement, LinkedTurret.turretDef.tracking); toHit = (int)Math.Floor(FireAccuracy * 100.0f); if (LinkedTurret.turretDef.baseBeamWeapon.range > DistanceToTarget && LinkedTurret.readyToFire() == true) { RangeIncrement = (int)Math.Floor(DistanceToTarget / 10000.0f); weaponFired = LinkedTurret.Fire(); if (weaponFired == true) { /// <summary> /// Some weapons have multiple shots, but most will have just 1. /// </summary> for (int TurretShotIterator = 0; TurretShotIterator < LinkedTurret.turretDef.totalShotCount; TurretShotIterator++) { int Hit = RNG.Next(1, 100); /// <summary> /// Did the weapon hit? /// </summary> if (toHit >= Hit) { /// <summary> /// Did the weapon destroy its target? /// </summary> ushort ToDestroy; if (m_oTarget.missileGroup.missiles[0].missileDef.armor == 0) ToDestroy = 100; else ToDestroy = (ushort)(Math.Round((LinkedTurret.turretDef.baseBeamWeapon.damage[RangeIncrement] / (m_oTarget.missileGroup.missiles[0].missileDef.armor + LinkedTurret.turretDef.baseBeamWeapon.damage[RangeIncrement]))) * 100.0f); ushort DestChance = (ushort)RNG.Next(1, 100); /// <summary> /// Does the weapon have the power to make a kill? /// </summary> if (ToDestroy >= DestChance) { String WeaponFireS = String.Format("{0} and destroyed a missile at {1}% tohit", LinkedTurret.Name, toHit); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringHit, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); /// <summary> /// Set destruction of targeted missile here. This is used by sim entity to determine how to handle missile group cleanup. /// </summary> m_oTarget.missileGroup.missilesDestroyed = m_oTarget.missileGroup.missilesDestroyed + 1; if (m_oTarget.missileGroup.missilesDestroyed == m_oTarget.missileGroup.missiles.Count) { break; } } else { String WeaponFireS = String.Format("{0} and failed to destroyed a missile at {1}% tohit", LinkedTurret.Name, toHit); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringHit, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } } else { String WeaponFireS = String.Format("{0} missed at {1}% tohit", LinkedTurret.Name, toHit); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringMissed, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } } }//end if weapon fired }//end if in range and can fire if (m_oTarget.missileGroup.missilesDestroyed == m_oTarget.missileGroup.missiles.Count) { /// <summary> /// This is cargo culting and this variable does not need to be set here, but for completeness sake I'll include it. /// </summary> noMissilesLeft = true; break; } }//end for linkedturrets. }//end if noMissilesLeft = true return weaponFired; } else if(m_oTarget.targetType == StarSystemEntityType.Population) { /// <summary> /// Planets can't dodge and will always be hit. /// </summary> foreach (BeamTN LinkedWeapon in m_lLinkedWeapons) { if (LinkedWeapon.beamDef.range > DistanceToTarget && LinkedWeapon.readyToFire() == true) { RangeIncrement = (int)Math.Floor(DistanceToTarget / 10000.0f); weaponFired = LinkedWeapon.Fire(); if (weaponFired == true) { for (int BeamShotIterator = 0; BeamShotIterator < LinkedWeapon.beamDef.shotCount; BeamShotIterator++) { bool PopDamaged = m_oTarget.pop.OnDamaged(LinkedWeapon.beamDef.damageType, LinkedWeapon.beamDef.damage[RangeIncrement], FiringShip); } } } else if (LinkedWeapon.isDestroyed == false) { String WeaponFireS = String.Format("{0} Recharging {1}/{2} Power", LinkedWeapon.Name, LinkedWeapon.currentCapacitor, LinkedWeapon.beamDef.weaponCapacitor); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringRecharging, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } } foreach (TurretTN LinkedTurret in m_lLinkedTurrets) { if (LinkedTurret.turretDef.baseBeamWeapon.range > DistanceToTarget && LinkedTurret.readyToFire() == true) { RangeIncrement = (int)Math.Floor(DistanceToTarget / 10000.0f); weaponFired = LinkedTurret.Fire(); if (weaponFired == true) { for (int TurretShotIterator = 0; TurretShotIterator < LinkedTurret.turretDef.totalShotCount; TurretShotIterator++) { bool ShipDest = m_oTarget.pop.OnDamaged(LinkedTurret.turretDef.baseBeamWeapon.damageType, LinkedTurret.turretDef.baseBeamWeapon.damage[RangeIncrement], FiringShip); } } else if (LinkedTurret.isDestroyed == false) { String WeaponFireS = String.Format("{0} Recharging {1}/{2} Power", LinkedTurret.Name, LinkedTurret.currentCapacitor, (LinkedTurret.turretDef.baseBeamWeapon.weaponCapacitor * LinkedTurret.turretDef.multiplier)); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.FiringRecharging, FiringShip.ShipsTaskGroup.Contact.Position.System, FiringShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, WeaponFireS); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } } } return weaponFired; } /// <summary> /// If I am targetting something that BFCs can't handle yet or shouldn't be able to handle just return false for now. /// </summary> return false; } }
/// <summary> /// Processes construction factory work for every faction. /// </summary> /// <param name="P">List of factions.</param> public static void ConstructionFactoryBuild(Faction CurrentFaction, Population CurrentPopulation) { /// <summary> /// How much construction work per day does this colony do? default should be 5 day construction cycle. /// </summary> float CurrentIndustry = CurrentPopulation.CalcTotalIndustry() * Constants.Colony.ConstructionCycleFraction; float BuildPercentage = 0.0f; foreach (ConstructionBuildQueueItem CurrentConstruction in CurrentPopulation.ConstructionBuildQueue) { /// <summary> /// Check to see if this item is in the current build queue, or has to wait for capacity to free up. /// </summary> if ((BuildPercentage + CurrentConstruction.buildCapacity) <= 100.0f) { BuildPercentage = BuildPercentage + CurrentConstruction.buildCapacity; } else { break; } /// <summary> /// Calculate Completion Estimate: /// </summary> float BPRequirement = (float)Math.Floor(CurrentConstruction.numToBuild) * (float)CurrentConstruction.costPerItem; float DaysInYear = (float)Constants.TimeInSeconds.RealYear / (float)Constants.TimeInSeconds.Day; float YearsOfProduction = (BPRequirement / CurrentConstruction.buildCapacity); if (CurrentConstruction.buildCapacity != 0.0f && YearsOfProduction < Constants.Colony.TimerYearMax) { int TimeToBuild = (int)Math.Floor(YearsOfProduction * DaysInYear); DateTime EstTime = GameState.Instance.GameDateTime; TimeSpan TS = new TimeSpan(TimeToBuild, 0, 0, 0); EstTime = EstTime.Add(TS); CurrentConstruction.completionDate = EstTime; } /// <summary> /// This construction project is paused right now. /// </summary> if (CurrentConstruction.inProduction == false) { continue; } /// <summary> /// how much of total industry does this build project use? /// </summary> float DevotedIndustry = (CurrentConstruction.buildCapacity / 100.0f) * CurrentIndustry; float Completion = DevotedIndustry / (float)CurrentConstruction.costPerItem; bool CIRequired = false; bool MineRequired = false; bool CanBuild = false; /// <summary> /// Conventional industry must also be used. /// </summary> if (CurrentConstruction.buildType == ConstructionBuildQueueItem.CBType.PlanetaryInstallation) { if ((int)CurrentConstruction.installationBuild.Type >= (int)Installation.InstallationType.ConvertCIToConstructionFactory && (int)CurrentConstruction.installationBuild.Type <= (int)Installation.InstallationType.ConvertCIToOrdnanceFactory) { CIRequired = true; CanBuild = CurrentPopulation.CIRequirement(Completion); if (CanBuild == false) { String Entry = String.Format("Insufficent Conventional Industry to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksCI, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } } if ((int)CurrentConstruction.installationBuild.Type == (int)Installation.InstallationType.ConvertMineToAutomated) { MineRequired = true; CanBuild = CurrentPopulation.MineRequirement(Completion); if (CanBuild == false) { String Entry = String.Format("Insufficent Mines to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksCI, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } } } /// <summary> /// Check mineral costs to see if this can be built. /// </summary> switch (CurrentConstruction.buildType) { #warning PDC construction needs to be implemented here and slightly further down. case ConstructionBuildQueueItem.CBType.PlanetaryInstallation: CanBuild = CurrentPopulation.MineralRequirement(CurrentConstruction.installationBuild.MinerialsCost, Completion); break; case ConstructionBuildQueueItem.CBType.ShipComponent: CanBuild = CurrentPopulation.MineralRequirement(CurrentConstruction.componentBuild.minerialsCost, Completion); break; case ConstructionBuildQueueItem.CBType.PDCConstruction: break; case ConstructionBuildQueueItem.CBType.PDCPrefab: break; case ConstructionBuildQueueItem.CBType.PDCAssembly: break; case ConstructionBuildQueueItem.CBType.PDCRefit: break; case ConstructionBuildQueueItem.CBType.MaintenanceSupplies: CanBuild = CurrentPopulation.MineralRequirement(Constants.Colony.MaintenanceMineralCost, Completion); break; } if (CanBuild == false) { String Entry = String.Format("Insufficent Minerals to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } /// <summary> /// Adjust number to build downward to reflect construction happening. /// </summary> CurrentConstruction.numToBuild = CurrentConstruction.numToBuild - Completion; /// <summary> /// Handle the cost and build the item. /// </summary> switch (CurrentConstruction.buildType) { case ConstructionBuildQueueItem.CBType.PlanetaryInstallation: if (CIRequired == false && MineRequired == false) CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, CurrentConstruction.installationBuild.MinerialsCost, Completion); else if (CIRequired == true && MineRequired == false) CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, CurrentConstruction.installationBuild.MinerialsCost, Completion, CIRequired, MineRequired); else if (CIRequired == false && MineRequired == true) CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, CurrentConstruction.installationBuild.MinerialsCost, Completion, CIRequired, MineRequired); CurrentPopulation.AddInstallation(CurrentConstruction.installationBuild, Completion); break; case ConstructionBuildQueueItem.CBType.ShipComponent: CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, CurrentConstruction.componentBuild.minerialsCost, Completion); CurrentPopulation.AddComponentsToStockpile(CurrentConstruction.componentBuild, Completion); break; case ConstructionBuildQueueItem.CBType.PDCConstruction: break; case ConstructionBuildQueueItem.CBType.PDCPrefab: break; case ConstructionBuildQueueItem.CBType.PDCAssembly: break; case ConstructionBuildQueueItem.CBType.PDCRefit: break; case ConstructionBuildQueueItem.CBType.MaintenanceSupplies: CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, Constants.Colony.MaintenanceMineralCost, Completion); CurrentPopulation.AddMSP(Completion); break; } } /// <summary> /// Cleanup the CBQ here. /// </summary> for (int CBQIterator = 0; CBQIterator < CurrentPopulation.ConstructionBuildQueue.Count; CBQIterator++) { if (CurrentPopulation.ConstructionBuildQueue[CBQIterator].numToBuild <= 0.0f) { CurrentPopulation.ConstructionBuildQueue.RemoveAt(CBQIterator); CBQIterator--; } } }
/// <summary> /// Do all fuel refining on planets and later on gas giant harvesters. /// </summary> /// <param name="P">list of factions.</param> public static void RefineFuel(Faction CurrentFaction, Population CurrentPopulation) { /// <summary> /// Skip this population. /// </summary> if (CurrentPopulation.IsRefining == false) return; float CurrentRefining = CurrentPopulation.CalcTotalRefining() * Constants.Colony.ConstructionCycleFraction; /// <summary> /// If the planet has no refineries or no sorium then no refining happens. /// </summary> if (CurrentRefining > 0.0f && CurrentPopulation.Minerials[(int)Constants.Minerals.MinerialNames.Sorium] > 0.0f) { /// <summary> /// 1 sorium = 2000 fuel /// </summary> float SoriumRequirement = CurrentRefining / Constants.Colony.SoriumToFuel; if (CurrentPopulation.Minerials[(int)Constants.Minerals.MinerialNames.Sorium] < SoriumRequirement) { SoriumRequirement = CurrentPopulation.Minerials[(int)Constants.Minerals.MinerialNames.Sorium]; CurrentRefining = SoriumRequirement * Constants.Colony.SoriumToFuel; } /// <summary> /// Convert Sorium into fuel. /// </summary> CurrentPopulation.Minerials[(int)Constants.Minerals.MinerialNames.Sorium] = CurrentPopulation.Minerials[(int)Constants.Minerals.MinerialNames.Sorium] - SoriumRequirement; CurrentPopulation.FuelStockpile = CurrentPopulation.FuelStockpile + CurrentRefining; } else if (CurrentRefining > 0.0f) { String Entry = String.Format("Insufficient Sorium on {0} to continue refining.", CurrentPopulation); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); } }
/// <summary> /// Handles ordnance factory construction. /// </summary> /// <param name="P">List of factions.</param> public static void OrdnanceFactoryBuild(Faction CurrentFaction, Population CurrentPopulation) { /// <summary> /// How much construction work per day does this colony do? default should be 5 day construction cycle. /// </summary> float CurrentIndustry = CurrentPopulation.CalcTotalOrdnanceIndustry() * Constants.Colony.ConstructionCycleFraction; float BuildPercentage = 0.0f; foreach (MissileBuildQueueItem CurrentConstruction in CurrentPopulation.MissileBuildQueue) { /// <summary> /// Check to see if this item is in the current build queue, or has to wait for capacity to free up. /// </summary> if ((BuildPercentage + CurrentConstruction.buildCapacity) <= 100.0f) { BuildPercentage = BuildPercentage + CurrentConstruction.buildCapacity; } else { break; } /// <summary> /// Calculate Completion Estimate: /// </summary> float BPRequirement = (float)Math.Floor(CurrentConstruction.numToBuild) * (float)CurrentConstruction.costPerItem; float DaysInYear = (float)Constants.TimeInSeconds.RealYear / (float)Constants.TimeInSeconds.Day; float YearsOfProduction = (BPRequirement / CurrentConstruction.buildCapacity); if (CurrentConstruction.buildCapacity != 0.0f && YearsOfProduction < Constants.Colony.TimerYearMax) { int TimeToBuild = (int)Math.Floor(YearsOfProduction * DaysInYear); DateTime EstTime = GameState.Instance.GameDateTime; TimeSpan TS = new TimeSpan(TimeToBuild, 0, 0, 0); EstTime = EstTime.Add(TS); CurrentConstruction.completionDate = EstTime; } /// <summary> /// This construction project is paused right now. /// </summary> if (CurrentConstruction.inProduction == false) { continue; } /// <summary> /// how much of total industry does this build project use? /// </summary> float DevotedIndustry = (CurrentConstruction.buildCapacity / 100.0f) * CurrentIndustry; float Completion = DevotedIndustry / (float)CurrentConstruction.costPerItem; /// <summary> /// Do I have the minerals to build this missile? /// </summary> bool CanBuild = CurrentPopulation.MineralRequirement(CurrentConstruction.ordnanceDef.minerialsCost, Completion); if (CanBuild == false) { String Entry = String.Format("Insufficent Minerals to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } /// <summary> /// Adjust number to build downward to reflect construction happening. /// </summary> CurrentConstruction.numToBuild = CurrentConstruction.numToBuild - Completion; CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, CurrentConstruction.ordnanceDef.minerialsCost, Completion); CurrentPopulation.LoadMissileToStockpile(CurrentConstruction.ordnanceDef, Completion); } /// <summary> /// Cleanup the CBQ here. /// </summary> for (int MBQIterator = 0; MBQIterator < CurrentPopulation.MissileBuildQueue.Count; MBQIterator++) { if (CurrentPopulation.MissileBuildQueue[MBQIterator].numToBuild <= 0.0f) { CurrentPopulation.MissileBuildQueue.RemoveAt(MBQIterator); MBQIterator--; } } }
/// <summary> /// Each tick every faction contact should be updated on the basis of collected sensor data. /// </summary> /// <param name="Thermal">Thermal detection?</param> /// <param name="Em">Detected on EM?</param> /// <param name="Active">Detected by actives?</param> /// <param name="tick">Current tick.</param> public void updateFactionContact(Faction CurrentFaction, bool Thermal, bool Em, int EMSig, bool Active, uint tick) { if (thermal == Thermal && EM == Em && active == Active) { return; } String Contact = "N/A"; MessageEntry.MessageType type = MessageEntry.MessageType.Count; if (Thermal == false && Em == false && Active == false) { Contact = "Existing contact lost"; type = MessageEntry.MessageType.ContactLost; } else { Contact = "Update on existing contact:"; type = MessageEntry.MessageType.ContactUpdate; if (thermal == false && Thermal == true) { /// <summary> /// New thermal detection event, message logic should be here. /// </summary> thermalTick = tick; if (ship != null) Contact = String.Format("{0} Thermal Signature {1}", Contact, ship.CurrentThermalSignature); else if (missileGroup != null) Contact = String.Format("{0} Thermal Signature {1} x{2}", Contact, (int)Math.Ceiling(missileGroup.missiles[0].missileDef.totalThermalSignature), missileGroup.missiles.Count); else if (pop != null) Contact = String.Format("{0} Thermal Signature {1}", Contact, pop.ThermalSignature); else { type = MessageEntry.MessageType.Error; Contact = "Error: ship,missile, and pop are null in UpdateFactionContact."; } } else if (thermal == true && Thermal == false) { /// <summary> /// Thermal contact lost. /// </summary> Contact = String.Format("{0} Thermal contact lost", Contact); } if (EM == false && Em == true) { /// <summary> /// New EM detection event, message logic should be here. /// </summary> EMTick = tick; EMSignature = EMSig; if (ship != null) Contact = String.Format("{0} EM Signature {1}", Contact, EMSignature); else if (missileGroup != null) { if (missileGroup.missiles[0].missileDef.aSD != null) { Contact = String.Format("{0} EM Signature {1} x{2}", Contact, missileGroup.missiles[0].missileDef.aSD.gps, missileGroup.missiles.Count); } else { type = MessageEntry.MessageType.Error; Contact = "Error: Missile lacks a sensor, but is emitting EM in UpdateFactionContact."; } } else if (pop != null) Contact = String.Format("{0} EM Signature {1}", Contact, pop.EMSignature); else { type = MessageEntry.MessageType.Error; Contact = "Error: ship,missile, and pop are null in UpdateFactionContact."; } } if (EM == true && Em == false) { EMSignature = -1; /// <summary> /// EM contact lost. /// </summary> Contact = String.Format("{0} EM contact lost", Contact); } if (active == false && Active == true) { /// <summary> /// New active detection event, message logic should be here. /// </summary> activeTick = tick; if (ship != null) Contact = String.Format("{0} TCS {1}", Contact, ship.TotalCrossSection); else if (missileGroup != null) Contact = String.Format("{0} TCS_MSP {1} x{2}", Contact, (int)Math.Ceiling(missileGroup.missiles[0].missileDef.size), missileGroup.missiles.Count); else if(pop != null) Contact = String.Format("{0} Active Ping", Contact); //don't bother printing TCS for planets. else { type = MessageEntry.MessageType.Error; Contact = "Error: ship,missile, and pop are null in UpdateFactionContact."; } } if (active == true && Active == false) { /// <summary> /// Active contact lost. /// </summary> Contact = String.Format("{0} Active contact lost", Contact); } } SystemContact SysCon = null; if (ship != null) SysCon = ship.ShipsTaskGroup.Contact; else if (missileGroup != null) SysCon = missileGroup.contact; else if (pop != null) SysCon = pop.Contact; else { type = MessageEntry.MessageType.Error; Contact = "Error: ship,missile, and pop are null in UpdateFactionContact."; } MessageEntry NMsg; if (type == MessageEntry.MessageType.Error) NMsg = new MessageEntry(type, null, null, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Contact); else NMsg = new MessageEntry(type, SysCon.Position.System, SysCon, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Contact); CurrentFaction.MessageLog.Add(NMsg); thermal = Thermal; EM = Em; active = Active; }
/// <summary> /// Populations can be detected as well, handle that constructor here. /// </summary> /// <param name="CurrentFaction">Faction that has detected this contact</param> /// <param name="DetectedPopulation">Population detected by faction.</param> /// <param name="Thermal">Whether detection was by thermal characteristics</param> /// <param name="em">Was it detected via em?</param> /// <param name="Active">was this population detected via actives?</param> /// <param name="tick">Tick this happened on.</param> public FactionContact(Faction CurrentFaction, Population DetectedPopulation, bool Thermal, bool em, bool Active, uint tick) { pop = DetectedPopulation; ship = null; missileGroup = null; thermal = Thermal; EM = em; EMSignature = pop.EMSignature; active = Active; String Contact = "New contact detected:"; if (thermal == true) { thermalTick = tick; Contact = String.Format("{0} Thermal Signature {1}", Contact, pop.ThermalSignature); } if (EM == true) { EMTick = tick; Contact = String.Format("{0} EM Signature {1}", Contact, EMSignature); } if (active == true) { activeTick = tick; Contact = String.Format("{0} Active Ping", Contact); } /// <summary> /// Print to the message log. /// </summary> MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ContactNew,pop.Position.System, pop.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Contact); CurrentFaction.MessageLog.Add(NMsg); /// <summary> /// Inform SimEntity. /// </summary> GameState.SE.SetInterrupt(InterruptType.NewSensorContact); }
/// <summary> /// When a new empire/faction is selected this will be run /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void EmpireComboBox_SelectedIndexChanged(object sender, EventArgs e) { if (_CurrnetFaction.ComponentList.MissileEngineDef.Count != 0) { _CurrnetMissileEngine = _CurrnetFaction.ComponentList.MissileEngineDef[0]; } else { m_oMissileDesignPanel.TotalEngineCostTextBox.Text = ""; m_oMissileDesignPanel.TotalEngineSizeTextBox.Text = ""; m_oMissileDesignPanel.TotalEPTextBox.Text = ""; m_oMissileDesignPanel.MissileEngineComboBox.Text = ""; _CurrnetMissileEngine = null; } if (_CurrnetFaction.OrdnanceSeries.Count != 0) { _CurrnetMissileSeries = _CurrnetFaction.OrdnanceSeries[0]; m_oMissileDesignPanel.MSeriesComboBox.SelectedIndex = 0; } else { String Error = String.Format("Faction {0} somehow has no default missile series \"No Series Selected\".", _CurrnetFaction.Name); MessageEntry MessageEntry = new MessageEntry(MessageEntry.MessageType.Error, null, null, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Error); _CurrnetFaction.MessageLog.Add(MessageEntry); _CurrnetMissileSeries = null; } if (_CurrnetFaction.ComponentList.MissileDef.Count == 0) { m_oMissileDesignPanel.WHMSPTextBox.Text = "0"; m_oMissileDesignPanel.FuelMSPTextBox.Text = "0"; m_oMissileDesignPanel.AgilityMSPTextBox.Text = "0"; m_oMissileDesignPanel.ActiveMSPTextBox.Text = "0"; m_oMissileDesignPanel.ThermalMSPTextBox.Text = "0"; m_oMissileDesignPanel.EMMSPTextBox.Text = "0"; m_oMissileDesignPanel.GeoMSPTextBox.Text = "0"; m_oMissileDesignPanel.ResolutionTextBox.Text = "1"; m_oMissileDesignPanel.ArmourMSPTextBox.Text = "0"; m_oMissileDesignPanel.ECMMSPTextBox.Text = "0"; m_oMissileDesignPanel.LaserWCheckBox.Checked = false; m_oMissileDesignPanel.ERCheckBox.Checked = false; m_oMissileDesignPanel.NumEnginesTextBox.Text = "1"; m_oMissileDesignPanel.SubNumberTextBox.Text = "0"; m_oMissileDesignPanel.SepRangeTextBox.Text = "150"; m_oMissileDesignPanel.SubSizeTextBox.Text = "0"; m_oMissileDesignPanel.SubCostTextBox.Text = "0"; m_oMissileDesignPanel.SubTotalSizeTextBox.Text = "0"; m_oMissileDesignPanel.SubTotalCostTextBox.Text = "0"; SubMunition = null; m_oMissileDesignPanel.PreviousOrdnanceComboBox.Text = ""; } BuildSubMunitionComboBox(); BuildMissileDesignPage(); }
/// <summary> /// Recharges the ships shields, if they are active. /// </summary> /// <param name="tick">Tick is the value in seconds the sim is being advanced by. 1 day = 86400 seconds. smallest practical value is 5.</param> public void RechargeShields(uint tick) { if (ShieldIsActive == true && CurrentShieldPool != CurrentShieldPoolMax) { String Charge = "Shield Recharge: "; float amt = (float)tick / 5.0f; float ShieldRecharge = CurrentShieldGen * amt; if (CurrentShieldPool + ShieldRecharge >= CurrentShieldPoolMax) { ShieldRecharge = CurrentShieldPoolMax - CurrentShieldPool; CurrentShieldPool = CurrentShieldPoolMax; } else { CurrentShieldPool = CurrentShieldPool + ShieldRecharge; } Charge = String.Format("{0} {1} points", Charge, ShieldRecharge); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShieldRecharge, this.ShipsTaskGroup.Contact.Position.System, this.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Charge); ShipsFaction.MessageLog.Add(NMsg); } }
public bool OnDamaged(DamageTypeTN TypeOfDamage, ushort Value, ShipTN FiringShip, int RadLevel = 0) { /// <summary> /// Check damage type to see if atmosphere blocks it. /// Populations are damaged in several ways. /// Civilian Population will die off, about 50k per point of damage. /// Atmospheric dust will be kicked into the atmosphere(regardless of whether or not there is an atmosphere) lowering temperature for a while. /// Some beam weapons are blocked(partially or in whole) by atmosphere(Lasers,Gauss,Railguns,particle beams, plasma), some are not(Mesons), and some have no effect(microwaves) on populations. /// PDCs will be similarly defended by this atmospheric blocking but are vulnerable to microwaves. /// Missiles will increase the radiation value of the colony. Enhanced Radiation warheads will add more for less overall damage done. Radiation is of course harmful to life on the world. but /// not immediately so. /// Installations will have a chance at being destroyed. bigger installations should be more resilient to damage, but even infrastructure should have a chance to survive. /// Shipyards must be targetted in orbit around the colony. Special handling will be required for that. /// </summary> ushort ActualDamage; switch (TypeOfDamage) { /// <summary> /// Neither missile nor meson damage is effected by atmospheric pressure. /// </summary> case DamageTypeTN.Missile: case DamageTypeTN.Meson: ActualDamage = Value; break; /// <summary> /// All other damage types must be adjusted by atmospheric pressure. /// </summary> default: ActualDamage = (ushort)Math.Round((float)Value * Planet.Atmosphere.Pressure); break; } /// <summary> /// No damage was done. either all damage was absorbed by the atmosphere or the missile had no warhead. Missiles with no warhead should "probably" be sensor missiles that loiter in orbit /// until their fuel is gone. /// </summary> if (ActualDamage == 0) { return false; } /// <summary> /// Each point of damage kills off 50,000 people, or 0.05f as 1.0f = 1M people. /// </summary> float PopulationDamage = 0.05f * ActualDamage; CivilianPopulation = CivilianPopulation - PopulationDamage; /// <summary> /// Increase the atmospheric dust and radiation of the planet. /// </summary> Planet.AtmosphericDust = Planet.AtmosphericDust + ActualDamage; Planet.RadiationLevel = Planet.RadiationLevel + RadLevel; if (GameState.Instance.DamagedPlanets.Contains(Planet) == false) GameState.Instance.DamagedPlanets.Add(Planet); String IndustrialDamage = "Industrial Damage:"; while (ActualDamage > 0) { ActualDamage = (ushort)(ActualDamage - 5); /// <summary> /// Installation destruction will be naive. pick an installation at random. /// </summary> int Inst = GameState.RNG.Next(0, (int)Installation.InstallationType.InstallationCount); if (Inst == (int)Installation.InstallationType.CommercialShipyard || Inst == (int)Installation.InstallationType.NavalShipyardComplex) { /// <summary> /// Damage was done, but installations escaped unharmed. Shipyards must be damaged from orbit. /// </summary> continue; } else if (Inst >= (int)Installation.InstallationType.ConvertCIToConstructionFactory && Inst <= (int)Installation.InstallationType.ConvertMineToAutomated) { /// <summary> /// These "installations" can't be damaged, so again, lucky planet. /// </summary> continue; } else { int InstCount = (int)Math.Floor(Installations[Inst].Number); /// <summary> /// Luckily for the planet it had none of the installations that just got targetted. /// </summary> if (InstCount == 0) { continue; } switch ((Installation.InstallationType)Inst) { case Installation.InstallationType.DeepSpaceTrackingStation: /// <summary> /// A DSTS was destroyed at this population, inform the UI to update the display. /// </summary> _SensorUpdateAck++; break; } /// <summary> /// Installation destroyed. /// </summary> Installations[Inst].Number = Installations[Inst].Number - 1.0f; #warning Industry damage should be reworked to have differing resilience ratings, and logging should compress industrial damage. IndustrialDamage = String.Format("{0} {1}: {2}", IndustrialDamage, Installations[Inst].Name, 1); } } String Entry = String.Format("{0} hit by {1} points of damage. Casualties: {2}{3}Environment Update: Atmospheric Dust:{4}, Radiation:{5}", Name,ActualDamage,PopulationDamage, IndustrialDamage,Planet.AtmosphericDust, Planet.RadiationLevel); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.PopulationDamage, Planet.Position.System, Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); Faction.MessageLog.Add(NMsg); return false; }
/// <summary> /// Handles ordnance factory construction. /// </summary> /// <param name="P">List of factions.</param> public static void OrdnanceFactoryBuild(BindingList <Faction> P) { /// <summary> /// Subtract the construction cycle from the construction tick. /// </summary> foreach (Faction CurrentFaction in P) { foreach (Population CurrentPopulation in CurrentFaction.Populations) { /// <summary> /// How much construction work per day does this colony do? default should be 5 day construction cycle. /// </summary> float CurrentIndustry = CurrentPopulation.CalcTotalOrdnanceIndustry() * Constants.Colony.ConstructionCycleFraction; float BuildPercentage = 0.0f; foreach (MissileBuildQueueItem CurrentConstruction in CurrentPopulation.MissileBuildQueue) { /// <summary> /// Check to see if this item is in the current build queue, or has to wait for capacity to free up. /// </summary> if ((BuildPercentage + CurrentConstruction.buildCapacity) <= 100.0f) { BuildPercentage = BuildPercentage + CurrentConstruction.buildCapacity; } else { break; } /// <summary> /// Calculate Completion Estimate: /// </summary> float BPRequirement = (float)Math.Floor(CurrentConstruction.numToBuild) * (float)CurrentConstruction.costPerItem; float DaysInYear = (float)Constants.TimeInSeconds.RealYear / (float)Constants.TimeInSeconds.Day; float YearsOfProduction = (BPRequirement / CurrentConstruction.buildCapacity); if (CurrentConstruction.buildCapacity != 0.0f && YearsOfProduction < Constants.Colony.TimerYearMax) { int TimeToBuild = (int)Math.Floor(YearsOfProduction * DaysInYear); DateTime EstTime = GameState.Instance.GameDateTime; TimeSpan TS = new TimeSpan(TimeToBuild, 0, 0, 0); EstTime = EstTime.Add(TS); CurrentConstruction.completionDate = EstTime; } /// <summary> /// This construction project is paused right now. /// </summary> if (CurrentConstruction.inProduction == false) { continue; } /// <summary> /// how much of total industry does this build project use? /// </summary> float DevotedIndustry = (CurrentConstruction.buildCapacity / 100.0f) * CurrentIndustry; float Completion = DevotedIndustry / (float)CurrentConstruction.costPerItem; /// <summary> /// Do I have the minerals to build this missile? /// </summary> bool CanBuild = CurrentPopulation.MineralRequirement(CurrentConstruction.ordnanceDef.minerialsCost, Completion); if (CanBuild == false) { String Entry = String.Format("Insufficent Minerals to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } /// <summary> /// Adjust number to build downward to reflect construction happening. /// </summary> CurrentConstruction.numToBuild = CurrentConstruction.numToBuild - Completion; CurrentPopulation.HandleBuildItemCost(CurrentConstruction.costPerItem, CurrentConstruction.ordnanceDef.minerialsCost, Completion); CurrentPopulation.LoadMissileToStockpile(CurrentConstruction.ordnanceDef, Completion); } /// <summary> /// Cleanup the CBQ here. /// </summary> for (int MBQIterator = 0; MBQIterator < CurrentPopulation.MissileBuildQueue.Count; MBQIterator++) { if (CurrentPopulation.MissileBuildQueue[MBQIterator].numToBuild <= 0.0f) { CurrentPopulation.MissileBuildQueue.RemoveAt(MBQIterator); MBQIterator--; } } } } }
/// <summary> /// TaskGroup order handling function /// </summary> /// <param name="TimeSlice">How much time the taskgroup is alloted to perform its orders. unit is in seconds</param> public void FollowOrders(uint TimeSlice) { //if (NewOrders == true) //{ GetHeading(); GetSpeed(); GetTimeRequirement(); //NewOrders = false; //} if (TimeRequirement < TimeSlice) { /// <summary> /// Is the movement phase over? /// </summary> if (TimeRequirement != 0) { /// <summary> /// increase the taskgroup's fuel use counter. /// </summary> UseFuel(TimeRequirement); /// <summary> /// Move the taskgroup to the targeted location. /// </summary> double dX, dY; if (TaskGroupOrders[0].target.SSEntity == StarSystemEntityType.Body) { dX = TaskGroupOrders[0].target.Position.X; dY = TaskGroupOrders[0].target.Position.Y; } else if (TaskGroupOrders[0].target.SSEntity == StarSystemEntityType.Population) { dX = TaskGroupOrders[0].pop.Planet.Position.X; dY = TaskGroupOrders[0].pop.Planet.Position.Y; } else { dX = TaskGroupOrders[0].target.Position.X; dY = TaskGroupOrders[0].target.Position.Y; } Contact.UpdateLocationInSystem(dX, dY); TotalOrderDistance = TotalOrderDistance - (double)(CurrentSpeed * TimeRequirement); /// <summary> /// Time requirement is the movement portion of the orders. subtract it here. /// </summary> TimeSlice = TimeSlice - TimeRequirement; TimeRequirement = 0; /// <summary> /// Did we pull into orbit? /// </summary> if (TaskGroupOrders[0].target.SSEntity == StarSystemEntityType.Body || TaskGroupOrders[0].target.SSEntity == StarSystemEntityType.Population) { IsOrbiting = true; if (TaskGroupOrders[0].target.SSEntity == StarSystemEntityType.Body) OrbitingBody = TaskGroupOrders[0].body; else OrbitingBody = TaskGroupOrders[0].pop.Planet; } } /// <summary> /// By now time requirement is 0 and the program has moved on to perform orders. /// this will require additional time beyond time requirement. /// </summary> TimeSlice = PerformOrders(TimeSlice); /// <summary> /// move on to next order if possible /// </summary> if (TimeSlice > 0) { if (TaskGroupOrders[0].target.SSEntity == StarSystemEntityType.TaskGroup) { /// <summary> /// Same TG, so TG to TG order /// </summary> if (TaskGroupOrders[0].taskGroup.TaskGroupFaction == TaskGroupFaction) { if (TaskGroupOrders[0].taskGroup.TaskGroupsOrdered.IndexOf(this) != -1) { TaskGroupOrders[0].taskGroup.TaskGroupsOrdered.Remove(this); } } else { int check = -1; for (int loop = 0; loop < TaskGroupOrders[0].taskGroup.Ships.Count; loop++) { if (TaskGroupOrders[0].taskGroup.Ships[loop].TaskGroupsOrdered.IndexOf(this) != -1) { check = loop; TaskGroupOrders[0].taskGroup.Ships[loop].TaskGroupsOrdered.Remove(this); break; } } if (check == -1) { String Entry = String.Format("Ship Error, no TaskGroupsOrdered found for TG {0} in Faction {1}, {2} has completed an order to move.", TaskGroupOrders[0].taskGroup.Name, TaskGroupOrders[0].taskGroup.TaskGroupFaction.Name, Name); MessageEntry NME = new MessageEntry(MessageEntry.MessageType.Error, Contact.Position.System, Contact, GameState.Instance.GameDateTime, GameState.Instance.CurrentSecond, Entry); TaskGroupFaction.MessageLog.Add(NME); } } } TaskGroupOrders.RemoveAt(0); if (TaskGroupOrders.Count > 0) { NewOrders = true; FollowOrders(TimeSlice); } else { DrawTravelLine = 1; CanOrder = Constants.ShipTN.OrderState.AcceptOrders; } } } else { Contact.Position.X = Contact.Position.X + Distance.ToAU(TimeSlice * CurrentSpeedX); Contact.Position.Y = Contact.Position.Y + Distance.ToAU(TimeSlice * CurrentSpeedY); TotalOrderDistance = TotalOrderDistance - Distance.ToAU(CurrentSpeed * TimeSlice); UseFuel(TimeSlice); /// <summary> /// This probably isn't needed since timeReqs are constantly recalculated. /// </summary> TimeRequirement = TimeRequirement - TimeSlice; TimeSlice = 0; } }
/// <summary> /// Shipyard modifications are done here. /// </summary> /// <param name="CurrentFaction">Current faction this shipyard belongs to.</param> /// <param name="CurrentPopulation">Current population this shipyard is on.</param> /// <param name="SYInfo">The shipyard itself.</param> private static void PerformShipyardActivity(Faction CurrentFaction, Population CurrentPopulation, Installation.ShipyardInformation SYInfo) { if (SYInfo.CurrentActivity.Activity != Constants.ShipyardInfo.ShipyardActivity.NoActivity && SYInfo.CurrentActivity.Paused == false) { float SYBP = SYInfo.CalcAnnualSYProduction() * Constants.Colony.ConstructionCycleFraction; int Adjustment = 1; if (SYInfo.ShipyardType == Constants.ShipyardInfo.SYType.Naval) { Adjustment = Constants.ShipyardInfo.NavalToCommercialRatio; } /// <summary> /// Don't bother with completion date, or progress, just add the capacity. /// </summary> if (SYInfo.CurrentActivity.Activity == Constants.ShipyardInfo.ShipyardActivity.CapExpansion || SYInfo.CurrentActivity.Activity == Constants.ShipyardInfo.ShipyardActivity.CapExpansionUntilX) { /// <summary> /// How many tons could this shipyard expand capacity by in this time increment? SYBP is the number of BP produced this cycle. BaseTotalCostOfExpansion is the cost for 500 tons. /// Adjustment is the factor that accounts for whether or not this is a commercial yard or a naval yard. /// </summary> float BaseCostIncrement = SYBP / (float)(Constants.ShipyardInfo.BaseTotalCostOfExpansion * Adjustment); float TonsPerCycle = BaseCostIncrement * (float)Constants.ShipyardInfo.TonnageDenominator; /// <summary> /// Don't build more than this many tons if the activity is CapX. /// </summary> if (SYInfo.CurrentActivity.Activity == Constants.ShipyardInfo.ShipyardActivity.CapExpansionUntilX) { if (SYInfo.Tonnage + (int)Math.Floor(TonsPerCycle) > SYInfo.CurrentActivity.CapExpansionLimit) { TonsPerCycle = SYInfo.CurrentActivity.CapExpansionLimit - SYInfo.Tonnage; } SYInfo.CurrentActivity.Progress = 1.0m - (((decimal)SYInfo.CurrentActivity.CapExpansionLimit - (decimal)(SYInfo.Tonnage + TonsPerCycle)) / (decimal)SYInfo.CurrentActivity.CapExpansionLimit); } decimal Cost = (Constants.ShipyardInfo.BaseTotalCostOfExpansion * Adjustment) * ((decimal)TonsPerCycle / (decimal)Constants.ShipyardInfo.TonnageDenominator) * SYInfo.Slipways * Adjustment; decimal[] mCost = new decimal[(int)Constants.Minerals.MinerialNames.MinerialCount]; mCost[(int)Constants.Minerals.MinerialNames.Duranium] = Cost / 2.0m; mCost[(int)Constants.Minerals.MinerialNames.Neutronium] = Cost / 2.0m; /// <summary> /// Can I build this tick's worth of production? /// </summary> bool CanBuild = CurrentPopulation.MineralRequirement(mCost, 1.0f); if (CanBuild == true) { CurrentPopulation.HandleShipyardCost(Cost, mCost, 1.0f); SYInfo.AddTonnage(CurrentFaction, (int)Math.Floor(TonsPerCycle)); } else { String Entry = String.Format("Not enough minerals to finish task {0} at Shipyard {1} on Population {2}", SYInfo.CurrentActivity.Activity, SYInfo, CurrentPopulation); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Position.System, CurrentPopulation, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); GameState.Instance.Factions[0].MessageLog.Add(NMsg); } } else { /// <summary> /// BP produced this construction cycle / the total cost of the activity. /// </summary> float CurrentProgress = SYBP / (float)SYInfo.CurrentActivity.CostOfActivity; if ((SYInfo.CurrentActivity.Progress + (decimal)CurrentProgress) > 1.0m) { CurrentProgress = (float)(1.0m - SYInfo.CurrentActivity.Progress); } bool CanBuild = CurrentPopulation.MineralRequirement(SYInfo.CurrentActivity.minerialsCost, CurrentProgress); if (CanBuild == true) { CurrentPopulation.HandleShipyardCost(SYInfo.CurrentActivity.CostOfActivity, SYInfo.CurrentActivity.minerialsCost, CurrentProgress); SYInfo.CurrentActivity.Progress = SYInfo.CurrentActivity.Progress + (decimal)CurrentProgress; } else { String Entry = String.Format("Not enough minerals to finish task {0} at Shipyard {1} on Population {2}", SYInfo.CurrentActivity.Activity, SYInfo, CurrentPopulation); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Position.System, CurrentPopulation, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); GameState.Instance.Factions[0].MessageLog.Add(NMsg); } /// <summary> /// handle standard task completion here. /// </summary> if (SYInfo.CurrentActivity.Progress >= 1.0m) { switch (SYInfo.CurrentActivity.Activity) { case Constants.ShipyardInfo.ShipyardActivity.AddSlipway: SYInfo.Slipways = SYInfo.Slipways + 1; break; case Constants.ShipyardInfo.ShipyardActivity.Add500Tons: SYInfo.AddTonnage(CurrentFaction, Constants.ShipyardInfo.TonnageDenominator); break; case Constants.ShipyardInfo.ShipyardActivity.Add1000Tons: SYInfo.AddTonnage(CurrentFaction, (Constants.ShipyardInfo.TonnageDenominator * 2)); break; case Constants.ShipyardInfo.ShipyardActivity.Add2000Tons: SYInfo.AddTonnage(CurrentFaction, (Constants.ShipyardInfo.TonnageDenominator * 4)); break; case Constants.ShipyardInfo.ShipyardActivity.Add5000Tons: SYInfo.AddTonnage(CurrentFaction, (Constants.ShipyardInfo.TonnageDenominator * 10)); break; case Constants.ShipyardInfo.ShipyardActivity.Add10000Tons: SYInfo.AddTonnage(CurrentFaction, (Constants.ShipyardInfo.TonnageDenominator * 20)); break; case Constants.ShipyardInfo.ShipyardActivity.Retool: SYInfo.AssignedClass = SYInfo.CurrentActivity.TargetOfRetool; if (SYInfo.AssignedClass.IsLocked == false) { SYInfo.AssignedClass.IsLocked = true; } break; } } else { /// <summary> /// Update the timer since this project won't finish just yet. /// </summary> decimal CostLeft = SYInfo.CurrentActivity.CostOfActivity * (1.0m - SYInfo.CurrentActivity.Progress); float YearsOfProduction = (float)CostLeft / SYInfo.CalcAnnualSYProduction(); DateTime EstTime = GameState.Instance.GameDateTime; if (YearsOfProduction < Constants.Colony.TimerYearMax) { float DaysInYear = (float)Constants.TimeInSeconds.RealYear / (float)Constants.TimeInSeconds.Day; int TimeToBuild = (int)Math.Floor(YearsOfProduction * DaysInYear); TimeSpan TS = new TimeSpan(TimeToBuild, 0, 0, 0); EstTime = EstTime.Add(TS); } SYInfo.CurrentActivity.CompletionDate = EstTime; } } /// <summary> /// Lastly clean up any completed activities. /// </summary> if (SYInfo.CurrentActivity.Activity == Constants.ShipyardInfo.ShipyardActivity.CapExpansionUntilX) { if (SYInfo.Tonnage >= SYInfo.CurrentActivity.CapExpansionLimit) { /// <summary> /// This activity has completed so end it. /// </summary> SYInfo.CurrentActivity = new Installation.ShipyardInformation.ShipyardActivity(); } } else if (SYInfo.CurrentActivity.Activity != Constants.ShipyardInfo.ShipyardActivity.CapExpansion && SYInfo.CurrentActivity.Activity != Constants.ShipyardInfo.ShipyardActivity.NoActivity) { if (SYInfo.CurrentActivity.Progress >= 1.0m) { /// <summary> /// This activity has completed so end it. /// </summary> SYInfo.CurrentActivity = new Installation.ShipyardInformation.ShipyardActivity(); } } } }
/// <summary> /// End FollowOrders() /// </summary> /// <summary> /// What order should be performed at the target location? /// Update the state of the taskgroup if it is doing a blocking order. also fill out the timer for how long the ship will be blocked. /// overhaul follow orders to make sure that this is handled. /// </summary> public uint PerformOrders(uint TimeSlice) { Order currentOrder = TaskGroupOrders[0]; /// <summary> /// Handle orbiting planets here. breaking orbits is done elsewhere. /// </summary> if (TaskGroupOrders[0].target.SSEntity == StarSystemEntityType.Body || TaskGroupOrders[0].target.SSEntity == StarSystemEntityType.Population) { IsOrbiting = true; if (TaskGroupOrders[0].target.SSEntity == StarSystemEntityType.Body) { OrbitingBody = TaskGroupOrders[0].body; } else { OrbitingBody = TaskGroupOrders[0].pop.Planet; } SystemBody OrbitingPlanet = OrbitingBody as SystemBody; if (!OrbitingPlanet.TaskGroupsInOrbit.Contains(this)) { OrbitingPlanet.TaskGroupsInOrbit.Add(this); } } if (TaskGroupOrders[0].orderDelay >= TimeSlice) { TaskGroupOrders[0].orderDelay = TaskGroupOrders[0].orderDelay - (int)TimeSlice; TimeSlice = 0; } else if (TaskGroupOrders[0].orderDelay < TimeSlice) { TimeSlice = TimeSlice - (uint)TaskGroupOrders[0].orderDelay; TaskGroupOrders[0].orderDelay = 0; switch ((int)TaskGroupOrders[0].typeOf) { #region MoveTo /// <summary> /// Perform no orders for moveto: /// </summary> case (int)Constants.ShipTN.OrderType.MoveTo: TaskGroupOrders[0].orderTimeRequirement = 0; break; #endregion #region Refuel From Colony case (int)Constants.ShipTN.OrderType.RefuelFromColony: TaskGroupOrders[0].orderTimeRequirement = 0; for (int loop = 0; loop < Ships.Count; loop++) { if (TaskGroupOrders[0].pop.FuelStockpile == 0.0f) { /// <summary> /// Orders could not be carried out. A sample message log is below, something like this will need to be done for every type of message. /// </summary> /*DateTime TimeOfMessage = new DateTime(); MessageEntry NewMessage = new MessageEntry(Contact.Position.System, Contact, TimeOfMessage, (int)TimeSlice, "Refueling order could not be completed."); this.Faction.MessageLog.Add(NewMessage);*/ break; } TaskGroupOrders[0].pop.FuelStockpile = Ships[loop].Refuel(TaskGroupOrders[0].pop.FuelStockpile); } break; #endregion #region Resupply From Colony case (int)Constants.ShipTN.OrderType.ResupplyFromColony: TaskGroupOrders[0].orderTimeRequirement = 0; for (int loop = 0; loop < Ships.Count; loop++) { if (TaskGroupOrders[0].pop.MaintenanceSupplies == 0) { /// <summary> /// Order could not be carried out. /// </summary> break; } TaskGroupOrders[0].pop.MaintenanceSupplies = (float)Ships[loop].Resupply((int)Math.Floor(TaskGroupOrders[0].pop.MaintenanceSupplies)); } break; #endregion #region Load Installation /// <summary> /// Load Installation: /// </summary> case (int)Constants.ShipTN.OrderType.LoadInstallation: if (TaskGroupOrders[0].orderTimeRequirement == -1) { CanOrder = Constants.ShipTN.OrderState.CurrentlyLoading; int TaskGroupLoadTime = CalcTaskGroupLoadTime(Constants.ShipTN.LoadType.Cargo); int PlanetaryLoadTime = TaskGroupOrders[0].pop.CalculateLoadTime(TaskGroupLoadTime); TaskGroupOrders[0].orderTimeRequirement = PlanetaryLoadTime; } if (TimeSlice > TaskGroupOrders[0].orderTimeRequirement) { TimeSlice = TimeSlice - (uint)TaskGroupOrders[0].orderTimeRequirement; TaskGroupOrders[0].orderTimeRequirement = 0; LoadCargo(TaskGroupOrders[0].pop, (Installation.InstallationType)TaskGroupOrders[0].secondary, TaskGroupOrders[0].tertiary); CanOrder = Constants.ShipTN.OrderState.AcceptOrders; } else { TaskGroupOrders[0].orderTimeRequirement = TaskGroupOrders[0].orderTimeRequirement - (int)TimeSlice; TimeSlice = 0; } break; #endregion #region Load Ship Component /// <summary> /// Load Ship Component: /// </summary> case (int)Constants.ShipTN.OrderType.LoadShipComponent: if (TaskGroupOrders[0].orderTimeRequirement == -1) { CanOrder = Constants.ShipTN.OrderState.CurrentlyLoading; int TaskGroupLoadTime = CalcTaskGroupLoadTime(Constants.ShipTN.LoadType.Cargo); int PlanetaryLoadTime = TaskGroupOrders[0].pop.CalculateLoadTime(TaskGroupLoadTime); TaskGroupOrders[0].orderTimeRequirement = PlanetaryLoadTime; } if (TimeSlice > TaskGroupOrders[0].orderTimeRequirement) { TimeSlice = TimeSlice - (uint)TaskGroupOrders[0].orderTimeRequirement; TaskGroupOrders[0].orderTimeRequirement = 0; LoadComponents(TaskGroupOrders[0].pop, TaskGroupOrders[0].secondary, TaskGroupOrders[0].tertiary); CanOrder = Constants.ShipTN.OrderState.AcceptOrders; } else { TaskGroupOrders[0].orderTimeRequirement = TaskGroupOrders[0].orderTimeRequirement - (int)TimeSlice; TimeSlice = 0; } break; #endregion #region Unload Installation /// <summary> /// Unload installation: /// </summary> case (int)Constants.ShipTN.OrderType.UnloadInstallation: if (TaskGroupOrders[0].orderTimeRequirement == -1) { CanOrder = Constants.ShipTN.OrderState.CurrentlyUnloading; int TaskGroupLoadTime = CalcTaskGroupLoadTime(Constants.ShipTN.LoadType.Cargo); int PlanetaryLoadTime = TaskGroupOrders[0].pop.CalculateLoadTime(TaskGroupLoadTime); TaskGroupOrders[0].orderTimeRequirement = PlanetaryLoadTime; } if (TimeSlice > TaskGroupOrders[0].orderTimeRequirement) { TimeSlice = TimeSlice - (uint)TaskGroupOrders[0].orderTimeRequirement; TaskGroupOrders[0].orderTimeRequirement = 0; UnloadCargo(TaskGroupOrders[0].pop, (Installation.InstallationType)TaskGroupOrders[0].secondary, TaskGroupOrders[0].tertiary); CanOrder = Constants.ShipTN.OrderState.AcceptOrders; } else { TaskGroupOrders[0].orderTimeRequirement = TaskGroupOrders[0].orderTimeRequirement - (int)TimeSlice; TimeSlice = 0; } break; #endregion #region Unload Ship Component /// <summary> /// Unload Ship Component: /// </summary> case (int)Constants.ShipTN.OrderType.UnloadShipComponent: if (TaskGroupOrders[0].orderTimeRequirement == -1) { CanOrder = Constants.ShipTN.OrderState.CurrentlyUnloading; int TaskGroupLoadTime = CalcTaskGroupLoadTime(Constants.ShipTN.LoadType.Cargo); int PlanetaryLoadTime = TaskGroupOrders[0].pop.CalculateLoadTime(TaskGroupLoadTime); TaskGroupOrders[0].orderTimeRequirement = PlanetaryLoadTime; } if (TimeSlice > TaskGroupOrders[0].orderTimeRequirement) { TimeSlice = TimeSlice - (uint)TaskGroupOrders[0].orderTimeRequirement; TaskGroupOrders[0].orderTimeRequirement = 0; ComponentDefTN UnloadOrder = null; int TGComponentCount = 0; for (int loop = 0; loop < Ships.Count; loop++) { if (Ships[loop].CargoComponentList.Count != 0) { foreach (KeyValuePair<ComponentDefTN, CargoListEntryTN> pair in Ships[loop].CargoComponentList) { if (TaskGroupOrders[0].secondary == TGComponentCount) { UnloadOrder = pair.Key; TGComponentCount = -1; break; } TGComponentCount++; } if (TGComponentCount == -1) break; } } UnloadComponents(TaskGroupOrders[0].pop, UnloadOrder, TaskGroupOrders[0].tertiary); CanOrder = Constants.ShipTN.OrderState.AcceptOrders; } else { TaskGroupOrders[0].orderTimeRequirement = TaskGroupOrders[0].orderTimeRequirement - (int)TimeSlice; TimeSlice = 0; } break; #endregion #region Load Colonists /// <summary> /// Load Colonists: /// </summary> case (int)Constants.ShipTN.OrderType.LoadColonists: if (TaskGroupOrders[0].orderTimeRequirement == -1) { CanOrder = Constants.ShipTN.OrderState.CurrentlyLoading; int TaskGroupLoadTime = CalcTaskGroupLoadTime(Constants.ShipTN.LoadType.Cryo); int PlanetaryLoadTime = TaskGroupOrders[0].pop.CalculateLoadTime(TaskGroupLoadTime); TaskGroupOrders[0].orderTimeRequirement = PlanetaryLoadTime; } if (TimeSlice > TaskGroupOrders[0].orderTimeRequirement) { TimeSlice = TimeSlice - (uint)TaskGroupOrders[0].orderTimeRequirement; TaskGroupOrders[0].orderTimeRequirement = 0; LoadColonists(TaskGroupOrders[0].pop, TaskGroupOrders[0].tertiary); CanOrder = Constants.ShipTN.OrderState.AcceptOrders; } else { TaskGroupOrders[0].orderTimeRequirement = TaskGroupOrders[0].orderTimeRequirement - (int)TimeSlice; TimeSlice = 0; } break; #endregion #region Unload Colonists /// <summary> /// Unload Colonists: /// </summary> case (int)Constants.ShipTN.OrderType.UnloadColonists: if (TaskGroupOrders[0].orderTimeRequirement == -1) { CanOrder = Constants.ShipTN.OrderState.CurrentlyUnloading; int TaskGroupLoadTime = CalcTaskGroupLoadTime(Constants.ShipTN.LoadType.Cryo); int PlanetaryLoadTime = TaskGroupOrders[0].pop.CalculateLoadTime(TaskGroupLoadTime); TaskGroupOrders[0].orderTimeRequirement = PlanetaryLoadTime; } if (TimeSlice > TaskGroupOrders[0].orderTimeRequirement) { TimeSlice = TimeSlice - (uint)TaskGroupOrders[0].orderTimeRequirement; TaskGroupOrders[0].orderTimeRequirement = 0; UnloadColonists(TaskGroupOrders[0].pop, TaskGroupOrders[0].tertiary); CanOrder = Constants.ShipTN.OrderState.AcceptOrders; } else { TaskGroupOrders[0].orderTimeRequirement = TaskGroupOrders[0].orderTimeRequirement - (int)TimeSlice; TimeSlice = 0; } break; #endregion #region Refuel Target Fleet case (int)Constants.ShipTN.OrderType.RefuelTargetFleet: TaskGroupOrders[0].orderTimeRequirement = 0; /// <summary> /// A specific tanker list could come in handy here. But this shouldn't be run every tick so it won't be that big an issue. /// </summary> int FuelPlace = 0; for (int loop = 0; loop < Ships.Count; loop++) { if (Ships[loop].ShipClass.IsTanker == true) { float FuelCutoff = Ships[loop].ShipClass.TotalFuelCapacity / 10.0f; float AvailableFuel = Ships[loop].CurrentFuel - FuelCutoff; for (int loop2 = FuelPlace; loop2 < TaskGroupOrders[0].taskGroup.Ships.Count; loop2++) { if (AvailableFuel <= 0.0f) { /// <summary> /// This tanker is done. /// </summary> break; } /// <summary> /// I want refuel target fleet to refuel all ships, even tankers. /// </summary> AvailableFuel = TaskGroupOrders[0].taskGroup.Ships[loop2].Refuel(AvailableFuel); FuelPlace++; } /// <summary> /// If the ship was below the fuel cutoff originally, that will be reflected in available fuel so this should cause no issues. /// </summary> Ships[loop].CurrentFuel = FuelCutoff + AvailableFuel; } } if (FuelPlace != TaskGroupOrders[0].taskGroup.Ships.Count || TaskGroupOrders[0].taskGroup.Ships.Last().CurrentFuel != TaskGroupOrders[0].taskGroup.Ships.Last().ShipClass.TotalFuelCapacity) { /// <summary> /// Order could not be carried out. /// </summary> } break; #endregion #region Refuel From Own Tankers case (int)Constants.ShipTN.OrderType.RefuelFromOwnTankers: TaskGroupOrders[0].orderTimeRequirement = 0; FuelPlace = 0; for (int loop = 0; loop < Ships.Count; loop++) { if (Ships[loop].ShipClass.IsTanker == true) { float FuelCutoff = Ships[loop].ShipClass.TotalFuelCapacity / 10.0f; float AvailableFuel = Ships[loop].CurrentFuel - FuelCutoff; for (int loop2 = FuelPlace; loop2 < Ships.Count; loop2++) { /// <summary> /// Don't refuel tankers from each other. /// </summary> if (Ships[loop2].ShipClass.IsTanker == false) { if (AvailableFuel <= 0.0f) { /// <summary> /// This Tanker is finished. /// </sumamry> break; } AvailableFuel = Ships[loop2].Refuel(AvailableFuel); FuelPlace++; } } Ships[loop].CurrentFuel = FuelCutoff + AvailableFuel; } } if (FuelPlace != Ships.Count || (Ships.Last().CurrentFuel != Ships.Last().ShipClass.TotalFuelCapacity && Ships.Last().ShipClass.IsTanker == false)) { /// <summary> /// Order could not be carried out. /// </summary> } break; #endregion #region Refuel From Target Fleet case (int)Constants.ShipTN.OrderType.RefuelFromTargetFleet: currentOrder.orderTimeRequirement = 0; FuelPlace = 0; TaskGroupTN targetTG = currentOrder.taskGroup as TaskGroupTN; foreach(ShipTN tankerShip in targetTG.Ships) { if (tankerShip.ShipClass.IsTanker) { float FuelCutoff = tankerShip.ShipClass.TotalFuelCapacity / 10.0f; float AvailableFuel = tankerShip.CurrentFuel - FuelCutoff; foreach (ShipTN myShip in this.Ships) { if (AvailableFuel <= 0.0f) { /// <summary> /// This Tanker is finished. /// </summary> break; } AvailableFuel = myShip.Refuel(AvailableFuel); FuelPlace++; } tankerShip.CurrentFuel = FuelCutoff + AvailableFuel; } } if (FuelPlace != Ships.Count || Ships.Last().CurrentFuel != Ships.Last().ShipClass.TotalFuelCapacity) { /// <summary> /// Order could not be carried out. /// </summary> } break; #endregion #region Unload 90% Of Fuel To SystemBody case (int)Constants.ShipTN.OrderType.UnloadFuelToPlanet: TaskGroupOrders[0].orderTimeRequirement = 0; for (int loop = 0; loop < Ships.Count; loop++) { if (Ships[loop].ShipClass.IsTanker == true) { float FuelCutoff = Ships[loop].ShipClass.TotalFuelCapacity / 10.0f; float AvailableFuel = Ships[loop].CurrentFuel - FuelCutoff; if (AvailableFuel > 0.0f) { TaskGroupOrders[0].pop.FuelStockpile = TaskGroupOrders[0].pop.FuelStockpile + AvailableFuel; Ships[loop].CurrentFuel = Ships[loop].CurrentFuel - AvailableFuel; } } } break; #endregion #region Resupply Target Fleet case (int)Constants.ShipTN.OrderType.ResupplyTargetFleet: TaskGroupOrders[0].orderTimeRequirement = 0; int SupplyPlace = 0; /// <summary> /// Likewise a Supply ship specific list could come in handy. /// </summary> for (int loop = 0; loop < Ships.Count; loop++) { if (Ships[loop].ShipClass.IsSupply == true) { int MSPCutoff = (Ships[loop].ShipClass.TotalMSPCapacity / 10); int AvailableMSP = Ships[loop].CurrentMSP - MSPCutoff; for (int loop2 = SupplyPlace; loop2 < TaskGroupOrders[0].taskGroup.Ships.Count; loop2++) { if (AvailableMSP <= 0) { ///<summary> ///This supply ship is finished. ///</summary> break; } AvailableMSP = TaskGroupOrders[0].taskGroup.Ships[loop2].Resupply(AvailableMSP); SupplyPlace++; } Ships[loop].CurrentMSP = MSPCutoff + AvailableMSP; } } if (SupplyPlace != TaskGroupOrders[0].taskGroup.Ships.Count || TaskGroupOrders[0].taskGroup.Ships.Last().CurrentMSP != TaskGroupOrders[0].taskGroup.Ships.Last().ShipClass.TotalMSPCapacity) { /// <summary> /// Order could not be carried out. /// </summary> } break; #endregion #region Resupply From Own Supply Ships case (int)Constants.ShipTN.OrderType.ResupplyFromOwnSupplyShips: TaskGroupOrders[0].orderTimeRequirement = 0; SupplyPlace = 0; for (int loop = 0; loop < Ships.Count; loop++) { if (Ships[loop].ShipClass.IsSupply == true) { int MSPCutoff = (Ships[loop].ShipClass.TotalMSPCapacity / 10); int AvailableMSP = Ships[loop].CurrentMSP - MSPCutoff; for (int loop2 = SupplyPlace; loop2 < Ships.Count; loop2++) { /// <summary> /// Don't resupply supply ships from each other, or juggle as it will henceforth be refered to. /// </summary> if (Ships[loop2].ShipClass.IsSupply == false) { if (AvailableMSP <= 0) { /// <summary> /// This supply ship is finished. /// </summary> break; } AvailableMSP = Ships[loop2].Resupply(AvailableMSP); SupplyPlace++; } } Ships[loop].CurrentMSP = MSPCutoff + AvailableMSP; } } if (SupplyPlace != Ships.Count || (Ships.Last().CurrentMSP != Ships.Last().ShipClass.TotalMSPCapacity && Ships.Last().ShipClass.IsSupply == false)) { /// <summary> /// Order could not be carried out. /// </summary> } break; #endregion #region Resupply From Target Fleet case (int)Constants.ShipTN.OrderType.ResupplyFromTargetFleet: TaskGroupOrders[0].orderTimeRequirement = 0; SupplyPlace = 0; for (int loop = 0; loop < TaskGroupOrders[0].taskGroup.Ships.Count; loop++) { if (TaskGroupOrders[0].taskGroup.Ships[loop].ShipClass.IsSupply == true) { int MSPCutoff = (TaskGroupOrders[0].taskGroup.Ships[loop].ShipClass.TotalMSPCapacity / 10); int AvailableMSP = TaskGroupOrders[0].taskGroup.Ships[loop].CurrentMSP - MSPCutoff; for (int loop2 = SupplyPlace; loop2 < Ships.Count; loop2++) { if (AvailableMSP <= 0) { /// <summary> /// This supply ship is done. /// </summary> break; } AvailableMSP = Ships[loop2].Resupply(AvailableMSP); SupplyPlace++; } TaskGroupOrders[0].taskGroup.Ships[loop].CurrentMSP = MSPCutoff + AvailableMSP; } } if (SupplyPlace != Ships.Count || Ships.Last().CurrentMSP != Ships.Last().ShipClass.TotalMSPCapacity) { /// <summary> /// Order could not be carried out. /// </summary> } break; #endregion #region Unload 90% Of Supplies To SystemBody case (int)Constants.ShipTN.OrderType.UnloadSuppliesToPlanet: TaskGroupOrders[0].orderTimeRequirement = 0; for (int loop = 0; loop < Ships.Count; loop++) { if (Ships[loop].ShipClass.IsSupply == true) { int MSPCutoff = (Ships[loop].ShipClass.TotalMSPCapacity / 10); int AvailableMSP = Ships[loop].CurrentMSP - MSPCutoff; if (AvailableMSP > 0) { TaskGroupOrders[0].pop.MaintenanceSupplies = TaskGroupOrders[0].pop.MaintenanceSupplies + AvailableMSP; Ships[loop].CurrentMSP = Ships[loop].CurrentMSP - AvailableMSP; } } } break; #endregion #region Load Ordnance From Colony case (int)Constants.ShipTN.OrderType.LoadOrdnanceFromColony: TaskGroupOrders[0].orderTimeRequirement = 0; for (int loop = 0; loop < Ships.Count; loop++) { if (Ships[loop].ShipMagazines.Count != 0) { Ships[loop].LoadOrdnance(TaskGroupOrders[0].pop); } } break; #endregion #region Unload Ordnance To Colony case (int)Constants.ShipTN.OrderType.UnloadOrdnanceToColony: TaskGroupOrders[0].orderTimeRequirement = 0; for (int loop = 0; loop < Ships.Count; loop++) { if (Ships[loop].ShipMagazines.Count != 0) { Ships[loop].UnloadOrdnance(TaskGroupOrders[0].pop); } } break; #endregion #region Standard Transit case (int)Constants.ShipTN.OrderType.StandardTransit: { TaskGroupOrders[0].orderTimeRequirement = 0; JumpPoint CurrentJP = TaskGroupOrders[0].target as JumpPoint; // Check if we can jump. Dictionary<JumpEngineTN, ShipTN> usedJumpEngines; if (CurrentJP.CanJump(this, true, out usedJumpEngines)) { // Handle the jump here. ///< @todo Put jump transition in it's own function within TaskGroup. SystemPosition newPos = CurrentJP.Connect.Position; Position.System.SystemContactList.Remove(Contact); newPos.System.SystemContactList.Add(Contact); Position = newPos; Contact.UpdateLocationAfterTransit(); /// <summary> /// Handle PDList stuff. /// </summary> foreach (KeyValuePair<ComponentTN, ShipTN> pair in TaskGroupPDL.PointDefenseFC) { /// <summary> /// If these aren't true... whoops. /// </summary> if (TaskGroupFaction.PointDefense.ContainsKey(CurrentJP.Position.System) == true) { if (TaskGroupFaction.PointDefense[CurrentJP.Position.System].PointDefenseFC.ContainsKey(pair.Key) == true) { ShipTN Ship = pair.Value; ComponentTN Comp = pair.Key; bool type = TaskGroupFaction.PointDefense[CurrentJP.Position.System].PointDefenseType[pair.Key]; TaskGroupFaction.PointDefense[CurrentJP.Position.System].RemoveComponent(pair.Key); if (TaskGroupFaction.PointDefense.ContainsKey(CurrentJP.Connect.Position.System) == false) { PointDefenseList NewPDL = new PointDefenseList(); TaskGroupFaction.PointDefense.Add(CurrentJP.Connect.Position.System, NewPDL); } TaskGroupFaction.PointDefense[CurrentJP.Connect.Position.System].AddComponent(Comp, Ship, type); } } } /// <summary> /// No jump gate was used. /// Set the jump engines used to transit. /// Set the ship to undergo jump recharge. /// Set every ship to have jump sickness /// There should be at most one civ and one military in this situation. assuming a mixed group. /// </summary> if (usedJumpEngines != null) { foreach (KeyValuePair<JumpEngineTN,ShipTN> JumpShipPair in usedJumpEngines) { JumpShipPair.Key.Transit(); if (TaskGroupFaction.RechargeList.ContainsKey(JumpShipPair.Value) == true) { if((TaskGroupFaction.RechargeList[JumpShipPair.Value] & (int)Faction.RechargeStatus.JumpRecharge) != (int)Faction.RechargeStatus.JumpRecharge) { TaskGroupFaction.RechargeList[JumpShipPair.Value] = TaskGroupFaction.RechargeList[JumpShipPair.Value] + (int)Faction.RechargeStatus.JumpRecharge; } } else { TaskGroupFaction.RechargeList.Add(JumpShipPair.Value, (int)Faction.RechargeStatus.JumpRecharge); } } } /// <summary> /// Set every ship as jump sick, this is done even if a gate is used. /// </summary> foreach (ShipTN CurrentShip in Ships) { CurrentShip.StandardTransit(); if (TaskGroupFaction.RechargeList.ContainsKey(CurrentShip) == true) { if ((TaskGroupFaction.RechargeList[CurrentShip] & (int)Faction.RechargeStatus.JumpStandardSickness) != (int)Faction.RechargeStatus.JumpStandardSickness) { TaskGroupFaction.RechargeList[CurrentShip] = TaskGroupFaction.RechargeList[CurrentShip] + (int)Faction.RechargeStatus.JumpStandardSickness; } } else { TaskGroupFaction.RechargeList.Add(CurrentShip, (int)Faction.RechargeStatus.JumpStandardSickness); } } } } break; #endregion #region Squadron Transit case (int)Constants.ShipTN.OrderType.SquadronTransit: { TaskGroupOrders[0].orderTimeRequirement = 0; // Check if we can jump. Dictionary<JumpEngineTN, ShipTN> usedJumpEngines; if ((TaskGroupOrders[0].target as JumpPoint).CanJump(this, false, out usedJumpEngines)) { // Handle the jump here. ///< @todo Put jump transition in it's own function within TaskGroup. SystemPosition newPos = (TaskGroupOrders[0].target as JumpPoint).Connect.Position; Position.System.SystemContactList.Remove(Contact); newPos.System.SystemContactList.Add(Contact); Position = newPos; Contact.UpdateLocationAfterTransit(); /// <summary> /// Handle PDList stuff. /// </summary> JumpPoint CurrentJP = TaskGroupOrders[0].target as JumpPoint; foreach (KeyValuePair<ComponentTN, ShipTN> pair in TaskGroupPDL.PointDefenseFC) { /// <summary> /// If these aren't true... whoops. /// </summary> if (TaskGroupFaction.PointDefense.ContainsKey(CurrentJP.Position.System) == true) { if (TaskGroupFaction.PointDefense[CurrentJP.Position.System].PointDefenseFC.ContainsKey(pair.Key) == true) { ShipTN Ship = pair.Value; ComponentTN Comp = pair.Key; bool type = TaskGroupFaction.PointDefense[CurrentJP.Position.System].PointDefenseType[pair.Key]; TaskGroupFaction.PointDefense[CurrentJP.Position.System].RemoveComponent(pair.Key); if (TaskGroupFaction.PointDefense.ContainsKey(CurrentJP.Connect.Position.System) == false) { PointDefenseList NewPDL = new PointDefenseList(); TaskGroupFaction.PointDefense.Add(CurrentJP.Connect.Position.System, NewPDL); } TaskGroupFaction.PointDefense[CurrentJP.Connect.Position.System].AddComponent(Comp, Ship, type); } } } /// <summary> /// if Jump Engine is null here, it means there was a problem. /// Set the jump engines used to transit. /// Set the ship to undergo jump recharge. /// Set every ship to have jump sickness /// There should be at least one civ or one military in this situation. potentially more. /// </summary> int MinJumpRadius = -1; if (usedJumpEngines != null) { foreach (KeyValuePair<JumpEngineTN, ShipTN> JumpShipPair in usedJumpEngines) { if (MinJumpRadius == -1 || JumpShipPair.Key.jumpEngineDef.jumpRadius < MinJumpRadius) { MinJumpRadius = JumpShipPair.Key.jumpEngineDef.jumpRadius; } JumpShipPair.Key.Transit(); if (TaskGroupFaction.RechargeList.ContainsKey(JumpShipPair.Value) == true) { if ((TaskGroupFaction.RechargeList[JumpShipPair.Value] & (int)Faction.RechargeStatus.JumpRecharge) != (int)Faction.RechargeStatus.JumpRecharge) { TaskGroupFaction.RechargeList[JumpShipPair.Value] = TaskGroupFaction.RechargeList[JumpShipPair.Value] + (int)Faction.RechargeStatus.JumpRecharge; } } else { TaskGroupFaction.RechargeList.Add(JumpShipPair.Value, (int)Faction.RechargeStatus.JumpRecharge); } } } else { String STE = "Squadron transit happened with no jump engines."; MessageEntry nMsg = new MessageEntry(MessageEntry.MessageType.Error, Position.System, this, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, STE); TaskGroupFaction.MessageLog.Add(nMsg); } /// <summary> /// Set every ship as jump sick, this is done even if a gate is used. /// </summary> foreach (ShipTN CurrentShip in Ships) { CurrentShip.SquadronTransit(); if (TaskGroupFaction.RechargeList.ContainsKey(CurrentShip) == true) { if ((TaskGroupFaction.RechargeList[CurrentShip] & (int)Faction.RechargeStatus.JumpSquadronSickness) != (int)Faction.RechargeStatus.JumpSquadronSickness) { TaskGroupFaction.RechargeList[CurrentShip] = TaskGroupFaction.RechargeList[CurrentShip] + (int)Faction.RechargeStatus.JumpSquadronSickness; } } else { TaskGroupFaction.RechargeList.Add(CurrentShip, (int)Faction.RechargeStatus.JumpSquadronSickness); } } /// <summary> /// MinJumpRadius is the number of raw km this taskgroup should jump by. /// </summary> int degree = GameState.RNG.Next(0, 359); int Jump = GameState.RNG.Next((int)Math.Floor((float)MinJumpRadius * 0.95f), MinJumpRadius); double X = Jump * Math.Cos(degree * Constants.Units.Radian); double Y = Jump * Math.Sin(degree * Constants.Units.Radian); X = Distance.ToAU(X); Y = Distance.ToAU(Y); Position.X = Position.X + X; Position.Y = Position.Y + Y; Contact.UpdateLocationAfterTransit(); } } break; #endregion } } return TimeSlice; }
/// <summary> /// Subpulse handler will decide what the subpulse/time setting should be. /// </summary> /// <param name="P">List of factions that will be passed to advanceSim</param> /// <param name="RNG">RNG that will be passed to advanceSim</param> /// <param name="desiredTimeInSeconds">user entered time value, may bear no resemblance to what actually happens however.</param> /// <returns>time in seconds that the subpulse handler processes.</returns> public int SubpulseHandler(BindingList<Faction> P, Random RNG, int desiredTimeInSeconds) { Interrupt = false; /// <summary> /// Update all last positions here, since this shouldn't be done mid subpulse. /// </summary> foreach (Faction faction in P) { foreach (TaskGroupTN TaskGroup in faction.TaskGroups) { ThreadPool.QueueUserWorkItem(new WaitCallback(HandleTaskGroupUpdatePos), TaskGroup); } } ///< @todo Determine fleet interception, check fire controls, jump transits into new systems, completed orders. /// <summary> /// right now all subpulses are doing is giving multiple finer time slices rather than one large time slice. interruptions are not yet handled. /// I want to interrupt not only the sub pulse timer, but also the loop when appropriate. /// Jumps to unknown systems will have to be a list, probably here in SE. /// fire controls I already have, I just need to check for recharged weapons here. /// Completed orders should again be a list. how should order failure be handled? /// Fleet interception, in the sensor model? /// Fleet Interception Preempt: are fleets within 1 days travel time of each other. /// Sensor detection preempt: try to find exact sensor detection time. /// </summary> /// <summary> /// How much time should pass? this can be modified by a missile event. /// </summary> int desiredTime = desiredTimeInSeconds; /// <summary> /// how much time has passed? /// </summary> int elapsedTime = 0; /// <summary> /// Last game tick I found that a fleet was within 5 days travel time of another factions fleet. /// </summary> if (FleetInterceptionPreemptTick == GameState.Instance.CurrentSecond) { if (desiredTimeInSeconds >= Constants.TimeInSeconds.Day) { desiredTimeInSeconds = (int)Constants.TimeInSeconds.Day; #warning this goes in the SM Log. String Entry = String.Format("Subpulse shortened due to potential fleet interception. This should go in the SM Log when that exists."); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.PotentialFleetInterception, null, null, GameState.Instance.GameDateTime, (GameState.Instance.LastTimestep), Entry); GameState.Instance.Factions[0].MessageLog.Add(Msg); } } /// <summary> /// Default subpulse data, how many seconds should be advanced for each Pulses iteration of the loop. /// </summary> AdvanceTime = SubPulse[desiredTimeInSeconds].TimeInSeconds; Pulses = SubPulse[desiredTimeInSeconds].SubPulses; /// <summary> /// Sensor pre-empt check. /// I need to get the best range active(passives are already done. use sensor model code? /// Need to calculate distance to traverse each others sensor bubble? or distance to sensor bubble edge? /// also make sure already detected ships aren't here. /// just check active for now. /// The idea here is that the time it would take a fleet to travel through the entire detection radius of the opposing fleet should be the subpulse value. /// this should make sure a spot event happens for now. I'll likely have to revise this though. /// </summary> /// <summary> /// can't foreach this one. /// </summary> for (int FleetInterceptIterator = 0; FleetInterceptIterator < FleetInterceptPreemptList.Count; FleetInterceptIterator++) { ThreadPool.QueueUserWorkItem(new WaitCallback(HandleFleetIntercept), FleetInterceptIterator); } /// <summary> /// A missile intercept preemption event has been detected. /// </summary> if (MissileInterceptPreemptTick == GameState.Instance.CurrentSecond) { if (MissileTimeToHit <= desiredTimeInSeconds) { /// <summary> /// How many 5 second ticks until this missile hits? /// </summary> int FiveSecondIncrements = (int)Math.Floor((float)MissileTimeToHit / 5.0f); desiredTime = FiveSecondIncrements * 5; if (desiredTime == 0) desiredTime = (int)Constants.TimeInSeconds.FiveSeconds; /// <summary> /// I want to pause right before the missile hits. /// </summary> if (desiredTime == MissileTimeToHit && desiredTime != (int)Constants.TimeInSeconds.FiveSeconds) { desiredTime = desiredTime - 5; } PreemptCheck(desiredTime, out AdvanceTime, out Pulses); } } int FCInterruptTime = -1; bool done = false; #region Weapons fire /// <summary> /// no point in calculating the subpulse if we're at 5 seconds. /// </summary> if (desiredTime != (int)Constants.TimeInSeconds.FiveSeconds) { /// <summary> /// Check if a weapon attached to a fire controller is about to be ready to fire. /// </summary> foreach (Faction faction in P) { foreach (KeyValuePair<ComponentTN, bool> pair in faction.OpenFireFCType) { /// <summary> /// BFC /// </summary> if (pair.Value == true) { foreach (BeamTN BeamWeapon in faction.OpenFireFC[pair.Key].ShipBFC[pair.Key.componentIndex].linkedWeapons) { if (BeamWeapon.readyToFire() == true) { FCInterruptTime = (int)Constants.TimeInSeconds.FiveSeconds; done = true; break; } else if (FCInterruptTime != ((int)Constants.TimeInSeconds.FiveSeconds * 2)) { FCInterruptTime = BeamWeapon.timeToFire(); } } if (done == true) break; foreach (TurretTN Turret in faction.OpenFireFC[pair.Key].ShipBFC[pair.Key.componentIndex].linkedTurrets) { if (Turret.readyToFire() == true) { FCInterruptTime = (int)Constants.TimeInSeconds.FiveSeconds; done = true; break; } else if (FCInterruptTime != ((int)Constants.TimeInSeconds.FiveSeconds * 2)) { FCInterruptTime = Turret.timeToFire(); } } if (done == true) break; } /// <summary> /// MFC /// </summary> else if (pair.Value == false) { foreach (MissileLauncherTN LaunchTube in faction.OpenFireFC[pair.Key].ShipMFC[pair.Key.componentIndex].linkedWeapons) { if (LaunchTube.readyToFire() == true) { FCInterruptTime = (int)Constants.TimeInSeconds.FiveSeconds; done = true; break; } else if (FCInterruptTime != ((int)Constants.TimeInSeconds.FiveSeconds * 2)) { FCInterruptTime = LaunchTube.timeToFire(); } } if (done == true) break; } } if (done == true) break; } if (FCInterruptTime != -1 && FCInterruptTime < desiredTime) { desiredTime = FCInterruptTime; PreemptCheck(desiredTime, out AdvanceTime, out Pulses); } } #endregion /// <summary> /// missile intercepts need a way of stepping down the sub pulse and advance time values, hence this loop. /// </summary> while (elapsedTime != desiredTime) { for (int loop = 0; loop < Pulses; loop++) { AdvanceSim(P, RNG, AdvanceTime); elapsedTime = elapsedTime + AdvanceTime; /// <summary> /// after advance sim is a missile intercept still in progress? if (MissileInterceptPreemptTick == GameState.Instance.CurrentSecond) { bool test = PreemptCheck((desiredTime - elapsedTime), out AdvanceTime, out Pulses); /// <summary> /// better just get out of here if this is ever false. /// </summary> if (test == false) { return elapsedTime; } } /// <summary> /// after running advance sim we find a potential fleet interception event occurred. /// </summary> else if (FleetInterceptionPreemptTick == GameState.Instance.CurrentSecond) { #warning this goes in the SM Log. String Entry2 = String.Format("Subpulse shortened due to potential fleet interception. This should go in the SM Log when that exists."); MessageEntry Msg2 = new MessageEntry(MessageEntry.MessageType.PotentialFleetInterception, null, null, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry2); GameState.Instance.Factions[0].MessageLog.Add(Msg2); return elapsedTime; } else if (Interrupt == true) { return elapsedTime; } } } return elapsedTime; }
/// <summary> /// Processes construction factory work for every faction. /// </summary> /// <param name="P">List of factions.</param> public static void ConstructionFactoryBuild(BindingList <Faction> P) { /// <summary> /// Subtract the construction cycle from the construction tick. /// </summary> foreach (Faction CurrentFaction in P) { foreach (Population CurrentPopulation in CurrentFaction.Populations) { /// <summary> /// How much construction work per day does this colony do? default should be 5 day construction cycle. /// </summary> float TimeAdjust = (float)Constants.Colony.ConstructionCycle / (float)Constants.TimeInSeconds.Year; float CurrentIndustry = CurrentPopulation.CalcTotalIndustry() * TimeAdjust; float BuildPercentage = 0.0f; foreach (ConstructionBuildQueueItem CurrentConstruction in CurrentPopulation.ConstructionBuildQueue) { /// <summary> /// Check to see if this item is in the current build queue, or has to wait for capacity to free up. /// </summary> if ((BuildPercentage + CurrentConstruction.buildCapacity) <= 100.0f) { BuildPercentage = BuildPercentage + CurrentConstruction.buildCapacity; } else { break; } /// <summary> /// This construction project is paused right now. /// </summary> if (CurrentConstruction.inProduction == false) { continue; } /// <summary> /// how much of total industry does this build project use? /// </summary> float DevotedIndustry = (CurrentConstruction.buildCapacity / 100.0f) * CurrentIndustry; float Completion = DevotedIndustry / (float)CurrentConstruction.costPerItem; int NumberBuilt = 0; /// <summary> /// Final bit of a construction project to complete. /// </summary> if (Completion >= CurrentConstruction.numToBuild) { NumberBuilt = (int)Math.Floor(CurrentConstruction.numToBuild); Completion = CurrentConstruction.numToBuild; } /// <summary> /// This is a new build item. /// </summary> else if (Math.Floor(CurrentConstruction.numToBuild) == CurrentConstruction.numToBuild) { /// <summary> /// This colony will build or start to build this many items. charge for all of them. /// </summary> NumberBuilt = (int)Math.Ceiling(Completion); /// <summary> /// the colony can build everything in one go. /// </summary> if (NumberBuilt >= Math.Floor(CurrentConstruction.numToBuild)) { NumberBuilt = (int)Math.Floor(CurrentConstruction.numToBuild); Completion = CurrentConstruction.numToBuild; } } /// <summary> /// This is an inprogress item, new items will be started however. /// </summary> else if ((CurrentConstruction.numToBuild - Completion) < Math.Floor(CurrentConstruction.numToBuild)) { /// <summary> /// Adjust out the current build fraction, and then calculate how many new items will be started. float CurBuildReq = (CurrentConstruction.numToBuild - (float)Math.Floor(CurrentConstruction.numToBuild)); NumberBuilt = (int)Math.Ceiling(Completion - CurBuildReq); if (NumberBuilt > Math.Floor(CurrentConstruction.numToBuild)) { NumberBuilt = (int)Math.Floor(CurrentConstruction.numToBuild); } } bool CIRequired = false; bool MineRequired = false; bool CanBuild = false; /// <summary> /// Conventional industry must also be used. /// </summary> if (CurrentConstruction.buildType == ConstructionBuildQueueItem.CBType.PlanetaryInstallation) { if ((int)CurrentConstruction.installationBuild.Type >= (int)Installation.InstallationType.ConvertCIToConstructionFactory && (int)CurrentConstruction.installationBuild.Type <= (int)Installation.InstallationType.ConvertCIToOrdnanceFactory) { CIRequired = true; CanBuild = CurrentPopulation.CIRequirement(NumberBuilt); if (CanBuild == false) { String Entry = String.Format("Insufficent Conventional Industry to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksCI, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } } if ((int)CurrentConstruction.installationBuild.Type == (int)Installation.InstallationType.ConvertMineToAutomated) { MineRequired = true; CanBuild = CurrentPopulation.MineRequirement(NumberBuilt); if (CanBuild == false) { String Entry = String.Format("Insufficent Mines to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksCI, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } } } /// <summary> /// Check mineral costs to see if this can be built. /// </summary> switch (CurrentConstruction.buildType) { #warning PDC construction needs to be implemented here and slightly further down. case ConstructionBuildQueueItem.CBType.PlanetaryInstallation: CanBuild = CurrentPopulation.MineralRequirement(CurrentConstruction.installationBuild.MinerialsCost); break; case ConstructionBuildQueueItem.CBType.ShipComponent: CanBuild = CurrentPopulation.MineralRequirement(CurrentConstruction.componentBuild.minerialsCost); break; case ConstructionBuildQueueItem.CBType.PDCConstruction: break; case ConstructionBuildQueueItem.CBType.PDCPrefab: break; case ConstructionBuildQueueItem.CBType.PDCAssembly: break; case ConstructionBuildQueueItem.CBType.PDCRefit: break; case ConstructionBuildQueueItem.CBType.MaintenanceSupplies: CanBuild = CurrentPopulation.MineralRequirement(Constants.Colony.MaintenanceMineralCost); break; } if (CanBuild == false) { String Entry = String.Format("Insufficent Minerals to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } /// <summary> /// Adjust number to build downward to reflect construction happening. /// </summary> int CompletedItems = (int)Math.Ceiling(CurrentConstruction.numToBuild); CurrentConstruction.numToBuild = CurrentConstruction.numToBuild - Completion; CompletedItems = CompletedItems - (int)Math.Ceiling(CurrentConstruction.numToBuild); switch (CurrentConstruction.buildType) { case ConstructionBuildQueueItem.CBType.PlanetaryInstallation: if (NumberBuilt != 0) { if (CIRequired == false && MineRequired == false) { CurrentPopulation.HandleBuildItemCost((CurrentConstruction.costPerItem * NumberBuilt), CurrentConstruction.installationBuild.MinerialsCost); } else if (CIRequired == true && MineRequired == false) { CurrentPopulation.HandleBuildItemCost((CurrentConstruction.costPerItem * NumberBuilt), CurrentConstruction.installationBuild.MinerialsCost, NumberBuilt, -1); } else if (CIRequired == false && MineRequired == true) { CurrentPopulation.HandleBuildItemCost((CurrentConstruction.costPerItem * NumberBuilt), CurrentConstruction.installationBuild.MinerialsCost, -1, NumberBuilt); } } CurrentPopulation.AddInstallation(CurrentConstruction.installationBuild, Completion); break; case ConstructionBuildQueueItem.CBType.ShipComponent: if (NumberBuilt != 0) { CurrentPopulation.HandleBuildItemCost((CurrentConstruction.costPerItem * NumberBuilt), CurrentConstruction.componentBuild.minerialsCost); } CurrentPopulation.AddComponentsToStockpile(CurrentConstruction.componentBuild, Completion); break; case ConstructionBuildQueueItem.CBType.PDCConstruction: break; case ConstructionBuildQueueItem.CBType.PDCPrefab: break; case ConstructionBuildQueueItem.CBType.PDCAssembly: break; case ConstructionBuildQueueItem.CBType.PDCRefit: break; case ConstructionBuildQueueItem.CBType.MaintenanceSupplies: if (NumberBuilt != 0) { CurrentPopulation.HandleBuildItemCost((CurrentConstruction.costPerItem * NumberBuilt), Constants.Colony.MaintenanceMineralCost); } CurrentPopulation.AddMSP(CompletedItems); break; } } /// <summary> /// Cleanup the CBQ here. /// </summary> for (int CBQIterator = 0; CBQIterator < CurrentPopulation.ConstructionBuildQueue.Count; CBQIterator++) { if (CurrentPopulation.ConstructionBuildQueue[CBQIterator].numToBuild == 0.0f) { CurrentPopulation.ConstructionBuildQueue.RemoveAt(CBQIterator); CBQIterator--; } } } } }
/// <summary> /// TaskGroupThermal Detection runs the thermal detection routine on TaskGroupToTest from CurrentTaskGroup /// </summary> /// <param name="System">Current System this is taking place in.</param> /// <param name="CurrentTaskGroup">TaskGroup that is performing the sensor sweep.</param> /// <param name="TaskGroupToTest">TaskGroup being tested against.</param> /// <param name="dist">Distance between these two taskgroups.</param> /// <param name="detListIterator">Iterator for where the TaskGroupToTest is in the various detection lists and the system contact list.</param> /// <param name="DetShipList">If a ship is detected it must be put into this list for the detectedContactsList later on.</param> private void TaskGroupThermalDetection(StarSystem System, TaskGroupTN CurrentTaskGroup, TaskGroupTN TaskGroupToTest, float dist, int detListIterator) { if (System.FactionDetectionLists[FactionID].Thermal[detListIterator] != GameState.Instance.CurrentSecond) { int sig = -1; int detection = -1; bool noDetection = false; bool allDetection = false; /// <summary> /// Get the best detection range for thermal signatures in loop. /// </summary> int ShipID = TaskGroupToTest.ThermalSortList.Last(); ShipTN scratch = TaskGroupToTest.Ships[ShipID]; sig = scratch.CurrentThermalSignature; /// <summary> /// Check to make sure the taskgroup has a thermal sensor available, otherwise use the default. /// </summary> if (CurrentTaskGroup.BestThermalCount != 0) { detection = CurrentTaskGroup.BestThermal.pSensorDef.GetPassiveDetectionRange(sig); } else { detection = ComponentList.DefaultPassives.GetPassiveDetectionRange(sig); } /// <summary> /// Test the biggest signature against the best sensor. /// </summary> bool det = LargeDetection(dist, detection); /// <summary> /// Good case, none of the ships are detected. /// </summary> if (det == false) { noDetection = true; } /// <summary> /// Atleast the biggest ship is detected. /// </summary if (noDetection == false) { ShipID = TaskGroupToTest.ThermalSortList.First(); scratch = TaskGroupToTest.Ships[ShipID]; sig = scratch.CurrentThermalSignature; /// <summary> /// Check to make sure the taskgroup has a thermal sensor available, otherwise use the default. /// </summary> if (CurrentTaskGroup.BestThermalCount != 0) { detection = CurrentTaskGroup.BestThermal.pSensorDef.GetPassiveDetectionRange(sig); } else { detection = ComponentList.DefaultPassives.GetPassiveDetectionRange(sig); } /// <summary> /// Now for the smallest vs the best. /// </summary> det = LargeDetection(dist, detection); /// <summary> /// Best case, everything is detected. /// </summary> if (det == true) { allDetection = true; for (int loop3 = 0; loop3 < TaskGroupToTest.Ships.Count; loop3++) { TaskGroupToTest.Ships[loop3].ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(TaskGroupToTest.Ships[loop3]) == false) { DetShipList.Add(TaskGroupToTest.Ships[loop3]); } } System.FactionDetectionLists[FactionID].Thermal[detListIterator] = GameState.Instance.CurrentSecond; } else if (noDetection == false && allDetection == false) { /// <summary> /// Worst case. some are detected, some aren't. /// </summary> for (int loop3 = 0; loop3 < TaskGroupToTest.Ships.Count; loop3++) { LinkedListNode<int> node = TaskGroupToTest.ThermalSortList.Last; bool done = false; while (!done) { scratch = TaskGroupToTest.Ships[node.Value]; if (scratch.ThermalDetection[FactionID] != GameState.Instance.CurrentSecond) { sig = scratch.CurrentThermalSignature; if (CurrentTaskGroup.BestThermalCount != 0) { detection = CurrentTaskGroup.BestThermal.pSensorDef.GetPassiveDetectionRange(sig); } else { detection = ComponentList.DefaultPassives.GetPassiveDetectionRange(sig); } /// <summary> /// Test each ship until I get to one I don't see. /// </summary> det = LargeDetection(dist, detection); if (det == true) { scratch.ThermalDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(scratch) == false) { DetShipList.Add(scratch); } } else { done = true; break; } } if (node == TaskGroupToTest.ThermalSortList.First) { /// <summary> /// This should not happen. /// </summary> String ErrorMessage = string.Format("Partial Thermal detect for TGs looped through every ship. {0} {1} {2} {3}", dist, detection, noDetection, allDetection); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.Error, TaskGroupToTest.Contact.Position.System, TaskGroupToTest.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, ErrorMessage); MessageLog.Add(NMsg); done = true; break; } node = node.Previous; } } } /// <summary> /// End else /// </summary> } } }
/// <summary> /// Handles ordnance factory construction. /// </summary> /// <param name="P">List of factions.</param> public static void OrdnanceFactoryBuild(BindingList <Faction> P) { /// <summary> /// Subtract the construction cycle from the construction tick. /// </summary> foreach (Faction CurrentFaction in P) { foreach (Population CurrentPopulation in CurrentFaction.Populations) { /// <summary> /// How much construction work per day does this colony do? default should be 5 day construction cycle. /// </summary> float TimeAdjust = (float)Constants.Colony.ConstructionCycle / (float)Constants.TimeInSeconds.Year; float CurrentIndustry = CurrentPopulation.CalcTotalOrdnanceIndustry() * TimeAdjust; float BuildPercentage = 0.0f; foreach (MissileBuildQueueItem CurrentConstruction in CurrentPopulation.MissileBuildQueue) { /// <summary> /// Check to see if this item is in the current build queue, or has to wait for capacity to free up. /// </summary> if ((BuildPercentage + CurrentConstruction.buildCapacity) <= 100.0f) { BuildPercentage = BuildPercentage + CurrentConstruction.buildCapacity; } else { break; } /// <summary> /// This construction project is paused right now. /// </summary> if (CurrentConstruction.inProduction == false) { continue; } /// <summary> /// how much of total industry does this build project use? /// </summary> float DevotedIndustry = (CurrentConstruction.buildCapacity / 100.0f) * CurrentIndustry; float Completion = DevotedIndustry / (float)CurrentConstruction.costPerItem; int NumberBuilt = 0; /// <summary> /// Final bit of a construction project to complete. /// </summary> if (Completion >= CurrentConstruction.numToBuild) { NumberBuilt = (int)Math.Floor(CurrentConstruction.numToBuild); Completion = CurrentConstruction.numToBuild; } /// <summary> /// This is a new build item. /// </summary> else if (Math.Floor(CurrentConstruction.numToBuild) == CurrentConstruction.numToBuild) { /// <summary> /// This colony will build or start to build this many items. charge for all of them. /// </summary> NumberBuilt = (int)Math.Ceiling(Completion); /// <summary> /// the colony can build everything in one go. /// </summary> if (NumberBuilt >= Math.Floor(CurrentConstruction.numToBuild)) { NumberBuilt = (int)Math.Floor(CurrentConstruction.numToBuild); Completion = CurrentConstruction.numToBuild; } } /// <summary> /// This is an inprogress item, new items will be started however. /// </summary> else if ((CurrentConstruction.numToBuild - Completion) < Math.Floor(CurrentConstruction.numToBuild)) { /// <summary> /// Adjust out the current build fraction, and then calculate how many new items will be started. float CurBuildReq = (CurrentConstruction.numToBuild - (float)Math.Floor(CurrentConstruction.numToBuild)); NumberBuilt = (int)Math.Ceiling(Completion - CurBuildReq); if (NumberBuilt > Math.Floor(CurrentConstruction.numToBuild)) { NumberBuilt = (int)Math.Floor(CurrentConstruction.numToBuild); } } /// <summary> /// Do I have the minerals to build this missile? /// </summary> bool CanBuild = CurrentPopulation.MineralRequirement(CurrentConstruction.ordnanceDef.minerialsCost); if (CanBuild == false) { String Entry = String.Format("Insufficent Minerals to continue build order on {0} for {1}x {2}", CurrentPopulation, CurrentConstruction.numToBuild, CurrentConstruction.Name); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.ColonyLacksMinerals, CurrentPopulation.Contact.Position.System, CurrentPopulation.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); CurrentFaction.MessageLog.Add(Msg); continue; } /// <summary> /// Adjust number to build downward to reflect construction happening. /// </summary> int CompletedItems = (int)Math.Ceiling(CurrentConstruction.numToBuild); CurrentConstruction.numToBuild = CurrentConstruction.numToBuild - Completion; CompletedItems = CompletedItems - (int)Math.Ceiling(CurrentConstruction.numToBuild); CurrentPopulation.HandleBuildItemCost((CurrentConstruction.costPerItem * NumberBuilt), CurrentConstruction.ordnanceDef.minerialsCost); CurrentPopulation.LoadMissileToStockpile(CurrentConstruction.ordnanceDef, CompletedItems); } /// <summary> /// Cleanup the CBQ here. /// </summary> for (int MBQIterator = 0; MBQIterator < CurrentPopulation.MissileBuildQueue.Count; MBQIterator++) { if (CurrentPopulation.MissileBuildQueue[MBQIterator].numToBuild == 0.0f) { CurrentPopulation.MissileBuildQueue.RemoveAt(MBQIterator); MBQIterator--; } } } } }
/// <summary> /// Taskgroup active detection will handle the active detection sensor sweeps, Resolution represents the biggest difference for TG Active detection vs passive detection. /// There isn't simply a best active sensor, and the correct one has to be searched for. luckily that work is already done by the taskgroup itself. /// </summary> /// <param name="System">Current System.</param> /// <param name="CurrentTaskGroup">Scanning Taskgroup</param> /// <param name="TaskGroupToTest">Taskgroup to test if detected.</param> /// <param name="dist">Distance between the two taskgroups</param> /// <param name="detListIterator">location of the test taskgroup in the detection lists</param> private void TaskGroupActiveDetection(StarSystem System, TaskGroupTN CurrentTaskGroup, TaskGroupTN TaskGroupToTest, float dist, int detListIterator) { if (System.FactionDetectionLists[FactionID].Active[detListIterator] != GameState.Instance.CurrentSecond && CurrentTaskGroup.ActiveSensorQue.Count > 0) { int sig = -1; int detection = -1; bool noDetection = false; bool allDetection = false; /// <summary> /// Get the best detection range for thermal signatures in loop. /// </summary> int ShipID = TaskGroupToTest.ActiveSortList.Last(); ShipTN scratch = TaskGroupToTest.Ships[ShipID]; sig = scratch.TotalCrossSection - 1; if (sig > Constants.ShipTN.ResolutionMax - 1) sig = Constants.ShipTN.ResolutionMax - 1; detection = CurrentTaskGroup.ActiveSensorQue[CurrentTaskGroup.TaskGroupLookUpST[sig]].aSensorDef.GetActiveDetectionRange(sig, -1); bool det = LargeDetection(dist, detection); /// <summary> /// Good case, none of the ships are detected. /// </summary> if (det == false) { noDetection = true; } /// <summary> /// Atleast the biggest ship is detected. /// </summary if (noDetection == false) { ShipID = TaskGroupToTest.ActiveSortList.First(); scratch = TaskGroupToTest.Ships[ShipID]; sig = scratch.TotalCrossSection - 1; if (sig > Constants.ShipTN.ResolutionMax - 1) sig = Constants.ShipTN.ResolutionMax - 1; detection = CurrentTaskGroup.ActiveSensorQue[CurrentTaskGroup.TaskGroupLookUpST[sig]].aSensorDef.GetActiveDetectionRange(sig, -1); det = LargeDetection(dist, detection); /// <summary> /// Best case, everything is detected. /// </summary> if (det == true) { allDetection = true; for (int loop3 = 0; loop3 < TaskGroupToTest.Ships.Count; loop3++) { TaskGroupToTest.Ships[loop3].ActiveDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(TaskGroupToTest.Ships[loop3]) == false) { DetShipList.Add(TaskGroupToTest.Ships[loop3]); } } /// <summary> /// FactionSystemDetection entry. I hope to deprecate this at some point. /// Be sure to erase the factionDetectionSystem entry first, to track down everywhere this overbloated thing is. /// update, not happening. FactionDetectionList is too important. /// </summary> System.FactionDetectionLists[FactionID].Active[detListIterator] = GameState.Instance.CurrentSecond; } else if (noDetection == false && allDetection == false) { /// <summary> /// Worst case. some are detected, some aren't. /// </summary> for (int loop3 = 0; loop3 < TaskGroupToTest.Ships.Count; loop3++) { LinkedListNode<int> node = TaskGroupToTest.ActiveSortList.Last; bool done = false; while (!done) { scratch = TaskGroupToTest.Ships[node.Value]; if (scratch.ActiveDetection[FactionID] != GameState.Instance.CurrentSecond) { sig = scratch.TotalCrossSection - 1; if (sig > Constants.ShipTN.ResolutionMax - 1) sig = Constants.ShipTN.ResolutionMax - 1; detection = CurrentTaskGroup.ActiveSensorQue[CurrentTaskGroup.TaskGroupLookUpST[sig]].aSensorDef.GetActiveDetectionRange(sig, -1); det = LargeDetection(dist, detection); if (det == true) { scratch.ActiveDetection[FactionID] = GameState.Instance.CurrentSecond; if (DetShipList.Contains(scratch) == false) { DetShipList.Add(scratch); } } else { done = true; break; } } if (node == TaskGroupToTest.ActiveSortList.First) { /// <summary> /// This should not happen. /// </summary> String ErrorMessage = string.Format("Partial Active detect for TGs looped through every ship. {0} {1} {2} {3}", dist, detection, noDetection, allDetection); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.Error, CurrentTaskGroup.Contact.Position.System, CurrentTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, ErrorMessage); MessageLog.Add(NMsg); done = true; break; } node = node.Previous; } } } /// <summary> /// End else /// </summary> } } }
/// <summary> /// Subpulse handler will decide what the subpulse/time setting should be. /// </summary> /// <param name="P">List of factions that will be passed to advanceSim</param> /// <param name="RNG">RNG that will be passed to advanceSim</param> /// <param name="desiredTimeInSeconds">user entered time value, may bear no resemblance to what actually happens however.</param> /// <returns>time in seconds that the subpulse handler processes.</returns> public int SubpulseHandler(BindingList<Faction> P, Random RNG, int desiredTimeInSeconds) { Interrupt = false; /// <summary> /// Update all last positions here, since this shouldn't be done mid subpulse. /// </summary> foreach (Faction faction in P) { foreach (TaskGroupTN TaskGroup in faction.TaskGroups) { TaskGroup.UpdateLastPosition(); } } ///< @todo Determine fleet interception, check fire controls, jump transits into new systems, completed orders. /// <summary> /// right now all subpulses are doing is giving multiple finer time slices rather than one large time slice. interruptions are not yet handled. /// I want to interrupt not only the sub pulse timer, but also the loop when appropriate. /// Jumps to unknown systems will have to be a list, probably here in SE. /// fire controls I already have, I just need to check for recharged weapons here. /// Completed orders should again be a list. how should order failure be handled? /// Fleet interception, in the sensor model? /// Fleet Interception Preempt: are fleets within 1 days travel time of each other. /// Sensor detection preempt: try to find exact sensor detection time. /// </summary> /// <summary> /// How much time should pass? this can be modified by a missile event. /// </summary> int desiredTime = desiredTimeInSeconds; /// <summary> /// how much time has passed? /// </summary> int elapsedTime = 0; /// <summary> /// Last game tick I found that a fleet was within 5 days travel time of another factions fleet. /// </summary> if (FleetInterceptionPreemptTick == GameState.Instance.CurrentSecond) { if (desiredTimeInSeconds >= Constants.TimeInSeconds.Day) { desiredTimeInSeconds = (int)Constants.TimeInSeconds.Day; #warning this goes in the SM Log. String Entry = String.Format("Subpulse shortened due to potential fleet interception. This should go in the SM Log when that exists."); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.PotentialFleetInterception, null, null, GameState.Instance.GameDateTime, (GameState.Instance.LastTimestep), Entry); GameState.Instance.Factions[0].MessageLog.Add(Msg); } } /// <summary> /// Default subpulse data, how many seconds should be advanced for each Pulses iteration of the loop. /// </summary> int AdvanceTime = SubPulse[desiredTimeInSeconds].TimeInSeconds; int Pulses = SubPulse[desiredTimeInSeconds].SubPulses; /// <summary> /// Sensor pre-empt check. /// I need to get the best range active(passives are already done. use sensor model code? /// Need to calculate distance to traverse each others sensor bubble? or distance to sensor bubble edge? /// also make sure already detected ships aren't here. /// just check active for now. /// The idea here is that the time it would take a fleet to travel through the entire detection radius of the opposing fleet should be the subpulse value. /// this should make sure a spot event happens for now. I'll likely have to revise this though. /// </summary> /// <summary> /// can't foreach this one. /// </summary> for (int FleetInterceptIterator = 0; FleetInterceptIterator < FleetInterceptPreemptList.Count; FleetInterceptIterator++) { TaskGroupTN FleetIntercept1 = FleetInterceptPreemptList[FleetInterceptIterator]; TaskGroupTN FleetIntercept2 = FleetInterceptPreemptList[FleetInterceptIterator + 1]; /// <summary> /// get the largest TCS ship ID from the active sort list. /// </summary> int ShipID1 = FleetIntercept1.ActiveSortList.Last(); int ShipID2 = FleetIntercept2.ActiveSortList.Last(); ShipTN Large1 = FleetIntercept1.Ships[ShipID1]; ShipTN Large2 = FleetIntercept2.Ships[ShipID2]; StarSystem CurrentSystem = FleetIntercept1.Contact.Position.System; /// <summary> /// get the distance, which involves going to the distance table(as opposed to recalculating distance here). /// </summary> int TGID1 = CurrentSystem.SystemContactList.IndexOf(FleetIntercept1.Contact); int TGID2 = CurrentSystem.SystemContactList.IndexOf(FleetIntercept2.Contact); float dist; FleetIntercept1.Contact.DistTable.GetDistance(FleetIntercept2.Contact, out dist); int sig1 = Large1.TotalCrossSection - 1; int sig2 = Large2.TotalCrossSection - 1; if (sig1 > Constants.ShipTN.ResolutionMax - 1) sig1 = Constants.ShipTN.ResolutionMax - 1; if (sig2 > Constants.ShipTN.ResolutionMax - 1) sig2 = Constants.ShipTN.ResolutionMax - 1; int detection1 = -1; int detection2 = -1; int TimeToCross1 = -1; int TimeToCross2 = -1; /// <summary> /// here I want to find out what the detection factors are, and how much time each taskgroup requires to cross the detection factor of the opposing taskgroup. /// </summary> if (FleetIntercept1.ActiveSensorQue.Count != 0) { detection1 = FleetIntercept1.ActiveSensorQue[FleetIntercept1.TaskGroupLookUpST[sig2]].aSensorDef.GetActiveDetectionRange(sig2, -1); float speedAdj = FleetIntercept2.CurrentSpeed / 10000.0f; TimeToCross1 = detection1 / (int)Math.Floor(speedAdj); } if (FleetIntercept2.ActiveSensorQue.Count != 0) { detection2 = FleetIntercept2.ActiveSensorQue[FleetIntercept2.TaskGroupLookUpST[sig1]].aSensorDef.GetActiveDetectionRange(sig1, -1); float speedAdj = FleetIntercept1.CurrentSpeed / 10000.0f; TimeToCross2 = detection2 / (int)Math.Floor(speedAdj); } if (TimeToCross1 == -1 && TimeToCross2 == -1) continue; else { /// <summary> /// if lower than the current advance time, this should be set as the current advance time. /// </summary> int time = -1; if (TimeToCross1 == -1) time = TimeToCross2; else if (TimeToCross2 == -1) time = TimeToCross1; else time = Math.Min(TimeToCross1, TimeToCross2); if (time < AdvanceTime) { AdvanceTime = time; Pulses = (int)(Constants.TimeInSeconds.Day / AdvanceTime); } } } /// <summary> /// A missile intercept preemption event has been detected. /// </summary> if (MissileInterceptPreemptTick == GameState.Instance.CurrentSecond) { if (MissileTimeToHit <= desiredTimeInSeconds) { /// <summary> /// How many 5 second ticks until this missile hits? /// </summary> int FiveSecondIncrements = (int)Math.Floor((float)MissileTimeToHit / 5.0f); desiredTime = FiveSecondIncrements * 5; if (desiredTime == 0) desiredTime = (int)Constants.TimeInSeconds.FiveSeconds; /// <summary> /// I want to pause right before the missile hits. /// </summary> if (desiredTime == MissileTimeToHit && desiredTime != (int)Constants.TimeInSeconds.FiveSeconds) { desiredTime = desiredTime - 5; } PreemptCheck(desiredTime, out AdvanceTime, out Pulses); } } int FCInterruptTime = -1; bool done = false; /// <summary> /// no point in calculating the subpulse if we're at 5 seconds. /// </summary> if (desiredTime != (int)Constants.TimeInSeconds.FiveSeconds) { /// <summary> /// Check if a weapon attached to a fire controller is about to be ready to fire. /// </summary> foreach (Faction faction in P) { foreach (KeyValuePair<ComponentTN, bool> pair in faction.OpenFireFCType) { /// <summary> /// BFC /// </summary> if (pair.Value == true) { foreach (BeamTN BeamWeapon in faction.OpenFireFC[pair.Key].ShipBFC[pair.Key.componentIndex].linkedWeapons) { if (BeamWeapon.readyToFire() == true) { FCInterruptTime = (int)Constants.TimeInSeconds.FiveSeconds; done = true; break; } else if (FCInterruptTime != ((int)Constants.TimeInSeconds.FiveSeconds * 2)) { FCInterruptTime = BeamWeapon.timeToFire(); } } if (done == true) break; foreach (TurretTN Turret in faction.OpenFireFC[pair.Key].ShipBFC[pair.Key.componentIndex].linkedTurrets) { if (Turret.readyToFire() == true) { FCInterruptTime = (int)Constants.TimeInSeconds.FiveSeconds; done = true; break; } else if (FCInterruptTime != ((int)Constants.TimeInSeconds.FiveSeconds * 2)) { FCInterruptTime = Turret.timeToFire(); } } if (done == true) break; } /// <summary> /// MFC /// </summary> else if (pair.Value == false) { foreach (MissileLauncherTN LaunchTube in faction.OpenFireFC[pair.Key].ShipMFC[pair.Key.componentIndex].linkedWeapons) { if (LaunchTube.readyToFire() == true) { FCInterruptTime = (int)Constants.TimeInSeconds.FiveSeconds; done = true; break; } else if (FCInterruptTime != ((int)Constants.TimeInSeconds.FiveSeconds * 2)) { FCInterruptTime = LaunchTube.timeToFire(); } } if (done == true) break; } } if (done == true) break; } if (FCInterruptTime != -1 && FCInterruptTime < desiredTime) { desiredTime = FCInterruptTime; PreemptCheck(desiredTime, out AdvanceTime, out Pulses); } } /// <summary> /// missile intercepts need a way of stepping down the sub pulse and advance time values, hence this loop. /// </summary> while (elapsedTime != desiredTime) { for (int loop = 0; loop < Pulses; loop++) { AdvanceSim(P, RNG, AdvanceTime); elapsedTime = elapsedTime + AdvanceTime; /// <summary> /// after advance sim is a missile intercept still in progress? if (MissileInterceptPreemptTick == GameState.Instance.CurrentSecond) { bool test = PreemptCheck((desiredTime - elapsedTime), out AdvanceTime, out Pulses); /// <summary> /// better just get out of here if this is ever false. /// </summary> if (test == false) { return elapsedTime; } } /// <summary> /// after running advance sim we find a potential fleet interception event occurred. /// </summary> else if (FleetInterceptionPreemptTick == GameState.Instance.CurrentSecond) { #warning this goes in the SM Log. String Entry2 = String.Format("Subpulse shortened due to potential fleet interception. This should go in the SM Log when that exists."); MessageEntry Msg2 = new MessageEntry(MessageEntry.MessageType.PotentialFleetInterception, null, null, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry2); GameState.Instance.Factions[0].MessageLog.Add(Msg2); return elapsedTime; } else if (Interrupt == true) { return elapsedTime; } } } return elapsedTime; }
/// <summary> /// Add all available system contacts to the contact list. /// </summary> private void BuildContactsList() { m_oDetailsPanel.ContactListBox.Items.Clear(); if (_CurrnetShip != null && _CurrnetFC != null) { /// <summary> /// Planetary enemy populations should always be displayed. /// </summary> if (isBFC == true) { /// <summary> /// BFC range is so short that we'll just print all contacts and let the user sort em out. /// </summary> if (_CurrnetFaction.DetectedContactLists.ContainsKey(_CurrnetShip.ShipsTaskGroup.Contact.Position.System) == true) { #region BFC contacts, All are printed. foreach (KeyValuePair<ShipTN, FactionContact> pair in _CurrnetFaction.DetectedContactLists[_CurrnetShip.ShipsTaskGroup.Contact.Position.System].DetectedContacts) { String TH = ""; if (pair.Value.thermal == true) { TH = String.Format("[Thermal {0}]", pair.Key.CurrentThermalSignature); } String EM = ""; if (pair.Value.EM == true) { EM = String.Format("[EM {0}]", pair.Value.EMSignature); } String ACT = ""; if (pair.Value.active == true) { ACT = String.Format("[ACT {0}]", pair.Key.TotalCrossSection); } String Entry = String.Format("{0} {1}{2}{3}", pair.Key.Name, TH, EM, ACT); m_oDetailsPanel.ContactListBox.Items.Add(Entry); } foreach (KeyValuePair<OrdnanceGroupTN, FactionContact> pair in _CurrnetFaction.DetectedContactLists[_CurrnetShip.ShipsTaskGroup.Contact.Position.System].DetectedMissileContacts) { if (pair.Key.missiles.Count == 0) { String Error = String.Format("BuildContactList has an empty missileGroup in detectedMissileContacts."); MessageEntry MessageEnter = new MessageEntry(MessageEntry.MessageType.Error, _CurrnetShip.ShipsTaskGroup.Contact.Position.System, _CurrnetShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Error); _CurrnetFaction.MessageLog.Add(MessageEnter); continue; } String TH = ""; if (pair.Value.thermal == true) { TH = String.Format("[Thermal {0}]", (int)Math.Ceiling(pair.Key.missiles[0].missileDef.totalThermalSignature)); } String EM = ""; if (pair.Value.EM == true) { if (pair.Key.missiles[0].missileDef.aSD != null) { EM = String.Format("[EM {0}]", pair.Key.missiles[0].missileDef.aSD.gps); } else { String Error = String.Format("BuildContactList has a missile detected via EM that has no Active sensor(which is the only way it can be detected via EM)"); MessageEntry MessageEnter = new MessageEntry(MessageEntry.MessageType.Error, _CurrnetShip.ShipsTaskGroup.Contact.Position.System, _CurrnetShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Error); _CurrnetFaction.MessageLog.Add(MessageEnter); } } String ACT = ""; if (pair.Value.active == true) { ACT = String.Format("[ACT {0}]", (int)Math.Ceiling(pair.Key.missiles[0].missileDef.size)); } String Entry = String.Format("{0} {1}{2}{3} x{4}", pair.Key.Name, TH, EM, ACT, pair.Key.missiles.Count); m_oDetailsPanel.ContactListBox.Items.Add(Entry); } /// <summary> /// Display all population contacts. /// </summary> foreach (KeyValuePair<Population, FactionContact> pair in _CurrnetFaction.DetectedContactLists[_CurrnetShip.ShipsTaskGroup.Contact.Position.System].DetectedPopContacts) { String TH = ""; if (pair.Value.thermal == true) { TH = String.Format("[Thermal {0}]", pair.Key.ThermalSignature); } String EM = ""; if (pair.Value.EM == true) { EM = String.Format("[EM {0}]", pair.Value.EMSignature); } String ACT = ""; if (pair.Value.active == true) { ACT = String.Format("[Active Ping]"); } String Entry = String.Format("{0} {1}{2}{3}", pair.Key.Name, TH, EM, ACT); m_oDetailsPanel.ContactListBox.Items.Add(Entry); } #endregion } } else { /// <summary> /// Each MFC entry will be range checked, also there have been some tick errors, this may be the place to hunt them down. /// </summary> if (_CurrnetFaction.DetectedContactLists.ContainsKey(_CurrnetShip.ShipsTaskGroup.Contact.Position.System) == true) { foreach (KeyValuePair<ShipTN, FactionContact> pair in _CurrnetFaction.DetectedContactLists[_CurrnetShip.ShipsTaskGroup.Contact.Position.System].DetectedContacts) { float distance; _CurrnetShip.ShipsTaskGroup.Contact.DistTable.GetDistance(pair.Key.ShipsTaskGroup.Contact, out distance); int TCS = pair.Key.TotalCrossSection; int detectFactor = _CurrnetShip.ShipMFC[_CurrnetFC.componentIndex].mFCSensorDef.GetActiveDetectionRange(TCS, -1); bool det = _CurrnetShip.ShipsFaction.LargeDetection(distance, detectFactor); if (det == true) { String TH = ""; if (pair.Value.thermal == true) { TH = String.Format("[Thermal {0}]", pair.Key.CurrentThermalSignature); } String EM = ""; if (pair.Value.EM == true) { EM = String.Format("[EM {0}]", pair.Value.EMSignature); } String ACT = ""; if (pair.Value.active == true) { ACT = String.Format("[ACT {0}]", pair.Key.TotalCrossSection); } String Entry = String.Format("{0} {1}{2}{3}", pair.Key.Name, TH, EM, ACT); m_oDetailsPanel.ContactListBox.Items.Add(Entry); } } foreach (KeyValuePair<OrdnanceGroupTN, FactionContact> pair in _CurrnetFaction.DetectedContactLists[_CurrnetShip.ShipsTaskGroup.Contact.Position.System].DetectedMissileContacts) { if (pair.Key.missiles.Count == 0) { String Error = String.Format("BuildContactList has an empty missileGroup in detectedMissileContacts."); MessageEntry MessageEnter = new MessageEntry(MessageEntry.MessageType.Error, _CurrnetShip.ShipsTaskGroup.Contact.Position.System, _CurrnetShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Error); _CurrnetFaction.MessageLog.Add(MessageEnter); continue; } float distance; _CurrnetShip.ShipsTaskGroup.Contact.DistTable.GetDistance(pair.Key.contact, out distance); int MSP = (int)Math.Ceiling(pair.Key.missiles[0].missileDef.size); int sig = -1; int detectFactor = 0; /// <summary> /// Missile detection goes from 1-6 = minimum, 7-19 MSP specific, 20 and above treated as HS /// </summary> if (MSP <= ((Constants.OrdnanceTN.MissileResolutionMaximum + 6) + 1)) { if (MSP <= (Constants.OrdnanceTN.MissileResolutionMinimum + 6)) { sig = Constants.OrdnanceTN.MissileResolutionMinimum; } else if (MSP <= (Constants.OrdnanceTN.MissileResolutionMaximum + 6)) { sig = MSP - 6; } detectFactor = _CurrnetShip.ShipMFC[_CurrnetFC.componentIndex].mFCSensorDef.GetActiveDetectionRange(0, sig); } else { /// <summary> /// Big missiles will be treated in HS terms: 21-40 MSP = 2 HS, 41-60 = 3 HS, 61-80 = 4 HS, 81-100 = 5 HS. The same should hold true for greater than 100 sized missiles. /// but those are impossible to build. /// </summary> sig = (int)Math.Ceiling((float)MSP / 20.0f); detectFactor = _CurrnetShip.ShipMFC[_CurrnetFC.componentIndex].mFCSensorDef.GetActiveDetectionRange(sig, -1); } bool det = _CurrnetShip.ShipsFaction.LargeDetection(distance, detectFactor); if (det == true) { String TH = ""; if (pair.Value.thermal == true) { TH = String.Format("[Thermal {0}]", (int)Math.Ceiling(pair.Key.missiles[0].missileDef.totalThermalSignature)); } String EM = ""; if (pair.Value.EM == true) { if (pair.Key.missiles[0].missileDef.aSD != null) { EM = String.Format("[EM {0}]", pair.Key.missiles[0].missileDef.aSD.gps); } else { String Error = String.Format("BuildContactList has a missile detected via EM that has no Active sensor(which is the only way it can be detected via EM)"); MessageEntry MessageEnter = new MessageEntry(MessageEntry.MessageType.Error, _CurrnetShip.ShipsTaskGroup.Contact.Position.System, _CurrnetShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Error); _CurrnetFaction.MessageLog.Add(MessageEnter); } } String ACT = ""; if (pair.Value.active == true) { ACT = String.Format("[ACT {0}]", (int)Math.Ceiling(pair.Key.missiles[0].missileDef.size)); } String Entry = String.Format("{0} {1}{2}{3} x{4}", pair.Key.Name, TH, EM, ACT, pair.Key.missiles.Count); m_oDetailsPanel.ContactListBox.Items.Add(Entry); } } /// <summary> /// Display all population contacts, MFCs can always target planets and populations. /// </summary> foreach (KeyValuePair<Population, FactionContact> pair in _CurrnetFaction.DetectedContactLists[_CurrnetShip.ShipsTaskGroup.Contact.Position.System].DetectedPopContacts) { String TH = ""; if (pair.Value.thermal == true) { TH = String.Format("[Thermal {0}]", pair.Key.ThermalSignature); } String EM = ""; if (pair.Value.EM == true) { EM = String.Format("[EM {0}]", pair.Value.EMSignature); } String ACT = ""; if (pair.Value.active == true) { ACT = String.Format("[Active Ping]"); } String Entry = String.Format("{0} {1}{2}{3}", pair.Key.Name, TH, EM, ACT); m_oDetailsPanel.ContactListBox.Items.Add(Entry); } #warning Also print all waypoints as potential missile targets. } } } }
/// <summary> /// PD mode and range will be handled in this function. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void SetPDModeButton_Click(object sender, EventArgs e) { int index = m_oDetailsPanel.PDComboBox.SelectedIndex; float PointDefenseRange = 0.0f; bool res = float.TryParse(m_oDetailsPanel.PDRangeTextBox.Text, out PointDefenseRange); if (isBFC) { if (index <= (int)PointDefenseState.FinalDefensiveFireSelf) { if (res) _CurrnetShip.ShipBFC[_CurrnetFC.componentIndex].SetPointDefenseRange(PointDefenseRange); _CurrnetShip.ShipBFC[_CurrnetFC.componentIndex].SetPointDefenseMode((PointDefenseState)index); StarSystem CurrentSystem = _CurrnetShip.ShipsTaskGroup.Contact.Position.System; if (index != 0) { /// <summary> /// Add the star system list if one does not exist. /// </summary> if (_CurrnetFaction.PointDefense.ContainsKey(CurrentSystem) == false) { PointDefenseList PDL = new PointDefenseList(); _CurrnetFaction.PointDefense.Add(CurrentSystem, PDL); } /// <summary> /// Add the FC to the point defense FC list. BFC is ALWAYS false. /// </summary> if (_CurrnetFaction.PointDefense[CurrentSystem].PointDefenseFC.ContainsKey(_CurrnetShip.ShipBFC[_CurrnetFC.componentIndex]) == false) { _CurrnetFaction.PointDefense[CurrentSystem].AddComponent(_CurrnetShip.ShipBFC[_CurrnetFC.componentIndex], _CurrnetShip, false); _CurrnetShip.ShipsTaskGroup.TaskGroupPDL.AddComponent(_CurrnetShip.ShipBFC[_CurrnetFC.componentIndex], _CurrnetShip, false); } } else { /// <summary> /// This bug will pop up if a ship gets transported to another star system and it isn't handled properly. /// </summary> if (_CurrnetFaction.PointDefense.ContainsKey(CurrentSystem) == false) { String Error = String.Format("Star System {0} not found in point defense listing for {1} on {2}.", CurrentSystem, _CurrnetShip.ShipBFC[_CurrnetFC.componentIndex], _CurrnetShip); MessageEntry MessageEnter = new MessageEntry(MessageEntry.MessageType.Error, _CurrnetShip.ShipsTaskGroup.Contact.Position.System, _CurrnetShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Error); _CurrnetFaction.MessageLog.Add(MessageEnter); } else { /// <summary> /// Remove the FC from the point defense FC list. /// </summary> if (_CurrnetFaction.PointDefense[CurrentSystem].PointDefenseFC.ContainsKey(_CurrnetShip.ShipBFC[_CurrnetFC.componentIndex]) == true) { _CurrnetFaction.PointDefense[CurrentSystem].RemoveComponent(_CurrnetShip.ShipBFC[_CurrnetFC.componentIndex]); _CurrnetShip.ShipsTaskGroup.TaskGroupPDL.RemoveComponent(_CurrnetShip.ShipBFC[_CurrnetFC.componentIndex]); } /// <summary> /// cleanup the starsystem so that the point defense list isn't cluttered. /// </summary> if (_CurrnetFaction.PointDefense[CurrentSystem].PointDefenseFC.Count == 0) { _CurrnetFaction.PointDefense.Remove(CurrentSystem); } } } } else { String Error = String.Format("Improper point defense state {0} assigned to BFC {1} on {2}", (PointDefenseState)index, _CurrnetFC, _CurrnetShip); MessageEntry MessageEnter = new MessageEntry(MessageEntry.MessageType.Error, _CurrnetShip.ShipsTaskGroup.Contact.Position.System, _CurrnetShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Error); _CurrnetFaction.MessageLog.Add(MessageEnter); } } else { if (index != 0) index = index + 3; if (index == 0 || (index >= (int)PointDefenseState.AMM1v2 && index <= (int)PointDefenseState.AMM5v1)) { if (res) _CurrnetShip.ShipMFC[_CurrnetFC.componentIndex].SetPointDefenseRange(PointDefenseRange); _CurrnetShip.ShipMFC[_CurrnetFC.componentIndex].SetPointDefenseMode((PointDefenseState)index); StarSystem CurrentSystem = _CurrnetShip.ShipsTaskGroup.Contact.Position.System; /// <summary> /// Clear Point defense. ClearTarget() further down also clears point defense so make any relevant changes there as well. /// </summary> if (index != 0) { /// <summary> /// Add the star system list if one does not exist. /// </summary> if (_CurrnetFaction.PointDefense.ContainsKey(CurrentSystem) == false) { PointDefenseList PDL = new PointDefenseList(); _CurrnetFaction.PointDefense.Add(CurrentSystem, PDL); } /// <summary> /// Add the FC to the point defense FC list. MFC is ALWAYS true. /// </summary> if (_CurrnetFaction.PointDefense[CurrentSystem].PointDefenseFC.ContainsKey(_CurrnetShip.ShipMFC[_CurrnetFC.componentIndex]) == false) { _CurrnetFaction.PointDefense[CurrentSystem].AddComponent(_CurrnetShip.ShipMFC[_CurrnetFC.componentIndex], _CurrnetShip, true); _CurrnetShip.ShipsTaskGroup.TaskGroupPDL.AddComponent(_CurrnetShip.ShipMFC[_CurrnetFC.componentIndex], _CurrnetShip, true); } } else { /// <summary> /// This bug will pop up if a ship gets transported to another star system and it isn't handled properly. /// </summary> if (_CurrnetFaction.PointDefense.ContainsKey(CurrentSystem) == false) { #warning leave this error message in for now String Error = String.Format("Star System {0} not found in point defense listing for {1} on {2}. Not necessarily a bug.", CurrentSystem, _CurrnetShip.ShipMFC[_CurrnetFC.componentIndex], _CurrnetShip); MessageEntry MessageEnter = new MessageEntry(MessageEntry.MessageType.Error, _CurrnetShip.ShipsTaskGroup.Contact.Position.System, _CurrnetShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Error); _CurrnetFaction.MessageLog.Add(MessageEnter); } /// <summary> /// Remove the FC from the point defense FC list. /// </summary> if (_CurrnetFaction.PointDefense.Count != 0) { if (_CurrnetFaction.PointDefense.ContainsKey(CurrentSystem) == true) { if (_CurrnetFaction.PointDefense[CurrentSystem].PointDefenseFC.ContainsKey(_CurrnetShip.ShipMFC[_CurrnetFC.componentIndex]) == true) { _CurrnetFaction.PointDefense[CurrentSystem].RemoveComponent(_CurrnetShip.ShipMFC[_CurrnetFC.componentIndex]); _CurrnetShip.ShipsTaskGroup.TaskGroupPDL.RemoveComponent(_CurrnetShip.ShipMFC[_CurrnetFC.componentIndex]); } /// <summary> /// Cleanup the starsystem so that the point defense list isn't cluttered. /// </summary> if (_CurrnetFaction.PointDefense[CurrentSystem].PointDefenseFC.Count == 0) { _CurrnetFaction.PointDefense.Remove(CurrentSystem); } } } } } else { String Error = String.Format("Improper point defense state {0} assigned to MFC {1} on {2}", (PointDefenseState)index, _CurrnetFC, _CurrnetShip); MessageEntry MessageEnter = new MessageEntry(MessageEntry.MessageType.Error, _CurrnetShip.ShipsTaskGroup.Contact.Position.System, _CurrnetShip.ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Error); _CurrnetFaction.MessageLog.Add(MessageEnter); } } BuildCombatSummary(); }
/// <summary> /// This function calculates whether a given BFC can intercept a missile /// </summary> /// <param name="RNG">RNG to use, should be the global one in _SE_</param> /// <param name="IncrementDistance">Range to the target missile</param> /// <param name="Ordnance">Ordnance we want to shoot at.</param> /// <param name="ShipFaction">Faction of the ship this BFC is on.</param> /// <param name="Contact">Contact of the taskgroup this BFC is in.</param> /// <param name="ShipOn">Ship this BFC is on.</param> /// <param name="WeaponsFired">Whether or not a weapon was fired. this is for the recharge list further up</param> /// <returns>whether the missile was intercepted.</returns> public bool InterceptTarget(Random RNG, int IncrementDistance, OrdnanceTN Ordnance, Faction ShipFaction, SystemContact Contact, ShipTN ShipOn, out bool WeaponsFired) { WeaponsFired = false; float ShipSpeed = ShipOn.CurrentSpeed; float track = (float)ShipFaction.BaseTracking; if (ShipSpeed > track) track = ShipSpeed; if (m_oBeamFireControlDef.tracking < track) track = m_oBeamFireControlDef.tracking; /// <summary> /// Throwaway target for point defense purposes. /// </summary> TargetTN OverrideTarget = new TargetTN(Ordnance.missileGroup); float Acc = GetFiringAccuracy(IncrementDistance, (int)track, OverrideTarget); int toHit = (int)Math.Floor(Acc * 100.0f); int range = (IncrementDistance + 1) * 10000; String Range = range.ToString("#,###0"); foreach (BeamTN LinkedWeapon in m_lLinkedWeapons) { /// <summary> /// Certain weapons will have already fired one or more of their shots, but may still have more available. /// </summary> bool AcceptPartialFire = (LinkedWeapon.beamDef.componentType == ComponentTypeTN.Rail || LinkedWeapon.beamDef.componentType == ComponentTypeTN.AdvRail || LinkedWeapon.beamDef.componentType == ComponentTypeTN.Gauss) && (LinkedWeapon.shotsExpended < LinkedWeapon.beamDef.shotCount); if (LinkedWeapon.readyToFire() == true || AcceptPartialFire == true) { if (LinkedWeapon.beamDef.componentType == ComponentTypeTN.Rail || LinkedWeapon.beamDef.componentType == ComponentTypeTN.AdvRail || LinkedWeapon.beamDef.componentType == ComponentTypeTN.Gauss) { WeaponsFired = LinkedWeapon.Fire(); /// <summary> /// multi-hit weapons will be a little wierd as far as PD goes. /// </summary> if (WeaponsFired == false && AcceptPartialFire == true) WeaponsFired = true; int expended = LinkedWeapon.shotsExpended; int ShotCount = LinkedWeapon.beamDef.shotCount; for (int BeamShotIterator = expended; BeamShotIterator < ShotCount; BeamShotIterator++) { ushort Hit = (ushort)RNG.Next(1, 100); LinkedWeapon.shotsExpended++; if (toHit >= Hit) { String Entry = String.Format("{0} Fired at {1} km and hit.", LinkedWeapon.Name, Range); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.FiringHit, Contact.Position.System, Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); ShipFaction.MessageLog.Add(Msg); return true; } else { String Entry = String.Format("{0} Fired at {1} km and missed.", LinkedWeapon.Name, Range); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.FiringHit, Contact.Position.System, Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); ShipFaction.MessageLog.Add(Msg); } } } else { ushort Hit = (ushort)RNG.Next(1, 100); WeaponsFired = LinkedWeapon.Fire(); if (toHit >= Hit) { String Entry = String.Format("{0} Fired at {1} km and hit.", LinkedWeapon.Name, Range); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.FiringHit, Contact.Position.System, Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); ShipFaction.MessageLog.Add(Msg); return true; } else { String Entry = String.Format("{0} Fired at {1} km and missed.", LinkedWeapon.Name, Range); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.FiringHit, Contact.Position.System, Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); ShipFaction.MessageLog.Add(Msg); } } } } foreach (TurretTN LinkedTurret in m_lLinkedTurrets) { /// <summary> /// Double, triple, and quad turrets have multiple shots. /// </summary> bool AcceptPartialFire = (LinkedTurret.shotsExpended < LinkedTurret.turretDef.totalShotCount); if (LinkedTurret.readyToFire() == true || AcceptPartialFire == true) { WeaponsFired = LinkedTurret.Fire(); /// <summary> /// multi-hit weapons will be a little wierd as far as PD goes. /// </summary> if (WeaponsFired == false && AcceptPartialFire == true) WeaponsFired = true; int expended = LinkedTurret.shotsExpended; int ShotCount = LinkedTurret.turretDef.totalShotCount; for (int TurretShotIterator = expended; TurretShotIterator < ShotCount; TurretShotIterator++) { ushort Hit = (ushort)RNG.Next(1, 100); LinkedTurret.shotsExpended++; if (toHit >= Hit) { String Entry = String.Format("{0} Fired at {1} km and hit.", LinkedTurret.Name, Range); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.FiringHit, Contact.Position.System, Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); ShipFaction.MessageLog.Add(Msg); return true; } else { String Entry = String.Format("{0} Fired at {1} km and missed.", LinkedTurret.Name, Range); MessageEntry Msg = new MessageEntry(MessageEntry.MessageType.FiringHit, Contact.Position.System, Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Entry); ShipFaction.MessageLog.Add(Msg); } } } } return false; }
/// <summary> /// Damage goes through a 3 part process, 1st shields subtract damage, then armor blocks damage, then internals take the hits. /// if 20 rolls happen without an internal in the list being targeted then call OnDestroyed(); Mesons skip to the internal damage section. /// Microwaves do shield damage, then move to the special electronic only DAC. /// </summary> /// <param name="Type">Type of damage, for armor penetration.</param> /// <param name="Value">How much damage is being done.</param> /// <param name="HitLocation">Where Armor damage is inflicted. Temporary argument for the time being. remove these when rngs are resolved.</param> /// <returns>Whether or not the ship was destroyed as a result of this action.</returns> public bool OnDamaged(DamageTypeTN Type, ushort Value, ushort HitLocation, ShipTN FiringShip) { ushort Damage = Value; ushort internalDamage = 0; ushort startDamage = Value; bool ColumnPenetration = false; int LastColumnValue = ShipArmor.armorDef.depth; if (Type != DamageTypeTN.Meson) { /// <summary> /// Handle Shield Damage. /// Microwaves do 3 damage to shields. Make them do 3xPowerReq? /// </summary> if (Type == DamageTypeTN.Microwave) { if (CurrentShieldPool >= 3.0f) { CurrentShieldPool = CurrentShieldPool - 3.0f; Damage = 0; } else if (CurrentShieldPool < 1.0f) { CurrentShieldPool = 0.0f; } else { /// <summary> /// Microwaves only do 1 damage to internals, so take away the bonus damage to shields here. /// </summary> Damage = 1; CurrentShieldPool = 0.0f; } } else { if (CurrentShieldPool >= Damage) { CurrentShieldPool = CurrentShieldPool - Damage; Damage = 0; } else if (CurrentShieldPool < 1.0f) { CurrentShieldPool = 0.0f; } else { Damage = (ushort)(Damage - (ushort)Math.Floor(CurrentShieldPool)); CurrentShieldPool = 0.0f; } } /// <summary> /// Shields absorbed all damage. /// </summary> if (Damage == 0) { String DamageString = String.Format("All damage to {0} absorbed by shields", Name); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamage, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); ShipsFaction.MessageLog.Add(NMsg); return false; } else { if ((startDamage - Damage) > 0) { String DamageString = String.Format("{0} damage to {1} absorbed by shields", (startDamage - Damage), Name); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamage, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); ShipsFaction.MessageLog.Add(NMsg); } } startDamage = Damage; if (Type != DamageTypeTN.Microwave) { /// <summary> /// Shock Damage: /// </summary> Random ShockRand = new Random((int)startDamage); float ShockChance = (float)Math.Floor(Math.Pow(startDamage, 1.3)); int shockTest = ShockRand.Next(0, 100); /// <summary> /// There is a chance for shock damage to occur /// </summary> if (shockTest > ShockChance) { float sTest = (float)ShockRand.Next(0, 100) / 100.0f; internalDamage = (ushort)Math.Floor(((startDamage / 3.0f) * sTest)); if (internalDamage != 0) { String DamageString = String.Format("{0} Shock Damage to {1}", (internalDamage), Name); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamage, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); ShipsFaction.MessageLog.Add(NMsg); } } /// <summary> /// Armor Penetration. /// </summary> ushort Columns = ShipArmor.armorDef.cNum; short left, right; ushort ImpactLevel = ShipArmor.armorDef.depth; if (ShipArmor.isDamaged == true) ImpactLevel = ShipArmor.armorColumns[HitLocation]; DamageTableTN Table; switch (Type) { case DamageTypeTN.Beam: Table = DamageValuesTN.EnergyTable[Damage - 1]; break; case DamageTypeTN.Kinetic: Table = DamageValuesTN.KineticTable[Damage - 1]; break; case DamageTypeTN.Missile: Table = DamageValuesTN.MissileTable[Damage - 1]; break; case DamageTypeTN.Plasma: Table = DamageValuesTN.PlasmaTable[Damage - 1]; break; default: Table = DamageValuesTN.MissileTable[Damage - 1]; break; } left = (short)(HitLocation - 1); right = (short)(HitLocation + 1); /// <summary> /// What is the column damage level at Hit Location? /// </summary> if (ShipArmor.isDamaged == true) { LastColumnValue = ShipArmor.armorColumns[HitLocation]; } /// <summary> /// internalDamage is all damage that passed through the armour. /// </summary> internalDamage = (ushort)ShipArmor.SetDamage(Columns, ShipArmor.armorDef.depth, HitLocation, Table.damageTemplate[Table.hitPoint]); /// <summary> /// If this is a new penetration note this fact. /// </summary> if (LastColumnValue != 0 && internalDamage != 0) { ColumnPenetration = true; } /// <summary> /// The plasma template is a little wierd and requires handling this condition. basically it has two maximum strength penetration attacks. /// </summary> if (Type == DamageTypeTN.Plasma && Table.hitPoint + 1 < Table.damageTemplate.Count) { if (ShipArmor.isDamaged == true) { LastColumnValue = ShipArmor.armorColumns[(HitLocation + 1)]; } internalDamage = (ushort)((ushort)internalDamage + (ushort)ShipArmor.SetDamage(Columns, ShipArmor.armorDef.depth, (ushort)(HitLocation + 1), Table.damageTemplate[Table.hitPoint + 1])); if (LastColumnValue != 0 && internalDamage != 0) { ColumnPenetration = true; } right++; } /// <summary> /// Calculate the armour damage to the left and right of the hitLocation. /// </summary> for (int loop = 1; loop <= Table.halfSpread; loop++) { if (left < 0) { left = (short)(Columns - 1); } if (right >= Columns) { right = 0; } /// <summary> /// side impact damage doesn't always reduce armor, the principle hitpoint should be the site of the deepest armor penetration. Damage can be wasted in this manner. /// </summary> if (Table.hitPoint - loop >= 0) { if (ShipArmor.isDamaged == true) { LastColumnValue = ShipArmor.armorColumns[left]; } if (ImpactLevel - Table.damageTemplate[Table.hitPoint - loop] < ShipArmor.armorColumns[left]) internalDamage = (ushort)((ushort)internalDamage + (ushort)ShipArmor.SetDamage(Columns, ShipArmor.armorDef.depth, (ushort)left, Table.damageTemplate[Table.hitPoint - loop])); if (LastColumnValue != 0 && internalDamage != 0) { ColumnPenetration = true; } } if (Table.hitPoint + loop < Table.damageTemplate.Count) { if (ShipArmor.isDamaged == true) { LastColumnValue = ShipArmor.armorColumns[right]; } if (ImpactLevel - Table.damageTemplate[Table.hitPoint + loop] < ShipArmor.armorColumns[right]) internalDamage = (ushort)((ushort)internalDamage + (ushort)ShipArmor.SetDamage(Columns, ShipArmor.armorDef.depth, (ushort)right, Table.damageTemplate[Table.hitPoint + loop])); if (LastColumnValue != 0 && internalDamage != 0) { ColumnPenetration = true; } } left--; right++; } if ((startDamage - internalDamage) > 0) { String DamageString = String.Format("{0} damage to {1} absorbed by Armour", (startDamage - internalDamage), Name); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamage, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); ShipsFaction.MessageLog.Add(NMsg); } if (ColumnPenetration == true) { String DamageString = "N/A"; /// <summary> /// Need a switch here for organic or solid state ships to change or remove this message. /// </summary> switch (TypeOf) { case ShipType.Standard: DamageString = String.Format("{0} is streaming atmosphere", Name); break; case ShipType.Organic: DamageString = String.Format("{0} is streaming fluid", Name); break; } if (TypeOf == ShipType.Standard || TypeOf == ShipType.Organic) { MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamageReport, FiringShip.ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); FiringShip.ShipsFaction.MessageLog.Add(NMsg); } } } else { /// <summary> /// This is a microwave strike. /// </summary> internalDamage = 1; } } else { /// <summary> /// This is a meson strike. /// </summary> internalDamage = 1; } /// <summary> /// Internal Component Damage. Each component with an HTK >0 can take atleast 1 hit. a random number is rolled over the entire dac. the selected component's HTK /// is tested against the internal damage value, and if greater than the damage value the component has a chance of surviving. otherwise, the component is destroyed, damage /// is reduced, and the next component is chosen. /// DAC Should be redone as a binary tree at some later date. /// /// The Electronic DAC should be used for microwave hits, ships can't be destroyed due to microwave strikes however. /// </summary> int Attempts = 0; Random DacRNG = new Random(HitLocation); if (Type != DamageTypeTN.Microwave) { /// <summary> /// If 20 attempts to damage a component are made unsuccessfully the ship is considered destroyed. Does this scale well with larger ships? /// </summary> while (Attempts < 20 && internalDamage > 0) { int DACHit = DacRNG.Next(1, ShipClass.DamageAllocationChart[ShipClass.ListOfComponentDefs[ShipClass.ListOfComponentDefs.Count - 1]]); int localDAC = 1; int previousDAC = 1; int destroy = -1; for (int loop = 0; loop < ShipClass.ListOfComponentDefs.Count; loop++) { localDAC = ShipClass.DamageAllocationChart[ShipClass.ListOfComponentDefs[loop]]; if (DACHit <= localDAC) { float size = ShipClass.ListOfComponentDefs[loop].size; if (size < 1.0) size = 1.0f; destroy = (int)Math.Floor(((float)(DACHit - previousDAC) / (float)size)); /// <summary> /// By this point total should definitely be >= destroy. destroy is the HS of the group being hit. /// Should I try to find the exact component hit, or merely loop through all of them? /// internalDamage: Damage done to all internals /// destroy: component to destroy from shipClass.ListOfComponentDefs /// ComponentDefIndex[loop] where in ShipComponents this definition group is. /// </summary> int DamageDone = DestroyComponent(ShipClass.ListOfComponentDefs[loop].componentType, loop, internalDamage, destroy, DacRNG); if (DamageDone != -1) { int ID = ComponentDefIndex[loop] + destroy; if (ShipComponents[ID].isDestroyed == true) { String DamageString = String.Format("{0} hit by {1} damage and was destroyed", ShipComponents[ID].Name, DamageDone); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamage, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); ShipsFaction.MessageLog.Add(NMsg); } else { String DamageString = String.Format("{0} Absorbed {1} damage", ShipComponents[ID].Name, DamageDone); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamage, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); ShipsFaction.MessageLog.Add(NMsg); } } /// <summary> /// No components are left to destroy, so short circuit the loops,destroy the ship, and create a wreck. /// </summary> if (DestroyedComponents.Count == ShipComponents.Count) { Attempts = 20; internalDamage = 0; break; } if (DamageDone == -1) { Attempts++; if (Attempts == 20) { internalDamage = 0; } break; } else if (DamageDone == -2) { Attempts = 20; break; } else { internalDamage = (ushort)(internalDamage - (ushort)DamageDone); break; } } previousDAC = localDAC + 1; } } if (Attempts == 20) { String DamageString = String.Format("{0} Destroyed", Name); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamage, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); ShipsFaction.MessageLog.Add(NMsg); FiringShip.ShipsFaction.MessageLog.Add(NMsg); IsDestroyed = true; return true; } } else { /// <summary> /// Electronic damage can never destroy a craft, only wreck its sensors, so we'll cut short the attempts to damage components to only 5. /// There is no list of only electronic components, and only destroyed electronic components so this will have to do for now. /// Having those would improve performance slightly however. /// </summary> while (Attempts < 5 && internalDamage > 0) { ComponentDefTN last = ShipClass.ElectronicDamageAllocationChart.Keys.Last(); int DACHit = DacRNG.Next(1, ShipClass.ElectronicDamageAllocationChart[last]); int localDAC = 1; int previousDAC = 1; int destroy = -1; foreach (KeyValuePair<ComponentDefTN, int> list in ShipClass.ElectronicDamageAllocationChart) { localDAC = ShipClass.ElectronicDamageAllocationChart[list.Key]; if (DACHit <= localDAC) { float size = list.Key.size; if (size < 1.0) size = 1.0f; /// <summary> /// Electronic component to attempt to destroy: /// </summary> destroy = (int)Math.Floor(((float)(DACHit - previousDAC) / (float)size)); /// <summary> /// Actually destroy the component. /// Store EDAC index values somewhere for speed? /// </summary> int CI = ShipClass.ListOfComponentDefs.IndexOf(list.Key); int ComponentIndex = ShipComponents[ComponentDefIndex[CI] + destroy].componentIndex; float hardCheck = (float)DacRNG.Next(1, 100); float hardValue = -1.0f; switch (list.Key.componentType) { case ComponentTypeTN.ActiveSensor: hardValue = ShipASensor[ComponentIndex].aSensorDef.hardening * 100.0f; break; case ComponentTypeTN.PassiveSensor: hardValue = ShipPSensor[ComponentIndex].pSensorDef.hardening * 100.0f; break; case ComponentTypeTN.BeamFireControl: hardValue = ShipBFC[ComponentIndex].beamFireControlDef.hardening * 100.0f; break; case ComponentTypeTN.MissileFireControl: hardValue = ShipMFC[ComponentIndex].mFCSensorDef.hardening * 100.0f; break; } int DamageDone = -1; if (hardValue == -1) { /// <summary> /// This is an error condition obviously. I likely forgot to put the component in above however. /// </summary> String ErrorString = String.Format("Unidentified electronic component in onDamaged() Type:{0}.", list.Key.componentType); MessageEntry EMsg = new MessageEntry(MessageEntry.MessageType.Error, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, ErrorString); ShipsFaction.MessageLog.Add(EMsg); } else { if (hardCheck < hardValue) { DamageDone = DestroyComponent(list.Key.componentType, CI, internalDamage, destroy, DacRNG); if (DamageDone != -1) { int ID = ComponentDefIndex[CI] + destroy; if (ShipComponents[ID].isDestroyed == true) { String DamageString = String.Format("{0} hit by {1} damage and was destroyed", ShipComponents[ID].Name, DamageDone); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamage, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); ShipsFaction.MessageLog.Add(NMsg); } else { String DamageString = String.Format("{0} Absorbed {1} damage. Electronic Components shouldn't resist damage like this", ShipComponents[ID].Name, DamageDone); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamage, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); ShipsFaction.MessageLog.Add(NMsg); } } } else { int ID = ComponentDefIndex[CI] + destroy; String DamageString = String.Format("{0} Absorbed {1} damage", ShipComponents[ID].Name, DamageDone); MessageEntry NMsg = new MessageEntry(MessageEntry.MessageType.ShipDamage, ShipsTaskGroup.Contact.Position.System, ShipsTaskGroup.Contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, DamageString); ShipsFaction.MessageLog.Add(NMsg); DamageDone = 0; } } if (DamageDone == -1) { Attempts++; if (Attempts == 5) { internalDamage = 0; } break; } else { /// <summary> /// Electronic damage should always be only 1. /// </summary> internalDamage = 0; break; } } previousDAC = localDAC + 1; } } } return false; }
/// <summary> /// Each tick every faction contact should be updated on the basis of collected sensor data. /// </summary> /// <param name="Thermal">Thermal detection?</param> /// <param name="Em">Detected on EM?</param> /// <param name="Active">Detected by actives?</param> /// <param name="tick">Current tick.</param> public void updateFactionContact(Faction CurrentFaction, bool Thermal, bool Em, int EMSig, bool Active, uint tick, uint year) { if (thermal == Thermal && EM == Em && active == Active) { return; } String Contact = "N/A"; MessageEntry.MessageType type = MessageEntry.MessageType.Count; if (Thermal == false && Em == false && Active == false) { Contact = "Existing contact lost"; type = MessageEntry.MessageType.ContactLost; } else { Contact = "Update on existing contact:"; type = MessageEntry.MessageType.ContactUpdate; if (thermal == false && Thermal == true) { /// <summary> /// New thermal detection event, message logic should be here. /// </summary> thermalTick = tick; thermalYear = year; if (ship != null) { Contact = String.Format("{0} Thermal Signature {1}", Contact, ship.CurrentThermalSignature); } else if (missileGroup != null) { Contact = String.Format("{0} Thermal Signature {1} x{2}", Contact, (int)Math.Ceiling(missileGroup.missiles[0].missileDef.totalThermalSignature), missileGroup.missiles.Count); } else if (pop != null) { Contact = String.Format("{0} Thermal Signature {1}", Contact, pop.ThermalSignature); } else { type = MessageEntry.MessageType.Error; Contact = "Error: ship,missile, and pop are null in UpdateFactionContact."; } } else if (thermal == true && Thermal == false) { /// <summary> /// Thermal contact lost. /// </summary> Contact = String.Format("{0} Thermal contact lost", Contact); } if (EM == false && Em == true) { /// <summary> /// New EM detection event, message logic should be here. /// </summary> EMTick = tick; EMYear = year; EMSignature = EMSig; if (ship != null) { Contact = String.Format("{0} EM Signature {1}", Contact, EMSignature); } else if (missileGroup != null) { if (missileGroup.missiles[0].missileDef.aSD != null) { Contact = String.Format("{0} EM Signature {1} x{2}", Contact, missileGroup.missiles[0].missileDef.aSD.gps, missileGroup.missiles.Count); } else { type = MessageEntry.MessageType.Error; Contact = "Error: Missile lacks a sensor, but is emitting EM in UpdateFactionContact."; } } else if (pop != null) { Contact = String.Format("{0} EM Signature {1}", Contact, pop.EMSignature); } else { type = MessageEntry.MessageType.Error; Contact = "Error: ship,missile, and pop are null in UpdateFactionContact."; } } if (EM == true && Em == false) { EMSignature = -1; /// <summary> /// EM contact lost. /// </summary> Contact = String.Format("{0} EM contact lost", Contact); } if (active == false && Active == true) { /// <summary> /// New active detection event, message logic should be here. /// </summary> activeTick = tick; activeYear = year; if (ship != null) { Contact = String.Format("{0} TCS {1}", Contact, ship.TotalCrossSection); } else if (missileGroup != null) { Contact = String.Format("{0} TCS_MSP {1} x{2}", Contact, (int)Math.Ceiling(missileGroup.missiles[0].missileDef.size), missileGroup.missiles.Count); } else if (pop != null) { Contact = String.Format("{0} Active Ping", Contact); //don't bother printing TCS for planets. } else { type = MessageEntry.MessageType.Error; Contact = "Error: ship,missile, and pop are null in UpdateFactionContact."; } } if (active == true && Active == false) { /// <summary> /// Active contact lost. /// </summary> Contact = String.Format("{0} Active contact lost", Contact); } } SystemContact SysCon = null; if (ship != null) { SysCon = ship.ShipsTaskGroup.Contact; } else if (missileGroup != null) { SysCon = missileGroup.contact; } else if (pop != null) { SysCon = pop.Contact; } else { type = MessageEntry.MessageType.Error; Contact = "Error: ship,missile, and pop are null in UpdateFactionContact."; } MessageEntry NMsg; if (type == MessageEntry.MessageType.Error) { NMsg = new MessageEntry(type, null, null, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Contact); } else { NMsg = new MessageEntry(type, SysCon.Position.System, SysCon, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Contact); } CurrentFaction.MessageLog.Add(NMsg); thermal = Thermal; EM = Em; active = Active; }
/// <summary> /// Initializer for detected missile event. FactionContact is the detector side of what is detected, while OrdnanceTN itself stores the detectee side. /// multiple of these can exist, but only 1 per faction hopefully. /// </summary> /// <param name="DetectedMissile">Missile detected.</param> /// <param name="Thermal">Was the detection thermal based?</param> /// <param name="em">Detection via EM?</param> /// <param name="Active">Active detection?</param> /// <param name="tick">What tick did this detection event occur on?</param> public FactionContact(Faction CurrentFaction, OrdnanceGroupTN DetectedMissileGroup, bool Thermal, bool em, bool Active, uint tick) { missileGroup = DetectedMissileGroup; ship = null; pop = null; thermal = Thermal; EM = em; EMSignature = 0; active = Active; MessageEntry NMsg; String Contact = "New contact detected:"; if (thermal == true) { thermalTick = tick; Contact = String.Format("{0} Thermal Signature {1} x{2}", Contact, (int)Math.Ceiling(DetectedMissileGroup.missiles[0].missileDef.totalThermalSignature), DetectedMissileGroup.missiles.Count); } if (EM == true) { EMTick = tick; if (DetectedMissileGroup.missiles[0].missileDef.aSD != null) { EMSignature = DetectedMissileGroup.missiles[0].missileDef.aSD.gps; Contact = String.Format("{0} EM Signature {1} x{2}", Contact, DetectedMissileGroup.missiles[0].missileDef.aSD.gps, DetectedMissileGroup.missiles.Count); } else { Contact = String.Format("Error with {0} : has EM signature but no active sensor.", Contact); NMsg = new MessageEntry(MessageEntry.MessageType.Error, DetectedMissileGroup.contact.Position.System, DetectedMissileGroup.contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Contact); CurrentFaction.MessageLog.Add(NMsg); } } if (active == true) { activeTick = tick; Contact = String.Format("{0} TCS {1} x{2}", Contact, (int)Math.Ceiling(DetectedMissileGroup.missiles[0].missileDef.size), DetectedMissileGroup.missiles.Count); } /// <summary> /// print to the message log. /// </summary> NMsg = new MessageEntry(MessageEntry.MessageType.ContactNew, DetectedMissileGroup.contact.Position.System, DetectedMissileGroup.contact, GameState.Instance.GameDateTime, GameState.Instance.LastTimestep, Contact); CurrentFaction.MessageLog.Add(NMsg); /// <summary> /// Inform SimEntity. /// </summary> GameState.SE.SetInterrupt(InterruptType.NewSensorContact); }