// 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); }
// Creates one line of the immune system information panel. private static void CreateOneImmuneInfo(Disease disease, GameObject target, GermIntegrator integrator, CollapsibleDetailContentPanel panel) { var exposure = GameUtil.GetExposureTypeForDisease(disease); var sickness = GameUtil.GetSicknessForDisease(disease); ICollection <string> required = exposure.required_traits, excluded = exposure. excluded_traits, noEffects = exposure.excluded_effects; var traits = target.GetComponent <Traits>(); var effects = target.GetComponent <Effects>(); // The part we actually changed float threshold = Mathf.Ceil(GermIntegrator.AdjustedThreshold(GermExposureTuning. ThresholdsFor(exposure).GetMinThreshold(), integrator.GetResistance(exposure))); string tooltip = ""; // Check for required traits to catch the disease if (required != null && required.Count > 0) { string traitList = ""; foreach (string trait in required) { if (!traits.HasTrait(trait)) { if (traitList.Length > 0) { traitList += ", "; } traitList += Db.Get().traits.Get(trait).Name; } } if (traitList.Length > 0) { // Immune: missing required traits threshold = 0.0f; tooltip = string.Format(STRINGS.DUPLICANTS.DISEASES. IMMUNE_FROM_MISSING_REQUIRED_TRAIT, traitList); } } // Check for traits that prevent catching it if (excluded != null && excluded.Count > 0) { string traitList = ""; foreach (string trait in excluded) { if (traits.HasTrait(trait)) { if (traitList.Length > 0) { traitList += ", "; } traitList += Db.Get().traits.Get(trait).Name; } } if (traitList.Length > 0) { // Immune: blocking trait threshold = 0.0f; if (tooltip.Length > 0) { tooltip += "\n"; } tooltip += string.Format(STRINGS.DUPLICANTS.DISEASES. IMMUNE_FROM_HAVING_EXLCLUDED_TRAIT, traitList); } } // Check for effects that prevent catching it if (noEffects != null && noEffects.Count > 0) { string effectList = ""; foreach (string effect in noEffects) { if (effects.HasEffect(effect)) { if (effectList.Length > 0) { effectList += ", "; } effectList += Db.Get().effects.Get(effect).Name; } } if (effectList.Length > 0) { // Immune: blocking effect threshold = 0.0f; if (tooltip.Length > 0) { tooltip += "\n"; } tooltip += string.Format(STRINGS.DUPLICANTS.DISEASES. IMMUNE_FROM_HAVING_EXCLUDED_EFFECT, effectList); } } // Update the label and tooltip if (tooltip.Length == 0) { if (threshold > 1.0f) { tooltip = string.Format(THRESHOLD_TOOLTIP, GameUtil.GetFormattedSimple( threshold), target.GetProperName(), sickness.Name); } else { tooltip = string.Format(THRESHOLD_TOOLTIP_1, target.GetProperName(), sickness.Name); } } panel.SetLabel("disease_" + disease.Id, " • " + disease.Name + ": " + (threshold == 0.0f ? THRESHOLD_IMMUNE.text : GameUtil.GetFormattedSimple( threshold)), tooltip); }
// 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; } } }