public bool CanAccess(EntityUid actor, EntityUid target, EntityUid itemUid) { // Can the actor reach the target? if (actor != target && !(_interactionSystem.InRangeUnobstructed(actor, target) && _containerSystem.IsInSameOrParentContainer(actor, target))) { return(false); } // Can the actor reach the item? if (_interactionSystem.InRangeUnobstructed(actor, itemUid) && _containerSystem.IsInSameOrParentContainer(actor, itemUid)) { return(true); } // Is the item in an open storage UI, i.e., is the user quick-equipping from an open backpack? if (_interactionSystem.CanAccessViaStorage(actor, itemUid)) { return(true); } // Is the actor currently stripping the target? Here we could check if the actor has the stripping UI open, but // that requires server/client specific code. so lets just check if they **could** open the stripping UI. // Note that this doesn't check that the item is equipped by the target, as this is done elsewhere. return(actor != target && TryComp(target, out SharedStrippableComponent? strip) && strip.CanBeStripped(actor)); }
public bool ValidateEntityTarget(EntityUid user, EntityUid target, EntityTargetAction action) { if (!target.IsValid() || Deleted(target)) { return(false); } if (action.Whitelist != null && !action.Whitelist.IsValid(target, EntityManager)) { return(false); } if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, target)) { return(false); } if (user == target) { return(action.CanTargetSelf); } if (!action.CheckCanAccess) { // even if we don't check for obstructions, we may still need to check the range. var xform = Transform(user); var targetXform = Transform(target); if (xform.MapID != targetXform.MapID) { return(false); } if (action.Range <= 0) { return(true); } return((xform.WorldPosition - targetXform.WorldPosition).Length <= action.Range); } if (_interactionSystem.InRangeUnobstructed(user, target, range: action.Range) && _containerSystem.IsInSameOrParentContainer(user, target)) { return(true); } return(_interactionSystem.CanAccessViaStorage(user, target)); }
public void FlashArea(EntityUid source, EntityUid?user, float range, float duration, float slowTo = 0.8f, bool displayPopup = false, SoundSpecifier?sound = null) { var transform = EntityManager.GetComponent <TransformComponent>(source); var mapPosition = transform.MapPosition; var flashableEntities = new List <EntityUid>(); var flashableQuery = GetEntityQuery <FlashableComponent>(); foreach (var entity in _entityLookup.GetEntitiesInRange(transform.Coordinates, range)) { if (!flashableQuery.HasComponent(entity)) { continue; } flashableEntities.Add(entity); } foreach (var entity in flashableEntities) { // Check for unobstructed entities while ignoring the mobs with flashable components. if (!_interactionSystem.InRangeUnobstructed(entity, mapPosition, range, CollisionGroup.Opaque, (e) => flashableEntities.Contains(e))) { continue; } // They shouldn't have flash removed in between right? Flash(entity, user, source, duration, slowTo, displayPopup, flashableQuery.GetComponent(entity)); } if (sound != null) { SoundSystem.Play(sound.GetSound(), Filter.Pvs(transform), source); } }
/// <summary> /// When activated, blasts everyone in LOS within n tiles /// with a high-probability disease infection attempt /// </summary> private void OnActivate(EntityUid uid, DiseaseArtifactComponent component, ArtifactActivatedEvent args) { if (component.SpawnDisease == null) { return; } var xform = Transform(uid); var carrierQuery = GetEntityQuery <DiseaseCarrierComponent>(); foreach (var entity in _lookup.GetEntitiesInRange(xform.Coordinates, component.Range)) { if (!carrierQuery.TryGetComponent(entity, out var carrier)) { continue; } if (!_interactionSystem.InRangeUnobstructed(uid, entity, component.Range)) { continue; } _disease.TryInfect(carrier, component.SpawnDisease, forced: true); } }
private void OnInteract(EntityUid uid, AirAlarmComponent component, InteractHandEvent args) { if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target)) { return; } if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) { return; } if (EntityManager.TryGetComponent(uid, out WiresComponent wire) && wire.IsPanelOpen) { args.Handled = false; return; } if (EntityManager.TryGetComponent(uid, out ApcPowerReceiverComponent recv) && !recv.Powered) { return; } _uiSystem.GetUiOrNull(component.Owner, SharedAirAlarmInterfaceKey.Key)?.Open(actor.PlayerSession); component.ActivePlayers.Add(actor.PlayerSession.UserId); AddActiveInterface(uid); SendAddress(uid); SendAlarmMode(uid); SendThresholds(uid); SyncAllDevices(uid); SendAirData(uid); }
/// <summary> /// Whether the table exists, and the player can interact with it. /// </summary> /// <param name="playerEntity">The player entity to check.</param> /// <param name="table">The table entity to check.</param> protected bool CanSeeTable(EntityUid playerEntity, EntityUid?table) { // Table may have been deleted, hence TryComp if (!TryComp(table, out MetaDataComponent? meta) || meta.EntityLifeStage >= EntityLifeStage.Terminating || (meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) { return(false); } return(_interactionSystem.InRangeUnobstructed(playerEntity, table.Value) && _actionBlockerSystem.CanInteract(playerEntity, table)); }
private void OnBeforeInteract(EntityUid uid, DoorRemoteComponent component, BeforeRangedInteractEvent args) { if (args.Handled || args.Target == null || !TryComp <DoorComponent>(args.Target, out var doorComp) || // If it isn't a door we don't use it !TryComp <AirlockComponent>(args.Target, out var airlockComp) // Remotes only work on airlocks // The remote can be used anywhere the user can see the door. // This doesn't work that well, but I don't know of an alternative || !_interactionSystem.InRangeUnobstructed(args.User, args.Target.Value, SharedInteractionSystem.MaxRaycastRange, CollisionGroup.Opaque)) { return; } args.Handled = true; if (!this.IsPowered(args.Target.Value, EntityManager)) { ShowPopupToUser("door-remote-no-power", args.User); return; } if (TryComp <AccessReaderComponent>(args.Target, out var accessComponent) && !_doorSystem.HasAccess(doorComp.Owner, args.Used, accessComponent)) { _doorSystem.Deny(airlockComp.Owner, doorComp, args.User); ShowPopupToUser("door-remote-denied", args.User); return; } switch (component.Mode) { case OperatingMode.OpenClose: _doorSystem.TryToggleDoor(doorComp.Owner, doorComp, args.Used); break; case OperatingMode.ToggleBolts: //TODO: What about cut wires...? airlockComp.SetBoltsWithAudio(!airlockComp.IsBolted()); break; case OperatingMode.ToggleEmergencyAccess: _sharedAirlockSystem.ToggleEmergencyAccess(airlockComp); break; default: throw new InvalidOperationException( $"{nameof(DoorRemoteComponent)} had invalid mode {component.Mode}"); } }
private void OnAfterInteract(EntityUid uid, DoorRemoteComponent component, AfterInteractEvent args) { if (args.Handled || args.Target == null || !TryComp <DoorComponent>(args.Target, out var doorComponent) || // If it isn't a door we don't use it !HasComp <AccessReaderComponent>(args.Target) || // Remotes do not work on doors without access requirements !TryComp <AirlockComponent>(args.Target, out var airlockComponent) || // Remotes only work on airlocks !_interactionSystem.InRangeUnobstructed(args.User, doorComponent.Owner, -1f, CollisionGroup.Opaque)) { return; } args.Handled = true; if (component.Mode == DoorRemoteComponent.OperatingMode.OpenClose) { _sharedDoorSystem.TryToggleDoor(doorComponent.Owner, user: args.Used); } if (component.Mode == DoorRemoteComponent.OperatingMode.ToggleBolts && airlockComponent.IsPowered()) { if (_doorSystem.HasAccess(doorComponent.Owner, args.Used)) { airlockComponent.SetBoltsWithAudio(!airlockComponent.IsBolted()); } else { if (doorComponent.State != DoorState.Open) { _sharedDoorSystem.Deny(airlockComponent.Owner, user: args.User); } else if (doorComponent.DenySound != null) { SoundSystem.Play(Filter.Pvs(args.Target.Value), doorComponent.DenySound.GetSound(), args.Target.Value); } } } if (component.Mode == DoorRemoteComponent.OperatingMode.ToggleEmergencyAccess && airlockComponent.IsPowered()) { if (_doorSystem.HasAccess(doorComponent.Owner, args.Used)) { _sharedAirlockSystem.ToggleEmergencyAccess(airlockComponent); } } }
public override void Update(float frameTime) { base.Update(frameTime); foreach (var component in EntityManager.EntityQuery <NetworkConfiguratorComponent>()) { if (component.ActiveDeviceList != null && EntityManager.EntityExists(component.ActiveDeviceList.Value) && _interactionSystem.InRangeUnobstructed(component.Owner, component.ActiveDeviceList.Value)) { return; } //The network configurator is a handheld device. There can only ever be an ui session open for the player holding the device. _uiSystem.GetUiOrNull(component.Owner, NetworkConfiguratorUiKey.Configure)?.CloseAll(); } }
/// <summary> /// When activated, blasts everyone in LOS within 3 tiles /// with a high-probability disease infection attempt /// </summary> private void OnActivate(EntityUid uid, DiseaseArtifactComponent component, ArtifactActivatedEvent args) { var xform = Transform(uid); foreach (var entity in _lookup.GetEntitiesInRange(xform.MapID, xform.WorldPosition, 3f)) { if (!_interactionSystem.InRangeUnobstructed(uid, entity, 3f)) { continue; } if (TryComp <DiseaseCarrierComponent>(entity, out var carrier)) { EntitySystem.Get <DiseaseSystem>().TryInfect(carrier, component.ResolveDisease); } } }
public bool CanOpen(EntityUid user, EntityUid target, bool silent = false, EntityStorageComponent?component = null) { if (!Resolve(target, ref component)) { return(false); } if (!HasComp <SharedHandsComponent>(user)) { return(false); } if (component.IsWeldedShut) { if (!silent && !component.Contents.Contains(user)) { _popupSystem.PopupEntity(Loc.GetString("entity-storage-component-welded-shut-message"), target, Filter.Pvs(target)); } return(false); } //Checks to see if the opening position, if offset, is inside of a wall. if (component.EnteringOffset != (0, 0)) //if the entering position is offset { var targetXform = Transform(target); var newCoords = new EntityCoordinates(target, component.EnteringOffset); if (!_interactionSystem.InRangeUnobstructed(target, newCoords, collisionMask: component.EnteringOffsetCollisionFlags)) { if (!silent) { _popupSystem.PopupEntity(Loc.GetString("entity-storage-component-cannot-open-no-space"), target, Filter.Pvs(target)); } return(false); } } var ev = new StorageOpenAttemptEvent(silent); RaiseLocalEvent(target, ev, true); return(!ev.Cancelled); }
/// <summary> /// Inserts an Entity (<paramref name="toInsert"/>) in the world into storage, informing <paramref name="player"/> if it fails. /// <paramref name="toInsert"/> is *NOT* held, see <see cref="PlayerInsertHeldEntity(Robust.Shared.GameObjects.EntityUid)"/>. /// </summary> /// <param name="player">The player to insert an entity with</param> /// <returns>true if inserted, false otherwise</returns> public bool PlayerInsertEntityInWorld(EntityUid uid, EntityUid player, EntityUid toInsert, ServerStorageComponent?storageComp = null) { if (!Resolve(uid, ref storageComp)) { return(false); } if (!_sharedInteractionSystem.InRangeUnobstructed(player, uid, popup: storageComp.ShowPopup)) { return(false); } if (!Insert(uid, toInsert, storageComp)) { Popup(uid, player, "comp-storage-cant-insert", storageComp); return(false); } return(true); }
/// <summary> /// Whether the table exists, and the player can interact with it. /// </summary> /// <param name="playerEntity">The player entity to check.</param> /// <param name="table">The table entity to check.</param> protected bool CanSeeTable(EntityUid playerEntity, EntityUid?table) { if (table == null) { return(false); } if (EntityManager.GetComponent <TransformComponent>(table.Value).Parent?.Owner is not { } parent) { return(false); } if (!EntityManager.HasComponent <MapComponent>(parent) && !EntityManager.HasComponent <IMapGridComponent>(parent)) { return(false); } return(_interactionSystem.InRangeUnobstructed(playerEntity, table.Value) && _actionBlockerSystem.CanInteract(playerEntity, table)); }
private void OnInteractHand(EntityUid uid, FireAlarmComponent component, InteractHandEvent args) { if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target)) { return; } if (EntityManager.TryGetComponent(args.User, out ActorComponent? actor) && EntityManager.TryGetComponent(uid, out AtmosMonitorComponent? monitor) && this.IsPowered(uid, EntityManager)) { if (monitor.HighestAlarmInNetwork == AtmosMonitorAlarmType.Normal) { _monitorSystem.Alert(uid, AtmosMonitorAlarmType.Danger); } else { _monitorSystem.ResetAll(uid); } } }
private bool TryUseUtensil(EntityUid user, EntityUid target, UtensilComponent component) { if (!EntityManager.TryGetComponent(target, out FoodComponent food)) { return(false); } //Prevents food usage with a wrong utensil if ((food.Utensil & component.Types) == 0) { _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", food.Owner), ("utensil", component.Owner)), user, Filter.Entities(user)); return(false); } if (!_interactionSystem.InRangeUnobstructed(user, target, popup: true)) { return(false); } return(_foodSystem.TryFeed(user, target, food)); }
public bool IsInDetailsRange(EntityUid examiner, EntityUid entity) { // check if the mob is in ciritcal or dead if (EntityManager.TryGetComponent(examiner, out MobStateComponent mobState) && mobState.IsIncapacitated()) { return(false); } if (!_interactionSystem.InRangeUnobstructed(examiner, entity, ExamineDetailsRange)) { return(false); } // Is the target hidden in a opaque locker or something? Currently this check allows players to examine // their organs, if they can somehow target them. Really this should be with userSeeInsideSelf: false, and a // separate check for if the item is in their inventory or hands. if (_containerSystem.IsInSameOrTransparentContainer(examiner, entity, userSeeInsideSelf: true)) { return(true); } // is it inside of an open storage (e.g., an open backpack)? return(_interactionSystem.CanAccessViaStorage(examiner, entity)); }
/// <summary> /// Raises a number of events in order to get all verbs of the given type(s) defined in local systems. This /// does not request verbs from the server. /// </summary> public virtual Dictionary <VerbType, SortedSet <Verb> > GetLocalVerbs(EntityUid target, EntityUid user, VerbType verbTypes, bool force = false) { Dictionary <VerbType, SortedSet <Verb> > verbs = new(); // accessibility checks bool canAccess = false; if (force || target == user) { canAccess = true; } else if (_interactionSystem.InRangeUnobstructed(user, target, ignoreInsideBlocker: true)) { if (user.IsInSameOrParentContainer(target)) { canAccess = true; } else { // the item might be in a backpack that the user has open canAccess = _interactionSystem.CanAccessViaStorage(user, target); } } // A large number of verbs need to check action blockers. Instead of repeatedly having each system individually // call ActionBlocker checks, just cache it for the verb request. var canInteract = force || _actionBlockerSystem.CanInteract(user); EntityUid @using = default; if (EntityManager.TryGetComponent(user, out SharedHandsComponent? hands) && (force || _actionBlockerSystem.CanUse(user))) { hands.TryGetActiveHeldEntity(out @using); // Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used". // This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging // their sprite. if (@using != default && EntityManager.TryGetComponent <HandVirtualItemComponent?>(@using, out var pull)) { @using = pull.BlockingEntity; } } if ((verbTypes & VerbType.Interaction) == VerbType.Interaction) { GetInteractionVerbsEvent getVerbEvent = new(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(target, getVerbEvent); verbs.Add(VerbType.Interaction, getVerbEvent.Verbs); } if ((verbTypes & VerbType.Activation) == VerbType.Activation) { GetActivationVerbsEvent getVerbEvent = new(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(target, getVerbEvent); verbs.Add(VerbType.Activation, getVerbEvent.Verbs); } if ((verbTypes & VerbType.Alternative) == VerbType.Alternative) { GetAlternativeVerbsEvent getVerbEvent = new(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(target, getVerbEvent); verbs.Add(VerbType.Alternative, getVerbEvent.Verbs); } if ((verbTypes & VerbType.Other) == VerbType.Other) { GetOtherVerbsEvent getVerbEvent = new(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(target, getVerbEvent); verbs.Add(VerbType.Other, getVerbEvent.Verbs); } return(verbs); }
/// <summary> /// Raises a number of events in order to get all verbs of the given type(s) defined in local systems. This /// does not request verbs from the server. /// </summary> public SortedSet <Verb> GetLocalVerbs(EntityUid target, EntityUid user, List <Type> types, bool force = false) { SortedSet <Verb> verbs = new(); // accessibility checks bool canAccess = false; if (force || target == user) { canAccess = true; } else if (EntityManager.EntityExists(target) && _interactionSystem.InRangeUnobstructed(user, target)) { if (ContainerSystem.IsInSameOrParentContainer(user, target)) { canAccess = true; } else { // the item might be in a backpack that the user has open canAccess = _interactionSystem.CanAccessViaStorage(user, target); } } // A large number of verbs need to check action blockers. Instead of repeatedly having each system individually // call ActionBlocker checks, just cache it for the verb request. var canInteract = force || _actionBlockerSystem.CanInteract(user, target); EntityUid? @using = null; if (TryComp(user, out SharedHandsComponent? hands) && (force || _actionBlockerSystem.CanUseHeldEntity(user))) { @using = hands.ActiveHandEntity; // Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used". // This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging // their sprite. if (TryComp(@using, out HandVirtualItemComponent? pull)) { @using = pull.BlockingEntity; } } if (types.Contains(typeof(InteractionVerb))) { var verbEvent = new GetVerbsEvent <InteractionVerb>(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(UtilityVerb)) && @using != null && @using != target) { var verbEvent = new GetVerbsEvent <UtilityVerb>(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(@using.Value, verbEvent, true); // directed at used, not at target verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(InnateVerb))) { var verbEvent = new GetVerbsEvent <InnateVerb>(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(user, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(AlternativeVerb))) { var verbEvent = new GetVerbsEvent <AlternativeVerb>(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(ActivationVerb))) { var verbEvent = new GetVerbsEvent <ActivationVerb>(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(ExamineVerb))) { var verbEvent = new GetVerbsEvent <ExamineVerb>(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } // generic verbs if (types.Contains(typeof(Verb))) { var verbEvent = new GetVerbsEvent <Verb>(user, target, @using, hands, canInteract, canAccess); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } return(verbs); }
private bool IsRCDStillValid(RCDComponent rcd, AfterInteractEvent eventArgs, IMapGrid mapGrid, TileRef tile, RcdMode startingMode) { //Less expensive checks first. Failing those ones, we need to check that the tile isn't obstructed. if (rcd.CurrentAmmo <= 0) { _popup.PopupEntity(Loc.GetString("rcd-component-no-ammo-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return(false); } if (rcd.Mode != startingMode) { return(false); } var unobstructed = eventArgs.Target == null ? _interactionSystem.InRangeUnobstructed(eventArgs.User, mapGrid.GridTileToWorld(tile.GridIndices), popup : true) : _interactionSystem.InRangeUnobstructed(eventArgs.User, eventArgs.Target.Value, popup: true); if (!unobstructed) { return(false); } switch (rcd.Mode) { //Floor mode just needs the tile to be a space tile (subFloor) case RcdMode.Floors: if (!tile.Tile.IsEmpty) { _popup.PopupEntity(Loc.GetString("rcd-component-cannot-build-floor-tile-not-empty-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return(false); } return(true); //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.Tile.IsEmpty) { return(false); } //They tried to decon a turf but the turf is blocked if (eventArgs.Target == null && tile.IsBlockedTurf(true)) { _popup.PopupEntity(Loc.GetString("rcd-component-tile-obstructed-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return(false); } //They tried to decon a non-turf but it's not in the whitelist if (eventArgs.Target != null && !_tagSystem.HasTag(eventArgs.Target.Value, "RCDDeconstructWhitelist")) { _popup.PopupEntity(Loc.GetString("rcd-component-deconstruct-target-not-on-whitelist-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return(false); } return(true); //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: if (tile.Tile.IsEmpty) { _popup.PopupEntity(Loc.GetString("rcd-component-cannot-build-wall-tile-not-empty-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return(false); } if (tile.IsBlockedTurf(true)) { _popup.PopupEntity(Loc.GetString("rcd-component-tile-obstructed-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return(false); } return(true); case RcdMode.Airlock: if (tile.Tile.IsEmpty) { _popup.PopupEntity(Loc.GetString("rcd-component-cannot-build-airlock-tile-not-empty-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return(false); } if (tile.IsBlockedTurf(true)) { _popup.PopupEntity(Loc.GetString("rcd-component-tile-obstructed-message"), rcd.Owner, Filter.Entities(eventArgs.User)); return(false); } return(true); default: return(false); //I don't know why this would happen, but sure I guess. Get out of here invalid state! } }
private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink) { // cannot stack do-afters if (drink.CancelToken != null) { drink.CancelToken.Cancel(); drink.CancelToken = null; return(true); } if (!EntityManager.HasComponent <SharedBodyComponent>(target)) { return(false); } if (!drink.Opened) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-not-open", ("owner", EntityManager.GetComponent <MetaDataComponent>(drink.Owner).EntityName)), drink.Owner, Filter.Entities(user)); return(true); } if (!_solutionContainerSystem.TryGetDrainableSolution(drink.Owner, out var drinkSolution) || drinkSolution.DrainAvailable <= 0) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-is-empty", ("entity", EntityManager.GetComponent <MetaDataComponent>(drink.Owner).EntityName)), drink.Owner, Filter.Entities(user)); return(true); } if (_foodSystem.IsMouthBlocked(target, user)) { return(true); } if (!_interactionSystem.InRangeUnobstructed(user, drink.Owner, popup: true)) { return(true); } var forceDrink = user != target; if (forceDrink) { EntityManager.TryGetComponent(user, out MetaDataComponent? meta); var userName = meta?.EntityName ?? string.Empty; _popupSystem.PopupEntity(Loc.GetString("drink-component-force-feed", ("user", userName)), user, Filter.Entities(target)); // logging _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to drink {ToPrettyString(drink.Owner):drink} {SolutionContainerSystem.ToPrettyString(drinkSolution)}"); } drink.CancelToken = new CancellationTokenSource(); _doAfterSystem.DoAfter(new DoAfterEventArgs(user, forceDrink ? drink.ForceFeedDelay : drink.Delay, drink.CancelToken.Token, target) { BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true, BreakOnTargetMove = true, MovementThreshold = 0.01f, TargetFinishedEvent = new DrinkEvent(user, drink, drinkSolution), BroadcastCancelledEvent = new DrinkCancelledEvent(drink), NeedHand = true, }); return(true); }
public bool TryFeed(EntityUid user, EntityUid target, FoodComponent food) { // if currently being used to feed, cancel that action. if (food.CancelToken != null) { food.CancelToken.Cancel(); food.CancelToken = null; return(true); } if (food.Owner == user || //Suppresses self-eating EntityManager.TryGetComponent <MobStateComponent>(food.Owner, out var mobState) && mobState.IsAlive()) // Suppresses eating alive mobs { return(false); } // Target can't be fed if (!EntityManager.HasComponent <SharedBodyComponent>(target)) { return(false); } if (!_solutionContainerSystem.TryGetSolution(food.Owner, food.SolutionName, out var foodSolution)) { return(false); } if (food.UsesRemaining <= 0) { _popupSystem.PopupEntity(Loc.GetString("food-system-try-use-food-is-empty", ("entity", food.Owner)), user, Filter.Entities(user)); DeleteAndSpawnTrash(food, user); return(false); } if (IsMouthBlocked(target, user)) { return(false); } if (!TryGetRequiredUtensils(user, food, out var utensils)) { return(false); } if (!_interactionSystem.InRangeUnobstructed(user, food.Owner, popup: true)) { return(true); } var forceFeed = user != target; food.CancelToken = new CancellationTokenSource(); if (forceFeed) { EntityManager.TryGetComponent(user, out MetaDataComponent? meta); var userName = meta?.EntityName ?? string.Empty; _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)), user, Filter.Entities(target)); // logging _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to eat {ToPrettyString(food.Owner):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}"); } var moveBreak = user != target; _doAfterSystem.DoAfter(new DoAfterEventArgs(user, forceFeed ? food.ForceFeedDelay : food.Delay, food.CancelToken.Token, target) { BreakOnUserMove = moveBreak, BreakOnDamage = true, BreakOnStun = true, BreakOnTargetMove = moveBreak, MovementThreshold = 0.01f, TargetFinishedEvent = new FeedEvent(user, food, foodSolution, utensils), BroadcastCancelledEvent = new ForceFeedCancelledEvent(food), NeedHand = true, }); return(true); }