// Calculates the infection chance for a given integrated exposure. public static float GetInfectionChance(GermExposure ge, int total, float resistance) { /* * Allow one exposure count with 0 effects, then use exponential function: * * Chance to infect = 1.0 - e^(FACTOR * -(totalOverThreshold / threshold)) * * With factor at 0.5: * Double exposure count ~ 40 % * Triple exposure count ~ 63 % * Quadruple exposure count ~ 77 % * * Resistance adjusts the thresholds */ int threshold = GermExposureTuning.ThresholdsFor(ge.Exposure).GetThreshold(ge. Vector); float adjThres = AdjustedThreshold(threshold, resistance), overTotal = Mathf.Floor( total - adjThres); // threshold of 1 is instant infection return((overTotal > 0.0f) ? ((threshold < 2) ? 1.0f : (1.0f - Mathf.Exp( -GermExposureTuning.CONTRACT_FACTOR * overTotal / adjThres))) : 0.0f); }
// Retrieves the worst chance for germ infection across all vectors. internal float GetWorstInfectionChance(ExposureType exposure, float resist) { float worstChance = 0.0f; if (exposure == null) { throw new ArgumentNullException("exposure"); } foreach (var vector in ALL_VECTORS) { var ge = new GermExposure(vector, exposure); if (integratedExposure.TryGetValue(ge, out int byVector)) { // Resistance should be calculated by the SMI float chance = GetInfectionChance(ge, byVector, resist); if (chance > worstChance) { worstChance = chance; } } } return(worstChance); }
// Performs the dirty work of counting germs on a Duplicant. private void DoInjectDisease(GermExposureMonitor.Instance monitor, Disease disease, int count, Tag source, GermExposure ge) { var exposure = ge.Exposure; int threshold = GermExposureTuning.ThresholdsFor(exposure).GetThreshold(ge.Vector); if (threshold > 0 && gameObject != null) { string germ = ge.GermID; var state = monitor.GetExposureState(germ); // Eligible addition to the integrated exposure calculations if (integratedExposure.TryGetValue(ge, out int total)) { total += count; } else { total = count; } #if DEBUG PUtil.LogDebug("{0} has {1:D} germs of {2} by {3} (threshold = {4:D})".F( gameObject.name, count, germ, ge.Vector, threshold)); #endif integratedExposure[ge] = total; // Resistance is required to move the thresholds which is used for tier // calculation float resist = GetResistance(exposure), adjThres = AdjustedThreshold( threshold, resist), tier = total / adjThres; switch (state) { case ExposureState.None: case ExposureState.Contact: // Update location where they encountered the germ monitor.lastDiseaseSources[disease.id] = new DiseaseSourceInfo(source, ge.Vector, GetInfectionChance(ge, total, resist), transform. GetPosition()); if (total > adjThres) { if (exposure.infect_immediately) { // The ultimate evil flag #if DEBUG PUtil.LogDebug("Infecting {0} with {1}".F(gameObject.name, germ)); #endif monitor.InfectImmediately(exposure); } else { // "Exposed to slimelung" monitor.SetExposureState(germ, ExposureState.Exposed); monitor.SetExposureTier(germ, tier); #if DEBUG PUtil.LogDebug("{0} exposed to {1} tier {2:F1}".F(gameObject.name, germ, tier)); #endif } } else if (state == ExposureState.None) { // "Contact with slimelung" monitor.SetExposureState(germ, ExposureState.Contact); } break; case ExposureState.Exposed: // Upgrade the visual tier if (total > threshold && tier > monitor.GetExposureTier(germ)) { monitor.SetExposureTier(germ, tier); #if DEBUG PUtil.LogDebug("{0} exposure of {1} upgraded to {2:F1}".F(gameObject. name, germ, tier)); #endif } break; case ExposureState.Contracted: case ExposureState.Sick: // Already sick break; default: break; } } }