<commit_before>﻿using System;
using System.Collections.Generic;
using System.Configuration;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;


namespace KERBALISM {


public sealed class Reliability : PartModule, ISpecifics, IModuleInfo, IPartCostModifier, IPartMassModifier
{
  // config
  [KSPField(isPersistant = true)] public string type;                 // component name
  [KSPField] public double mtbf   = 21600000.0;                       // mean time between failures, in seconds
  [KSPField] public string repair = string.Empty;                     // repair crew specs
  [KSPField] public string title  = string.Empty;                     // short description of component
  [KSPField] public string redundancy = string.Empty;                 // redundancy group
  [KSPField] public double extra_cost;                                // extra cost for high-quality
  [KSPField] public double extra_mass;                                // extra mass for high-quality

  // persistence
  [KSPField(isPersistant = true)] public bool broken;                 // true if broken
  [KSPField(isPersistant = true)] public bool critical;               // true if failure can't be repaired
  [KSPField(isPersistant = true)] public bool quality;                // true if the component is high-quality
  [KSPField(isPersistant = true)] public double last;                 // time of last failure
  [KSPField(isPersistant = true)] public double next;                 // time of next failure

  // status ui
  [KSPField(guiActive = false, guiName = "_")] public string Status;  // show component status

  // data
  List<PartModule> modules;                                           // components cache
  CrewSpecs repair_cs;                                                // crew specs


  public override void OnStart(StartState state)
  {
    // do nothing in the editors and when compiling parts
    if (!Lib.IsFlight()) return;

    // cache list of modules
    modules = part.FindModulesImplementing<PartModule>().FindAll(k => k.moduleName == type);

    // parse crew specs
    repair_cs = new CrewSpecs(repair);

    // setup ui
    Fields["Status"].guiName = title;
    Events["Inspect"].guiName = Lib.BuildString("Inspect <b>", title, "</b>");
    Events["Repair"].guiName = Lib.BuildString("Repair <b>", title, "</b>");

    // sync monobehaviour state with module state
    // - required as the monobehaviour state is not serialized
    if (broken)
    {
      foreach(PartModule m in modules)
      {
        m.enabled = false;
      }
    }

    // type-specific hacks
    if (broken) apply(true);
  }


  public void Update()
  {
    if (Lib.IsFlight())
    {
      // enforce state
      // - required as things like Configure or AnimationGroup can re-enable broken modules
      if (broken)
      {
        foreach(PartModule m in modules)
        {
          m.enabled = false;
          m.isEnabled = false;
        }
      }

      // update ui
      if (broken)
      {
        Status = !critical
          ? "<color=yellow>Malfunction</color=yellow>"
          : "<color=red>Critical failure</color>";
      }
      Fields["Status"].guiActive = broken;
      Events["Inspect"].active = !broken;
      Events["Repair"].active = repair_cs && broken && !critical;

      // set highlight
      highlight(part);
    }
    else
    {
      // update ui
      string quality_label = part.FindModulesImplementing<Reliability>().Count > 1
        ? Lib.BuildString("<b>", title, "</b> quality") : "Quality";
      Events["Quality"].guiName = Lib.StatusToggle(quality_label, quality ? "high" : "standard");
    }
  }


  public void FixedUpdate()
  {
    // do nothing in the editor
    if (Lib.IsEditor()) return;

    // if it has not malfunctioned
    if (!broken)
    {
      // calculate time of next failure if necessary
      if (next <= double.Epsilon)
      {
        last = Planetarium.GetUniversalTime();
        next = last + mtbf * (quality ? Settings.QualityScale : 1.0) * 2.0 * Lib.RandomDouble();
      }

      // if it has failed, trigger malfunction
      if (Planetarium.GetUniversalTime() > next) Break();
    }
  }


  public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Reliability reliability)
  {
    // if it has not malfunctioned
    if (!Lib.Proto.GetBool(m, "broken"))
    {
      // get time of next failure
      double next = Lib.Proto.GetDouble(m, "next");

      // get quality
      bool quality = Lib.Proto.GetBool(m, "quality");

      // calculate epoch of failure if necessary
      if (next <= double.Epsilon)
      {
        double last = Planetarium.GetUniversalTime();
        next = last + reliability.mtbf * (quality ? Settings.QualityScale : 1.0) * 2.0 * Lib.RandomDouble();
        Lib.Proto.Set(m, "last", last);
        Lib.Proto.Set(m, "next", next);
      }

      // if it has failed, trigger malfunction
      if (Planetarium.GetUniversalTime() > next) ProtoBreak(v, p, m);
    }
  }


  // toggle between standard and high quality
  [KSPEvent(guiActiveEditor = true, guiName = "_", active = true)]
  public void Quality()
  {
    quality = !quality;

    // sync all other modules in the symmetry group
    foreach(Part p in part.symmetryCounterparts)
    {
      Reliability reliability = p.Modules[part.Modules.IndexOf(this)] as Reliability;
      if (reliability != null)
      {
        reliability.quality = !reliability.quality;
      }
    }
  }


  // show a message with some hint on time to next failure
  [KSPEvent(guiActiveUnfocused = true, guiName = "_", active = false)]
  public void Inspect()
  {
    // disable for dead eva kerbals
    Vessel v = FlightGlobals.ActiveVessel;
    if (v == null || EVA.IsDead(v)) return;

    // get normalized time to failure
    double time_k = (Planetarium.GetUniversalTime() - last) / (next - last);

    // notify user
    if (time_k < 0.2) Message.Post("It is practically new");
    else if (time_k < 0.4) Message.Post("It is still in good shape");
    else if (time_k < 0.6) Message.Post("It will keep working for some more time");
    else if (time_k < 0.8) Message.Post("It is reaching its operational limits");
    else Message.Post("It could fail at any moment now");<commit_msg>fix: refresh VAB ui when changing Reliability quality<commit_after>﻿using System;
using System.Collections.Generic;
using System.Configuration;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;


namespace KERBALISM {


public sealed class Reliability : PartModule, ISpecifics, IModuleInfo, IPartCostModifier, IPartMassModifier
{
  // config
  [KSPField(isPersistant = true)] public string type;                 // component name
  [KSPField] public double mtbf   = 21600000.0;                       // mean time between failures, in seconds
  [KSPField] public string repair = string.Empty;                     // repair crew specs
  [KSPField] public string title  = string.Empty;                     // short description of component
  [KSPField] public string redundancy = string.Empty;                 // redundancy group
  [KSPField] public double extra_cost;                                // extra cost for high-quality
  [KSPField] public double extra_mass;                                // extra mass for high-quality

  // persistence
  [KSPField(isPersistant = true)] public bool broken;                 // true if broken
  [KSPField(isPersistant = true)] public bool critical;               // true if failure can't be repaired
  [KSPField(isPersistant = true)] public bool quality;                // true if the component is high-quality
  [KSPField(isPersistant = true)] public double last;                 // time of last failure
  [KSPField(isPersistant = true)] public double next;                 // time of next failure

  // status ui
  [KSPField(guiActive = false, guiName = "_")] public string Status;  // show component status

  // data
  List<PartModule> modules;                                           // components cache
  CrewSpecs repair_cs;                                                // crew specs


  public override void OnStart(StartState state)
  {
    // do nothing in the editors and when compiling parts
    if (!Lib.IsFlight()) return;

    // cache list of modules
    modules = part.FindModulesImplementing<PartModule>().FindAll(k => k.moduleName == type);

    // parse crew specs
    repair_cs = new CrewSpecs(repair);

    // setup ui
    Fields["Status"].guiName = title;
    Events["Inspect"].guiName = Lib.BuildString("Inspect <b>", title, "</b>");
    Events["Repair"].guiName = Lib.BuildString("Repair <b>", title, "</b>");

    // sync monobehaviour state with module state
    // - required as the monobehaviour state is not serialized
    if (broken)
    {
      foreach(PartModule m in modules)
      {
        m.enabled = false;
      }
    }

    // type-specific hacks
    if (broken) apply(true);
  }


  public void Update()
  {
    if (Lib.IsFlight())
    {
      // enforce state
      // - required as things like Configure or AnimationGroup can re-enable broken modules
      if (broken)
      {
        foreach(PartModule m in modules)
        {
          m.enabled = false;
          m.isEnabled = false;
        }
      }

      // update ui
      if (broken)
      {
        Status = !critical
          ? "<color=yellow>Malfunction</color=yellow>"
          : "<color=red>Critical failure</color>";
      }
      Fields["Status"].guiActive = broken;
      Events["Inspect"].active = !broken;
      Events["Repair"].active = repair_cs && broken && !critical;

      // set highlight
      highlight(part);
    }
    else
    {
      // update ui
      string quality_label = part.FindModulesImplementing<Reliability>().Count > 1
        ? Lib.BuildString("<b>", title, "</b> quality") : "Quality";
      Events["Quality"].guiName = Lib.StatusToggle(quality_label, quality ? "high" : "standard");
    }
  }


  public void FixedUpdate()
  {
    // do nothing in the editor
    if (Lib.IsEditor()) return;

    // if it has not malfunctioned
    if (!broken)
    {
      // calculate time of next failure if necessary
      if (next <= double.Epsilon)
      {
        last = Planetarium.GetUniversalTime();
        next = last + mtbf * (quality ? Settings.QualityScale : 1.0) * 2.0 * Lib.RandomDouble();
      }

      // if it has failed, trigger malfunction
      if (Planetarium.GetUniversalTime() > next) Break();
    }
  }


  public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Reliability reliability)
  {
    // if it has not malfunctioned
    if (!Lib.Proto.GetBool(m, "broken"))
    {
      // get time of next failure
      double next = Lib.Proto.GetDouble(m, "next");

      // get quality
      bool quality = Lib.Proto.GetBool(m, "quality");

      // calculate epoch of failure if necessary
      if (next <= double.Epsilon)
      {
        double last = Planetarium.GetUniversalTime();
        next = last + reliability.mtbf * (quality ? Settings.QualityScale : 1.0) * 2.0 * Lib.RandomDouble();
        Lib.Proto.Set(m, "last", last);
        Lib.Proto.Set(m, "next", next);
      }

      // if it has failed, trigger malfunction
      if (Planetarium.GetUniversalTime() > next) ProtoBreak(v, p, m);
    }
  }


  // toggle between standard and high quality
  [KSPEvent(guiActiveEditor = true, guiName = "_", active = true)]
  public void Quality()
  {
    quality = !quality;

    // sync all other modules in the symmetry group
    foreach(Part p in part.symmetryCounterparts)
    {
      Reliability reliability = p.Modules[part.Modules.IndexOf(this)] as Reliability;
      if (reliability != null)
      {
        reliability.quality = !reliability.quality;
      }
    }

    // refresh VAB ui
    GameEvents.onEditorShipModified.Fire(EditorLogic.fetch.ship);
  }


  // show a message with some hint on time to next failure
  [KSPEvent(guiActiveUnfocused = true, guiName = "_", active = false)]
  public void Inspect()
  {
    // disable for dead eva kerbals
    Vessel v = FlightGlobals.ActiveVessel;
    if (v == null || EVA.IsDead(v)) return;

    // get normalized time to failure
    double time_k = (Planetarium.GetUniversalTime() - last) / (next - last);

    // notify user
    if (time_k < 0.2) Message.Post("It is practically new");
    else if (time_k < 0.4) Message.Post("It is still in good shape");
    else if (time_k < 0.6) Message.Post("It will keep working for some more time");
    else if (time_k < 0.8) Message.Post("It is reaching its operational limits");
    else Message.Post("It could fail at any moment now");
{'fraction_non_alphanumeric': 0.11106722489928114, 'fraction_numerical': 0.004107749427284936, 'mean_word_length': 2.291731669266771, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7028231', 'n_tokens_mistral': 3716, 'n_tokens_neox': 3517, 'n_words': 1086}