public void Activate(ActivateEventArgs eventArgs) { if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) { return; } if (!Powered) { return; } if (!ActionBlockerSystem.CanInteract(actor.playerSession.AttachedEntity)) { return; } if (_wiresComponent?.IsPanelOpen == true) { _wiresComponent.OpenInterface(actor.playerSession); } else { UserInterface?.Toggle(actor.playerSession); } }
private void OnHandleState(EntityUid uid, PilotComponent component, ref ComponentHandleState args) { if (args.Current is not PilotComponentState state) { return; } var console = state.Console.GetValueOrDefault(); if (!console.IsValid()) { component.Console = null; return; } if (!TryComp <ShuttleConsoleComponent>(console, out var shuttleConsoleComponent)) { Logger.Warning($"Unable to set Helmsman console to {console}"); return; } component.Console = shuttleConsoleComponent; ActionBlockerSystem.UpdateCanMove(uid); }
protected override void GetData(IEntity user, BaseCharger component, VerbData data) { if (!ActionBlockerSystem.CanInteract(user)) { data.Visibility = VerbVisibility.Invisible; return; } if (!user.TryGetComponent(out HandsComponent? handsComponent)) { data.Visibility = VerbVisibility.Invisible; return; } if (component._container.ContainedEntity != null || handsComponent.GetActiveHand == null) { data.Visibility = VerbVisibility.Invisible; return; } var heldItemName = Loc.GetString(handsComponent.GetActiveHand.Owner.Name); data.Text = Loc.GetString("Insert {0}", heldItemName); data.IconTexture = "/Textures/Interface/VerbIcons/insert.svg.192dpi.png"; }
protected override void GetData(IEntity user, PowerCellSlotComponent component, VerbData data) { if (!component.ShowVerb || !ActionBlockerSystem.CanInteract(user)) { data.Visibility = VerbVisibility.Invisible; return; } if (component.Cell == null) { data.Text = Loc.GetString("No cell"); data.Visibility = VerbVisibility.Disabled; } else { data.Text = Loc.GetString("Eject cell"); data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png"; } if (component.Cell == null || !component.CanRemoveCell) { data.Visibility = VerbVisibility.Disabled; } }
/// <summary> /// Checks whether an item can be put in the specified slot. /// </summary> /// <param name="slot">The slot to check for.</param> /// <param name="item">The item to check for.</param> /// <param name="reason">The translated reason why the item cannot be equiped, if this function returns false. Can be null.</param> /// <returns>True if the item can be inserted into the specified slot.</returns> public bool CanEquip(Slots slot, ItemComponent item, out string reason) { var pass = false; reason = null; if (!ActionBlockerSystem.CanEquip(Owner)) { return(false); } if (item is ClothingComponent clothing) { if (clothing.SlotFlags != SlotFlags.PREVENTEQUIP && (clothing.SlotFlags & SlotMasks[slot]) != 0) { pass = true; } else { reason = Loc.GetString("This doesn't fit."); } } if (Owner.TryGetComponent(out IInventoryController controller)) { pass = controller.CanEquip(slot, item.Owner, pass, out var controllerReason); reason = controllerReason ?? reason; } if (!pass && reason == null) { reason = Loc.GetString("You can't equip this!"); } return(pass && SlotContainers[slot].CanInsert(item.Owner)); }
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage obj) { switch (obj.Message) { case BlockGameMessages.BlockGameUserUnregisterMessage unregisterMessage: UnRegisterPlayerSession(obj.Session); break; case BlockGameMessages.BlockGamePlayerActionMessage playerActionMessage: if (obj.Session != _player) { break; } if (!ActionBlockerSystem.CanInteract(Owner)) { DeactivePlayer(obj.Session); break; } if (playerActionMessage.PlayerAction == BlockGamePlayerAction.NewGame) { if (_game?.Started == true) { _game = new BlockGame(this); } _game?.StartGame(); } else { _game?.ProcessInput(playerActionMessage.PlayerAction); } break; } }
protected override void GetData(IEntity user, SolutionContainerComponent component, VerbData data) { if (!ActionBlockerSystem.CanInteract(user) || !user.TryGetComponent <HandsComponent>(out var hands) || hands.GetActiveHand == null || hands.GetActiveHand.Owner == component.Owner || !hands.GetActiveHand.Owner.TryGetComponent <SolutionContainerComponent>(out var solution) || !solution.CanAddSolutions || !component.CanRemoveSolutions) { data.Visibility = VerbVisibility.Invisible; return; } var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? "<Item>"; var myName = component.Owner.Prototype?.Name ?? "<Item>"; var locHeldEntityName = Loc.GetString(heldEntityName); var locMyName = Loc.GetString(myName); data.Visibility = VerbVisibility.Visible; data.Text = Loc.GetString("Transfer liquid from [{0}] to [{1}].", locMyName, locHeldEntityName); return; }
async Task <bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { if (eventArgs.Target == null) { return(false); } if (!eventArgs.Target.TryGetComponent(out IDamageableComponent damageable)) { return(true); } if (!ActionBlockerSystem.CanInteract(eventArgs.User)) { return(true); } if (eventArgs.User != eventArgs.Target && !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) { return(true); } if (Owner.TryGetComponent(out StackComponent stack) && !stack.Use(1)) { return(true); } foreach (var(type, amount) in Heal) { damageable.ChangeDamage(type, -amount, true); } return(true); }
private bool TryBuckle(IEntity user, IEntity to) { if (user == null || user == to) { return(false); } if (!ActionBlockerSystem.CanInteract(user)) { _notifyManager.PopupMessage(user, user, Loc.GetString("You can't do that!")); return(false); } var strapPosition = Owner.Transform.MapPosition; var range = SharedInteractionSystem.InteractionRange / 2; if (!InteractionChecks.InRangeUnobstructed(user, strapPosition, range)) { _notifyManager.PopupMessage(user, user, Loc.GetString("You can't reach there!")); return(false); } if (!user.TryGetComponent(out HandsComponent hands)) { _notifyManager.PopupMessage(user, user, Loc.GetString("You don't have hands!")); return(false); } if (hands.GetActiveHand != null) { _notifyManager.PopupMessage(user, user, Loc.GetString("Your hand isn't free!")); return(false); } if (BuckledTo != null) { _notifyManager.PopupMessage(Owner, user, Loc.GetString(Owner == user ? "You are already buckled in!" : "{0:They} are already buckled in!", Owner)); return(false); } if (!to.TryGetComponent(out StrapComponent strap)) { _notifyManager.PopupMessage(Owner, user, Loc.GetString(Owner == user ? "You can't buckle yourself there!" : "You can't buckle {0:them} there!", Owner)); return(false); } var parent = to.Transform.Parent; while (parent != null) { if (parent == user.Transform) { _notifyManager.PopupMessage(Owner, user, Loc.GetString(Owner == user ? "You can't buckle yourself there!" : "You can't buckle {0:them} there!", Owner)); return(false); } parent = parent.Parent; } if (!strap.HasSpace(this)) { _notifyManager.PopupMessage(Owner, user, Loc.GetString(Owner == user ? "You can't fit there!" : "{0:They} can't fit there!", Owner)); return(false); } _entitySystem.GetEntitySystem <AudioSystem>() .PlayFromEntity(strap.BuckleSound, Owner); if (!strap.TryAdd(this)) { _notifyManager.PopupMessage(Owner, user, Loc.GetString(Owner == user ? "You can't buckle yourself there!" : "You can't buckle {0:them} there!", Owner)); return(false); } BuckledTo = strap; if (Owner.TryGetComponent(out AppearanceComponent appearance)) { appearance.SetData(BuckleVisuals.Buckled, true); } var ownTransform = Owner.Transform; var strapTransform = strap.Owner.Transform; ownTransform.GridPosition = strapTransform.GridPosition; ownTransform.AttachParent(strapTransform); switch (strap.Position) { case StrapPosition.None: ownTransform.WorldRotation = strapTransform.WorldRotation; break; case StrapPosition.Stand: StandingStateHelper.Standing(Owner); ownTransform.WorldRotation = strapTransform.WorldRotation; break; case StrapPosition.Down: StandingStateHelper.Down(Owner); ownTransform.WorldRotation = Angle.South; break; } BuckleStatus(); return(true); }
bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs) { if (!ActionBlockerSystem.CanInteract(eventArgs.User)) { _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't do that!")); return(false); } if (eventArgs.User == eventArgs.Dropped) // user is dragging themselves onto a climbable { if (!eventArgs.User.HasComponent <ClimbingComponent>()) { _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are incapable of climbing!")); return(false); } var bodyManager = eventArgs.User.GetComponent <BodyManagerComponent>(); if (bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Leg).Count == 0 || bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Foot).Count == 0) { _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are unable to climb!")); return(false); } var userPosition = eventArgs.User.Transform.MapPosition; var climbablePosition = eventArgs.Target.Transform.MapPosition; var interaction = EntitySystem.Get <SharedInteractionSystem>(); bool Ignored(IEntity entity) => (entity == eventArgs.Target || entity == eventArgs.User); if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored)) { _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't reach there!")); return(false); } } else // user is dragging some other entity onto a climbable { if (eventArgs.Target == null || !eventArgs.Dropped.HasComponent <ClimbingComponent>()) { _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't do that!")); return(false); } var userPosition = eventArgs.User.Transform.MapPosition; var otherUserPosition = eventArgs.Dropped.Transform.MapPosition; var climbablePosition = eventArgs.Target.Transform.MapPosition; var interaction = EntitySystem.Get <SharedInteractionSystem>(); bool Ignored(IEntity entity) => (entity == eventArgs.Target || entity == eventArgs.User || entity == eventArgs.Dropped); if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored) || !interaction.InRangeUnobstructed(userPosition, otherUserPosition, _range, predicate: Ignored)) { _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't reach there!")); return(false); } } return(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; }
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 (!ActionBlockerSystem.CanInteract(user)) { return(false); } if (item == null) { _notifyManager.PopupMessageCursor(user, Loc.GetString("You aren't holding anything!")); return(false); } if (!userHands.CanDrop(userHands.ActiveHand !)) { _notifyManager.PopupMessageCursor(user, Loc.GetString("You can't drop that!")); return(false); } if (!inventory.HasSlot(slot)) { return(false); } if (inventory.TryGetSlotItem(slot, out ItemComponent _)) { _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} already {0:have} something there!", Owner)); return(false); } if (!inventory.CanEquip(slot, item, false)) { _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} cannot equip 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(item !.Owner, false); inventory.Equip(slot, item !.Owner, false); UpdateSubscribed(); }
/// <summary> /// Process thermal regulation /// </summary> /// <param name="frameTime"></param> private void ProcessThermalRegulation(float frameTime) { if (!Owner.TryGetComponent(out TemperatureComponent temperatureComponent)) { return; } temperatureComponent.ReceiveHeat(MetabolismHeat); temperatureComponent.RemoveHeat(RadiatedHeat); // implicit heat regulation var tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - NormalBodyTemperature); var targetHeat = tempDiff * temperatureComponent.HeatCapacity; if (temperatureComponent.CurrentTemperature > NormalBodyTemperature) { temperatureComponent.RemoveHeat(Math.Min(targetHeat, ImplicitHeatRegulation)); } else { temperatureComponent.ReceiveHeat(Math.Min(targetHeat, ImplicitHeatRegulation)); } // recalc difference and target heat tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - NormalBodyTemperature); targetHeat = tempDiff * temperatureComponent.HeatCapacity; // if body temperature is not within comfortable, thermal regulation // processes starts if (tempDiff < ThermalRegulationTemperatureThreshold) { if (_isShivering || _isSweating) { Owner.PopupMessage(Loc.GetString("You feel comfortable")); } _isShivering = false; _isSweating = false; return; } if (temperatureComponent.CurrentTemperature > NormalBodyTemperature) { if (!ActionBlockerSystem.CanSweat(Owner)) { return; } if (!_isSweating) { Owner.PopupMessage(Loc.GetString("You are sweating")); _isSweating = true; } // creadth: sweating does not help in airless environment if (Owner.Transform.Coordinates.TryGetTileAir(out _, _entityManager)) { temperatureComponent.RemoveHeat(Math.Min(targetHeat, SweatHeatRegulation)); } } else { if (!ActionBlockerSystem.CanShiver(Owner)) { return; } if (!_isShivering) { Owner.PopupMessage(Loc.GetString("You are shivering")); _isShivering = true; } temperatureComponent.ReceiveHeat(Math.Min(targetHeat, ShiveringHeatRegulation)); } }
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); if (item != null) { userHands.PutInHandOrDrop(item); } UpdateSubscribed(); }
public async Task BuckleUnbuckleCooldownRangeTest() { var cOptions = new ClientIntegrationOptions { ExtraPrototypes = Prototypes }; var sOptions = new ServerIntegrationOptions { ExtraPrototypes = Prototypes }; var(client, server) = await StartConnectedServerClientPair(cOptions, sOptions); IEntity human = null; IEntity chair = null; BuckleComponent buckle = null; StrapComponent strap = null; await server.WaitAssertion(() => { var mapManager = IoCManager.Resolve <IMapManager>(); var entityManager = IoCManager.Resolve <IEntityManager>(); var gridId = new GridId(1); var grid = mapManager.GetGrid(gridId); var coordinates = grid.GridEntityId.ToCoordinates(); human = entityManager.SpawnEntity(BuckleDummyId, coordinates); chair = entityManager.SpawnEntity(StrapDummyId, coordinates); // Default state, unbuckled Assert.True(human.TryGetComponent(out buckle)); Assert.NotNull(buckle); Assert.Null(buckle.BuckledTo); Assert.False(buckle.Buckled); Assert.True(ActionBlockerSystem.CanMove(human)); Assert.True(ActionBlockerSystem.CanChangeDirection(human)); Assert.True(EffectBlockerSystem.CanFall(human)); // Default state, no buckled entities, strap Assert.True(chair.TryGetComponent(out strap)); Assert.NotNull(strap); Assert.IsEmpty(strap.BuckledEntities); Assert.Zero(strap.OccupiedSize); // Side effects of buckling Assert.True(buckle.TryBuckle(human, chair)); Assert.NotNull(buckle.BuckledTo); Assert.True(buckle.Buckled); var player = IoCManager.Resolve <IPlayerManager>().GetAllPlayers().Single(); Assert.True(((BuckleComponentState)buckle.GetComponentState(player)).Buckled); Assert.False(ActionBlockerSystem.CanMove(human)); Assert.False(ActionBlockerSystem.CanChangeDirection(human)); Assert.False(EffectBlockerSystem.CanFall(human)); Assert.That(human.Transform.WorldPosition, Is.EqualTo(chair.Transform.WorldPosition)); // Side effects of buckling for the strap Assert.That(strap.BuckledEntities, Does.Contain(human)); Assert.That(strap.OccupiedSize, Is.EqualTo(buckle.Size)); Assert.Positive(strap.OccupiedSize); // Trying to buckle while already buckled fails Assert.False(buckle.TryBuckle(human, chair)); // Trying to unbuckle too quickly fails Assert.False(buckle.TryUnbuckle(human)); Assert.False(buckle.ToggleBuckle(human, chair)); Assert.True(buckle.Buckled); }); // Wait enough ticks for the unbuckling cooldown to run out await server.WaitRunTicks(60); await server.WaitAssertion(() => { // Still buckled Assert.True(buckle.Buckled); // Unbuckle Assert.True(buckle.TryUnbuckle(human)); Assert.Null(buckle.BuckledTo); Assert.False(buckle.Buckled); Assert.True(ActionBlockerSystem.CanMove(human)); Assert.True(ActionBlockerSystem.CanChangeDirection(human)); Assert.True(EffectBlockerSystem.CanFall(human)); // Unbuckle, strap Assert.IsEmpty(strap.BuckledEntities); Assert.Zero(strap.OccupiedSize); // Re-buckling has no cooldown Assert.True(buckle.TryBuckle(human, chair)); Assert.True(buckle.Buckled); // On cooldown Assert.False(buckle.TryUnbuckle(human)); Assert.True(buckle.Buckled); Assert.False(buckle.ToggleBuckle(human, chair)); Assert.True(buckle.Buckled); Assert.False(buckle.ToggleBuckle(human, chair)); Assert.True(buckle.Buckled); }); // Wait enough ticks for the unbuckling cooldown to run out await server.WaitRunTicks(60); await server.WaitAssertion(() => { // Still buckled Assert.True(buckle.Buckled); // Unbuckle Assert.True(buckle.TryUnbuckle(human)); Assert.False(buckle.Buckled); // Move away from the chair human.Transform.WorldPosition += (1000, 1000); // Out of range Assert.False(buckle.TryBuckle(human, chair)); Assert.False(buckle.TryUnbuckle(human)); Assert.False(buckle.ToggleBuckle(human, chair)); // Move near the chair human.Transform.WorldPosition = chair.Transform.WorldPosition + (0.5f, 0); // In range Assert.True(buckle.TryBuckle(human, chair)); Assert.True(buckle.Buckled); Assert.False(buckle.TryUnbuckle(human)); Assert.True(buckle.Buckled); Assert.False(buckle.ToggleBuckle(human, chair)); Assert.True(buckle.Buckled); // Force unbuckle Assert.True(buckle.TryUnbuckle(human, true)); Assert.False(buckle.Buckled); Assert.True(ActionBlockerSystem.CanMove(human)); Assert.True(ActionBlockerSystem.CanChangeDirection(human)); Assert.True(EffectBlockerSystem.CanFall(human)); // Re-buckle Assert.True(buckle.TryBuckle(human, chair)); // Move away from the chair human.Transform.WorldPosition += (1, 0); }); await server.WaitRunTicks(1); await server.WaitAssertion(() => { // No longer buckled Assert.False(buckle.Buckled); Assert.Null(buckle.BuckledTo); Assert.IsEmpty(strap.BuckledEntities); }); }
public async Task <bool> InteractUsing(InteractUsingEventArgs eventArgs) { var user = eventArgs.User; var usingItem = eventArgs.Using; if (usingItem == null || usingItem.Deleted || !ActionBlockerSystem.CanInteract(user)) { return(false); } if (usingItem.TryGetComponent(out SeedComponent? seeds)) { if (Seed == null) { if (seeds.Seed == null) { user.PopupMessageCursor(Loc.GetString("The packet seems to be empty. You throw it away.")); usingItem.Delete(); return(false); } user.PopupMessageCursor(Loc.GetString("You plant the {0} {1}.", seeds.Seed.SeedName, seeds.Seed.SeedNoun)); Seed = seeds.Seed; Dead = false; Age = 1; Health = Seed.Endurance; _lastCycle = _gameTiming.CurTime; usingItem.Delete(); CheckLevelSanity(); UpdateSprite(); return(true); } user.PopupMessageCursor(Loc.GetString("The {0} already has seeds in it!", Owner.Name)); return(false); } if (usingItem.HasComponent <HoeComponent>()) { if (WeedLevel > 0) { user.PopupMessageCursor(Loc.GetString("You remove the weeds from the {0}.", Owner.Name)); user.PopupMessageOtherClients(Loc.GetString("{0} starts uprooting the weeds.", user.Name)); WeedLevel = 0; UpdateSprite(); } else { user.PopupMessageCursor(Loc.GetString("This plot is devoid of weeds! It doesn't need uprooting.")); } return(true); } if (usingItem.TryGetComponent(out SolutionContainerComponent? solution) && solution.CanRemoveSolutions) { var amount = 5f; var sprayed = false; if (usingItem.TryGetComponent(out SprayComponent? spray)) { sprayed = true; amount = 1f; EntitySystem.Get <AudioSystem>().PlayFromEntity(spray.SpraySound, usingItem, AudioHelpers.WithVariation(0.125f)); } var chemAmount = ReagentUnit.New(amount); var split = solution.Solution.SplitSolution(chemAmount <= solution.Solution.TotalVolume ? chemAmount : solution.Solution.TotalVolume); user.PopupMessageCursor(Loc.GetString(sprayed ? $"You spray {Owner.Name} with {usingItem.Name}." : $"You transfer {split.TotalVolume.ToString()}u to {Owner.Name}")); _solutionContainer?.TryAddSolution(split); SkipAging++; // We're forcing an update cycle, so one age hasn't passed. ForceUpdate = true; Update(); return(true); } if (usingItem.HasComponent <PlantSampleTakerComponent>()) { if (Seed == null) { user.PopupMessageCursor(Loc.GetString("There is nothing to take a sample of!")); return(false); } if (Sampled) { user.PopupMessageCursor(Loc.GetString("This plant has already been sampled.")); return(false); } if (Dead) { user.PopupMessageCursor(Loc.GetString("This plant is dead.")); return(false); } var seed = Seed.SpawnSeedPacket(user.Transform.Coordinates); seed.RandomOffset(0.25f); user.PopupMessageCursor(Loc.GetString($"You take a sample from the {Seed.DisplayName}.")); Health -= (_random.Next(3, 5) * 10); if (_random.Prob(0.3f)) { Sampled = true; } // Just in case. CheckLevelSanity(); SkipAging++; // We're forcing an update cycle, so one age hasn't passed. ForceUpdate = true; Update(); return(true); } if (usingItem.HasComponent <BotanySharpComponent>()) { return(DoHarvest(user)); } return(false); }
private bool UserCanFire(IEntity user) { return((UserCanFireHandler == null || UserCanFireHandler(user)) && ActionBlockerSystem.CanAttack(user)); }
public async Task BuckleUnbuckleCooldownRangeTest() { var server = StartServerDummyTicker(); IEntity human = null; IEntity chair = null; BuckleComponent buckle = null; StrapComponent strap = null; await server.WaitAssertion(() => { var mapManager = IoCManager.Resolve <IMapManager>(); mapManager.CreateNewMapEntity(MapId.Nullspace); var entityManager = IoCManager.Resolve <IEntityManager>(); human = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace); chair = entityManager.SpawnEntity("ChairWood", MapCoordinates.Nullspace); // Default state, unbuckled Assert.True(human.TryGetComponent(out buckle)); Assert.NotNull(buckle); Assert.Null(buckle.BuckledTo); Assert.False(buckle.Buckled); Assert.True(ActionBlockerSystem.CanMove(human)); Assert.True(ActionBlockerSystem.CanChangeDirection(human)); Assert.True(EffectBlockerSystem.CanFall(human)); // Default state, no buckled entities, strap Assert.True(chair.TryGetComponent(out strap)); Assert.NotNull(strap); Assert.IsEmpty(strap.BuckledEntities); Assert.Zero(strap.OccupiedSize); // Side effects of buckling Assert.True(buckle.TryBuckle(human, chair)); Assert.NotNull(buckle.BuckledTo); Assert.True(buckle.Buckled); Assert.True(((BuckleComponentState)buckle.GetComponentState()).Buckled); Assert.False(ActionBlockerSystem.CanMove(human)); Assert.False(ActionBlockerSystem.CanChangeDirection(human)); Assert.False(EffectBlockerSystem.CanFall(human)); Assert.That(human.Transform.WorldPosition, Is.EqualTo(chair.Transform.WorldPosition)); // Side effects of buckling for the strap Assert.That(strap.BuckledEntities, Does.Contain(human)); Assert.That(strap.OccupiedSize, Is.EqualTo(buckle.Size)); Assert.Positive(strap.OccupiedSize); // Trying to buckle while already buckled fails Assert.False(buckle.TryBuckle(human, chair)); // Trying to unbuckle too quickly fails Assert.False(buckle.TryUnbuckle(human)); Assert.False(buckle.ToggleBuckle(human, chair)); Assert.True(buckle.Buckled); }); // Wait enough ticks for the unbuckling cooldown to run out await server.WaitRunTicks(60); await server.WaitAssertion(() => { // Still buckled Assert.True(buckle.Buckled); // Unbuckle Assert.True(buckle.TryUnbuckle(human)); Assert.Null(buckle.BuckledTo); Assert.False(buckle.Buckled); Assert.True(ActionBlockerSystem.CanMove(human)); Assert.True(ActionBlockerSystem.CanChangeDirection(human)); Assert.True(EffectBlockerSystem.CanFall(human)); // Unbuckle, strap Assert.IsEmpty(strap.BuckledEntities); Assert.Zero(strap.OccupiedSize); // Re-buckling has no cooldown Assert.True(buckle.TryBuckle(human, chair)); Assert.True(buckle.Buckled); // On cooldown Assert.False(buckle.TryUnbuckle(human)); Assert.True(buckle.Buckled); Assert.False(buckle.ToggleBuckle(human, chair)); Assert.True(buckle.Buckled); Assert.False(buckle.ToggleBuckle(human, chair)); Assert.True(buckle.Buckled); }); // Wait enough ticks for the unbuckling cooldown to run out await server.WaitRunTicks(60); await server.WaitAssertion(() => { // Still buckled Assert.True(buckle.Buckled); // Unbuckle Assert.True(buckle.TryUnbuckle(human)); Assert.False(buckle.Buckled); // Move away from the chair human.Transform.WorldPosition += (1000, 1000); // Out of range Assert.False(buckle.TryBuckle(human, chair)); Assert.False(buckle.TryUnbuckle(human)); Assert.False(buckle.ToggleBuckle(human, chair)); // Move near the chair human.Transform.WorldPosition = chair.Transform.WorldPosition + (0.5f, 0); // In range Assert.True(buckle.TryBuckle(human, chair)); Assert.True(buckle.Buckled); Assert.False(buckle.TryUnbuckle(human)); Assert.True(buckle.Buckled); Assert.False(buckle.ToggleBuckle(human, chair)); Assert.True(buckle.Buckled); // Force unbuckle Assert.True(buckle.TryUnbuckle(human, true)); Assert.False(buckle.Buckled); Assert.True(ActionBlockerSystem.CanMove(human)); Assert.True(ActionBlockerSystem.CanChangeDirection(human)); Assert.True(EffectBlockerSystem.CanFall(human)); // Re-buckle Assert.True(buckle.TryBuckle(human, chair)); // Move away from the chair human.Transform.WorldPosition += (1, 0); }); await server.WaitRunTicks(1); await server.WaitAssertion(() => { // No longer buckled Assert.False(buckle.Buckled); Assert.Null(buckle.BuckledTo); Assert.IsEmpty(strap.BuckledEntities); }); }
public bool CanBeStripped(IEntity by) { return(by != Owner && by.HasComponent <ISharedHandsComponent>() && ActionBlockerSystem.CanInteract(by)); }
public async void UserInteraction(IEntity user, EntityCoordinates coordinates, EntityUid clickedUid) { if (user.TryGetComponent(out CombatModeComponent? combatMode) && combatMode.IsInCombatMode) { DoAttack(user, coordinates, false, clickedUid); return; } if (!ValidateInteractAndFace(user, coordinates)) { return; } if (!ActionBlockerSystem.CanInteract(user)) { return; } // Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null EntityManager.TryGetEntity(clickedUid, out var target); // Check if interacted entity is in the same container, the direct child, or direct parent of the user. if (target != null && !user.IsInSameOrParentContainer(target)) { Logger.WarningS("system.interaction", $"User entity named {user.Name} clicked on object {target.Name} that isn't the parent, child, or in the same container"); return; } // Verify user has a hand, and find what object he is currently holding in his active hand if (!user.TryGetComponent <IHandsComponent>(out var hands)) { return; } var item = hands.GetActiveHand?.Owner; // TODO: Replace with body interaction range when we get something like arm length or telekinesis or something. var inRangeUnobstructed = user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true); if (target == null || !inRangeUnobstructed) { if (item == null) { return; } if (!await InteractUsingRanged(user, item, target, coordinates, inRangeUnobstructed) && !inRangeUnobstructed) { var message = Loc.GetString("You can't reach there!"); user.PopupMessage(message); } return; } else { // We are close to the nearby object and the object isn't contained in our active hand // InteractUsing/AfterInteract: We will either use the item on the nearby object if (item != null) { await InteractUsing(user, item, target, coordinates); } // InteractHand/Activate: Since our hand is empty we will use InteractHand/Activate else { InteractHand(user, target); } } }
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> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { var user = eventArgs.User; var usingItem = eventArgs.Using; if (usingItem == null || usingItem.Deleted || !ActionBlockerSystem.CanInteract(user)) { return(false); } if (usingItem.TryGetComponent(out SeedComponent? seeds)) { if (Seed == null) { if (seeds.Seed == null) { user.PopupMessageCursor(Loc.GetString("The packet seems to be empty. You throw it away.")); usingItem.Delete(); return(false); } user.PopupMessageCursor(Loc.GetString("You plant the {0} {1}.", seeds.Seed.SeedName, seeds.Seed.SeedNoun)); Seed = seeds.Seed; Dead = false; Age = 1; Health = Seed.Endurance; _lastCycle = _gameTiming.CurTime; usingItem.Delete(); CheckLevelSanity(); UpdateSprite(); return(true); } user.PopupMessageCursor(Loc.GetString("The {0} already has seeds in it!", Owner.Name)); return(false); } if (usingItem.HasTag("Hoe")) { if (WeedLevel > 0) { user.PopupMessageCursor(Loc.GetString("You remove the weeds from the {0}.", Owner.Name)); user.PopupMessageOtherClients(Loc.GetString("{0} starts uprooting the weeds.", user.Name)); WeedLevel = 0; UpdateSprite(); } else { user.PopupMessageCursor(Loc.GetString("This plot is devoid of weeds! It doesn't need uprooting.")); } return(true); } if (usingItem.HasTag("Shovel")) { if (Seed != null) { user.PopupMessageCursor(Loc.GetString("You remove the plant from the {0}.", Owner.Name)); user.PopupMessageOtherClients(Loc.GetString("{0} removes the plant.", user.Name)); RemovePlant(); } else { user.PopupMessageCursor(Loc.GetString("There is no plant to remove.")); } return(true); } if (usingItem.TryGetComponent(out ISolutionInteractionsComponent? solution) && solution.CanDrain) { var amount = ReagentUnit.New(5); var sprayed = false; if (usingItem.TryGetComponent(out SprayComponent? spray)) { sprayed = true; amount = ReagentUnit.New(1); if (!string.IsNullOrEmpty(spray.SpraySound)) { SoundSystem.Play(Filter.Pvs(usingItem), spray.SpraySound, usingItem, AudioHelpers.WithVariation(0.125f)); } } var split = solution.Drain(amount); if (split.TotalVolume == 0) { user.PopupMessageCursor(Loc.GetString("{0:TheName} is empty!", usingItem)); return(true); } user.PopupMessageCursor(Loc.GetString( sprayed ? "You spray {0:TheName}" : "You transfer {1}u to {0:TheName}", Owner, split.TotalVolume)); _solutionContainer?.TryAddSolution(split); ForceUpdateByExternalCause(); return(true); } if (usingItem.HasTag("PlantSampleTaker")) { if (Seed == null) { user.PopupMessageCursor(Loc.GetString("There is nothing to take a sample of!")); return(false); } if (Sampled) { user.PopupMessageCursor(Loc.GetString("This plant has already been sampled.")); return(false); } if (Dead) { user.PopupMessageCursor(Loc.GetString("This plant is dead.")); return(false); } var seed = Seed.SpawnSeedPacket(user.Transform.Coordinates); seed.RandomOffset(0.25f); user.PopupMessageCursor(Loc.GetString($"You take a sample from the {Seed.DisplayName}.")); Health -= (_random.Next(3, 5) * 10); if (_random.Prob(0.3f)) { Sampled = true; } // Just in case. CheckLevelSanity(); ForceUpdateByExternalCause(); return(true); } if (usingItem.HasTag("BotanySharp")) { return(DoHarvest(user)); } if (usingItem.HasComponent <ProduceComponent>()) { user.PopupMessageCursor(Loc.GetString("You compost {1:theName} into {0:theName}.", Owner, usingItem)); user.PopupMessageOtherClients(Loc.GetString("{0:TheName} composts {1:theName} into {2:theName}.", user, usingItem, Owner)); if (usingItem.TryGetComponent(out SolutionContainerComponent? solution2)) { // This deliberately discards overfill. _solutionContainer?.TryAddSolution(solution2.SplitSolution(solution2.Solution.TotalVolume)); ForceUpdateByExternalCause(); } usingItem.Delete(); return(true); } return(false); }
/// <summary> /// Throw an entity in the direction of <paramref name="targetLoc"/> from <paramref name="sourceLoc"/>. /// </summary> /// <param name="thrownEnt">The entity to throw.</param> /// <param name="throwForce"> /// The force to throw the entity with. /// Total impulse applied is equal to this force applied for one second. /// </param> /// <param name="targetLoc"> /// The target location to throw at. /// This is only used to calculate a direction, /// actual distance is purely determined by <paramref name="throwForce"/>. /// </param> /// <param name="sourceLoc"> /// The position to start the throw from. /// </param> /// <param name="spread"> /// If true, slightly spread the actual throw angle. /// </param> /// <param name="throwSourceEnt"> /// The entity that did the throwing. An opposite impulse will be applied to this entity if passed in. /// </param> public static void Throw(this IEntity thrownEnt, float throwForce, EntityCoordinates targetLoc, EntityCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null) { if (!thrownEnt.TryGetComponent(out IPhysicsComponent colComp)) { return; } var entityManager = IoCManager.Resolve <IEntityManager>(); colComp.CanCollide = true; // I can now collide with player, so that i can do damage. if (!thrownEnt.TryGetComponent(out ThrownItemComponent projComp)) { projComp = thrownEnt.AddComponent <ThrownItemComponent>(); if (colComp.PhysicsShapes.Count == 0) { colComp.PhysicsShapes.Add(new PhysShapeAabb()); } colComp.PhysicsShapes[0].CollisionMask |= (int)CollisionGroup.ThrownItem; colComp.Status = BodyStatus.InAir; } var angle = new Angle(targetLoc.ToMapPos(entityManager) - sourceLoc.ToMapPos(entityManager)); if (spread) { var spreadRandom = IoCManager.Resolve <IRobustRandom>(); angle += Angle.FromDegrees(spreadRandom.NextGaussian(0, 3)); } if (throwSourceEnt != null) { projComp.User = throwSourceEnt; projComp.IgnoreEntity(throwSourceEnt); if (ActionBlockerSystem.CanChangeDirection(throwSourceEnt)) { throwSourceEnt.Transform.LocalRotation = angle.GetCardinalDir().ToAngle(); } } // scaling is handled elsewhere, this is just multiplying by 60 independent of timing as a fix until elsewhere values are updated var spd = throwForce * 60; projComp.StartThrow(angle.ToVec(), spd); if (throwSourceEnt != null && throwSourceEnt.TryGetComponent <IPhysicsComponent>(out var physics)) { if (throwSourceEnt.IsWeightless()) { // We don't check for surrounding entities, // so you'll still get knocked around if you're hugging the station wall in zero g. // I got kinda lazy is the reason why. Also it makes a bit of sense. // If somebody wants they can come along and make it so magboots completely hold you still. // Would be a cool incentive to use them. const float throwFactor = 0.2f; // Break Newton's Third Law for better gameplay var mover = physics.EnsureController <ThrowKnockbackController>(); mover.Push(-angle.ToVec(), spd * throwFactor); } } }
public void DoAttack(IEntity user, EntityCoordinates coordinates, bool wideAttack, EntityUid targetUid = default) { if (!ValidateInteractAndFace(user, coordinates)) { return; } if (!ActionBlockerSystem.CanAttack(user)) { return; } IEntity?targetEnt = null; if (!wideAttack) { // Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null EntityManager.TryGetEntity(targetUid, out targetEnt); // Check if interacted entity is in the same container, the direct child, or direct parent of the user. if (targetEnt != null && !user.IsInSameOrParentContainer(targetEnt)) { Logger.WarningS("system.interaction", $"User entity named {user.Name} clicked on object {targetEnt.Name} that isn't the parent, child, or in the same container"); return; } // TODO: Replace with body attack range when we get something like arm length or telekinesis or something. if (!user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true)) { return; } } // Verify user has a hand, and find what object he is currently holding in his active hand if (user.TryGetComponent <IHandsComponent>(out var hands)) { var item = hands.GetActiveHand?.Owner; if (item != null) { if (wideAttack) { var ev = new WideAttackEvent(item, user, coordinates); RaiseLocalEvent(item.Uid, ev, false); if (ev.Handled) { return; } } else { var ev = new ClickAttackEvent(item, user, coordinates, targetUid); RaiseLocalEvent(item.Uid, ev, false); if (ev.Handled) { return; } } } else if (!wideAttack && (targetEnt != null || EntityManager.TryGetEntity(targetUid, out targetEnt)) && targetEnt.HasComponent <ItemComponent>()) { // We pick up items if our hand is empty, even if we're in combat mode. InteractHand(user, targetEnt); return; } } // TODO: Make this saner? // Attempt to do unarmed combat. We don't check for handled just because at this point it doesn't matter. if (wideAttack) { RaiseLocalEvent(user.Uid, new WideAttackEvent(user, user, coordinates), false); } else { RaiseLocalEvent(user.Uid, new ClickAttackEvent(user, user, coordinates, targetUid), false); } }