From f8ffb5fb9a7593b8b8f4a3285e82b40a192b19bc Mon Sep 17 00:00:00 2001 From: Firemixx Date: Sun, 22 Feb 2026 14:35:52 +0200 Subject: [PATCH 1/5] offeritem --- ...20\274\320\265\321\202\320\276\320\262.md" | 509 ++++++++++++++++++ 1 file changed, 509 insertions(+) create mode 100644 "_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" diff --git "a/_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" "b/_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" new file mode 100644 index 0000000..3ac7134 --- /dev/null +++ "b/_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" @@ -0,0 +1,509 @@ +## Система передачи из рук в руки (Offer Item System) - документация +Данный документ — сводка информации по новым файлам, системам/компонентам и изменениям в существующих файлах связанным с системой анимации искр сварки (Welding Sparks Animation). + +# Оглавление +1. [Суть системы](#сутьсистемы) +2. [Архитектура](#архитектура) +3. [Server](#сервер) +4. [Shared](#шеред) +5. [Client](#клиент) +6. [Что было изменено в офф. файлах](#оффы) +7. [Изменения Yaml и в текстурах](#ямл) + + +### Суть системы +После нажатия на бинд OfferItem на F, у передающего появляется оверлей OfferItemIndicatorsOverlay, переключая в OfferItemComponent IsInOfferMode. После клика по тому, кому надо передать предмет, у клиента появляется алерт. При клике на алерт предмет передается IsInOfferMode становится на офф + + +## Архитектура +``` +Схема потока данных: + +[Игрок нажимает на F] + │ + ▼ +[Shared: SharedOfferItemSystem.Interactions] + └── Регестрируется система основная при нажатии на бинд + │ + ▼ +[Shared: SharedOfferItemSystem.Interactions.SetInOfferMode()] + ├── Проверка на предиктед фярст тайм + ├── Проверка на сессию + ├── Проверка есть ли ентити + ├── Проверка на OfferItemComponent + ├── Проверка на HandsComponent и получаем активную руку + ├── Если нету предмета в руке возращаемся очищая за собой все и выдавая попап + └── Если есть предмет в руке, делаем анресиев, очищаем исиноффермод и возращаемся + + │ + ▼ +[Shared: SharedOfferItemSystem.Interactions.OnClickAlertEvent()] + └── Получаем предмет при клике + + │ + ▼ +[Shared: SharedOfferItemSystem.Interactions.Recieve()] + ├── Проверка на исфярсттаймпредиктед, зарезолвленно ли, есть ли компонент офферайтема, есть ли рука, есть ли сам таргет, есть ли у таргета компонент руки + ├── Если не ноль айтем у передающего, то передаем в руку предмет и воспроизводим попап + ├── Чистим offerItem.Item + └── UnOffer +[Server: WeldingSparksSystem.OnUseTool()] + ├── Играет звук сварки + ├── Определяет позицию спавна эффекта (координаты цели или клика) + ├── Spawn("EchoEffectWeldingSparks") — создаёт сущность-эффект + └── RaiseNetworkEvent(SpawnedWeldingSparksEvent) → все клиенты + │ + ▼ +[Shared: SharedOfferItemSystem.Interactions.UnOffer()] + └── После проверок очищаем переменные +``` + + +## Server +Есть только один файл, OfferItemSystem , в нем мы при обновлении кадра проверяем на то, есть ли те кто передают, а после анресиваем и переключаем ис ин офер мод, если нету айтема в руке, отключаем офер. Если человек не в ресив моде, чистим алерт "Взять", а иначе выдаем алерт +а ну и тут в наличии антиспам апдейта с помощью: +``` + private float _offerAcc = 0; + private const float OfferAccMax = 0.5f; + + + _offerAcc += frameTime; + + if (_offerAcc >= OfferAccMax) + _offerAcc -= OfferAccMax; + else + return; + + +``` +сам код: +``` + public override void Update(float frameTime) + { + _offerAcc += frameTime; + + if (_offerAcc >= OfferAccMax) + _offerAcc -= OfferAccMax; + else + return; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var offerItem, out var hands)) + { + if (hands.ActiveHandId is null) + continue; + + if (offerItem.Hand is not null && _hands.GetActiveItem(uid) is null) + if (offerItem.Target is not null) + { + UnReceive(offerItem.Target.Value, offerItem: offerItem); + offerItem.IsInOfferMode = false; + Dirty(uid, offerItem); + } + else + UnOffer(uid, offerItem); + + if (!offerItem.IsInReceiveMode) + { + _alertsSystem.ClearAlert(uid, OfferAlert); + continue; + } + + _alertsSystem.ShowAlert(uid, OfferAlert); + } + } +``` + + +## Shared +### Interaction +Делаем привязку к бинду воида SetInOfferMode() +``` + private void InitializeInteractions() + { + CommandBinds.Builder + .Bind(ContentKeyFunctions.OfferItem, InputCmdHandler.FromDelegate(SetInOfferMode, handle: false, outsidePrediction: false)) + .Register(); + } +``` +Переключаем оффер мод с проверками +``` + private void SetInOfferMode(ICommonSession? session) + { + if (!_timing.IsFirstTimePredicted) + return; + + if (session is null) + return; + + if (session.AttachedEntity is not { Valid: true } uid || !Exists(uid) || !_actionBlocker.CanInteract(uid, null)) + return; + + if (!TryComp(uid, out var offerItem)) + return; + + if (!TryComp(uid, out var hands) || hands.ActiveHandId is null) + return; + + offerItem.Item = _hand.GetActiveItem((uid, hands)); + + if (!offerItem.IsInOfferMode) + { + if (offerItem.Item is null) + { + _popup.PopupClient(Loc.GetString("offer-item-empty-hand"), uid, uid); + return; + } + + if (offerItem.Hand is null || offerItem.Target is null) + { + offerItem.IsInOfferMode = true; + offerItem.Hand = hands.ActiveHandId; + + Dirty(uid, offerItem); + return; + } + } + + if (offerItem.Target is not null) + { + UnReceive(offerItem.Target.Value, offerItem: offerItem); + offerItem.IsInOfferMode = false; + Dirty(uid, offerItem); + return; + } + + UnOffer(uid, offerItem); + } +``` +## System +Привязка к интеракту, перемещению игрока, клику по ивенту +``` + public override void Initialize() + { + SubscribeLocalEvent(SetInReceiveMode); + SubscribeLocalEvent(OnMove); + + InitializeInteractions(); + + SubscribeLocalEvent(OnClickAlertEvent); + } +``` +При клике на алерт обращаемся к войду и получаем предмет +``` + private void OnClickAlertEvent(Entity ent, ref AcceptOfferAlertEvent ev) + { + if (ev.Handled || ev.AlertId != OfferAlert) + return; + ev.Handled = true; + Receive(ent!); + } +``` +Проверки и перемещение в руки с попапом предмета +``` + public void Receive(Entity ent) + { + if (!_timing.IsFirstTimePredicted) + return; + + if (!Resolve(ent, ref ent.Comp)) + return; + + if (!TryComp(ent.Comp.Target, out var offerItem)) + return; + + if (offerItem.Hand is null) + return; + + if (ent.Comp.Target is null) + return; + + if (!TryComp(ent, out var hands)) + return; + + if (offerItem.Item is not null) + { + if (!_hand.TryPickup(ent, offerItem.Item.Value, handsComp: hands)) + { + _popup.PopupClient(Loc.GetString("offer-item-full-hand"), ent, ent); + return; + } + + _popup.PopupClient(Loc.GetString("offer-item-give", + ("item", Identity.Entity(offerItem.Item.Value, EntityManager)), + ("target", Identity.Entity(ent, EntityManager))), ent.Comp.Target.Value, ent.Comp.Target.Value); + + _popup.PopupPredicted(Loc.GetString("offer-item-give-other", + ("user", Identity.Entity(ent.Comp.Target.Value, EntityManager)), + ("item", Identity.Entity(offerItem.Item.Value, EntityManager)), + ("target", Identity.Entity(ent, EntityManager))) + , ent.Comp.Target.Value, ent); + } + + offerItem.Item = null; + Dirty(ent); + UnReceive(ent, ent, offerItem); + } +``` +Если выходим за пределы максимальной дистанции, отключаем офер +``` + private void OnMove(EntityUid uid, OfferItemComponent component, MoveEvent args) + { + if (component.Target is null || + _transform.InRange(args.NewPosition, + Transform(component.Target.Value).Coordinates, + component.MaxOfferDistance) + ) + return; + + UnOffer(uid, component); + } +``` + +Делаем офферы и ресетаем все значения с попапами +``` + protected void UnOffer(EntityUid uid, OfferItemComponent component) + { + if (!TryComp(uid, out var hands) || hands.ActiveHandId is null) + return; + + if (TryComp(component.Target, out var offerItem) && component.Target is not null) + { + if (component.Item is not null) + { + if (!_timing.IsFirstTimePredicted) + { + _popup.PopupClient(Loc.GetString("offer-item-no-give", + ("item", Identity.Entity(component.Item.Value, EntityManager)), + ("target", Identity.Entity(component.Target.Value, EntityManager))), uid, uid); + _popup.PopupClient(Loc.GetString("offer-item-no-give-target", + ("user", Identity.Entity(uid, EntityManager)), + ("item", Identity.Entity(component.Item.Value, EntityManager))), uid, component.Target.Value); + } + + } + else if (offerItem.Item is not null) + if (!_timing.IsFirstTimePredicted) + { + _popup.PopupClient(Loc.GetString("offer-item-no-give", + ("item", Identity.Entity(offerItem.Item.Value, EntityManager)), + ("target", Identity.Entity(uid, EntityManager))), component.Target.Value, component.Target.Value); + _popup.PopupClient(Loc.GetString("offer-item-no-give-target", + ("user", Identity.Entity(component.Target.Value, EntityManager)), + ("item", Identity.Entity(offerItem.Item.Value, EntityManager))), component.Target.Value, uid); + } + + offerItem.IsInOfferMode = false; + offerItem.IsInReceiveMode = false; + offerItem.Hand = null; + offerItem.Target = null; + offerItem.Item = null; + + Dirty(component.Target.Value, offerItem); + } + + component.IsInOfferMode = false; + component.IsInReceiveMode = false; + component.Hand = null; + component.Target = null; + component.Item = null; + + Dirty(uid, component); + } +``` + +Проверки и ануление всех переменных +``` + protected void UnReceive(EntityUid uid, OfferItemComponent? component = null, OfferItemComponent? offerItem = null) + { + if (component is null && !TryComp(uid, out component)) + return; + + if (offerItem is null && !TryComp(component.Target, out offerItem)) + return; + + if (!TryComp(uid, out var hands) || hands.ActiveHandId is null || + component.Target is null) + return; + + if (offerItem.Item is not null) + { + _popup.PopupClient(Loc.GetString("offer-item-no-give", + ("item", Identity.Entity(offerItem.Item.Value, EntityManager)), + ("target", Identity.Entity(uid, EntityManager))), component.Target.Value, component.Target.Value); + _popup.PopupClient(Loc.GetString("offer-item-no-give-target", + ("user", Identity.Entity(component.Target.Value, EntityManager)), + ("item", Identity.Entity(offerItem.Item.Value, EntityManager))), component.Target.Value, uid); + } + + if (!offerItem.IsInReceiveMode) + { + offerItem.Target = null; + component.Target = null; + } + + offerItem.Item = null; + offerItem.Hand = null; + component.IsInReceiveMode = false; + + Dirty(uid, component); + } +``` + +Проверка на то, в режиме предложения ли +``` + protected bool IsInOfferMode(EntityUid? entity, OfferItemComponent? component = null) + { + return entity is not null && Resolve(entity.Value, ref component, false) && component.IsInOfferMode; + } +``` + +## Component +``` + [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + public bool IsInOfferMode; + + [DataField, AutoNetworkedField] + public bool IsInReceiveMode; + + [DataField, AutoNetworkedField] + public string? Hand; + + [DataField, AutoNetworkedField] + public EntityUid? Item; + + [DataField, AutoNetworkedField] + public EntityUid? Target; + + [DataField] + public float MaxOfferDistance = 2f; +``` + +## Events +Ивент принятия офера при нажатии на алерт +``` +public sealed partial class AcceptOfferAlertEvent : BaseAlertEvent; +``` + + +### Client +## IndicatorsOverlay +переменные: +``` + private readonly IInputManager _inputManager; + private readonly IEntityManager _entMan; + private readonly IEyeManager _eye; + private readonly OfferItemSystem _offer; + + private readonly Texture _sight; + + public override OverlaySpace Space => OverlaySpace.ScreenSpace; + + private readonly Color _mainColor = Color.White.WithAlpha(0.3f); + private readonly Color _strokeColor = Color.Black.WithAlpha(0.5f); + private readonly float _scale = 0.6f; // 1 is a little big +``` + +сам оверлей + +``` + public OfferItemIndicatorsOverlay(IInputManager input, IEntityManager entMan, IEyeManager eye, OfferItemSystem offerSys) + { + _inputManager = input; + _entMan = entMan; + _eye = eye; + _offer = offerSys; + + var spriteSys = _entMan.EntitySysManager.GetEntitySystem(); + _sight = spriteSys.Frame0(new SpriteSpecifier.Rsi(new ResPath("/Textures/_ECHO/Misc/give_item.rsi"), "give_item")); + } +``` + +Ну и операции по перед отрисовкой, отрисовка сама, и сайгхт +``` + protected override bool BeforeDraw(in OverlayDrawArgs args) + { + return _offer.IsInOfferMode() && base.BeforeDraw(in args); + } + + protected override void Draw(in OverlayDrawArgs args) + { + var mousePosMap = _eye.PixelToMap(_inputManager.MouseScreenPosition); + + if (mousePosMap.MapId != args.MapId) + return; + + var limitedScale = Math.Min(1.25f, (args.ViewportControl as Control)?.UIScale ?? 1f) * _scale; + + DrawSight(_sight, args.ScreenHandle, _inputManager.MouseScreenPosition.Position, limitedScale); + } + + private void DrawSight(Texture sight, DrawingHandleScreen screen, Vector2 centerPos, float scale) + { + var sightSize = sight.Size * scale; + var expandedSize = sightSize + new Vector2(7); + + screen.DrawTextureRect(sight, UIBox2.FromDimensions(centerPos - sightSize * 0.5f, sightSize), _strokeColor); + screen.DrawTextureRect(sight, UIBox2.FromDimensions(centerPos - expandedSize * 0.5f, expandedSize), _mainColor); + } +``` + + +## Что было изменено в офф. файлах +### Content.Shared/Input/ContentKeyFunctions.cs +Бинд +``` +public static readonly BoundKeyFunction OfferItem = "OfferItem"; +``` +### Content.Client/Input/ContentContext.cs +``` +human.AddFunction(ContentKeyFunctions.OfferItem); +``` + +### Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs +``` +AddButton(ContentKeyFunctions.OfferItem); +``` + + +## Изменения Yaml +###Алерт в /Prototypes/_ECHO/Alerts/alerts.yml +``` +- type: alert + id: Offer + clickEvent: !type:AcceptOfferAlertEvent + icons: + - sprite: /Textures/_ECHO/Alerts/offer_item.rsi + state: offer_item + name: alerts-offer-name + description: alerts-offer-desc +``` +### /Resources/keybinds.yml +Бинды +``` +- function: OfferItem + type: State + key: F +``` +### /Resources/Prototypes/Entities/Mobs/base.yml +Добавил всем контролируемым мобам возможность трейда +``` +- type: entity + abstract: true + save: false + parent: BaseControllable + id: BaseMob + components: + - type: MobCollision + - type: Physics + bodyType: KinematicController + - type: InputMover + - type: MobMover + - type: MovementSpeedModifier + - type: SpeechBarks # ECHO-Tweak : Barks + - type: OfferItem # ECHO-Tweak: Добавил оффер компонент для того, чтобы сущности могли оффер делать предметов +``` + + +# Ну вот и все +Надеюсь вам понятна документация и вы ею воспользуетесь \ No newline at end of file From b14828dcadafef5bef613acb4e073e8600a49486 Mon Sep 17 00:00:00 2001 From: Firemixx Date: Sun, 22 Feb 2026 14:36:38 +0200 Subject: [PATCH 2/5] =?UTF-8?q?Update=20=D0=A1=D0=B8=D1=81=D1=82=D0=B5?= =?UTF-8?q?=D0=BC=D0=B0=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B0=D1=87=D0=B8?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B5=D0=B4=D0=BC=D0=B5=D1=82=D0=BE=D0=B2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\265\320\264\320\274\320\265\321\202\320\276\320\262.md" | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git "a/_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" "b/_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" index 3ac7134..8ac08c2 100644 --- "a/_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" +++ "b/_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" @@ -467,7 +467,7 @@ AddButton(ContentKeyFunctions.OfferItem); ## Изменения Yaml -###Алерт в /Prototypes/_ECHO/Alerts/alerts.yml +### Алерт в /Prototypes/_ECHO/Alerts/alerts.yml ``` - type: alert id: Offer @@ -506,4 +506,5 @@ AddButton(ContentKeyFunctions.OfferItem); # Ну вот и все -Надеюсь вам понятна документация и вы ею воспользуетесь \ No newline at end of file + +Надеюсь вам понятна документация и вы ею воспользуетесь From 7bed2ed37403886e2996e6ab36f26dcd37312ef1 Mon Sep 17 00:00:00 2001 From: Firemixx Date: Sun, 22 Feb 2026 14:38:17 +0200 Subject: [PATCH 3/5] Rename documentation file for item transfer system --- ...00\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename "_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" => "_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" (100%) diff --git "a/_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" "b/_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" similarity index 100% rename from "_ECHO/Code docs/\320\241\320\270\321\201\321\202\320\265\320\274\320\260 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" rename to "_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" From 910cbb242a5f1a7021f7de1f9771edcec6563b86 Mon Sep 17 00:00:00 2001 From: Firemixx Date: Sun, 22 Feb 2026 14:39:11 +0200 Subject: [PATCH 4/5] Revise documentation for Offer Item System Updated documentation for the Offer Item System, including system overview, architecture, server, shared, and client details. --- ...320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git "a/_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" "b/_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" index 8ac08c2..841a00f 100644 --- "a/_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" +++ "b/_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" @@ -1,5 +1,5 @@ ## Система передачи из рук в руки (Offer Item System) - документация -Данный документ — сводка информации по новым файлам, системам/компонентам и изменениям в существующих файлах связанным с системой анимации искр сварки (Welding Sparks Animation). +Данный документ — сводка информации по новым файлам, системам/компонентам и изменениям в существующих файлах связанным с системой передачи предметов. # Оглавление 1. [Суть системы](#сутьсистемы) @@ -508,3 +508,4 @@ AddButton(ContentKeyFunctions.OfferItem); # Ну вот и все Надеюсь вам понятна документация и вы ею воспользуетесь + From 6cbc41c8080685b4eba184a3b2e098cf35acc6a8 Mon Sep 17 00:00:00 2001 From: Firemixx Date: Thu, 26 Feb 2026 18:21:49 +0200 Subject: [PATCH 5/5] =?UTF-8?q?=D0=92=D1=80=D0=BE=D0=B4=D0=B5=20=D0=B2?= =?UTF-8?q?=D1=81=D0=B5=20=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...20\274\320\265\321\202\320\276\320\262.md" | 96 ++++--------------- 1 file changed, 18 insertions(+), 78 deletions(-) diff --git "a/_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" "b/_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" index 841a00f..b73636b 100644 --- "a/_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" +++ "b/_ECHO/Code docs/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\320\260\321\206\320\270\321\217 \320\272 \321\201\320\270\321\201\321\202\320\265\320\274\320\265 \320\277\320\265\321\200\320\265\320\264\320\260\321\207\320\270 \320\277\321\200\320\265\320\264\320\274\320\265\321\202\320\276\320\262.md" @@ -22,97 +22,37 @@ [Игрок нажимает на F] │ ▼ -[Shared: SharedOfferItemSystem.Interactions] - └── Регестрируется система основная при нажатии на бинд - │ - ▼ [Shared: SharedOfferItemSystem.Interactions.SetInOfferMode()] - ├── Проверка на предиктед фярст тайм - ├── Проверка на сессию - ├── Проверка есть ли ентити - ├── Проверка на OfferItemComponent - ├── Проверка на HandsComponent и получаем активную руку - ├── Если нету предмета в руке возращаемся очищая за собой все и выдавая попап - └── Если есть предмет в руке, делаем анресиев, очищаем исиноффермод и возращаемся + └── Если до этого были какие либо данные в компоненте, обнуляем, если еще не в оффер моде - переводим │ ▼ -[Shared: SharedOfferItemSystem.Interactions.OnClickAlertEvent()] - └── Получаем предмет при клике +[Shared: SharedOfferItemSystem.SetInRecieveMode()] + └── За счет InteractUsingEvent, который вызывается при клике в оффер моде по сущности, заполняем Target в компоненте вставляя в него сущность цель, выводим из оффер мода если тот был у цели │ ▼ -[Shared: SharedOfferItemSystem.Interactions.Recieve()] - ├── Проверка на исфярсттаймпредиктед, зарезолвленно ли, есть ли компонент офферайтема, есть ли рука, есть ли сам таргет, есть ли у таргета компонент руки - ├── Если не ноль айтем у передающего, то передаем в руку предмет и воспроизводим попап - ├── Чистим offerItem.Item - └── UnOffer -[Server: WeldingSparksSystem.OnUseTool()] - ├── Играет звук сварки - ├── Определяет позицию спавна эффекта (координаты цели или клика) - ├── Spawn("EchoEffectWeldingSparks") — создаёт сущность-эффект - └── RaiseNetworkEvent(SpawnedWeldingSparksEvent) → все клиенты +[Server: OfferItemSystem.Update()] + ├── каждый кадр проверяем таймер + ├── Если есть рука и нету предмета = обнуляем все и отменяем оффер + └── Выдаем алерт или убираем его если в режиме получения или нет + │ + ▼ +[Shared: SharedOfferItemSystem.OnClickAlertEvent()] + └── При клике на алерт переходим в метод Recieve │ ▼ -[Shared: SharedOfferItemSystem.Interactions.UnOffer()] - └── После проверок очищаем переменные +[Shared: SharedOfferItemSystem.Recieve()] + └── После проверок передаем предмет и переходим в UnRecieve + │ + ▼ +[Shared: SharedOfferItemSystem.Recieve()] + └── После проверок обнуляем все и отменяем передачу ``` ## Server -Есть только один файл, OfferItemSystem , в нем мы при обновлении кадра проверяем на то, есть ли те кто передают, а после анресиваем и переключаем ис ин офер мод, если нету айтема в руке, отключаем офер. Если человек не в ресив моде, чистим алерт "Взять", а иначе выдаем алерт -а ну и тут в наличии антиспам апдейта с помощью: -``` - private float _offerAcc = 0; - private const float OfferAccMax = 0.5f; - - - _offerAcc += frameTime; - - if (_offerAcc >= OfferAccMax) - _offerAcc -= OfferAccMax; - else - return; - - -``` -сам код: -``` - public override void Update(float frameTime) - { - _offerAcc += frameTime; - - if (_offerAcc >= OfferAccMax) - _offerAcc -= OfferAccMax; - else - return; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var offerItem, out var hands)) - { - if (hands.ActiveHandId is null) - continue; - - if (offerItem.Hand is not null && _hands.GetActiveItem(uid) is null) - if (offerItem.Target is not null) - { - UnReceive(offerItem.Target.Value, offerItem: offerItem); - offerItem.IsInOfferMode = false; - Dirty(uid, offerItem); - } - else - UnOffer(uid, offerItem); - - if (!offerItem.IsInReceiveMode) - { - _alertsSystem.ClearAlert(uid, OfferAlert); - continue; - } - - _alertsSystem.ShowAlert(uid, OfferAlert); - } - } -``` +Своеобразный менеджер, каждый кадр, после таймера проверяем сущности на то, есть ли у них руки и так далее, а после если нету, обнуляем компоненты оффера, или же потом или показываем, если offerItem.IsInReceiveMode=true, или же очищаем алерт оффера ## Shared