Skip to content

Commit

Permalink
YaRRRR
Browse files Browse the repository at this point in the history
  • Loading branch information
Vonsant committed Dec 24, 2024
1 parent 2eb1f70 commit b5af159
Show file tree
Hide file tree
Showing 16 changed files with 263 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Content.Server.Speech.EntitySystems;
using Content.Shared.Whitelist;

namespace Content.Server.Speech.Components;

[RegisterComponent]
[Access(typeof(ParrotSpeechSystem))]
public sealed partial class ParrotSpeechComponent : Component
{
/// <summary>
/// The maximum number of words the parrot can learn per phrase.
/// Phrases are 1 to MaxPhraseLength words in length.
/// </summary>
[DataField]
public int MaximumPhraseLength = 7;

[DataField]
public int MaximumPhraseCount = 20;

[DataField]
public int MinimumWait = 60; // 1 minutes

[DataField]
public int MaximumWait = 120; // 2 minutes

/// <summary>
/// The probability that a parrot will learn from something an overheard phrase.
/// </summary>
[DataField]
public float LearnChance = 0.2f;

[DataField]
public EntityWhitelist Blacklist { get; private set; } = new();

[DataField]
public TimeSpan? NextUtterance;

[DataField(readOnly: true)]
public List<string> LearnedPhrases = new();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System.Linq;
using Content.Server.Chat.Systems;
using Content.Server.Speech.Components;
using Content.Shared.Mind.Components;
using Content.Shared.Whitelist;
using Robust.Shared.Random;
using Robust.Shared.Timing;

namespace Content.Server.Speech.EntitySystems;

public sealed class ParrotSpeechSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[Dependency] private readonly ChatSystem _chat = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<ParrotSpeechComponent, ListenEvent>(OnListen);
SubscribeLocalEvent<ParrotSpeechComponent, ListenAttemptEvent>(CanListen);
}

public override void Update(float frameTime)
{
var query = EntityQueryEnumerator<ParrotSpeechComponent>();
while (query.MoveNext(out var uid, out var component))
{
if (component.LearnedPhrases.Count == 0)
// This parrot has not learned any phrases, so can't say anything interesting.
continue;
if (TryComp<MindContainerComponent>(uid, out var mind) && mind.HasMind)
// Pause parrot speech when someone is controlling the parrot.
continue;
if (_timing.CurTime < component.NextUtterance)
continue;

if (component.NextUtterance != null)
{
_chat.TrySendInGameICMessage(
uid,
_random.Pick(component.LearnedPhrases),
InGameICChatType.Speak,
hideChat: true, // Don't spam the chat with randomly generated messages
hideLog: true, // TODO: Don't spam admin logs either.
// If a parrot learns something inappropriate, admins can search for
// the player that said the inappropriate thing.
checkRadioPrefix: false);
}

component.NextUtterance = _timing.CurTime + TimeSpan.FromSeconds(_random.Next(component.MinimumWait, component.MaximumWait));
}
}

private void OnListen(EntityUid uid, ParrotSpeechComponent component, ref ListenEvent args)
{
if (_random.Prob(component.LearnChance))
{
// Very approximate word splitting. But that's okay: parrots aren't smart enough to
// split words correctly.
var words = args.Message.Split(" ", StringSplitOptions.RemoveEmptyEntries);
// Prefer longer phrases
var phraseLength = 1 + (int) (Math.Sqrt(_random.NextDouble()) * component.MaximumPhraseLength);

var startIndex = _random.Next(0, Math.Max(0, words.Length - phraseLength + 1));

var phrase = string.Join(" ", words.Skip(startIndex).Take(phraseLength)).ToLower();

while (component.LearnedPhrases.Count >= component.MaximumPhraseCount)
{
_random.PickAndTake(component.LearnedPhrases);
}

component.LearnedPhrases.Add(phrase);
}
}

private void CanListen(EntityUid uid, ParrotSpeechComponent component, ref ListenAttemptEvent args)
{
if (_whitelistSystem.IsBlacklistPass(component.Blacklist, args.Source))
args.Cancel();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ent-MobParrotSmart = { ent-MobParrot }
.desc = Проникает в ваши владения, шпионит за вами и при этом остаётся классным питомцем. Умеет говорить.
.suffix = Умный
2 changes: 1 addition & 1 deletion Resources/Prototypes/Catalog/Fills/Crates/npc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@
components:
- type: StorageFill
contents:
- id: MobParrot
- id: MobParrotSmart # Corvax-Next-Parrot: replaced MobParrot
amount: 3

- type: entity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
- MobLizard
- MobSlug
- MobFrog
- MobParrot
- MobParrotSmart # Corvax-Next-Parrot: replaced MobParrot
- MobPenguin
- MobSnake
- MobPossum
Expand Down
2 changes: 1 addition & 1 deletion Resources/Prototypes/Entities/Markers/Spawners/mobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@
sprite: Mobs/Animals/parrot.rsi
- type: ConditionalSpawner
prototypes:
- MobParrot
- MobParrotSmart # Corvax-Next-Parrot: replaced MobParrot

- type: entity
name: Butterfly Spawner
Expand Down
2 changes: 1 addition & 1 deletion Resources/Prototypes/XenoArch/Effects/normal_effects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@
prob: 0.03
- id: MobMouse
orGroup: fauna
- id: MobParrot
- id: MobParrotSmart # Corvax-Next-Parrot: replaced MobParrot
orGroup: fauna
maxAmount: 1
- id: MobPenguin
Expand Down
88 changes: 88 additions & 0 deletions Resources/Prototypes/_CorvaxNext/Entities/Mobs/NPCs/animals.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
- type: entity
name: parrot
parent: [ SimpleMobBase, FlyingMobBase ]
id: MobParrotSmart
description: Infiltrates your domain, spies on you, and somehow still a cool pet. Actually can talk.
suffix: Smart
components:
- type: MovementSpeedModifier
baseWalkSpeed : 2.5
baseSprintSpeed : 6
- type: Sprite
drawdepth: Mobs
layers:
- map: ["enum.DamageStateVisualLayers.Base", "movement"]
state: parrot-moving # until upstream
sprite: _CorvaxNext/Mobs/Animals/parrot.rsi
- type: SpriteMovement
movementLayers:
movement:
state: parrot-moving
noMovementLayers:
movement:
state: parrot-moving # until upstream
- type: Item
sprite: _CorvaxNext/Mobs/Animals/parrot.rsi
size: Normal
inhandVisuals:
left:
- state: inhand-left
right:
- state: inhand-right
- type: Clothing
quickEquip: false
sprite: _CorvaxNext/Mobs/Animals/parrot.rsi
slots:
- HEAD
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeCircle
radius: 0.25
density: 10
mask:
- FlyingMobMask
layer:
- FlyingMobLayer
- type: Appearance
- type: DamageStateVisuals
states:
Alive:
Base: parrot
Critical:
Base: dead
Dead:
Base: dead
- type: Butcherable
spawned:
- id: FoodMeat
amount: 1
- type: Speech
speechSounds: Parrot
speechVerb: Parrot
- type: Vocal
sounds:
Unsexed: Parrot
- type: CanEscapeInventory
- type: ParrotAccent
- type: Strippable
- type: InteractionPopup
successChance: 0.6
interactSuccessString: petting-success-bird
interactFailureString: petting-failure-generic
interactSuccessSpawn: EffectHearts
interactSuccessSound:
path: /Audio/Animals/parrot_raught.ogg
- type: Bloodstream
bloodMaxVolume: 50
- type: ActiveListener
range: 5
- type: ParrotSpeech
blacklist:
components:
- ParrotSpeech # Stop parrots repeating their own speech
- SurveillanceCamera
- SurveillanceCameraMonitor
- RadioSpeaker
- VendingMachine
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions Resources/Textures/_CorvaxNext/Mobs/Animals/parrot.rsi/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-3.0",
"copyright": "Taken from https://github.com/tgstation/tgstation/commit/53d1f1477d22a11a99c6c6924977cd431075761b",
"states": [
{
"name": "parrot-moving",
"directions": 4,
"delays": [
[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
]
},
{
"name": "parrot",
"directions": 4
},
{
"name": "dead"
},
{
"name": "icon"
},
{
"name": "equipped-HELMET",
"directions": 4
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit b5af159

Please sign in to comment.