Skip to content

add: Парные эмоуты (duo emotes), порт funky-station#179 - #418

Open
ultradyper wants to merge 2 commits into
ss14-ganimed:masterfrom
ultradyper:add/duo-emotes
Open

add: Парные эмоуты (duo emotes), порт funky-station#179#418
ultradyper wants to merge 2 commits into
ss14-ganimed:masterfrom
ultradyper:add/duo-emotes

Conversation

@ultradyper

Copy link
Copy Markdown
Contributor

Описание PR

Порт парных эмоутов (duo emotes) из funky-station/forky-station#179.

При наведении курсора на другого игрока и нажатии клавиши эмоут-колеса вместо обычного колеса открывается радиальное меню парных эмоутов: кулачок, дай пять, объятие, рукопожатие и взмах хвостом (для видов с хвостом). Второй игрок может ответить кликом по инициатору в течение 10 секунд, после чего оба разворачиваются друг к другу и выполняют анимацию с звуком. Если ответа нет, приглашение отменяется.

Тексты обычных объятий заменены на нейтральное похлопывание по спине.

Технические детали

  • Новые системы в Content.*/_Ganimed/Emoting/: SharedDuoEmoteSystem (проверки, таймаут, попапы), ServerDuoEmoteSystem (валидация и запуск), клиентский DuoEmoteSystem (перехват клавиши эмоут-колеса при наведении на игрока, радиальное меню, анимация рывка), DuoEmoteAnimationSystem (кастомные анимации, для tail swipe используется Spin).
  • Новый тип прототипа duoEmote: иконка, спрайт эффекта, звук и ключи локализации. Новые эмоуты добавляются чисто через YAML, без кода.
  • Компоненты: DuoEmoteComponent (активная попытка, цель, таймаут 10 секунд), DuoEmoteVisualsComponent (спрайт эффекта над инициатором), TailDuoEmoteComponent (хвостатые виды).
  • Эффект-сущность GanimedEffectDuoEmoteBase спавнится над инициатором на время ожидания ответа.
  • Правки ванили с маркерами Ganimed-Add/Ganimed-Edit: DuoEmote на BaseMobSpeciesOrganic, TailDuoEmote на reptilian/vulpkanin/vox, duo-эмоуты для обезьян (MobBaseAncestor) и кобольдов (MobBaseKobold), тексты объятий.
  • Локализация: полный перевод на русский (ru-RU) + английский (en-US).
  • Ассеты: RSI с иконками и эффектами (CC-BY-SA-4.0, автор zergologist), звуки рукопожатия и хлыста с attributions.

Медиа

Скриншоты добавлю отдельным комментом.

Чек-лист

  • PR полностью завершён и мне не нужна помощь, чтобы его закончить.
  • Я запускал локальный сервер со своими изменениями, всё протестировал, и всё работает как должно.

Список изменений

🆑

  • add: Добавлены парные эмоуты: кулачок, дай пять, объятие, рукопожатие, взмах хвостом.
  • tweak: Обычные объятия заменены на похлопывание по спине.

Радиальное меню парных эмоутов на клавише эмоут-колеса при наведении
на другого игрока: кулачок, дай пять, объятие, рукопожатие, взмах
хвостом (для хвостатых видов). Второй игрок отвечает кликом в течение
10 секунд, оба выполняют анимацию и звук. Новые эмоуты добавляются
через прототип duoEmote без кода. Тексты объятий заменены на
нейтральное похлопывание по спине.
@github-actions github-actions Bot added size/L S: Needs Review Status: Требуется рассмотрение Changes: Sprites Changes: Localization Изменения затрагивают локализацию. Changes: UI Changes: Audio Изменения в звуковой составляющей игры, такие как звуковые эффекты и музыка. and removed S: Needs Review Status: Требуется рассмотрение labels Aug 7, 2026
@ultradyper

Copy link
Copy Markdown
Contributor Author
изображение изображение

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

RSI Diff Bot; head commit 19244d4 merging into 1f46142
This PR makes changes to 1 or more RSIs. Here is a summary of all changes:

Resources/Textures/_Funkystation/Effects/duo_emotes.rsi

State Old New Status
effect-fistbump Added
effect-handshake Added
effect-highfive Added
effect-hug Added
effect-tailswipe Added
icon-fistbump Added
icon-handshake Added
icon-highfive Added
icon-hug Added
icon-tailswipe Added

Edit: diff updated after 19244d4

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ultradyper, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 59 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 11082e8c-c561-4301-87fb-428df16d7119

📥 Commits

Reviewing files that changed from the base of the PR and between 2f7a59b and 19244d4.

⛔ Files ignored due to path filters (12)
  • Resources/Audio/_Funkystation/Effects/Emotes/handshake.ogg is excluded by !**/*.ogg
  • Resources/Audio/_Funkystation/Effects/Emotes/whip_crack.ogg is excluded by !**/*.ogg
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/effect-fistbump.png is excluded by !**/*.png
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/effect-handshake.png is excluded by !**/*.png
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/effect-highfive.png is excluded by !**/*.png
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/effect-hug.png is excluded by !**/*.png
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/effect-tailswipe.png is excluded by !**/*.png
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/icon-fistbump.png is excluded by !**/*.png
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/icon-handshake.png is excluded by !**/*.png
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/icon-highfive.png is excluded by !**/*.png
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/icon-hug.png is excluded by !**/*.png
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/icon-tailswipe.png is excluded by !**/*.png
📒 Files selected for processing (16)
  • Content.Client/_Funkystation/Emoting/DuoEmoteAnimationSystem.cs
  • Content.Client/_Funkystation/Emoting/DuoEmoteSystem.cs
  • Content.Client/_Funkystation/Emoting/UI/DuoEmoteMenu.xaml.cs
  • Content.Server/_Funkystation/Emoting/DuoEmoteSystem.cs
  • Content.Shared/_Funkystation/Emoting/Components/DuoEmoteComponent.cs
  • Content.Shared/_Funkystation/Emoting/Components/DuoEmoteVisualsComponent.cs
  • Content.Shared/_Funkystation/Emoting/Components/TailDuoEmoteComponent.cs
  • Content.Shared/_Funkystation/Emoting/DuoEmoteAttemptEvent.cs
  • Content.Shared/_Funkystation/Emoting/DuoEmoteLungeEvent.cs
  • Content.Shared/_Funkystation/Emoting/EntitySystems/SharedDuoEmoteSystem.cs
  • Content.Shared/_Funkystation/Emoting/Prototypes/DuoEmotePrototype.cs
  • Resources/Audio/_Funkystation/Effects/Emotes/attributions.yml
  • Resources/Locale/en-US/_Funkystation/emotes/duo-emote.ftl
  • Resources/Locale/ru-RU/_Funkystation/emotes/duo-emote.ftl
  • Resources/Prototypes/_Funkystation/Entities/Effects/duo_emotes.yml
  • Resources/Textures/_Funkystation/Effects/duo_emotes.rsi/meta.json
📝 Walkthrough

Summary by CodeRabbit

  • Новые возможности

    • Добавлены парные эмоции: «кулачок», «дай пять», объятие, рукопожатие и взмах хвостом.
    • Добавлено радиальное меню выбора эмоций с иконками и локализованными подсказками.
    • Эмоции синхронно воспроизводятся у обоих участников с визуальными эффектами, звуками и анимациями.
    • Добавлены проверки расстояния, доступности цели и требований к хвосту.
    • Поддержка включена для органических видов, рептилий, вульпканинов, воксов, обезьян и кобольдов.
  • Локализация

    • Добавлены русские и английские тексты для новых эмоций и игровых сообщений.

Walkthrough

Добавлена система дуо-эмоций с общими компонентами, сетевыми событиями, серверной проверкой и клиентским радиальным меню. Добавлены пять прототипов эмоций, анимация Spin, визуальные ресурсы, локализация и поддержка выбранных видов.

Changes

Дуо-эмоции

Layer / File(s) Summary
Контракты и прототипы
Content.Shared/_Ganimed/Emoting/Components/*, Content.Shared/_Ganimed/Emoting/*Event.cs, Content.Shared/_Ganimed/Emoting/Prototypes/DuoEmotePrototype.cs
Добавлены компоненты состояния и визуального эффекта, маркер хвоста, сетевые события и прототип дуо-эмоции.
Общий и серверный жизненный цикл
Content.Shared/_Ganimed/Emoting/EntitySystems/SharedDuoEmoteSystem.cs, Content.Server/_Ganimed/Emoting/DuoEmoteSystem.cs
Системы проверяют участников, дистанцию, препятствия, активное состояние и требования прототипа. Эмоция запускается, выполняется и отменяется по движению или тайм-ауту.
Клиентское меню и анимации
Content.Client/_Ganimed/Emoting/DuoEmoteSystem.cs, Content.Client/_Ganimed/Emoting/DuoEmoteAnimationSystem.cs, Content.Client/_Ganimed/Emoting/UI/DuoEmoteMenu.xaml.cs
Клиент находит цель под курсором, открывает радиальное меню, отправляет выбранную эмоцию, отображает эффект и запускает анимации участников.
Игровые прототипы и ресурсы
Resources/Prototypes/_Ganimed/Entities/Effects/duo_emotes.yml, Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/meta.json, Resources/Locale/*/_Ganimed/emotes/duo-emote.ftl, Resources/Prototypes/**, Resources/Audio/_Ganimed/Effects/Emotes/attributions.yml
Добавлены Fistbump, Highfive, Hug, TailSwipe и Handshake, их изображения, звуки, переводы и компоненты для поддерживаемых видов.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Игрок
  participant DuoEmoteSystem
  participant DuoEmoteMenu
  participant ServerDuoEmoteSystem
  participant SharedDuoEmoteSystem
  participant DuoEmoteAnimationSystem

  Игрок->>DuoEmoteSystem: Открывает меню у цели
  DuoEmoteSystem->>DuoEmoteMenu: Создаёт и открывает меню
  DuoEmoteMenu->>DuoEmoteSystem: Передаёт выбранный ProtoId
  DuoEmoteSystem->>ServerDuoEmoteSystem: Отправляет DuoEmoteAttemptEvent
  ServerDuoEmoteSystem->>SharedDuoEmoteSystem: Проверяет участников и дистанцию
  SharedDuoEmoteSystem->>DuoEmoteSystem: Отправляет DuoEmoteLungeEvent
  DuoEmoteSystem->>DuoEmoteAnimationSystem: Запускает пользовательскую анимацию
Loading

Possibly related PRs

  • ss14-ganimed/Ganimed14#383: Связан с общей конфигурацией анимации Spin и списком AnimatedEmotes.

Suggested labels: T: Enhancement, A: Chat, A: Roleplay/Lore, CL: Valid

Suggested reviewers: crimemoot, hyperb1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 50.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Заголовок точно описывает основное изменение: добавление парных эмоутов и их портирование из другого проекта.
Description check ✅ Passed Описание напрямую связано с изменениями и подробно описывает функциональность, техническую реализацию и затронутые ресурсы.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2500960b-dc6a-4d5a-8b10-bbabc670bba3

📥 Commits

Reviewing files that changed from the base of the PR and between 1f46142 and 2f7a59b.

⛔ Files ignored due to path filters (12)
  • Resources/Audio/_Ganimed/Effects/Emotes/handshake.ogg is excluded by !**/*.ogg
  • Resources/Audio/_Ganimed/Effects/Emotes/whip_crack.ogg is excluded by !**/*.ogg
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/effect-fistbump.png is excluded by !**/*.png
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/effect-handshake.png is excluded by !**/*.png
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/effect-highfive.png is excluded by !**/*.png
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/effect-hug.png is excluded by !**/*.png
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/effect-tailswipe.png is excluded by !**/*.png
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/icon-fistbump.png is excluded by !**/*.png
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/icon-handshake.png is excluded by !**/*.png
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/icon-highfive.png is excluded by !**/*.png
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/icon-hug.png is excluded by !**/*.png
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/icon-tailswipe.png is excluded by !**/*.png
📒 Files selected for processing (23)
  • Content.Client/_Ganimed/Emoting/DuoEmoteAnimationSystem.cs
  • Content.Client/_Ganimed/Emoting/DuoEmoteSystem.cs
  • Content.Client/_Ganimed/Emoting/UI/DuoEmoteMenu.xaml.cs
  • Content.Server/_Ganimed/Emoting/DuoEmoteSystem.cs
  • Content.Shared/_Ganimed/Emoting/Components/DuoEmoteComponent.cs
  • Content.Shared/_Ganimed/Emoting/Components/DuoEmoteVisualsComponent.cs
  • Content.Shared/_Ganimed/Emoting/Components/TailDuoEmoteComponent.cs
  • Content.Shared/_Ganimed/Emoting/DuoEmoteAttemptEvent.cs
  • Content.Shared/_Ganimed/Emoting/DuoEmoteLungeEvent.cs
  • Content.Shared/_Ganimed/Emoting/EntitySystems/SharedDuoEmoteSystem.cs
  • Content.Shared/_Ganimed/Emoting/Prototypes/DuoEmotePrototype.cs
  • Resources/Audio/_Ganimed/Effects/Emotes/attributions.yml
  • Resources/Locale/en-US/_Ganimed/emotes/duo-emote.ftl
  • Resources/Locale/en-US/interaction/interaction-popup-component.ftl
  • Resources/Locale/ru-RU/_Ganimed/emotes/duo-emote.ftl
  • Resources/Locale/ru-RU/interaction/interaction-popup-component.ftl
  • Resources/Prototypes/ADT/Entities/Mobs/Species/Vulpkanin.yml
  • Resources/Prototypes/Entities/Mobs/NPCs/animals.yml
  • Resources/Prototypes/Entities/Mobs/Species/base.yml
  • Resources/Prototypes/Entities/Mobs/Species/reptilian.yml
  • Resources/Prototypes/Entities/Mobs/Species/vox.yml
  • Resources/Prototypes/_Ganimed/Entities/Effects/duo_emotes.yml
  • Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/meta.json
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: build (ubuntu-latest)
  • GitHub Check: YAML Linter
  • GitHub Check: Test Packaging
  • GitHub Check: YAML map schema validator
  • GitHub Check: check-maps
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-28T21:02:49.548Z
Learnt from: HyperB1
Repo: ss14-ganimed/Ganimed14 PR: 350
File: Resources/Locale/en-US/_Harmony/game-ticking/game-presets/preset-conspirator.ftl:14-14
Timestamp: 2026-01-28T21:02:49.548Z
Learning: Verify that locale FTl files under Resources/Locale maintain consistency with control keycodes. If a fork modifies a keycode (e.g., 'o' → 'г' in radio_channels.yml for a specific channel), the English (en-US) locale should be updated to reflect the intended behavior or explicitly document the exception. Ensure the English instruction uses the same keycode as the default/original behavior unless the fork’s logic requires a deliberate change, to avoid localization discrepancies.

Applied to files:

  • Resources/Locale/en-US/interaction/interaction-popup-component.ftl
  • Resources/Locale/ru-RU/interaction/interaction-popup-component.ftl
  • Resources/Locale/en-US/_Ganimed/emotes/duo-emote.ftl
  • Resources/Locale/ru-RU/_Ganimed/emotes/duo-emote.ftl
🔇 Additional comments (23)
Resources/Prototypes/_Ganimed/Entities/Effects/duo_emotes.yml (1)

7-121: LGTM!

Resources/Textures/_Ganimed/Effects/duo_emotes.rsi/meta.json (1)

1-81: LGTM!

Resources/Prototypes/ADT/Entities/Mobs/Species/Vulpkanin.yml (1)

124-125: LGTM!

Resources/Prototypes/Entities/Mobs/Species/base.yml (1)

321-322: LGTM!

Resources/Prototypes/Entities/Mobs/Species/reptilian.yml (1)

74-75: LGTM!

Resources/Prototypes/Entities/Mobs/Species/vox.yml (1)

242-243: LGTM!

Resources/Locale/en-US/_Ganimed/emotes/duo-emote.ftl (1)

5-37: LGTM!

Resources/Locale/ru-RU/_Ganimed/emotes/duo-emote.ftl (1)

6-38: LGTM!

Resources/Locale/en-US/interaction/interaction-popup-component.ftl (1)

100-103: LGTM!

Resources/Locale/ru-RU/interaction/interaction-popup-component.ftl (1)

95-98: LGTM!

Resources/Audio/_Ganimed/Effects/Emotes/attributions.yml (1)

1-16: LGTM!

Content.Shared/_Ganimed/Emoting/Components/DuoEmoteComponent.cs (1)

13-54: LGTM!

Content.Shared/_Ganimed/Emoting/Components/DuoEmoteVisualsComponent.cs (1)

11-15: LGTM!

Content.Shared/_Ganimed/Emoting/Components/TailDuoEmoteComponent.cs (1)

9-15: LGTM!

Content.Shared/_Ganimed/Emoting/DuoEmoteAttemptEvent.cs (1)

11-18: LGTM!

Content.Shared/_Ganimed/Emoting/DuoEmoteLungeEvent.cs (1)

9-17: LGTM!

Content.Shared/_Ganimed/Emoting/Prototypes/DuoEmotePrototype.cs (1)

11-45: LGTM!

Content.Shared/_Ganimed/Emoting/EntitySystems/SharedDuoEmoteSystem.cs (2)

30-76: LGTM!


171-205: LGTM!

Content.Server/_Ganimed/Emoting/DuoEmoteSystem.cs (1)

13-18: LGTM!

Content.Client/_Ganimed/Emoting/DuoEmoteSystem.cs (1)

43-148: LGTM!

Also applies to: 150-231

Content.Client/_Ganimed/Emoting/UI/DuoEmoteMenu.xaml.cs (1)

19-27: LGTM!

Also applies to: 33-56

Content.Client/_Ganimed/Emoting/DuoEmoteAnimationSystem.cs (1)

22-87: LGTM!

Comment on lines +28 to +32
var hasTail = entityManager.HasComponent<TailDuoEmoteComponent>(player);

foreach (var proto in prototypeManager.EnumeratePrototypes<DuoEmotePrototype>()
.Where(p => !p.RequireTail || hasTail)
.OrderBy(p => p.ID))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Проверьте, какие участники валидируются для RequireTail.
rg -n -C 8 --glob '*.cs' \
  '\bRequireTail\b|TailDuoEmoteComponent|DuoEmoteAttemptEvent' \
  Content.Shared Content.Server Content.Client

# Проверьте семантику поля в прототипах.
rg -n -C 4 'requireTail|RequireTail' Resources Content.Shared

Repository: ss14-ganimed/Ganimed14

Length of output: 13321


Проверяйте RequireTail и для цели.

DuoEmotePrototype.RequireTail применяется к обоим участникам: сервер проверяет TailDuoEmoteComponent и у initiator, и у target, а прототип TailSwipe указывает, что эмоция работает только с хвостатыми с обеих сторон. Скройте эти варианты, когда у target.Owner нет TailDuoEmoteComponent.

Comment on lines +79 to +103
protected void AttemptDuoEmote(Entity<DuoEmoteComponent> initiator, Entity<DuoEmoteComponent> target, ProtoId<DuoEmotePrototype> emoteId)
{
if (initiator.Comp.Active || target.Comp.Active)
return;

if (!_prototype.TryIndex(emoteId, out var proto))
return;

if (proto.RequireTail)
{
if (!HasComp<TailDuoEmoteComponent>(initiator.Owner))
return;

if (!HasComp<TailDuoEmoteComponent>(target.Owner))
{
var msg = Loc.GetString("duo-emote-no-tail", ("target", target.Owner));
_popup.PopupEntity(msg, initiator.Owner, initiator.Owner, PopupType.SmallCaution);
return;
}
}

initiator.Comp.Active = true;
initiator.Comp.Target = target.Owner;
initiator.Comp.EmoteId = emoteId;
initiator.Comp.LeaveHangingAt = _timing.CurTime + initiator.Comp.LeftHangingDelay;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Запретите выбор самого себя.

Клиент может передать собственный NetEntity как Target. Система установит Active для одной и той же сущности. Затем Line 45 запретит этой сущности принять собственный запрос. Эффект останется до тайм-аута, а игрок не сможет начать другую парную эмоцию 10 секунд.

Добавьте проверку initiator.Owner == target.Owner до изменения состояния.

Предлагаемое исправление
 protected void AttemptDuoEmote(Entity<DuoEmoteComponent> initiator, Entity<DuoEmoteComponent> target, ProtoId<DuoEmotePrototype> emoteId)
 {
+    if (initiator.Owner == target.Owner)
+        return;
+
     if (initiator.Comp.Active || target.Comp.Active)
         return;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected void AttemptDuoEmote(Entity<DuoEmoteComponent> initiator, Entity<DuoEmoteComponent> target, ProtoId<DuoEmotePrototype> emoteId)
{
if (initiator.Comp.Active || target.Comp.Active)
return;
if (!_prototype.TryIndex(emoteId, out var proto))
return;
if (proto.RequireTail)
{
if (!HasComp<TailDuoEmoteComponent>(initiator.Owner))
return;
if (!HasComp<TailDuoEmoteComponent>(target.Owner))
{
var msg = Loc.GetString("duo-emote-no-tail", ("target", target.Owner));
_popup.PopupEntity(msg, initiator.Owner, initiator.Owner, PopupType.SmallCaution);
return;
}
}
initiator.Comp.Active = true;
initiator.Comp.Target = target.Owner;
initiator.Comp.EmoteId = emoteId;
initiator.Comp.LeaveHangingAt = _timing.CurTime + initiator.Comp.LeftHangingDelay;
protected void AttemptDuoEmote(Entity<DuoEmoteComponent> initiator, Entity<DuoEmoteComponent> target, ProtoId<DuoEmotePrototype> emoteId)
{
if (initiator.Owner == target.Owner)
return;
if (initiator.Comp.Active || target.Comp.Active)
return;
if (!_prototype.TryIndex(emoteId, out var proto))
return;
if (proto.RequireTail)
{
if (!HasComp<TailDuoEmoteComponent>(initiator.Owner))
return;
if (!HasComp<TailDuoEmoteComponent>(target.Owner))
{
var msg = Loc.GetString("duo-emote-no-tail", ("target", target.Owner));
_popup.PopupEntity(msg, initiator.Owner, initiator.Owner, PopupType.SmallCaution);
return;
}
}
initiator.Comp.Active = true;
initiator.Comp.Target = target.Owner;
initiator.Comp.EmoteId = emoteId;
initiator.Comp.LeaveHangingAt = _timing.CurTime + initiator.Comp.LeftHangingDelay;

if (sound != null)
_audio.PlayPvs(sound, initiatorUid);

RaiseNetworkEvent(new DuoEmoteLungeEvent(GetNetEntity(initiatorUid), GetNetEntity(partnerUid), proto.Animation));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -t f 'DuoEmoteAnimationSystem.cs|SharedDuoEmoteSystem.cs' Content
rg -n -C 8 --type=cs 'DuoEmoteLungeEvent|RaiseNetworkEvent\s*\(|Filter\.Pvs' Content

Repository: ss14-ganimed/Ganimed14

Length of output: 255


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'repo root:\n'
pwd
printf '\ntracked matching files:\n'
git ls-files | rg -n 'DuoEmoteAnimationSystem\.cs$|SharedDuoEmoteSystem\.cs$' || true

printf '\nsearch all tracked C# files:\n'
rg -n -C 8 --glob '*.cs' 'DuoEmoteLungeEvent|RaiseNetworkEvent\s*\(|Filter\.Pvs' . || true

Repository: ss14-ganimed/Ganimed14

Length of output: 50379


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'tracked matching files:\n'
git ls-files | rg 'DuoEmoteAnimationSystem\.cs$|SharedDuoDuoEmoteSystem\.cs$|SharedDuoEmoteSystem\.cs$' || true

printf '\nall tracked files with DuoEmote path/base names:\n'
git ls-files | rg 'DuoEmote|/_Ganimed/Emoting/' || true

printf '\ncount occurrences with --max-count if available:\n'
rg -n --glob '*.cs' 'DuoEmoteLungeEvent|class .*DuoEmote|struct DuoEmoteLungeEvent|RaiseNetworkEvent\s*\(' . --max-count 200 || true

Repository: ss14-ganimed/Ganimed14

Length of output: 29713


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'SharedDuoEmoteSystem relevant section:\n'
sed -n '130,180p' Content.Shared/_Ganimed/Emoting/EntitySystems/SharedDuoEmoteSystem.cs | cat -n

printf '\nDuoEmoteLungeEvent definition:\n'
cat -n Content.Shared/_Ganimed/Emoting/DuoEmoteLungeEvent.cs

printf '\nDuoEmoteAnimationSystem relevant section:\n'
sed -n '1,140p' Content.Client/_Ganimed/Emoting/DuoEmoteAnimationSystem.cs | cat -n

printf '\nDuoEmoteSystem client relevant section:\n'
sed -n '1,120p' Content.Client/_Ganimed/Emoting/DuoEmoteSystem.cs | cat -n

printf '\nSharedDuoEmoteSystem references to popup/audio:\n'
rg -n -C 3 --glob '*.cs' 'Popup|_popup|Audio|_audio|Filter\.Pvs' Content.Shared/_Ganimed/Emoting Content.Server/_Ganimed/Emoting Content.Client/_Ganimed/Emoting || true

Repository: ss14-ganimed/Ganimed14

Length of output: 20757


🏁 Script executed:

#!/bin/bash
 set -euo pipefail

printf 'Locate NetManager/RaiseNetworkEvent definitions in repository dependencies/source:\n'
rg -n --glob '*.cs' 'void raiseNetworkEvent|Task RaiseNetworkEvent|RaiseNetworkEvent\(' . --max-count 80 || true

printf '\nFind network filter-related usages near EntitySystem:\n'
rg -n --glob '*.cs' 'class .*EntitySystem|RaiseNetworkEvent\([^)]*Filter|RaiseNetworkEvent\([^)]*EntityUid|Filter\.Pvs(Except)?\(' RobustToolbox Robust.Shared Robust.Client Robust.Server Robust.UnitTesting -g '*.cs' 2>/dev/null | head -300 || true

printf '\nInspect likely Shared/Network EntitySystem definitions via git ls-files:\n'
git ls-files | rg 'EntitySystem|NetManager|Network' | head -200 || true

Repository: ss14-ganimed/Ganimed14

Length of output: 40136


🌐 Web query:

RobustToolbox RaiseNetworkEvent Filter.Pvs entity arguments overload source

💡 Result:

In the RobustToolbox engine, there is no direct functional relationship between the RaiseNetworkEvent method and a Filter.Pvs entity arguments overload [1][2]. These are separate networking and state-management concepts within the engine [1]. RaiseNetworkEvent is a core method used to send network messages (EntityEvents) from the server to clients [2]. Its overloads typically allow you to specify the event instance and optionally a target, such as an ICommonSession or a list of sessions, to restrict which clients receive the event [2][3]. Filter.Pvs (Potentially Visible Set) is a specific component of the RobustToolbox networking system responsible for managing entity visibility and state synchronization between the server and clients [4][5]. It determines which entities are sent to which players based on proximity and other criteria, rather than being an argument passed directly into RaiseNetworkEvent [6][7]. While the PVS system uses events—such as ExpandPvsEvent—to manage visibility logic (e.g., ensuring entities with open UIs are sent to a player), these are distinct from the standard RaiseNetworkEvent used for general game logic communication [6][7]. If you are attempting to filter network traffic, it is common practice to use PVS-related systems to manage entity visibility or to manually manage recipient lists in RaiseNetworkEvent calls [2][3].

Citations:


🌐 Web query:

github RobustToolbox EntitySystem RaiseNetworkEvent Filter.Empty Broadcast signature

💡 Result:

In RobustToolbox, the RaiseNetworkEvent method is used to send network events, and it typically accepts a message object alongside an optional filter or recipient to determine which clients receive the event [1][2][3]. When using a filter, Filter.Empty is commonly used as a starting point to construct a specific set of recipients [2][3]. You can then chain methods onto this, such as AddPlayersByPvs, to define who should receive the message [3]. A typical signature for RaiseNetworkEvent within an EntitySystem context often looks like this [2][3]: RaiseNetworkEvent(EntityEventArgs message, Filter filter, bool replayRecord = false); Alternatively, to send to a single specific player: RaiseNetworkEvent(EntityEventArgs message, ICommonSession recipient); Regarding your query about Broadcast, networked events in RobustToolbox can be directed at specific entities or filters [4]. While some events are broadcast by default, many systems now utilize specific filters (like those based on PVS, or Player Visibility System) to ensure efficiency by only sending data to relevant clients [3]. Note that Broadcast is also a term used for local entity events managed by the IEventBus, which are distinct from network-level events [4].

Citations:


Настройте область доставки DuoEmoteLungeEvent.

RaiseNetworkEvent без Filter не ограничивает событие по PVS, а client handler запускает клиентскую анимацию для обоих участников. Добавьте PVS-фильтр в сторону initiatorUid, аналогично _popup и audio, чтобы анимацию не запускали игроки вне зоны видимости эмоции.

Comment thread Resources/Prototypes/Entities/Mobs/NPCs/animals.yml

@HyperB1 HyperB1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ХРП панель :)
Концептуально замечательная вещь, единственное что могу попросить - это оставлять неймспейс других форков, то есть контент funky station должен быть в их неймспейсе (_Funky, если не ошибаюсь? Проверь).

@HyperB1 HyperB1 added T: Enhancement Type: Новый контент, QoL и улучшения T: Port Type: Изменения были портированы с другого репозитория P3: Standard Стандартный приоритет рассмотрения S: Conceptual Approval Статус: Концепция PR'а одобрена A: Character/Species Area: Player characters and species features and content. A: Chat Area: Chat-related features and changes, most likely technical A: General Interactions General in-game interactions that don't relate to another area. labels Aug 8, 2026
@ThundraBoomerov ThundraBoomerov added S: Awaiting Changes Status: В ожидании запрошенных изменений P1: High Высокий приоритет. Рассматривать в первую очередь. and removed P3: Standard Стандартный приоритет рассмотрения labels Aug 8, 2026
@ultradyper

Copy link
Copy Markdown
Contributor Author

Проверил оригинал: в forky-station неймспейс называется _Funkystation (не _Funky). Весь порт перенесён туда: C# (namespace Content.*._Funkystation.Emoting), прототипы, локали, аудио, текстуры. Прототип эффекта переименован в FunkyEffectDuoEmoteBase, как в оригинале. Правки ванили помечены Ganimed-Add/Ganimed-Edit.

@HyperB1 HyperB1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Только перевод и комментарии поправить.

#
# SPDX-License-Identifier: AGPL-3.0-or-later

duo-emote-left-hanging = Вас оставили висеть...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нормальную локализацию пожалуйста, а не дословный перевод

Comment on lines -95 to +98
hugging-success-generic = Вы обнимаете { $target }.
hugging-success-generic-others = { CAPITALIZE($user) } обнимает { $target }.
hugging-success-generic-target = { CAPITALIZE($user) } обнимает вас.
# Ganimed-Edit: duo emotes port (funky-station#179), hug -> neutral pat on the back
hugging-success-generic = Вы похлопываете { $target } по спине.
hugging-success-generic-others = { CAPITALIZE($user) } похлопывает { $target } по спине.
hugging-success-generic-target = { CAPITALIZE($user) } похлопывает вас по спине.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

И так нормально было ИМХО

Comment on lines +124 to +125
# Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид.
- type: TailDuoEmote

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Однострочные изменения - однострочные комментарии

Suggested change
# Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид.
- type: TailDuoEmote
- type: TailDuoEmote # Ganimed-Add: Duo emotes port (funky-station#179)

Comment on lines +1506 to +1508
# Ganimed-Add: duo emotes port (funky-station#179), обезьяны участвуют в парных эмоутах.
- type: DuoEmote
- type: TailDuoEmote

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Ganimed-Add: duo emotes port (funky-station#179), обезьяны участвуют в парных эмоутах.
- type: DuoEmote
- type: TailDuoEmote
# Ganimed-Add-Start: Duo emotes port (funky-station#179)
- type: DuoEmote
- type: TailDuoEmote
# Ganimed-Add-End

Comment on lines +1707 to +1709
# Ganimed-Add: duo emotes port (funky-station#179), кобольды участвуют в парных эмоутах.
- type: DuoEmote
- type: TailDuoEmote

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Ganimed-Add: duo emotes port (funky-station#179), кобольды участвуют в парных эмоутах.
- type: DuoEmote
- type: TailDuoEmote
# Ganimed-Add-Start: Duo emotes port (funky-station#179)
- type: DuoEmote
- type: TailDuoEmote
# Ganimed-Add-End

Comment on lines +321 to +322
# Ganimed-Add: duo emotes port (funky-station#179), все игровые виды могут участвовать.
- type: DuoEmote

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Ganimed-Add: duo emotes port (funky-station#179), все игровые виды могут участвовать.
- type: DuoEmote
- type: DuoEmote # Ganimed-Add: Duo emotes port (funky-station#179)

Comment on lines +74 to +75
# Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид.
- type: TailDuoEmote

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид.
- type: TailDuoEmote
- type: TailDuoEmote # Ganimed-Add: Duo emotes port (funky-station#179)

Comment on lines +242 to +243
# Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид.
- type: TailDuoEmote

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид.
- type: TailDuoEmote
- type: TailDuoEmote # Ganimed-Add: Duo emotes port (funky-station#179)

{
"version": 1,
"license": "CC-BY-SA-4.0",
"copyright": "sproten by zergologist (Discord) for Funky Station, ported to Ganimed14 from funky-station/forky-station PR #179",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Какие "шпротен"?

Suggested change
"copyright": "sproten by zergologist (Discord) for Funky Station, ported to Ganimed14 from funky-station/forky-station PR #179",
"copyright": "Made by zergologist (Discord) for Funky Station, taken from Funky Station at commit https://github.com/funky-station/forky-station/commit/c7898d4dacafd52114cb7df54971c93701f213c6",

@github-actions github-actions Bot removed the S: Awaiting Changes Status: В ожидании запрошенных изменений label Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A: Character/Species Area: Player characters and species features and content. A: Chat Area: Chat-related features and changes, most likely technical A: General Interactions General in-game interactions that don't relate to another area. Changes: Audio Изменения в звуковой составляющей игры, такие как звуковые эффекты и музыка. Changes: Localization Изменения затрагивают локализацию. Changes: Sprites Changes: UI P1: High Высокий приоритет. Рассматривать в первую очередь. S: Conceptual Approval Статус: Концепция PR'а одобрена S: Needs Review Status: Требуется рассмотрение size/L T: Enhancement Type: Новый контент, QoL и улучшения T: Port Type: Изменения были портированы с другого репозитория

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants