private void StartReassembly(EntityUid uid, BodyReassembleComponent component, float multiplier = 1f) { if (component.CancelToken != null) { return; } if (!GetNearbyParts(uid, component, out var partList)) { return; } if (partList == null) { return; } var doAfterTime = component.DoAfterTime * multiplier; var cancelToken = new CancellationTokenSource(); component.CancelToken = cancelToken; var doAfterEventArgs = new DoAfterEventArgs(component.Owner, doAfterTime, cancelToken.Token, component.Owner) { BreakOnTargetMove = true, BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true, NeedHand = false, TargetCancelledEvent = new ReassembleCancelledEvent(), TargetFinishedEvent = new ReassembleCompleteEvent(uid, uid, partList), }; _doAfterSystem.DoAfter(doAfterEventArgs); }
public void StartOpeningStripper(EntityUid user, StrippableComponent component) { if (component.CancelTokens.ContainsKey(user)) { return; } if (TryComp <ActorComponent>(user, out var actor)) { if (component.Owner.GetUIOrNull(StrippingUiKey.Key)?.SessionHasOpen(actor.PlayerSession) == true) { return; } } var token = new CancellationTokenSource(); var doAfterArgs = new DoAfterEventArgs(user, component.OpenDelay, token.Token, component.Owner) { BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, NeedHand = true, TargetCancelledEvent = new OpenStrippingCancelledEvent(user), TargetFinishedEvent = new OpenStrippingCompleteEvent(user), }; component.CancelTokens[user] = token; _doAfterSystem.DoAfter(doAfterArgs); }
public async Task TestFinished() { Task <DoAfterStatus> task = null; await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true, ExtraPrototypes = Prototypes }); var server = pairTracker.Pair.Server; // That it finishes successfully await server.WaitPost(() => { var tickTime = 1.0f / IoCManager.Resolve <IGameTiming>().TickRate; var mapManager = IoCManager.Resolve <IMapManager>(); mapManager.CreateNewMapEntity(MapId.Nullspace); var entityManager = IoCManager.Resolve <IEntityManager>(); var mob = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace); var cancelToken = new CancellationTokenSource(); var args = new DoAfterEventArgs(mob, tickTime / 2, cancelToken.Token); task = EntitySystem.Get <DoAfterSystem>().WaitDoAfter(args); }); await server.WaitRunTicks(1); Assert.That(task.Result == DoAfterStatus.Finished); await pairTracker.CleanReturnAsync(); }
public async Task TestCancelled() { Task <DoAfterStatus> task = null; await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true, ExtraPrototypes = Prototypes }); var server = pairTracker.Pair.Server; await server.WaitPost(() => { var tickTime = 1.0f / IoCManager.Resolve <IGameTiming>().TickRate; var mapManager = IoCManager.Resolve <IMapManager>(); mapManager.CreateNewMapEntity(MapId.Nullspace); var entityManager = IoCManager.Resolve <IEntityManager>(); var mob = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace); var cancelToken = new CancellationTokenSource(); var args = new DoAfterEventArgs(mob, tickTime * 2, cancelToken.Token); task = EntitySystem.Get <DoAfterSystem>().WaitDoAfter(args); cancelToken.Cancel(); }); await server.WaitRunTicks(3); Assert.That(task.Status, Is.EqualTo(TaskStatus.RanToCompletion)); #pragma warning disable RA0004 Assert.That(task.Result, Is.EqualTo(DoAfterStatus.Cancelled), $"Result was {task.Result}"); #pragma warning restore RA0004 await pairTracker.CleanReturnAsync(); }
private void OnInteractUsing(EntityUid uid, GatherableComponent component, InteractUsingEvent args) { if (!TryComp <GatheringToolComponent>(args.Used, out var tool) || component.ToolWhitelist?.IsValid(args.Used) == false || tool.GatheringEntities.TryGetValue(uid, out var cancelToken)) { return; } // Can't gather too many entities at once. if (tool.MaxGatheringEntities < tool.GatheringEntities.Count + 1) { return; } cancelToken = new CancellationTokenSource(); tool.GatheringEntities[uid] = cancelToken; var doAfter = new DoAfterEventArgs(args.User, tool.GatheringTime, cancelToken.Token, uid) { BreakOnDamage = true, BreakOnStun = true, BreakOnTargetMove = true, BreakOnUserMove = true, MovementThreshold = 0.25f, BroadcastCancelledEvent = new GatheringDoafterCancel { Tool = args.Used, Resource = uid }, TargetFinishedEvent = new GatheringDoafterSuccess { Tool = args.Used, Resource = uid, Player = args.User } }; _doAfterSystem.DoAfter(doAfter); }
public async Task TestCancelled() { Task <DoAfterStatus> task = null; var options = new ServerIntegrationOptions { ExtraPrototypes = Prototypes }; var server = StartServerDummyTicker(options); server.Post(() => { var tickTime = 1.0f / IoCManager.Resolve <IGameTiming>().TickRate; var mapManager = IoCManager.Resolve <IMapManager>(); mapManager.CreateNewMapEntity(MapId.Nullspace); var entityManager = IoCManager.Resolve <IEntityManager>(); var mob = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace); var cancelToken = new CancellationTokenSource(); var args = new DoAfterEventArgs(mob, tickTime * 2, cancelToken.Token); task = EntitySystem.Get <DoAfterSystem>().DoAfter(args); cancelToken.Cancel(); }); await server.WaitRunTicks(3); Assert.That(task.Result == DoAfterStatus.Cancelled, $"Result was {task.Result}"); }
/// <summary> /// Takes an item from a hand and places it in the user's active hand. /// </summary> private async void TakeItemFromHands(EntityUid user, string hand) { var hands = _entities.GetComponent <HandsComponent>(Owner); var userHands = _entities.GetComponent <HandsComponent>(user); bool Check() { if (!hands.HasHand(hand)) { return(false); } if (!hands.TryGetItem(hand, out var heldItem)) { user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Owner))); return(false); } if (_entities.HasComponent <HandVirtualItemComponent>(heldItem.Owner)) { return(false); } if (!hands.CanDrop(hand, false)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Owner))); return(false); } return(true); } var doAfterSystem = EntitySystem.Get <DoAfterSystem>(); var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner) { ExtraCheck = Check, BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, }; var result = await doAfterSystem.WaitDoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } if (!hands.TryGetHeldEntity(hand, out var entity)) { return; } hands.Drop(hand, false); userHands.PutInHandOrDrop(entity.Value); // hand update will trigger strippable update }
private async void HandleAfterInteract(EntityUid uid, SpawnAfterInteractComponent component, AfterInteractEvent args) { if (string.IsNullOrEmpty(component.Prototype)) { return; } if (!_mapManager.TryGetGrid(args.ClickLocation.GetGridId(EntityManager), out var grid)) { return; } if (!grid.TryGetTileRef(args.ClickLocation, out var tileRef)) { return; } bool IsTileClear() { return(tileRef.Tile.IsEmpty == false && args.User.InRangeUnobstructed(args.ClickLocation, popup: true)); } if (!IsTileClear()) { return; } if (component.DoAfterTime > 0 && TryGet <DoAfterSystem>(out var doAfterSystem)) { var doAfterArgs = new DoAfterEventArgs(args.User, component.DoAfterTime) { BreakOnUserMove = true, BreakOnStun = true, PostCheck = IsTileClear, }; var result = await doAfterSystem.DoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } } if (component.Deleted || component.Owner.Deleted) { return; } if (component.Owner.TryGetComponent <SharedStackComponent>(out var stackComp) && component.RemoveOnInteract && !Get <StackSystem>().Use(uid, stackComp, 1)) { return; } EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid)); if (component.RemoveOnInteract && stackComp == null && !component.Owner.Deleted) { component.Owner.Delete(); } }
private async void HandleAfterInteract(EntityUid uid, SpawnAfterInteractComponent component, AfterInteractEvent args) { if (string.IsNullOrEmpty(component.Prototype)) { return; } if (!_mapManager.TryGetGrid(args.ClickLocation.GetGridId(EntityManager), out var grid)) { return; } if (!grid.TryGetTileRef(args.ClickLocation, out var tileRef)) { return; } bool IsTileClear() { return(tileRef.Tile.IsEmpty == false); } if (!IsTileClear()) { return; } if (component.DoAfterTime > 0) { var doAfterArgs = new DoAfterEventArgs(args.User, component.DoAfterTime) { BreakOnUserMove = true, BreakOnStun = true, PostCheck = IsTileClear, }; var result = await _doAfterSystem.WaitDoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } } if (component.Deleted || Deleted(component.Owner)) { return; } if (EntityManager.TryGetComponent <SharedStackComponent?>(component.Owner, out var stackComp) && component.RemoveOnInteract && !_stackSystem.Use(uid, 1, stackComp)) { return; } EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid)); if (component.RemoveOnInteract && stackComp == null && !((!EntityManager.EntityExists(component.Owner) ? EntityLifeStage.Deleted : EntityManager.GetComponent <MetaDataComponent>(component.Owner).EntityLifeStage) >= EntityLifeStage.Deleted)) { EntityManager.DeleteEntity(component.Owner); } }
/// <summary> /// Takes an item from the inventory and places it in the user's active hand. /// </summary> private async void TakeItemFromInventory(EntityUid user, string slot) { var inventory = _entities.GetComponent <InventoryComponent>(Owner); var userHands = _entities.GetComponent <HandsComponent>(user); var invSystem = EntitySystem.Get <InventorySystem>(); bool Check() { if (!EntitySystem.Get <ActionBlockerSystem>().CanInteract(user)) { return(false); } if (!invSystem.HasSlot(Owner, slot)) { return(false); } if (!invSystem.TryGetSlotEntity(Owner, slot, out var item)) { user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Owner))); return(false); } if (!invSystem.CanUnequip(user, Owner, slot, out _)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-unequip-message", ("owner", Owner))); return(false); } return(true); } var doAfterSystem = EntitySystem.Get <DoAfterSystem>(); var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner) { ExtraCheck = Check, BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, }; var result = await doAfterSystem.WaitDoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } if (invSystem.TryGetSlotEntity(Owner, slot, out var item) && invSystem.TryUnequip(user, Owner, slot)) { userHands.PutInHandOrDrop(item.Value); } UpdateState(); }
/// <summary> /// Places item in user's active hand in one of the entity's hands. /// </summary> private async void PlaceActiveHandItemInHands(EntityUid user, string handName) { var hands = _entities.GetComponent <HandsComponent>(Owner); var userHands = _entities.GetComponent <HandsComponent>(user); var sys = _sysMan.GetEntitySystem <SharedHandsSystem>(); bool Check() { if (userHands.ActiveHandEntity == null) { user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything")); return(false); } if (!sys.CanDropHeld(user, userHands.ActiveHand !)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop")); return(false); } if (!hands.Hands.TryGetValue(handName, out var hand) || !sys.CanPickupToHand(Owner, userHands.ActiveHandEntity.Value, hand, checkActionBlocker: false, hands)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-put-message", ("owner", Owner))); return(false); } return(true); } var doAfterSystem = _sysMan.GetEntitySystem <DoAfterSystem>(); var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner) { ExtraCheck = Check, BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, NeedHand = true, }; var result = await doAfterSystem.WaitDoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } if (userHands.ActiveHandEntity is not EntityUid held) { return; } sys.TryDrop(user, checkActionBlocker: false, handsComp: userHands); sys.TryPickup(Owner, held, handName, checkActionBlocker: false, animateUser: true, handsComp: hands); // hand update will trigger strippable update }
/// <summary> /// Takes an item from the inventory and places it in the user's active hand. /// </summary> private async void TakeItemFromInventory(EntityUid user, string slot) { var inventory = _entities.GetComponent <InventoryComponent>(Owner); var userHands = _entities.GetComponent <HandsComponent>(user); var invSystem = _sysMan.GetEntitySystem <InventorySystem>(); bool Check() { if (!invSystem.HasSlot(Owner, slot)) { return(false); } if (!invSystem.TryGetSlotEntity(Owner, slot, out var item)) { user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Owner))); return(false); } if (!invSystem.CanUnequip(user, Owner, slot, out _)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-unequip-message", ("owner", Owner))); return(false); } return(true); } var doAfterSystem = _sysMan.GetEntitySystem <DoAfterSystem>(); var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner) { ExtraCheck = Check, BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, }; var result = await doAfterSystem.WaitDoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } if (invSystem.TryGetSlotEntity(Owner, slot, out var item) && invSystem.TryUnequip(user, Owner, slot)) { // Raise a dropped event, so that things like gas tank internals properly deactivate when stripping _entities.EventBus.RaiseLocalEvent(item.Value, new DroppedEvent(user)); _sysMan.GetEntitySystem <SharedHandsSystem>().PickupOrDrop(user, item.Value); } UpdateState(); }
private async void TakeItemFromInventory(IEntity user, Slots slot) { var inventory = Owner.GetComponent <InventoryComponent>(); var userHands = user.GetComponent <HandsComponent>(); bool Check() { if (!ActionBlockerSystem.CanInteract(user)) { return(false); } if (!inventory.HasSlot(slot)) { return(false); } if (!inventory.TryGetSlotItem(slot, out ItemComponent itemToTake)) { user.PopupMessageCursor(Loc.GetString("{0:They} {0:have} nothing there!", Owner)); return(false); } if (!inventory.CanUnequip(slot, false)) { user.PopupMessageCursor(Loc.GetString("{0:They} cannot unequip that!", Owner)); return(false); } return(true); } var doAfterSystem = EntitySystem.Get <DoAfterSystem>(); var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner) { ExtraCheck = Check, BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, }; var result = await doAfterSystem.DoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } var item = inventory.GetSlotItem(slot); inventory.Unequip(slot, false); userHands.PutInHandOrDrop(item); UpdateSubscribed(); }
private async void TakeItemFromHands(IEntity user, string hand) { var hands = Owner.GetComponent <HandsComponent>(); var userHands = user.GetComponent <HandsComponent>(); bool Check() { if (!EntitySystem.Get <ActionBlockerSystem>().CanInteract(user)) { return(false); } if (!hands.HasHand(hand)) { return(false); } if (!hands.TryGetItem(hand, out var heldItem)) { user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Owner))); return(false); } if (!hands.CanDrop(hand, false)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Owner))); return(false); } return(true); } var doAfterSystem = EntitySystem.Get <DoAfterSystem>(); var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner) { ExtraCheck = Check, BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, }; var result = await doAfterSystem.DoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } var item = hands.GetItem(hand); hands.Drop(hand, false); userHands.PutInHandOrDrop(item !); UpdateSubscribed(); }
/// <summary> /// Takes an item from a hand and places it in the user's active hand. /// </summary> private async void TakeItemFromHands(EntityUid user, string handName) { var hands = _entities.GetComponent <HandsComponent>(Owner); var userHands = _entities.GetComponent <HandsComponent>(user); var handSys = _sysMan.GetEntitySystem <SharedHandsSystem>(); bool Check() { if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity == null) { user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Owner))); return(false); } if (_entities.HasComponent <HandVirtualItemComponent>(hand.HeldEntity)) { return(false); } if (!handSys.CanDropHeld(Owner, hand, false)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Owner))); return(false); } return(true); } var doAfterSystem = _sysMan.GetEntitySystem <DoAfterSystem>(); var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner) { ExtraCheck = Check, BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, }; var result = await doAfterSystem.WaitDoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } if (!hands.Hands.TryGetValue(handName, out var hand) || hand.HeldEntity is not EntityUid held) { return; } handSys.TryDrop(Owner, hand, checkActionBlocker: false, handsComp: hands); handSys.PickupOrDrop(user, held, handsComp: userHands); // hand update will trigger strippable update }
public async void AttemptDisassemble(EntityUid uid, EntityUid user, EntityUid target, DisassembleOnAltVerbComponent?component = null) { if (!Resolve(uid, ref component)) { return; } if (string.IsNullOrEmpty(component.Prototype)) { return; } if (component.DoAfterTime > 0 && TryGet <DoAfterSystem>(out var doAfterSystem)) { var doAfterArgs = new DoAfterEventArgs(user, component.DoAfterTime, component.TokenSource.Token) { BreakOnUserMove = true, BreakOnStun = true, }; var result = await doAfterSystem.WaitDoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } component.TokenSource.Cancel(); } if (component.Deleted || Deleted(component.Owner)) { return; } if (!TryComp <TransformComponent>(component.Owner, out var transformComp)) { return; } var entity = EntityManager.SpawnEntity(component.Prototype, transformComp.Coordinates); if (TryComp <HandsComponent?>(user, out var hands) && TryComp <SharedItemComponent?>(entity, out var item)) { hands.PutInHandOrDrop(item); } EntityManager.DeleteEntity(component.Owner); return; }
private void DisarmBombDoafter(EntityUid uid, EntityUid user, NukeComponent nuke) { nuke.DisarmCancelToken = new(); var doafter = new DoAfterEventArgs(user, nuke.DisarmDoafterLength, nuke.DisarmCancelToken.Value, uid) { TargetCancelledEvent = new NukeDisarmCancelledEvent(), TargetFinishedEvent = new NukeDisarmSuccessEvent(), BreakOnDamage = true, BreakOnStun = true, BreakOnTargetMove = true, BreakOnUserMove = true, NeedHand = true, }; _doAfterSystem.DoAfter(doafter); _popups.PopupEntity(Loc.GetString("nuke-component-doafter-warning"), user, Filter.Entities(user), PopupType.LargeCaution); }
private async void HandleActivateInWorld(EntityUid uid, DisassembleOnActivateComponent component, ActivateInWorldEvent args) { if (string.IsNullOrEmpty(component.Prototype)) { return; } if (!args.User.InRangeUnobstructed(args.Target)) { return; } if (component.DoAfterTime > 0 && TryGet <DoAfterSystem>(out var doAfterSystem)) { var doAfterArgs = new DoAfterEventArgs(args.User, component.DoAfterTime, component.TokenSource.Token) { BreakOnUserMove = true, BreakOnStun = true, }; var result = await doAfterSystem.WaitDoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } component.TokenSource.Cancel(); } if (component.Deleted || Deleted(component.Owner)) { return; } var entity = EntityManager.SpawnEntity(component.Prototype, EntityManager.GetComponent <TransformComponent>(component.Owner).Coordinates); if (EntityManager.TryGetComponent <HandsComponent?>(args.User, out var hands) && EntityManager.TryGetComponent <ItemComponent?>(entity, out var item)) { hands.PutInHandOrDrop(item); } EntityManager.DeleteEntity(component.Owner); return; }
private void AttemptEscape(EntityUid user, EntityUid container, CanEscapeInventoryComponent component) { component.CancelToken = new(); var doAfterEventArgs = new DoAfterEventArgs(user, component.ResistTime, component.CancelToken.Token, container) { BreakOnTargetMove = false, BreakOnUserMove = false, BreakOnDamage = true, BreakOnStun = true, NeedHand = false, UserFinishedEvent = new EscapeDoAfterComplete(), UserCancelledEvent = new EscapeDoAfterCancel(), }; component.IsResisting = true; _popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting"), user, Filter.Entities(user)); _popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting-target"), container, Filter.Entities(container)); _doAfterSystem.DoAfter(doAfterEventArgs); }
private void AttemptLearn(EntityUid uid, SpellbookComponent component, UseInHandEvent args) { if (component.CancelToken != null) { return; } component.CancelToken = new CancellationTokenSource(); var doAfterEventArgs = new DoAfterEventArgs(args.User, component.LearnTime, component.CancelToken.Token, uid) { BreakOnTargetMove = true, BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true, NeedHand = true, //What, are you going to read with your eyes only?? TargetFinishedEvent = new LearnDoAfterComplete(args.User), TargetCancelledEvent = new LearnDoAfterCancel(), }; _doAfter.DoAfter(doAfterEventArgs); }
public async Task TestFinished() { Task <DoAfterStatus> task = null; var server = StartServerDummyTicker(); // That it finishes successfully server.Post(() => { var tickTime = 1.0f / IoCManager.Resolve <IGameTiming>().TickRate; var mapManager = IoCManager.Resolve <IMapManager>(); mapManager.CreateNewMapEntity(MapId.Nullspace); var entityManager = IoCManager.Resolve <IEntityManager>(); var mob = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace); var cancelToken = new CancellationTokenSource(); var args = new DoAfterEventArgs(mob, tickTime / 2, cancelToken.Token); task = EntitySystem.Get <DoAfterSystem>().DoAfter(args); }); await server.WaitRunTicks(1); Assert.That(task.Result == DoAfterStatus.Finished); }
private void AttemptResist(EntityUid user, EntityUid target, EntityStorageComponent?storageComponent = null, ResistLockerComponent?resistLockerComponent = null) { if (!Resolve(target, ref storageComponent, ref resistLockerComponent)) { return; } resistLockerComponent.CancelToken = new(); var doAfterEventArgs = new DoAfterEventArgs(user, resistLockerComponent.ResistTime, resistLockerComponent.CancelToken.Token, target) { BreakOnTargetMove = false, BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true, NeedHand = false, //No hands 'cause we be kickin' TargetFinishedEvent = new ResistDoAfterComplete(user, target), TargetCancelledEvent = new ResistDoAfterCancelled(user) }; resistLockerComponent.IsResisting = true; _popupSystem.PopupEntity(Loc.GetString("resist-locker-component-start-resisting"), user, Filter.Entities(user)); _doAfterSystem.DoAfter(doAfterEventArgs); }
async Task <bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { // FIXME: Make this work properly. Right now it relies on the click location being on a grid, which is bad. if (!eventArgs.ClickLocation.IsValid(Owner.EntityManager) || !eventArgs.ClickLocation.GetGridId(Owner.EntityManager).IsValid()) { return(false); } //No changing mode mid-RCD var startingMode = _mode; var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GetGridId(Owner.EntityManager)); var tile = mapGrid.GetTileRef(eventArgs.ClickLocation); var snapPos = mapGrid.TileIndicesFor(eventArgs.ClickLocation); //Using an RCD isn't instantaneous var cancelToken = new CancellationTokenSource(); var doAfterEventArgs = new DoAfterEventArgs(eventArgs.User, _delay, cancelToken.Token, eventArgs.Target) { BreakOnDamage = true, BreakOnStun = true, NeedHand = true, ExtraCheck = () => IsRCDStillValid(eventArgs, mapGrid, tile, snapPos, startingMode) //All of the sanity checks are here }; var result = await _doAfterSystem.WaitDoAfter(doAfterEventArgs); if (result == DoAfterStatus.Cancelled) { return(true); } switch (_mode) { //Floor mode just needs the tile to be a space tile (subFloor) case RcdMode.Floors: mapGrid.SetTile(eventArgs.ClickLocation, new Robust.Shared.Map.Tile(_tileDefinitionManager["floor_steel"].TileId)); break; //We don't want to place a space tile on something that's already a space tile. Let's do the inverse of the last check. case RcdMode.Deconstruct: if (!tile.IsBlockedTurf(true)) //Delete the turf { mapGrid.SetTile(snapPos, Robust.Shared.Map.Tile.Empty); } else //Delete what the user targeted { eventArgs.Target?.Delete(); } break; //Walls are a special behaviour, and require us to build a new object with a transform rather than setting a grid tile, thus we early return to avoid the tile set code. case RcdMode.Walls: var ent = _serverEntityManager.SpawnEntity("WallSolid", mapGrid.GridTileToLocal(snapPos)); ent.Transform.LocalRotation = Angle.Zero; // Walls always need to point south. break; case RcdMode.Airlock: var airlock = _serverEntityManager.SpawnEntity("Airlock", mapGrid.GridTileToLocal(snapPos)); airlock.Transform.LocalRotation = Owner.Transform.LocalRotation; //Now apply icon smoothing. break; default: return(true); //I don't know why this would happen, but sure I guess. Get out of here invalid state! } SoundSystem.Play(Filter.Pvs(Owner), _successSound.GetSound(), Owner); _ammo--; return(true); }
private async void PlaceActiveHandItemInHands(IEntity user, string hand) { var hands = Owner.GetComponent <HandsComponent>(); var userHands = user.GetComponent <HandsComponent>(); var item = userHands.GetActiveHand; bool Check() { if (!EntitySystem.Get <ActionBlockerSystem>().CanInteract(user)) { return(false); } if (item == null) { user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything")); return(false); } if (!userHands.CanDrop(userHands.ActiveHand !)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop")); return(false); } if (!hands.HasHand(hand)) { return(false); } if (hands.TryGetItem(hand, out var _)) { user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-occupied-message", ("owner", Owner))); return(false); } if (!hands.CanPickupEntity(hand, item.Owner, checkActionBlocker: false)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-put-message", ("owner", Owner))); return(false); } return(true); } var doAfterSystem = EntitySystem.Get <DoAfterSystem>(); var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner) { ExtraCheck = Check, BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, NeedHand = true, }; var result = await doAfterSystem.DoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } userHands.Drop(hand); hands.TryPickupEntity(hand, item !.Owner, checkActionBlocker: false); UpdateSubscribed(); }
private async void PlaceActiveHandItemInInventory(IEntity user, Slots slot) { var inventory = Owner.GetComponent <InventoryComponent>(); var userHands = user.GetComponent <HandsComponent>(); var item = userHands.GetActiveHand; bool Check() { if (!EntitySystem.Get <ActionBlockerSystem>().CanInteract(user)) { return(false); } if (item == null) { user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything")); return(false); } if (!userHands.CanDrop(userHands.ActiveHand !)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop")); return(false); } if (!inventory.HasSlot(slot)) { return(false); } if (inventory.TryGetSlotItem(slot, out ItemComponent _)) { user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-occupied", ("owner", Owner))); return(false); } if (!inventory.CanEquip(slot, item, false)) { user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-equip-message", ("owner", Owner))); return(false); } return(true); } var doAfterSystem = EntitySystem.Get <DoAfterSystem>(); var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner) { ExtraCheck = Check, BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, NeedHand = true, }; var result = await doAfterSystem.DoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } userHands.Drop(item !.Owner, false); inventory.Equip(slot, item !.Owner, false); UpdateSubscribed(); }
private async void PlaceActiveHandItemInHands(IEntity user, string hand) { var hands = Owner.GetComponent <HandsComponent>(); var userHands = user.GetComponent <HandsComponent>(); var item = userHands.GetActiveHand; bool Check() { if (!ActionBlockerSystem.CanInteract(user)) { return(false); } if (item == null) { user.PopupMessageCursor(Loc.GetString("You aren't holding anything!")); return(false); } if (!userHands.CanDrop(userHands.ActiveHand !)) { user.PopupMessageCursor(Loc.GetString("You can't drop that!")); return(false); } if (!hands.HasHand(hand)) { return(false); } if (hands.TryGetItem(hand, out var _)) { user.PopupMessageCursor(Loc.GetString("{0:They} already {0:have} something there!", Owner)); return(false); } if (!hands.CanPutInHand(item, hand, false)) { user.PopupMessageCursor(Loc.GetString("{0:They} cannot put that there!", Owner)); return(false); } return(true); } var doAfterSystem = EntitySystem.Get <DoAfterSystem>(); var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner) { ExtraCheck = Check, BreakOnStun = true, BreakOnDamage = true, BreakOnTargetMove = true, BreakOnUserMove = true, NeedHand = true, }; var result = await doAfterSystem.DoAfter(doAfterArgs); if (result != DoAfterStatus.Finished) { return; } userHands.Drop(hand, false); hands.PutInHand(item !, hand, false, false); UpdateSubscribed(); }
async Task <bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { //No changing mode mid-RCD var startingMode = _mode; var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GetGridId(Owner.EntityManager)); var tile = mapGrid.GetTileRef(eventArgs.ClickLocation); var snapPos = mapGrid.SnapGridCellFor(eventArgs.ClickLocation, SnapGridOffset.Center); //Using an RCD isn't instantaneous var cancelToken = new CancellationTokenSource(); var doAfterEventArgs = new DoAfterEventArgs(eventArgs.User, _delay, cancelToken.Token, eventArgs.Target) { BreakOnDamage = true, BreakOnStun = true, NeedHand = true, ExtraCheck = () => IsRCDStillValid(eventArgs, mapGrid, tile, snapPos, startingMode) //All of the sanity checks are here }; var result = await doAfterSystem.DoAfter(doAfterEventArgs); if (result == DoAfterStatus.Cancelled) { return(true); } switch (_mode) { //Floor mode just needs the tile to be a space tile (subFloor) case RcdMode.Floors: mapGrid.SetTile(eventArgs.ClickLocation, new Tile(_tileDefinitionManager["floor_steel"].TileId)); break; //We don't want to place a space tile on something that's already a space tile. Let's do the inverse of the last check. case RcdMode.Deconstruct: if (!tile.IsBlockedTurf(true)) //Delete the turf { mapGrid.SetTile(snapPos, Tile.Empty); } else //Delete what the user targeted { eventArgs.Target.Delete(); } break; //Walls are a special behaviour, and require us to build a new object with a transform rather than setting a grid tile, thus we early return to avoid the tile set code. case RcdMode.Walls: var ent = _serverEntityManager.SpawnEntity("solid_wall", mapGrid.GridTileToLocal(snapPos)); ent.Transform.LocalRotation = Owner.Transform.LocalRotation; //Now apply icon smoothing. break; case RcdMode.Airlock: var airlock = _serverEntityManager.SpawnEntity("Airlock", mapGrid.GridTileToLocal(snapPos)); airlock.Transform.LocalRotation = Owner.Transform.LocalRotation; //Now apply icon smoothing. break; default: return(true); //I don't know why this would happen, but sure I guess. Get out of here invalid state! } _entitySystemManager.GetEntitySystem <AudioSystem>().PlayFromEntity("/Audio/Items/deconstruct.ogg", Owner); _ammo--; return(true); }
private async void OnAfterInteract(EntityUid uid, RCDComponent rcd, AfterInteractEvent args) { if (args.Handled || !args.CanReach) { return; } if (rcd.CancelToken != null) { rcd.CancelToken?.Cancel(); rcd.CancelToken = null; args.Handled = true; return; } if (!args.ClickLocation.IsValid(EntityManager)) { return; } var clickLocationMod = args.ClickLocation; // Initial validity check if (!clickLocationMod.IsValid(EntityManager)) { return; } // Try to fix it (i.e. if clicking on space) // Note: Ideally there'd be a better way, but there isn't right now. var gridID = clickLocationMod.GetGridId(EntityManager); if (!gridID.IsValid()) { clickLocationMod = clickLocationMod.AlignWithClosestGridTile(); gridID = clickLocationMod.GetGridId(EntityManager); } // Check if fixing it failed / get final grid ID if (!gridID.IsValid()) { return; } var mapGrid = _mapManager.GetGrid(gridID); var tile = mapGrid.GetTileRef(clickLocationMod); var snapPos = mapGrid.TileIndicesFor(clickLocationMod); //No changing mode mid-RCD var startingMode = rcd.Mode; args.Handled = true; var user = args.User; //Using an RCD isn't instantaneous rcd.CancelToken = new CancellationTokenSource(); var doAfterEventArgs = new DoAfterEventArgs(user, rcd.Delay, rcd.CancelToken.Token, args.Target) { BreakOnDamage = true, BreakOnStun = true, NeedHand = true, ExtraCheck = () => IsRCDStillValid(rcd, args, mapGrid, tile, startingMode) //All of the sanity checks are here }; var result = await _doAfterSystem.WaitDoAfter(doAfterEventArgs); rcd.CancelToken = null; if (result == DoAfterStatus.Cancelled) { return; } switch (rcd.Mode) { //Floor mode just needs the tile to be a space tile (subFloor) case RcdMode.Floors: mapGrid.SetTile(snapPos, new Tile(_tileDefinitionManager["floor_steel"].TileId)); _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to set grid: {tile.GridIndex} {snapPos} to floor_steel"); break; //We don't want to place a space tile on something that's already a space tile. Let's do the inverse of the last check. case RcdMode.Deconstruct: if (!tile.IsBlockedTurf(true)) //Delete the turf { mapGrid.SetTile(snapPos, Tile.Empty); _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to set grid: {tile.GridIndex} tile: {snapPos} to space"); } else //Delete what the user targeted { if (args.Target is { Valid : true } target) { _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to delete {ToPrettyString(target):target}"); QueueDel(target); } } break; //Walls are a special behaviour, and require us to build a new object with a transform rather than setting a grid tile, // thus we early return to avoid the tile set code. case RcdMode.Walls: var ent = EntityManager.SpawnEntity("WallSolid", mapGrid.GridTileToLocal(snapPos)); Transform(ent).LocalRotation = Angle.Zero; // Walls always need to point south. _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to spawn {ToPrettyString(ent)} at {snapPos} on grid {mapGrid.Index}"); break; case RcdMode.Airlock: var airlock = EntityManager.SpawnEntity("Airlock", mapGrid.GridTileToLocal(snapPos)); Transform(airlock).LocalRotation = Transform(rcd.Owner).LocalRotation; //Now apply icon smoothing. _logs.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(args.User):user} used RCD to spawn {ToPrettyString(airlock)} at {snapPos} on grid {mapGrid.Index}"); break; default: args.Handled = true; return; //I don't know why this would happen, but sure I guess. Get out of here invalid state! } SoundSystem.Play(Filter.Pvs(uid, entityManager: EntityManager), rcd.SuccessSound.GetSound(), rcd.Owner); rcd.CurrentAmmo--; args.Handled = true; }
/// <summary> /// Attempt to uncuff a cuffed entity. Can be called by the cuffed entity, or another entity trying to help uncuff them. /// If the uncuffing succeeds, the cuffs will drop on the floor. /// </summary> /// <param name="user">The cuffed entity</param> /// <param name="cuffsToRemove">Optional param for the handcuff entity to remove from the cuffed entity. If null, uses the most recently added handcuff entity.</param> public async void TryUncuff(IEntity user, IEntity cuffsToRemove = null) { var isOwner = user == Owner; if (cuffsToRemove == null) { cuffsToRemove = LastAddedCuffs; } else { if (!_container.ContainedEntities.Contains(cuffsToRemove)) { Logger.Warning("A user is trying to remove handcuffs that aren't in the owner's container. This should never happen!"); } } if (!cuffsToRemove.TryGetComponent <HandcuffComponent>(out var cuff)) { Logger.Warning($"A user is trying to remove handcuffs without a {nameof(HandcuffComponent)}. This should never happen!"); return; } if (!isOwner && !ActionBlockerSystem.CanInteract(user)) { user.PopupMessage(user, Loc.GetString("You can't do that!")); return; } if (!isOwner && user.InRangeUnobstructed(Owner, _interactRange)) { user.PopupMessage(user, Loc.GetString("You are too far away to remove the cuffs.")); return; } if (!cuffsToRemove.InRangeUnobstructed(Owner, _interactRange)) { Logger.Warning("Handcuffs being removed from player are obstructed or too far away! This should not happen!"); return; } user.PopupMessage(user, Loc.GetString("You start removing the cuffs.")); var audio = EntitySystem.Get <AudioSystem>(); audio.PlayFromEntity(isOwner ? cuff.StartBreakoutSound : cuff.StartUncuffSound, Owner); var uncuffTime = isOwner ? cuff.BreakoutTime : cuff.UncuffTime; var doAfterEventArgs = new DoAfterEventArgs(user, uncuffTime) { BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true, NeedHand = true }; var doAfterSystem = EntitySystem.Get <DoAfterSystem>(); var result = await doAfterSystem.DoAfter(doAfterEventArgs); if (result != DoAfterStatus.Cancelled) { audio.PlayFromEntity(cuff.EndUncuffSound, Owner); _container.ForceRemove(cuffsToRemove); cuffsToRemove.Transform.AttachToGridOrMap(); cuffsToRemove.Transform.WorldPosition = Owner.Transform.WorldPosition; if (cuff.BreakOnRemove) { cuff.Broken = true; cuffsToRemove.Name = cuff.BrokenName; cuffsToRemove.Description = cuff.BrokenDesc; if (cuffsToRemove.TryGetComponent <SpriteComponent>(out var sprite)) { sprite.LayerSetState(0, cuff.BrokenState); // TODO: safety check to see if RSI contains the state? } } CanStillInteract = _hands.Hands.Count() > CuffedHandCount; OnCuffedStateChanged.Invoke(); UpdateStatusEffect(); Dirty(); if (CuffedHandCount == 0) { _notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs.")); if (!isOwner) { _notifyManager.PopupMessage(user, Owner, Loc.GetString("{0:theName} uncuffs your hands.", user)); } } else { if (!isOwner) { _notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs. {0} of {1:theName}'s hands remain cuffed.", CuffedHandCount, user)); _notifyManager.PopupMessage(user, Owner, Loc.GetString("{0:theName} removes your cuffs. {1} of your hands remain cuffed.", user, CuffedHandCount)); } else { _notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs. {0} of your hands remain cuffed.", CuffedHandCount)); } } } else { _notifyManager.PopupMessage(user, user, Loc.GetString("You fail to remove the cuffs.")); } return; }
/// <summary> /// Allows a user to pick up entities by clicking them, or pick up all entities in a certain radius /// around a click. /// </summary> /// <returns></returns> private async void AfterInteract(EntityUid uid, ServerStorageComponent storageComp, AfterInteractEvent eventArgs) { if (!eventArgs.CanReach) { return; } if (storageComp.CancelToken != null) { storageComp.CancelToken.Cancel(); storageComp.CancelToken = null; return; } // Pick up all entities in a radius around the clicked location. // The last half of the if is because carpets exist and this is terrible if (storageComp.AreaInsert && (eventArgs.Target == null || !HasComp <ItemComponent>(eventArgs.Target.Value))) { var validStorables = new List <EntityUid>(); foreach (var entity in _entityLookupSystem.GetEntitiesInRange(eventArgs.ClickLocation, storageComp.AreaInsertRadius, LookupFlags.None)) { if (entity == eventArgs.User || !HasComp <ItemComponent>(entity) || !_interactionSystem.InRangeUnobstructed(eventArgs.User, entity)) { continue; } validStorables.Add(entity); } //If there's only one then let's be generous if (validStorables.Count > 1) { storageComp.CancelToken = new CancellationTokenSource(); var doAfterArgs = new DoAfterEventArgs(eventArgs.User, 0.2f * validStorables.Count, storageComp.CancelToken.Token, uid) { BreakOnStun = true, BreakOnDamage = true, BreakOnUserMove = true, NeedHand = true, }; await _doAfterSystem.WaitDoAfter(doAfterArgs); } // TODO: Make it use the event DoAfter var successfullyInserted = new List <EntityUid>(); var successfullyInsertedPositions = new List <EntityCoordinates>(); foreach (var entity in validStorables) { // Check again, situation may have changed for some entities, but we'll still pick up any that are valid if (_containerSystem.IsEntityInContainer(entity) || entity == eventArgs.User || !HasComp <ItemComponent>(entity)) { continue; } if (TryComp <TransformComponent>(uid, out var transformOwner) && TryComp <TransformComponent>(entity, out var transformEnt)) { var position = EntityCoordinates.FromMap(transformOwner.Parent?.Owner ?? uid, transformEnt.MapPosition); if (PlayerInsertEntityInWorld(uid, eventArgs.User, entity, storageComp)) { successfullyInserted.Add(entity); successfullyInsertedPositions.Add(position); } } } // If we picked up atleast one thing, play a sound and do a cool animation! if (successfullyInserted.Count > 0) { if (storageComp.StorageInsertSound is not null) { SoundSystem.Play(storageComp.StorageInsertSound.GetSound(), Filter.Pvs(uid, entityManager: EntityManager), uid, AudioParams.Default); } RaiseNetworkEvent(new AnimateInsertingEntitiesEvent(uid, successfullyInserted, successfullyInsertedPositions)); } return; } // Pick up the clicked entity else if (storageComp.QuickInsert) { if (eventArgs.Target is not { Valid : true } target) { return; } if (_containerSystem.IsEntityInContainer(target) || target == eventArgs.User || !HasComp <ItemComponent>(target)) { return; } if (TryComp <TransformComponent>(uid, out var transformOwner) && TryComp <TransformComponent>(target, out var transformEnt)) { var parent = transformOwner.ParentUid; var position = EntityCoordinates.FromMap( parent.IsValid() ? parent : uid, transformEnt.MapPosition); if (PlayerInsertEntityInWorld(uid, eventArgs.User, target, storageComp)) { RaiseNetworkEvent(new AnimateInsertingEntitiesEvent(uid, new List <EntityUid> { target }, new List <EntityCoordinates> { position })); } } } return; }