SharedBuckleSystem.Buckle.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Numerics;
  3. using Content.Shared.Alert;
  4. using Content.Shared.Buckle.Components;
  5. using Content.Shared.Cuffs.Components;
  6. using Content.Shared.Database;
  7. using Content.Shared.DoAfter;
  8. using Content.Shared.Hands.Components;
  9. using Content.Shared.IdentityManagement;
  10. using Content.Shared.Movement.Events;
  11. using Content.Shared.Movement.Pulling.Events;
  12. using Content.Shared.Popups;
  13. using Content.Shared.Pulling.Events;
  14. using Content.Shared.Rotation;
  15. using Content.Shared.Standing;
  16. using Content.Shared.Storage.Components;
  17. using Content.Shared.Stunnable;
  18. using Content.Shared.Throwing;
  19. using Content.Shared.Whitelist;
  20. using Robust.Shared.Containers;
  21. using Robust.Shared.GameStates;
  22. using Robust.Shared.Map;
  23. using Robust.Shared.Physics.Components;
  24. using Robust.Shared.Physics.Events;
  25. using Robust.Shared.Prototypes;
  26. using Robust.Shared.Utility;
  27. namespace Content.Shared.Buckle;
  28. public abstract partial class SharedBuckleSystem
  29. {
  30. public static ProtoId<AlertCategoryPrototype> BuckledAlertCategory = "Buckled";
  31. [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
  32. private void InitializeBuckle()
  33. {
  34. SubscribeLocalEvent<BuckleComponent, ComponentShutdown>(OnBuckleComponentShutdown);
  35. SubscribeLocalEvent<BuckleComponent, MoveEvent>(OnBuckleMove);
  36. SubscribeLocalEvent<BuckleComponent, EntParentChangedMessage>(OnParentChanged);
  37. SubscribeLocalEvent<BuckleComponent, EntGotInsertedIntoContainerMessage>(OnInserted);
  38. SubscribeLocalEvent<BuckleComponent, StartPullAttemptEvent>(OnPullAttempt);
  39. SubscribeLocalEvent<BuckleComponent, BeingPulledAttemptEvent>(OnBeingPulledAttempt);
  40. SubscribeLocalEvent<BuckleComponent, PullStartedMessage>(OnPullStarted);
  41. SubscribeLocalEvent<BuckleComponent, UnbuckleAlertEvent>(OnUnbuckleAlert);
  42. SubscribeLocalEvent<BuckleComponent, InsertIntoEntityStorageAttemptEvent>(OnBuckleInsertIntoEntityStorageAttempt);
  43. SubscribeLocalEvent<BuckleComponent, PreventCollideEvent>(OnBucklePreventCollide);
  44. SubscribeLocalEvent<BuckleComponent, DownAttemptEvent>(OnBuckleDownAttempt);
  45. SubscribeLocalEvent<BuckleComponent, StandAttemptEvent>(OnBuckleStandAttempt);
  46. SubscribeLocalEvent<BuckleComponent, ThrowPushbackAttemptEvent>(OnBuckleThrowPushbackAttempt);
  47. SubscribeLocalEvent<BuckleComponent, UpdateCanMoveEvent>(OnBuckleUpdateCanMove);
  48. SubscribeLocalEvent<BuckleComponent, BuckleDoAfterEvent>(OnBuckleDoafter);
  49. SubscribeLocalEvent<BuckleComponent, DoAfterAttemptEvent<BuckleDoAfterEvent>>((uid, comp, ev) =>
  50. {
  51. BuckleDoafterEarly((uid, comp), ev.Event, ev);
  52. });
  53. }
  54. private void OnBuckleComponentShutdown(Entity<BuckleComponent> ent, ref ComponentShutdown args)
  55. {
  56. Unbuckle(ent!, null);
  57. }
  58. #region Pulling
  59. private void OnPullAttempt(Entity<BuckleComponent> ent, ref StartPullAttemptEvent args)
  60. {
  61. // Prevent people pulling the chair they're on, etc.
  62. if (ent.Comp.BuckledTo == args.Pulled && !ent.Comp.PullStrap)
  63. args.Cancel();
  64. }
  65. private void OnBeingPulledAttempt(Entity<BuckleComponent> ent, ref BeingPulledAttemptEvent args)
  66. {
  67. if (args.Cancelled || !ent.Comp.Buckled)
  68. return;
  69. if (!CanUnbuckle(ent!, args.Puller, false))
  70. args.Cancel();
  71. }
  72. private void OnPullStarted(Entity<BuckleComponent> ent, ref PullStartedMessage args)
  73. {
  74. Unbuckle(ent!, args.PullerUid);
  75. }
  76. private void OnUnbuckleAlert(Entity<BuckleComponent> ent, ref UnbuckleAlertEvent args)
  77. {
  78. if (args.Handled)
  79. return;
  80. args.Handled = TryUnbuckle(ent, ent, ent);
  81. }
  82. #endregion
  83. #region Transform
  84. private void OnParentChanged(Entity<BuckleComponent> ent, ref EntParentChangedMessage args)
  85. {
  86. BuckleTransformCheck(ent, args.Transform);
  87. }
  88. private void OnInserted(Entity<BuckleComponent> ent, ref EntGotInsertedIntoContainerMessage args)
  89. {
  90. BuckleTransformCheck(ent, Transform(ent));
  91. }
  92. private void OnBuckleMove(Entity<BuckleComponent> ent, ref MoveEvent ev)
  93. {
  94. BuckleTransformCheck(ent, ev.Component);
  95. }
  96. /// <summary>
  97. /// Check if the entity should get unbuckled as a result of transform or container changes.
  98. /// </summary>
  99. private void BuckleTransformCheck(Entity<BuckleComponent> buckle, TransformComponent xform)
  100. {
  101. if (_gameTiming.ApplyingState)
  102. return;
  103. if (buckle.Comp.BuckledTo is not { } strapUid)
  104. return;
  105. if (!TryComp<StrapComponent>(strapUid, out var strapComp))
  106. {
  107. Log.Error($"Encountered buckle entity {ToPrettyString(buckle)} without a valid strap entity {ToPrettyString(strapUid)}");
  108. SetBuckledTo(buckle, null);
  109. return;
  110. }
  111. if (xform.ParentUid != strapUid || _container.IsEntityInContainer(buckle))
  112. {
  113. Unbuckle(buckle, (strapUid, strapComp), null);
  114. return;
  115. }
  116. var delta = (xform.LocalPosition - strapComp.BuckleOffset).LengthSquared();
  117. if (delta > 1e-5)
  118. Unbuckle(buckle, (strapUid, strapComp), null);
  119. }
  120. #endregion
  121. private void OnBuckleInsertIntoEntityStorageAttempt(EntityUid uid, BuckleComponent component, ref InsertIntoEntityStorageAttemptEvent args)
  122. {
  123. if (component.Buckled)
  124. args.Cancelled = true;
  125. }
  126. private void OnBucklePreventCollide(EntityUid uid, BuckleComponent component, ref PreventCollideEvent args)
  127. {
  128. if (args.OtherEntity == component.BuckledTo && component.DontCollide)
  129. args.Cancelled = true;
  130. }
  131. private void OnBuckleDownAttempt(EntityUid uid, BuckleComponent component, DownAttemptEvent args)
  132. {
  133. if (component.Buckled)
  134. args.Cancel();
  135. }
  136. private void OnBuckleStandAttempt(EntityUid uid, BuckleComponent component, StandAttemptEvent args)
  137. {
  138. if (component.Buckled)
  139. args.Cancel();
  140. }
  141. private void OnBuckleThrowPushbackAttempt(EntityUid uid, BuckleComponent component, ThrowPushbackAttemptEvent args)
  142. {
  143. if (component.Buckled)
  144. args.Cancel();
  145. }
  146. private void OnBuckleUpdateCanMove(EntityUid uid, BuckleComponent component, UpdateCanMoveEvent args)
  147. {
  148. if (component.Buckled)
  149. args.Cancel();
  150. }
  151. public bool IsBuckled(EntityUid uid, BuckleComponent? component = null)
  152. {
  153. return Resolve(uid, ref component, false) && component.Buckled;
  154. }
  155. protected void SetBuckledTo(Entity<BuckleComponent> buckle, Entity<StrapComponent?>? strap)
  156. {
  157. if (TryComp(buckle.Comp.BuckledTo, out StrapComponent? old))
  158. {
  159. old.BuckledEntities.Remove(buckle);
  160. Dirty(buckle.Comp.BuckledTo.Value, old);
  161. }
  162. if (strap is {} strapEnt && Resolve(strapEnt.Owner, ref strapEnt.Comp))
  163. {
  164. strapEnt.Comp.BuckledEntities.Add(buckle);
  165. Dirty(strapEnt);
  166. _alerts.ShowAlert(buckle, strapEnt.Comp.BuckledAlertType);
  167. }
  168. else
  169. {
  170. _alerts.ClearAlertCategory(buckle, BuckledAlertCategory);
  171. }
  172. buckle.Comp.BuckledTo = strap;
  173. buckle.Comp.BuckleTime = _gameTiming.CurTime;
  174. ActionBlocker.UpdateCanMove(buckle);
  175. Appearance.SetData(buckle, StrapVisuals.State, buckle.Comp.Buckled);
  176. Dirty(buckle);
  177. }
  178. /// <summary>
  179. /// Checks whether or not buckling is possible
  180. /// </summary>
  181. /// <param name="buckleUid"> Uid of the owner of BuckleComponent </param>
  182. /// <param name="user">
  183. /// Uid of a third party entity,
  184. /// i.e, the uid of someone else you are dragging to a chair.
  185. /// Can equal buckleUid sometimes
  186. /// </param>
  187. /// <param name="strapUid"> Uid of the owner of strap component </param>
  188. /// <param name="strapComp"></param>
  189. /// <param name="buckleComp"></param>
  190. private bool CanBuckle(EntityUid buckleUid,
  191. EntityUid? user,
  192. EntityUid strapUid,
  193. bool popup,
  194. [NotNullWhen(true)] out StrapComponent? strapComp,
  195. BuckleComponent buckleComp)
  196. {
  197. strapComp = null;
  198. if (!Resolve(strapUid, ref strapComp, false))
  199. return false;
  200. // Does it pass the Whitelist
  201. if (_whitelistSystem.IsWhitelistFail(strapComp.Whitelist, buckleUid) ||
  202. _whitelistSystem.IsBlacklistPass(strapComp.Blacklist, buckleUid))
  203. {
  204. if (popup)
  205. _popup.PopupClient(Loc.GetString("buckle-component-cannot-fit-message"), user, PopupType.Medium);
  206. return false;
  207. }
  208. if (!_interaction.InRangeUnobstructed(buckleUid,
  209. strapUid,
  210. buckleComp.Range,
  211. predicate: entity => entity == buckleUid || entity == user || entity == strapUid,
  212. popup: true))
  213. {
  214. return false;
  215. }
  216. if (!_container.IsInSameOrNoContainer((buckleUid, null, null), (strapUid, null, null)))
  217. return false;
  218. if (user != null && !HasComp<HandsComponent>(user))
  219. {
  220. if (popup)
  221. _popup.PopupClient(Loc.GetString("buckle-component-no-hands-message"), user);
  222. return false;
  223. }
  224. if (buckleComp.Buckled && !TryUnbuckle(buckleUid, user, buckleComp))
  225. {
  226. if (popup)
  227. {
  228. var message = Loc.GetString(buckleUid == user
  229. ? "buckle-component-already-buckled-message"
  230. : "buckle-component-other-already-buckled-message",
  231. ("owner", Identity.Entity(buckleUid, EntityManager)));
  232. _popup.PopupClient(message, user);
  233. }
  234. return false;
  235. }
  236. // Check whether someone is attempting to buckle something to their own child
  237. var parent = Transform(strapUid).ParentUid;
  238. while (parent.IsValid())
  239. {
  240. if (parent != buckleUid)
  241. {
  242. parent = Transform(parent).ParentUid;
  243. continue;
  244. }
  245. if (popup)
  246. {
  247. var message = Loc.GetString(buckleUid == user
  248. ? "buckle-component-cannot-buckle-message"
  249. : "buckle-component-other-cannot-buckle-message",
  250. ("owner", Identity.Entity(buckleUid, EntityManager)));
  251. _popup.PopupClient(message, user);
  252. }
  253. return false;
  254. }
  255. if (!StrapHasSpace(strapUid, buckleComp, strapComp))
  256. {
  257. if (popup)
  258. {
  259. var message = Loc.GetString(buckleUid == user
  260. ? "buckle-component-cannot-buckle-message"
  261. : "buckle-component-other-cannot-buckle-message",
  262. ("owner", Identity.Entity(buckleUid, EntityManager)));
  263. _popup.PopupClient(message, user);
  264. }
  265. return false;
  266. }
  267. var buckleAttempt = new BuckleAttemptEvent((strapUid, strapComp), (buckleUid, buckleComp), user, popup);
  268. RaiseLocalEvent(buckleUid, ref buckleAttempt);
  269. if (buckleAttempt.Cancelled)
  270. return false;
  271. var strapAttempt = new StrapAttemptEvent((strapUid, strapComp), (buckleUid, buckleComp), user, popup);
  272. RaiseLocalEvent(strapUid, ref strapAttempt);
  273. if (strapAttempt.Cancelled)
  274. return false;
  275. return true;
  276. }
  277. /// <summary>
  278. /// Attempts to buckle an entity to a strap
  279. /// </summary>
  280. /// <param name="buckle"> Uid of the owner of BuckleComponent </param>
  281. /// <param name="user">
  282. /// Uid of a third party entity,
  283. /// i.e, the uid of someone else you are dragging to a chair.
  284. /// Can equal buckleUid sometimes
  285. /// </param>
  286. /// <param name="strap"> Uid of the owner of strap component </param>
  287. public bool TryBuckle(EntityUid buckle, EntityUid? user, EntityUid strap, BuckleComponent? buckleComp = null, bool popup = true)
  288. {
  289. if (!Resolve(buckle, ref buckleComp, false))
  290. return false;
  291. if (!CanBuckle(buckle, user, strap, popup, out var strapComp, buckleComp))
  292. return false;
  293. Buckle((buckle, buckleComp), (strap, strapComp), user);
  294. return true;
  295. }
  296. private void Buckle(Entity<BuckleComponent> buckle, Entity<StrapComponent> strap, EntityUid? user)
  297. {
  298. if (user == buckle.Owner)
  299. _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user):player} buckled themselves to {ToPrettyString(strap)}");
  300. else if (user != null)
  301. _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user):player} buckled {ToPrettyString(buckle)} to {ToPrettyString(strap)}");
  302. _audio.PlayPredicted(strap.Comp.BuckleSound, strap, user);
  303. SetBuckledTo(buckle, strap!);
  304. Appearance.SetData(strap, StrapVisuals.State, true);
  305. Appearance.SetData(buckle, BuckleVisuals.Buckled, true);
  306. _rotationVisuals.SetHorizontalAngle(buckle.Owner, strap.Comp.Rotation);
  307. var xform = Transform(buckle);
  308. var coords = new EntityCoordinates(strap, strap.Comp.BuckleOffset);
  309. _transform.SetCoordinates(buckle, xform, coords, rotation: Angle.Zero);
  310. _joints.SetRelay(buckle, strap);
  311. switch (strap.Comp.Position)
  312. {
  313. case StrapPosition.Stand:
  314. _standing.Stand(buckle, force: true);
  315. break;
  316. case StrapPosition.Down:
  317. _standing.Down(buckle, false, false, force: true);
  318. break;
  319. }
  320. var ev = new StrappedEvent(strap, buckle);
  321. RaiseLocalEvent(strap, ref ev);
  322. var gotEv = new BuckledEvent(strap, buckle);
  323. RaiseLocalEvent(buckle, ref gotEv);
  324. if (TryComp<PhysicsComponent>(buckle, out var physics))
  325. _physics.ResetDynamics(buckle, physics);
  326. DebugTools.AssertEqual(xform.ParentUid, strap.Owner);
  327. }
  328. /// <summary>
  329. /// Tries to unbuckle the Owner of this component from its current strap.
  330. /// </summary>
  331. /// <param name="buckleUid">The entity to unbuckle.</param>
  332. /// <param name="user">The entity doing the unbuckling.</param>
  333. /// <param name="buckleComp">The buckle component of the entity to unbuckle.</param>
  334. /// <returns>
  335. /// true if the owner was unbuckled, otherwise false even if the owner
  336. /// was previously already unbuckled.
  337. /// </returns>
  338. public bool TryUnbuckle(EntityUid buckleUid,
  339. EntityUid? user,
  340. BuckleComponent? buckleComp = null,
  341. bool popup = true)
  342. {
  343. return TryUnbuckle((buckleUid, buckleComp), user, popup);
  344. }
  345. public bool TryUnbuckle(Entity<BuckleComponent?> buckle, EntityUid? user, bool popup)
  346. {
  347. if (!Resolve(buckle.Owner, ref buckle.Comp, false))
  348. return false;
  349. if (!CanUnbuckle(buckle, user, popup, out var strap))
  350. return false;
  351. Unbuckle(buckle!, strap, user);
  352. return true;
  353. }
  354. public void Unbuckle(Entity<BuckleComponent?> buckle, EntityUid? user)
  355. {
  356. if (!Resolve(buckle.Owner, ref buckle.Comp, false))
  357. return;
  358. if (buckle.Comp.BuckledTo is not { } strap)
  359. return;
  360. if (!TryComp(strap, out StrapComponent? strapComp))
  361. {
  362. Log.Error($"Encountered buckle {ToPrettyString(buckle.Owner)} with invalid strap entity {ToPrettyString(strap)}");
  363. SetBuckledTo(buckle!, null);
  364. return;
  365. }
  366. Unbuckle(buckle!, (strap, strapComp), user);
  367. }
  368. private void Unbuckle(Entity<BuckleComponent> buckle, Entity<StrapComponent> strap, EntityUid? user)
  369. {
  370. if (user == buckle.Owner)
  371. _adminLogger.Add(LogType.Action, LogImpact.Low, $"{user} unbuckled themselves from {strap}");
  372. else if (user != null)
  373. _adminLogger.Add(LogType.Action, LogImpact.Low, $"{user} unbuckled {buckle} from {strap}");
  374. _audio.PlayPredicted(strap.Comp.UnbuckleSound, strap, user);
  375. SetBuckledTo(buckle, null);
  376. var buckleXform = Transform(buckle);
  377. var oldBuckledXform = Transform(strap);
  378. if (buckleXform.ParentUid == strap.Owner && !Terminating(buckleXform.ParentUid))
  379. {
  380. _transform.PlaceNextTo((buckle, buckleXform), (strap.Owner, oldBuckledXform));
  381. buckleXform.ActivelyLerping = false;
  382. var oldBuckledToWorldRot = _transform.GetWorldRotation(strap);
  383. _transform.SetWorldRotationNoLerp((buckle, buckleXform), oldBuckledToWorldRot);
  384. // TODO: This is doing 4 moveevents this is why I left the warning in, if you're going to remove it make it only do 1 moveevent.
  385. if (strap.Comp.BuckleOffset != Vector2.Zero)
  386. {
  387. buckleXform.Coordinates = oldBuckledXform.Coordinates.Offset(strap.Comp.BuckleOffset);
  388. }
  389. }
  390. _rotationVisuals.ResetHorizontalAngle(buckle.Owner);
  391. Appearance.SetData(strap, StrapVisuals.State, strap.Comp.BuckledEntities.Count != 0);
  392. Appearance.SetData(buckle, BuckleVisuals.Buckled, false);
  393. if (HasComp<KnockedDownComponent>(buckle) || _mobState.IsIncapacitated(buckle))
  394. _standing.Down(buckle, playSound: false);
  395. else
  396. _standing.Stand(buckle);
  397. _joints.RefreshRelay(buckle);
  398. var buckleEv = new UnbuckledEvent(strap, buckle);
  399. RaiseLocalEvent(buckle, ref buckleEv);
  400. var strapEv = new UnstrappedEvent(strap, buckle);
  401. RaiseLocalEvent(strap, ref strapEv);
  402. }
  403. public bool CanUnbuckle(Entity<BuckleComponent?> buckle, EntityUid user, bool popup)
  404. {
  405. return CanUnbuckle(buckle, user, popup, out _);
  406. }
  407. private bool CanUnbuckle(Entity<BuckleComponent?> buckle, EntityUid? user, bool popup, out Entity<StrapComponent> strap)
  408. {
  409. strap = default;
  410. if (!Resolve(buckle.Owner, ref buckle.Comp))
  411. return false;
  412. if (buckle.Comp.BuckledTo is not { } strapUid)
  413. return false;
  414. if (!TryComp(strapUid, out StrapComponent? strapComp))
  415. {
  416. Log.Error($"Encountered buckle {ToPrettyString(buckle.Owner)} with invalid strap entity {ToPrettyString(strap)}");
  417. SetBuckledTo(buckle!, null);
  418. return false;
  419. }
  420. strap = (strapUid, strapComp);
  421. if (_gameTiming.CurTime < buckle.Comp.BuckleTime + buckle.Comp.Delay)
  422. return false;
  423. if (user != null && !_interaction.InRangeUnobstructed(user.Value, strap.Owner, buckle.Comp.Range, popup: popup))
  424. return false;
  425. var unbuckleAttempt = new UnbuckleAttemptEvent(strap, buckle!, user, popup);
  426. RaiseLocalEvent(buckle, ref unbuckleAttempt);
  427. if (unbuckleAttempt.Cancelled)
  428. return false;
  429. var unstrapAttempt = new UnstrapAttemptEvent(strap, buckle!, user, popup);
  430. RaiseLocalEvent(strap, ref unstrapAttempt);
  431. return !unstrapAttempt.Cancelled;
  432. }
  433. /// <summary>
  434. /// Once the do-after is complete, try to buckle target to chair/bed
  435. /// </summary>
  436. /// <param name="args.Target"> The person being put in the chair/bed</param>
  437. /// <param name="args.User"> The person putting a person in a chair/bed</param>
  438. /// <param name="args.Used"> The chair/bed </param>
  439. private void OnBuckleDoafter(Entity<BuckleComponent> entity, ref BuckleDoAfterEvent args)
  440. {
  441. if (args.Cancelled || args.Handled || args.Target == null || args.Used == null)
  442. return;
  443. args.Handled = TryBuckle(args.Target.Value, args.User, args.Used.Value, popup: false);
  444. }
  445. /// <summary>
  446. /// If the target being buckled to a chair/bed goes crit or is cuffed
  447. /// Cancel the do-after time and try to buckle the target immediately
  448. /// </summary>
  449. /// <param name="args.Target"> The person being put in the chair/bed</param>
  450. /// <param name="args.User"> The person putting a person in a chair/bed</param>
  451. /// <param name="args.Used"> The chair/bed </param>
  452. private void BuckleDoafterEarly(Entity<BuckleComponent> entity, BuckleDoAfterEvent args, CancellableEntityEventArgs ev)
  453. {
  454. if (args.Target == null || args.Used == null)
  455. return;
  456. if (TryComp<CuffableComponent>(args.Target, out var targetCuffableComp) && targetCuffableComp.CuffedHandCount > 0
  457. || _mobState.IsIncapacitated(args.Target.Value))
  458. {
  459. ev.Cancel();
  460. TryBuckle(args.Target.Value, args.User, args.Used.Value, popup: false);
  461. }
  462. }
  463. }