private void OnGrassGrown()
        {
            bool flag = false;

            foreach (Pawn item in GetPawnsThatCanPsylink())
            {
                if (!pawnsThatCanPsylinkLastGrassGrow.Contains(item))
                {
                    flag = true;
                    break;
                }
            }
            if (flag)
            {
                CompSpawnSubplant compSpawnSubplant = parent.TryGetComp <CompSpawnSubplant>();
                string            text = "";
                for (int i = 0; i < Props.requiredSubplantCountPerPsylinkLevel.Count; i++)
                {
                    IEnumerable <string> enumerable = from p in GetPawnsThatCanPsylink(i)
                                                      select p.LabelShort;
                    if (enumerable.Count() > 0)
                    {
                        text = text + (string)("- " + "Level".Translate() + " ") + (i + 1) + ": " + Props.requiredSubplantCountPerPsylinkLevel[i] + " " + compSpawnSubplant.Props.subplant.label + " (" + enumerable.ToCommaList() + ")\n";
                    }
                }
                Find.LetterStack.ReceiveLetter(Props.enoughPlantsLetterLabel, Props.enoughPlantsLetterText.Formatted(compSpawnSubplant.SubplantsForReading.Count, text.TrimEndNewlines()), LetterDefOf.NeutralEvent, new LookTargets(GetPawnsThatCanPsylink()));
            }
            pawnsThatCanPsylinkLastGrassGrow.Clear();
            pawnsThatCanPsylinkLastGrassGrow.AddRange(GetPawnsThatCanPsylink());
        }
        public AcceptanceReport CanPsylink(Pawn pawn, LocalTargetInfo?knownSpot = null)
        {
            if (pawn.Dead || pawn.Faction != Faction.OfPlayer)
            {
                return(false);
            }
            CompSpawnSubplant compSpawnSubplant = parent.TryGetComp <CompSpawnSubplant>();
            int requiredPlantCount = GetRequiredPlantCount(pawn);

            if (requiredPlantCount == -1)
            {
                return(false);
            }
            if (!Props.requiredFocus.CanPawnUse(pawn))
            {
                return(new AcceptanceReport("BeginLinkingRitualNeedFocus".Translate(Props.requiredFocus.label)));
            }
            if (pawn.GetPsylinkLevel() >= pawn.GetMaxPsylinkLevel())
            {
                return(new AcceptanceReport("InstallImplantAlreadyMaxLevel".Translate()));
            }
            if (!pawn.Map.reservationManager.CanReserve(pawn, parent))
            {
                Pawn pawn2 = pawn.Map.reservationManager.FirstRespectedReserver(parent, pawn);
                return(new AcceptanceReport((pawn2 == null) ? "Reserved".Translate() : "ReservedBy".Translate(pawn.LabelShort, pawn2)));
            }
            if (compSpawnSubplant.SubplantsForReading.Count < requiredPlantCount)
            {
                return(new AcceptanceReport("BeginLinkingRitualNeedSubplants".Translate(requiredPlantCount.ToString(), compSpawnSubplant.Props.subplant.label, compSpawnSubplant.SubplantsForReading.Count.ToString())));
            }
            LocalTargetInfo spot;

            if (knownSpot.HasValue)
            {
                if (!CanUseSpot(pawn, knownSpot.Value))
                {
                    return(new AcceptanceReport("BeginLinkingRitualNeedLinkSpot".Translate()));
                }
            }
            else if (!TryFindLinkSpot(pawn, out spot))
            {
                return(new AcceptanceReport("BeginLinkingRitualNeedLinkSpot".Translate()));
            }
            return(AcceptanceReport.WasAccepted);
        }
        public void FinishLinkingRitual(Pawn pawn)
        {
            if (!ModLister.RoyaltyInstalled)
            {
                Log.ErrorOnce("Psylinkables are a Royalty-specific game system. If you want to use this code please check ModLister.RoyaltyInstalled before calling it. See rules on the Ludeon forum for more info.", 5464564);
                return;
            }
            MoteMaker.MakeStaticMote(parent.Position, pawn.Map, ThingDefOf.Mote_PsycastAreaEffect, 10f);
            SoundDefOf.PsycastPsychicPulse.PlayOneShot(new TargetInfo(parent));
            CompSpawnSubplant compSpawnSubplant = parent.TryGetComp <CompSpawnSubplant>();
            int          requiredPlantCount     = GetRequiredPlantCount(pawn);
            List <Thing> list = compSpawnSubplant.SubplantsForReading.OrderByDescending((Thing p) => p.Position.DistanceTo(parent.Position)).ToList();

            for (int i = 0; i < requiredPlantCount && i < list.Count; i++)
            {
                list[i].Destroy();
            }
            compSpawnSubplant.Cleanup();
            pawn.ChangePsylinkLevel(1);
            string str = "LetterTextLinkingRitualCompleted".Translate(pawn.Named("PAWN"), parent.Named("LINKABLE"));

            Find.LetterStack.ReceiveLetter("LetterLabelLinkingRitualCompleted".Translate(), str, LetterDefOf.PositiveEvent, new LookTargets(pawn, parent));
        }