public void ServerPerformInteraction(HandApply interaction) { // Locking/Unlocking by alt clicking if (interaction.IsAltClick) { if (IsLockable && AccessRestrictions != null && ClosetStatus.Equals(ClosetStatus.Closed)) { // Default CheckAccess will check for the ID slot first // so the default AltClick interaction will prioritize // the ID slot, only when that would fail the hand // will be checked, alternatively the user can also // just click the locker with the ID inhand. if (AccessRestrictions.CheckAccess(interaction.Performer)) { if (isLocked) { SyncLocked(isLocked, false); Chat.AddExamineMsg(interaction.Performer, $"You unlock the {closetName}."); } else { SyncLocked(isLocked, true); Chat.AddExamineMsg(interaction.Performer, $"You lock the {closetName}."); } } } // Alt clicking is the locker's only alt click behaviour. return; } // Is the player trying to put something in the closet? if (Validations.HasItemTrait(interaction.HandObject, CommonTraits.Instance.Emag) && interaction.HandObject.TryGetComponent <Emag>(out var emag) && emag.EmagHasCharges()) { if (IsClosed && !isEmagged) { AudioSourceParameters audioSourceParameters = new AudioSourceParameters(pitch: 1f); SoundManager.PlayNetworkedAtPos(soundOnEmag, registerTile.WorldPositionServer, audioSourceParameters, gameObject); //ServerHandleContentsOnStatusChange(false); isEmagged = true; emag.UseCharge(interaction); Chat.AddActionMsgToChat(interaction, "The access panel errors. A slight amount of smoke pours from behind the panel...", "You can smell caustic smoke from somewhere..."); //SyncStatus(statusSync, ClosetStatus.Open); BreakLock(); } }
protected void StartUnwrapAction(GameObject performer) { var cfg = new StandardProgressActionConfig( StandardProgressActionType.Restrain); Chat.AddActionMsgToChat( performer, string.Format(originatorUnwrapText, gameObject.ExpensiveName()), string.Format(othersUnwrapText, performer.ExpensiveName(), gameObject.ExpensiveName())); StandardProgressAction.Create(cfg, UnWrap) .ServerStartProgress(ActionTarget.Object(performer.RegisterTile()), timeToUnwrap, performer); }
/// <summary> /// Use this instead of rewriting Chat.AddActionMsgToChat() when adding text to a failed conditon. /// </summary> /// <param name="player">The orignator</param> /// <param name="type">Normal : Displays failText string. Critical: Displays critViewText string.</param> protected void FailText(GameObject player, FailType type) { switch (type) { case FailType.Normal: Chat.AddActionMsgToChat(player, $"{failText}", $""); break; case FailType.Critical: Chat.AddActionMsgToChat(player, $"{player.ExpensiveName()} {critViewText}.", $"{player.ExpensiveName()} {critViewText}."); break; } }
protected void RemoveLimbLossBleed(LivingHealthMasterBase targetBodyPart, HandApply interaction) { foreach (var container in targetBodyPart.RootBodyPartContainers) { if (container.BodyPartType == interaction.TargetBodyPart && container.IsBleeding == true) { container.IsBleeding = false; Chat.AddActionMsgToChat(interaction.Performer.gameObject, $"You stopped {interaction.TargetObject.ExpensiveName()}'s bleeding.", $"{interaction.PerformerPlayerScript.visibleName} stopped {interaction.TargetObject.ExpensiveName()}'s bleeding."); } } }
/// <summary> /// Triggered when the reader has read all of the pages. /// </summary> protected virtual void FinishReading(ConnectedPlayer player) { hasBeenRead = true; if (canBeReadMultipleTimes) { readerProgress[player] = 0; } Chat.AddActionMsgToChat(player.GameObject, $"You finish reading {gameObject.ExpensiveName()}!", $"{player.Script.visibleName} finishes reading {gameObject.ExpensiveName()}!"); }
protected void RemoveLimbLossBleed(LivingHealthMasterBase livingHealth, HandApply interaction) { foreach (var bodyPart in livingHealth.BodyPartList) { if (bodyPart.BodyPartType == interaction.TargetBodyPart && bodyPart.IsBleeding == true) { bodyPart.IsBleeding = false; Chat.AddActionMsgToChat(interaction.Performer.gameObject, $"You stopped {interaction.TargetObject.ExpensiveName()}'s bleeding.", $"{interaction.PerformerPlayerScript.visibleName} stopped {interaction.TargetObject.ExpensiveName()}'s bleeding."); } } }
public void ServerPerformInteraction(HandApply interaction) { if (accessRestrictions.CheckAccessCard(interaction.HandObject)) { IsLocked = !IsLocked; Chat.AddActionMsgToChat(interaction.Performer, $"You {(IsLocked ? "lock" : "unlock")} the air controller unit.", $"{interaction.PerformerPlayerScript.visibleName} {(IsLocked ? "locks" : "unlocks")} the air controller unit."); OnStateChanged?.Invoke(); } }
private void LayDown(GameObject player) { if (CheckHandState(player) == false) { Chat.AddActionMsgToChat(player, "You surrender in defeat then lay faced down on the ground.", $"{player.ExpensiveName()} surrenders in defeat then lay faced down on the ground."); } else { base.Do(player); } registerPlayer.ServerSetIsStanding(false); }
private bool TryTransferToOven(PositionalHandApply interaction) { // Close the oven if nothing is held. if (interaction == null || interaction.UsedObject == null) { SoundManager.PlayNetworkedAtPos(doorCloseSfx, WorldPosition, sourceObj: gameObject); return(false); } // Add held item to the oven. ItemSlot storageSlot = storage.GetNextFreeIndexedSlot(); bool added = false; if (interaction.HandSlot.ItemObject.TryGetComponent(out Stackable stack)) { if (stack.Amount == 1) { added = Inventory.ServerTransfer(interaction.HandSlot, storageSlot); } else { var item = stack.ServerRemoveOne(); added = Inventory.ServerAdd(item, storageSlot); if (!added) { stack.ServerIncrease(1); // adds back the lost amount to prevent ovens from eating stackable items } } } else { added = Inventory.ServerTransfer(interaction.HandSlot, storageSlot); } if (storageSlot == null || added == false) { Chat.AddActionMsgToChat(interaction, "The oven's matter bins are full!", string.Empty); return(true); } if (storageSlot.ItemObject.TryGetComponent(out Cookable cookable) && cookable.CookableBy.HasFlag(CookSource.Oven)) { storedCookables.Add(storageSlot, cookable); } Chat.AddActionMsgToChat( interaction, $"You add the {interaction.HandObject.ExpensiveName()} to the oven.", $"{interaction.PerformerPlayerScript.visibleName} adds the {interaction.HandObject.ExpensiveName()} to the oven."); return(true); }
/// <summary> /// Spawn blob player and gib old body! /// </summary> private void FormBlob() { UpdateManager.Remove(CallbackType.PERIODIC_UPDATE, PeriodicUpdate); var playerScript = gameObject.GetComponent <PlayerScript>(); var bound = MatrixManager.MainStationMatrix.LocalBounds; //To ensure that it has to be station matrix var matrixInfo = GetComponent <RegisterTile>().Matrix.MatrixInfo; //Teleport user to random location on station if outside radius of 600 or on a space tile if (((gameObject.AssumedWorldPosServer() - MatrixManager.MainStationMatrix.GameObject.AssumedWorldPosServer()) .magnitude > 600f) || MatrixManager.IsSpaceAt(gameObject.GetComponent <PlayerSync>().ServerPosition, true, matrixInfo) || matrixInfo != MatrixManager.MainStationMatrix) { Vector3 position = new Vector3(Random.Range(bound.xMin, bound.xMax), Random.Range(bound.yMin, bound.yMax), 0); while (MatrixManager.IsSpaceAt(Vector3Int.FloorToInt(position), true, matrixInfo) || MatrixManager.IsWallAt(Vector3Int.FloorToInt(position), true)) { position = new Vector3(Random.Range(bound.xMin, bound.xMax), Random.Range(bound.yMin, bound.yMax), 0); } gameObject.GetComponent <PlayerSync>().SetPosition(position, true); } var spawnResult = Spawn.ServerPrefab(AntagManager.Instance.blobPlayerViewer, gameObject.RegisterTile().WorldPositionServer, gameObject.transform.parent); if (!spawnResult.Successful) { Logger.LogError("Failed to spawn blob!", Category.Blob); Destroy(this); return; } spawnResult.GameObject.GetComponent <PlayerScript>().mind = playerScript.mind; var connection = GetComponent <NetworkIdentity>().connectionToClient; PlayerSpawn.ServerTransferPlayerToNewBody(connection, spawnResult.GameObject, playerScript.mind.GetCurrentMob(), Event.BlobSpawned, playerScript.characterSettings); playerScript.mind = null; //Start the blob control script spawnResult.GameObject.GetComponent <BlobPlayer>().BlobStart(); Chat.AddActionMsgToChat(spawnResult.GameObject, $"<color=#FF151F>You explode from your {bodyPart}, a new being has been born.</color>", $"<color=#FF151F>{gameObject.ExpensiveName()} explodes into a pile of mush.</color>"); gameObject.GetComponent <LivingHealthMasterBase>().Gib(); Destroy(this); }
private void FinishHack(HandApply interaction) { if (DMMath.Prob(chanceToFailHack)) { Chat.AddActionMsgToChat(interaction.Performer, $"Your attempt to hack the {gameObject.ExpensiveName()} failed", $"{interaction.Performer.ExpensiveName()} failed to hack the {gameObject.ExpensiveName()}"); return; } Chat.AddActionMsgToChat(interaction.Performer, $"You hack the {gameObject.ExpensiveName()}", $"{interaction.Performer.ExpensiveName()} hacked the {gameObject.ExpensiveName()}"); isHacked = true; }
public static void SendGenericConsumeMessage(PlayerScript feeder, PlayerScript eater, HungerState eaterHungerState, string consumableName, string eatVerb) { if (feeder == eater) //If you're eating it yourself. { switch (eaterHungerState) { case HungerState.Full: Chat.AddActionMsgToChat(eater.gameObject, $"You cannot force any more of the {consumableName} to go down your throat!", $"{eater.playerName} cannot force any more of the {consumableName} to go down {eater.characterSettings.TheirPronoun(eater)} throat!"); return; //Not eating! case HungerState.Normal: Chat.AddActionMsgToChat(eater.gameObject, $"You unwillingly {eatVerb} the {consumableName}.", //"a bit of" $"{eater.playerName} unwillingly {eatVerb}s the {consumableName}."); //"a bit of" break; case HungerState.Hungry: Chat.AddActionMsgToChat(eater.gameObject, $"You {eatVerb} the {consumableName}.", $"{eater.playerName} {eatVerb}s the {consumableName}."); break; case HungerState.Malnourished: Chat.AddActionMsgToChat(eater.gameObject, $"You hungrily {eatVerb} the {consumableName}.", $"{eater.playerName} hungrily {eatVerb}s the {consumableName}."); break; case HungerState.Starving: Chat.AddActionMsgToChat(eater.gameObject, $"You hungrily {eatVerb} the {consumableName}, gobbling it down!", $"{eater.playerName} hungrily {eatVerb}s the {consumableName}, gobbling it down!"); break; } } else //If you're feeding it to someone else. { if (eaterHungerState == HungerState.Full) { Chat.AddActionMsgToChat(eater.gameObject, $"{feeder.playerName} cannot force any more of {consumableName} down your throat!", $"{feeder.playerName} cannot force any more of {consumableName} down {eater.playerName}'s throat!"); return; //Not eating! } else { Chat.AddActionMsgToChat(eater.gameObject, $"{feeder.playerName} attempts to feed you {consumableName}.", $"{feeder.playerName} attempts to feed {eater.playerName} {consumableName}."); } } }
public override void InternalDamageLogic() { base.InternalDamageLogic(); if (RelatedPart.CurrentInternalBleedingDamage > 50 && alarmedForInternalBleeding == false) { Chat.AddActionMsgToChat(RelatedPart.HealthMaster.gameObject, $"You feel a sharp pain in your {RelatedPart.gameObject.ExpensiveName()}!", $"{RelatedPart.HealthMaster.playerScript.visibleName} holds their {RelatedPart.gameObject.ExpensiveName()} in pain!"); alarmedForInternalBleeding = true; } if (RelatedPart.CurrentInternalBleedingDamage > RelatedPart.MaximumInternalBleedDamage) { DoHeartAttack(); } }
private void Meow(GameObject meowed = null) { //TODO play meow sound if (meowed != null) { Chat.AddActionMsgToChat(meowed, $"{capCatName} meows at you!", $"{capCatName} meows at {meowed.ExpensiveName()}"); } else { Chat.AddActionMsgToChat(gameObject, $"{capCatName} meows!", $"{capCatName} meows!"); } }
private void Purr(GameObject purred = null) { //TODO play purr sound if (purred != null) { Chat.AddActionMsgToChat(purred, $"{capCatName} purrs at you!", $"{capCatName} purrs at {purred.ExpensiveName()}"); } else { Chat.AddActionMsgToChat(gameObject, $"{capCatName} purrs!", $"{capCatName} purrs!"); } }
private void Hiss(GameObject hissed = null) { //TODO play hiss sound if (hissed != null) { Chat.AddActionMsgToChat(hissed, $"{capCatName} hisses at you!", $"{capCatName} hisses at {hissed.ExpensiveName()}"); } else { Chat.AddActionMsgToChat(gameObject, $"{capCatName} hisses!", $"{capCatName} hisses!"); } }
/// <summary> /// Gendered Emote is designed for Players only and any NPC that uses HealthV2 /// </summary> public override void Do(GameObject player) { if (CheckAllBaseConditions(player) == false) { return; } HealthCheck(player); Chat.AddActionMsgToChat(player, $"{youText}", $"{player.ExpensiveName()} {viewTextFinal}."); if (soundsAreTyped) { PlayAudio(GetBodyTypeAudio(player), player); return; } PlayAudio(defaultSounds, player); }
/// <summary> /// Performs common tool usage logic and also sends start / end action messages, and invokes a callback on success. /// </summary> /// <param name="performer">player using the tool</param> /// <param name="tool">tool being used</param> /// <param name="worldTilePos">tile position the action is being performed on</param> /// <param name="seconds">seconds taken to perform the action, 0 if it should be instant</param> /// <param name="performerStartActionMessage">message to show performer when action begins.</param> /// <param name="othersStartActionMessage">message to show others when action begins.</param> /// <param name="performerFinishActionMessage">message to show performer when action completes successfully.</param> /// <param name="othersFinishActionMessage">message to show others when action completes successfully.</param> /// <param name="onSuccessfulCompletion">called when action is completed</param> public static void ServerUseToolWithActionMessages(GameObject performer, GameObject tool, Vector2 worldTilePos, float seconds, string performerStartActionMessage, string othersStartActionMessage, string performerFinishActionMessage, string othersFinishActionMessage, Action onSuccessfulCompletion) { Chat.AddActionMsgToChat(performer, performerStartActionMessage, othersStartActionMessage); var progressFinishAction = new ProgressCompleteAction(() => { Chat.AddActionMsgToChat(performer, performerFinishActionMessage, othersFinishActionMessage); onSuccessfulCompletion.Invoke(); }); ServerUseTool(performer, tool, worldTilePos, seconds, progressFinishAction); }
private void UseEmag(HandApply interaction) { #pragma warning disable CS0162 // Unreachable code detected if (!ALARM_SYSTEM_ENABLED || !ALLOW_EMAGGING) return; #pragma warning restore CS0162 // Unreachable code detected if (alarmBroken) return; alarmBroken = true; Chat.AddActionMsgToChat(interaction, $"You wave the {interaction.HandObject.name.ToLower()} over the {name.ToLower()}'s electrical panel. " + "The status panel flickers and the buzzer makes sickly popping noises. You can smell smoke...", "You can smell caustic smoke from somewhere..."); SoundManager.PlayNetworkedAtPos("SnapCracklePop1", DrawerWorldPosition, sourceObj: gameObject); StartCoroutine(PlayEmagAnimation()); }
private void AddSeedPacketToStorage(SeedPacket packet, HandApply interaction) { if (Inventory.ServerTransfer(interaction.HandSlot, storage.GetBestSlotFor(interaction.HandObject))) { seedPackets.Add(packet); UpdateEvent.Invoke(); Chat.AddActionMsgToChat(interaction.Performer, $"You place the {packet.gameObject.ExpensiveName()} into the seed extractor.", $"{interaction.Performer.name} places the {packet.gameObject.ExpensiveName()} into the seed extractor."); return; } Chat.AddActionMsgToChat(interaction.Performer, $"You try and place the {packet.gameObject.ExpensiveName()} into the seed extractor but it is full!", $"{interaction.Performer.name} tries to place the {packet.gameObject.ExpensiveName()} into the seed extractor but it is full!"); }
protected override void Wrap(GameObject performer, WrappingPaper paper) { var cfg = new StandardProgressActionConfig( StandardProgressActionType.Restrain); Chat.AddActionMsgToChat( performer, string.Format(actionTextOriginator, gameObject.ExpensiveName(), paper.gameObject.ExpensiveName()), string.Format(actionTextOthers, performer.ExpensiveName(), gameObject.ExpensiveName(), paper.gameObject.ExpensiveName())); StandardProgressAction.Create( cfg, () => FinishWrapping(paper) ).ServerStartProgress(ActionTarget.Object(performer.RegisterTile()), wrapTime, performer); }
public void ServerPerformInteraction(HandApply interaction) { Inventory.ServerConsume(interaction.HandSlot, 1); mood.OnFoodEaten(); AudioSourceParameters audioSourceParameters = new AudioSourceParameters(pitch: 1f); SoundManager.PlayNetworkedAtPos(SingletonSOSounds.Instance.EatFood, gameObject.RegisterTile().WorldPosition, audioSourceParameters, sourceObj: gameObject); Chat.AddActionMsgToChat( interaction.Performer, $"You feed {mobAI.mobName.Capitalize()} with {interaction.HandObject.ExpensiveName()}", $"{interaction.Performer.ExpensiveName()}" + $" feeds some {interaction.HandObject.ExpensiveName()} to {mobAI.mobName.Capitalize()}"); }
/// <summary> /// for triggering traps when inside storage containers /// </summary> /// <param name="health"></param> private void HurtHand(LivingHealthMasterBase health) { foreach (var hand in health.playerScript.DynamicItemStorage.GetNamedItemSlots(NamedSlot.hands)) { if (ignoresHandwear == false && hand.IsEmpty == false) { continue; } ApplyDamageToPartyType(health, handTypes.PickRandom()); } Chat.AddActionMsgToChat(gameObject, $"You are surprised with a {gameObject.ExpensiveName()} biting your hand!", $"{health.playerScript.visibleName} screams in pain and surprise as {gameObject.ExpensiveName()} " + $"bites {health.playerScript.characterSettings.TheirPronoun(health.playerScript)} hand!"); PlayStepAudio(); }
public void ServerPerformInteraction(PositionalHandApply interaction) { if (reagentContainer.ReagentMixTotal < 1) { //warning Chat.AddExamineMsg(interaction.Performer, "Your mop is dry!"); return; } //server is performing server-side logic for the interaction //do the mopping void CompleteProgress() { Vector3Int worldPos = interaction.WorldPositionTarget.RoundToInt(); MatrixInfo matrixInfo = MatrixManager.AtPoint(worldPos, true); Vector3Int localPos = MatrixManager.WorldToLocalInt(worldPos, matrixInfo); if (reagentContainer) { if (reagentContainer.MajorMixReagent == Water) { matrixInfo.MetaDataLayer.Clean(worldPos, localPos, true); reagentContainer.TakeReagents(reagentsPerUse); } else if (reagentContainer.MajorMixReagent == SpaceCleaner) { matrixInfo.MetaDataLayer.Clean(worldPos, localPos, false); reagentContainer.TakeReagents(reagentsPerUse); } else { MatrixManager.ReagentReact(reagentContainer.TakeReagents(reagentsPerUse), worldPos); } } Chat.AddExamineMsg(interaction.Performer, "You finish mopping."); } //Start the progress bar: var bar = StandardProgressAction.Create(ProgressConfig, CompleteProgress) .ServerStartProgress(interaction.WorldPositionTarget.RoundToInt(), useTime, interaction.Performer); if (bar) { Chat.AddActionMsgToChat(interaction.Performer, $"You begin to clean the floor with the {gameObject.ExpensiveName()}...", $"{interaction.Performer.name} begins to clean the floor with the {gameObject.ExpensiveName()}."); } }
/// <summary> /// Pulls in the desired gas, as well as others, from the specified gas mix and adds them to the blood stream /// </summary> /// <param name="gasMix">The gas mix to breathe in from</param> /// <param name="blood">The blood to put gases into</param> /// <returns> True if breathGasMix was changed </returns> private bool BreatheIn(GasMix breathGasMix, ReagentMix blood, float efficiency) { if (healthMaster.RespiratorySystem.CanBreathAnywhere) { blood.Add(requiredReagent, bloodType.GetSpareGasCapacity(blood)); return(false); } ReagentMix toInhale = new ReagentMix(); for (int i = 0; i < breathGasMix.Gases.Length; i++) { if (GAS2ReagentSingleton.Instance.DictionaryGasToReagent.ContainsKey(Gas.All[i])) { // n = PV/RT float gasMoles = breathGasMix.GetPressure(Gas.All[i]) * LungSize / 8.314f / breathGasMix.Temperature; // Get as much as we need, or as much as in the lungs, whichever is lower Reagent gasReagent = GAS2ReagentSingleton.Instance.GetGasToReagent(Gas.All[i]); float molesRecieved = Mathf.Min(gasMoles, bloodType.GetSpareGasCapacity(blood, gasReagent)); toInhale.Add(gasReagent, molesRecieved * efficiency); //TODO: Add pressureSafeMax check here, for hyperoxia } } healthMaster.RespiratorySystem.GasExchangeToBlood(breathGasMix, blood, toInhale); // Counterintuitively, in humans respiration is stimulated by pressence of CO2 in the blood, not lack of oxygen // May want to change this code to reflect that in the future so people don't hyperventilate when they are on nitrous oxide var inGas = GAS2ReagentSingleton.Instance.GetGasToReagent(requiredGas); var saturation = (toInhale[inGas] + blood[inGas]) / bloodType.GetGasCapacity(blood); if (saturation >= HealthMaster.CirculatorySystem.BloodInfo.BLOOD_REAGENT_SATURATION_OKAY) { currentBreatheCooldown = breatheCooldown; //Slow breathing, we're all good healthMaster.HealthStateController.SetSuffocating(false); } else if (saturation <= HealthMaster.CirculatorySystem.BloodInfo.BLOOD_REAGENT_SATURATION_BAD) { healthMaster.HealthStateController.SetSuffocating(true); if (Random.value < 0.2) { Chat.AddActionMsgToChat(gameObject, "You gasp for breath", $"{healthMaster.gameObject.ExpensiveName()} gasps"); } } //Debug.Log("Gas inhaled: " + toInhale.Total + " Saturation: " + saturation); return(toInhale.Total > 0); }
private void SingleBark(GameObject barked = null) { SoundManager.PlayNetworkedAtPos("Bark", gameObject.transform.position, Random.Range(.8F, 1.3F)); if (barked != null) { Chat.AddActionMsgToChat(barked, $"{mobNameCap} barks at you!", $"{mobNameCap} barks at {barked.ExpensiveName()}"); } else { Chat.AddActionMsgToChat(gameObject, $"{mobNameCap} barks!", $"{mobNameCap} barks!"); } }
private void SingleBark(GameObject barked = null) { AudioSourceParameters audioSourceParameters = new AudioSourceParameters(pitch: Random.Range(.8F, 1.3F)); SoundManager.PlayNetworkedAtPos(barkSound, gameObject.transform.position, audioSourceParameters); if (barked != null) { Chat.AddActionMsgToChat(barked, $"{MobName} barks at you!", $"{MobName} barks at {barked.ExpensiveName()}"); } else { Chat.AddActionMsgToChat(gameObject, $"{MobName} barks!", $"{MobName} barks!"); } }
private void Purr(GameObject purred = null) { SoundManager.PlayNetworkedAtPos("Purr", gameObject.WorldPosServer(), Random.Range(.8f, 1.2f)); if (purred != null) { Chat.AddActionMsgToChat( purred, $"{capCatName} purrs at you!", $"{capCatName} purrs at {purred.ExpensiveName()}"); } else { Chat.AddActionMsgToChat(gameObject, $"{capCatName} purrs!", $"{capCatName} purrs!"); } }
private void Hiss(GameObject hissed = null) { SoundManager.PlayNetworkedAtPos("CatHiss", gameObject.WorldPosServer(), Random.Range(.9f, 1f)); if (hissed != null) { Chat.AddActionMsgToChat( hissed, $"{capCatName} hisses at you!", $"{capCatName} hisses at {hissed.ExpensiveName()}"); } else { Chat.AddActionMsgToChat(gameObject, $"{capCatName} hisses!", $"{capCatName} hisses!"); } }
private void Meow(GameObject meowed = null) { SoundManager.PlayNetworkedAtPos("Meow#", gameObject.WorldPosServer(), Random.Range(.8f, 1.2f)); if (meowed != null) { Chat.AddActionMsgToChat( meowed, $"{capCatName} meows at you!", $"{capCatName} meows at {meowed.ExpensiveName()}"); } else { Chat.AddActionMsgToChat(gameObject, $"{capCatName} meows!", $"{capCatName} meows!"); } }