add: Парные эмоуты (duo emotes), порт funky-station#179 - #418
Conversation
Радиальное меню парных эмоутов на клавише эмоут-колеса при наведении на другого игрока: кулачок, дай пять, объятие, рукопожатие, взмах хвостом (для хвостатых видов). Второй игрок отвечает кликом в течение 10 секунд, оба выполняют анимацию и звук. Новые эмоуты добавляются через прототип duoEmote без кода. Тексты объятий заменены на нейтральное похлопывание по спине.
|
RSI Diff Bot; head commit 19244d4 merging into 1f46142 Resources/Textures/_Funkystation/Effects/duo_emotes.rsi
|
|
Warning Review limit reached
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 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 configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (12)
📒 Files selected for processing (16)
📝 WalkthroughSummary by CodeRabbit
WalkthroughДобавлена система дуо-эмоций с общими компонентами, сетевыми событиями, серверной проверкой и клиентским радиальным меню. Добавлены пять прототипов эмоций, анимация ChangesДуо-эмоции
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: Запускает пользовательскую анимацию
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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. Comment |
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (12)
Resources/Audio/_Ganimed/Effects/Emotes/handshake.oggis excluded by!**/*.oggResources/Audio/_Ganimed/Effects/Emotes/whip_crack.oggis excluded by!**/*.oggResources/Textures/_Ganimed/Effects/duo_emotes.rsi/effect-fistbump.pngis excluded by!**/*.pngResources/Textures/_Ganimed/Effects/duo_emotes.rsi/effect-handshake.pngis excluded by!**/*.pngResources/Textures/_Ganimed/Effects/duo_emotes.rsi/effect-highfive.pngis excluded by!**/*.pngResources/Textures/_Ganimed/Effects/duo_emotes.rsi/effect-hug.pngis excluded by!**/*.pngResources/Textures/_Ganimed/Effects/duo_emotes.rsi/effect-tailswipe.pngis excluded by!**/*.pngResources/Textures/_Ganimed/Effects/duo_emotes.rsi/icon-fistbump.pngis excluded by!**/*.pngResources/Textures/_Ganimed/Effects/duo_emotes.rsi/icon-handshake.pngis excluded by!**/*.pngResources/Textures/_Ganimed/Effects/duo_emotes.rsi/icon-highfive.pngis excluded by!**/*.pngResources/Textures/_Ganimed/Effects/duo_emotes.rsi/icon-hug.pngis excluded by!**/*.pngResources/Textures/_Ganimed/Effects/duo_emotes.rsi/icon-tailswipe.pngis excluded by!**/*.png
📒 Files selected for processing (23)
Content.Client/_Ganimed/Emoting/DuoEmoteAnimationSystem.csContent.Client/_Ganimed/Emoting/DuoEmoteSystem.csContent.Client/_Ganimed/Emoting/UI/DuoEmoteMenu.xaml.csContent.Server/_Ganimed/Emoting/DuoEmoteSystem.csContent.Shared/_Ganimed/Emoting/Components/DuoEmoteComponent.csContent.Shared/_Ganimed/Emoting/Components/DuoEmoteVisualsComponent.csContent.Shared/_Ganimed/Emoting/Components/TailDuoEmoteComponent.csContent.Shared/_Ganimed/Emoting/DuoEmoteAttemptEvent.csContent.Shared/_Ganimed/Emoting/DuoEmoteLungeEvent.csContent.Shared/_Ganimed/Emoting/EntitySystems/SharedDuoEmoteSystem.csContent.Shared/_Ganimed/Emoting/Prototypes/DuoEmotePrototype.csResources/Audio/_Ganimed/Effects/Emotes/attributions.ymlResources/Locale/en-US/_Ganimed/emotes/duo-emote.ftlResources/Locale/en-US/interaction/interaction-popup-component.ftlResources/Locale/ru-RU/_Ganimed/emotes/duo-emote.ftlResources/Locale/ru-RU/interaction/interaction-popup-component.ftlResources/Prototypes/ADT/Entities/Mobs/Species/Vulpkanin.ymlResources/Prototypes/Entities/Mobs/NPCs/animals.ymlResources/Prototypes/Entities/Mobs/Species/base.ymlResources/Prototypes/Entities/Mobs/Species/reptilian.ymlResources/Prototypes/Entities/Mobs/Species/vox.ymlResources/Prototypes/_Ganimed/Entities/Effects/duo_emotes.ymlResources/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.ftlResources/Locale/ru-RU/interaction/interaction-popup-component.ftlResources/Locale/en-US/_Ganimed/emotes/duo-emote.ftlResources/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!
| var hasTail = entityManager.HasComponent<TailDuoEmoteComponent>(player); | ||
|
|
||
| foreach (var proto in prototypeManager.EnumeratePrototypes<DuoEmotePrototype>() | ||
| .Where(p => !p.RequireTail || hasTail) | ||
| .OrderBy(p => p.ID)) |
There was a problem hiding this comment.
🎯 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.SharedRepository: ss14-ganimed/Ganimed14
Length of output: 13321
Проверяйте RequireTail и для цели.
DuoEmotePrototype.RequireTail применяется к обоим участникам: сервер проверяет TailDuoEmoteComponent и у initiator, и у target, а прототип TailSwipe указывает, что эмоция работает только с хвостатыми с обеих сторон. Скройте эти варианты, когда у target.Owner нет TailDuoEmoteComponent.
| 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; |
There was a problem hiding this comment.
🎯 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.
| 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)); |
There was a problem hiding this comment.
🚀 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' ContentRepository: 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' . || trueRepository: 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 || trueRepository: 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 || trueRepository: 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 || trueRepository: 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:
- 1: https://meowmew.mintlify.app/en/ss14-by-example/basic-networking-and-you
- 2: https://github.com/space-wizards/docs/blob/master/src/en/ss14-by-example/basic-networking-and-you.md
- 3: https://github.com/space-wizards/space-station-14/blob/master/Content.Server/Tabletop/TabletopSystem.Session.cs
- 4: Entity movement updates space-wizards/RobustToolbox#2512
- 5: space-wizards/RobustToolbox@e3d29ae
- 6: https://github.com/space-wizards/RobustToolbox/blob/baa607532d0e9fc4a7d9212ab332d938d9324fbe/RELEASE-NOTES.md
- 7: https://github.com/space-wizards/RobustToolbox/blob/da56851846d3fb33f7a77233fc3d505affd81ac1/RELEASE-NOTES.md
🌐 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:
- 1: https://meowmew.mintlify.app/en/ss14-by-example/basic-networking-and-you
- 2: https://github.com/imperial-space/SW-public/blob/ef491eae/Content.Server/Pointing/EntitySystems/PointingSystem.cs
- 3: space-syndicate/space-station-14@606c5a8
- 4: space-wizards/RobustToolbox@f7e8178
Настройте область доставки DuoEmoteLungeEvent.
RaiseNetworkEvent без Filter не ограничивает событие по PVS, а client handler запускает клиентскую анимацию для обоих участников. Добавьте PVS-фильтр в сторону initiatorUid, аналогично _popup и audio, чтобы анимацию не запускали игроки вне зоны видимости эмоции.
HyperB1
left a comment
There was a problem hiding this comment.
ХРП панель :)
Концептуально замечательная вещь, единственное что могу попросить - это оставлять неймспейс других форков, то есть контент funky station должен быть в их неймспейсе (_Funky, если не ошибаюсь? Проверь).
|
Проверил оригинал: в forky-station неймспейс называется _Funkystation (не _Funky). Весь порт перенесён туда: C# (namespace Content.*._Funkystation.Emoting), прототипы, локали, аудио, текстуры. Прототип эффекта переименован в FunkyEffectDuoEmoteBase, как в оригинале. Правки ванили помечены Ganimed-Add/Ganimed-Edit. |
HyperB1
left a comment
There was a problem hiding this comment.
Только перевод и комментарии поправить.
| # | ||
| # SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
|
||
| duo-emote-left-hanging = Вас оставили висеть... |
There was a problem hiding this comment.
Нормальную локализацию пожалуйста, а не дословный перевод
| 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) } похлопывает вас по спине. |
| # Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид. | ||
| - type: TailDuoEmote |
There was a problem hiding this comment.
Однострочные изменения - однострочные комментарии
| # Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид. | |
| - type: TailDuoEmote | |
| - type: TailDuoEmote # Ganimed-Add: Duo emotes port (funky-station#179) |
| # Ganimed-Add: duo emotes port (funky-station#179), обезьяны участвуют в парных эмоутах. | ||
| - type: DuoEmote | ||
| - type: TailDuoEmote |
There was a problem hiding this comment.
| # 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 |
| # Ganimed-Add: duo emotes port (funky-station#179), кобольды участвуют в парных эмоутах. | ||
| - type: DuoEmote | ||
| - type: TailDuoEmote |
There was a problem hiding this comment.
| # 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 |
| # Ganimed-Add: duo emotes port (funky-station#179), все игровые виды могут участвовать. | ||
| - type: DuoEmote |
There was a problem hiding this comment.
| # Ganimed-Add: duo emotes port (funky-station#179), все игровые виды могут участвовать. | |
| - type: DuoEmote | |
| - type: DuoEmote # Ganimed-Add: Duo emotes port (funky-station#179) |
| # Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид. | ||
| - type: TailDuoEmote |
There was a problem hiding this comment.
| # Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид. | |
| - type: TailDuoEmote | |
| - type: TailDuoEmote # Ganimed-Add: Duo emotes port (funky-station#179) |
| # Ganimed-Add: duo emotes port (funky-station#179), хвостатый вид. | ||
| - type: TailDuoEmote |
There was a problem hiding this comment.
| # 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", |
There was a problem hiding this comment.
Какие "шпротен"?
| "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", |







Описание 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), тексты объятий.Медиа
Скриншоты добавлю отдельным комментом.
Чек-лист
Список изменений
🆑