private async void HandleStartStructureConstruction(TryStartStructureConstructionMessage ev, EntitySessionEventArgs args) { if (!_prototypeManager.TryIndex(ev.PrototypeName, out ConstructionPrototype constructionPrototype)) { Logger.Error($"Tried to start construction of invalid recipe '{ev.PrototypeName}'!"); RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack)); return; } if (!_prototypeManager.TryIndex(constructionPrototype.Graph, out ConstructionGraphPrototype constructionGraph)) { Logger.Error($"Invalid construction graph '{constructionPrototype.Graph}' in recipe '{ev.PrototypeName}'!"); RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack)); return; } var startNode = constructionGraph.Nodes[constructionPrototype.StartNode]; var targetNode = constructionGraph.Nodes[constructionPrototype.TargetNode]; var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name); var user = args.SenderSession.AttachedEntity; if (_beingBuilt.TryGetValue(args.SenderSession, out var set)) { if (!set.Add(ev.Ack)) { user.PopupMessageCursor(Loc.GetString("You are already building that!")); return; } } else { var newSet = new HashSet <int> { ev.Ack }; _beingBuilt[args.SenderSession] = newSet; } foreach (var condition in constructionPrototype.Conditions) { if (!condition.Condition(user, ev.Location, ev.Angle.GetCardinalDir())) { Cleanup(); return; } } void Cleanup() { _beingBuilt[args.SenderSession].Remove(ev.Ack); } if (user == null || !ActionBlockerSystem.CanInteract(user) || !user.TryGetComponent(out HandsComponent? hands) || hands.GetActiveHand == null || !user.InRangeUnobstructed(ev.Location, ignoreInsideBlocker:constructionPrototype.CanBuildInImpassable)) { Cleanup(); return; } if (pathFind == null) { throw new InvalidDataException($"Can't find path from starting node to target node in construction! Recipe: {ev.PrototypeName}"); } var edge = startNode.GetEdge(pathFind[0].Name); if (edge == null) { throw new InvalidDataException($"Can't find edge from starting node to the next node in pathfinding! Recipe: {ev.PrototypeName}"); } var valid = false; var holding = hands.GetActiveHand?.Owner; if (holding == null) { Cleanup(); return; } // No support for conditions here! foreach (var step in edge.Steps) { switch (step) { case EntityInsertConstructionGraphStep entityInsert: if (entityInsert.EntityValid(holding)) { valid = true; } break; case ToolConstructionGraphStep _: case NestedConstructionGraphStep _: throw new InvalidDataException("Invalid first step for item recipe!"); } if (valid) { break; } } if (!valid) { Cleanup(); return; } var structure = await Construct(user, (ev.Ack + constructionPrototype.GetHashCode()).ToString(), constructionGraph, edge, targetNode); if (structure == null) { Cleanup(); return; } structure.Transform.Coordinates = ev.Location; structure.Transform.LocalRotation = constructionPrototype.CanRotate ? ev.Angle : Angle.South; RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack)); Cleanup(); }
private void UserInteraction(IEntity player, GridCoordinates coordinates, EntityUid clickedUid) { // Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null if (!EntityManager.TryGetEntity(clickedUid, out var attacked)) { attacked = null; } // Verify player has a transform component if (!player.TryGetComponent <ITransformComponent>(out var playerTransform)) { return; } // Verify player is on the same map as the entity he clicked on if (_mapManager.GetGrid(coordinates.GridID).ParentMapId != playerTransform.MapID) { Logger.WarningS("system.interaction", $"Player named {player.Name} clicked on a map he isn't located on"); return; } // Verify player has a hand, and find what object he is currently holding in his active hand if (!player.TryGetComponent <IHandsComponent>(out var hands)) { return; } var item = hands.GetActiveHand?.Owner; if (!ActionBlockerSystem.CanInteract(player)) { return; } playerTransform.LocalRotation = new Angle(coordinates.ToMapPos(_mapManager) - playerTransform.MapPosition.Position); // TODO: Check if client should be able to see that object to click on it in the first place // Clicked on empty space behavior, try using ranged attack if (attacked == null) { if (item != null) { // After attack: Check if we clicked on an empty location, if so the only interaction we can do is AfterAttack InteractAfterAttack(player, item, coordinates); } return; } // Verify attacked object is on the map if we managed to click on it somehow if (!attacked.Transform.IsMapTransform) { Logger.WarningS("system.interaction", $"Player named {player.Name} clicked on object {attacked.Name} that isn't currently on the map somehow"); return; } // Check if ClickLocation is in object bounds here, if not lets log as warning and see why if (attacked.TryGetComponent(out ICollidableComponent collideComp)) { if (!collideComp.WorldAABB.Contains(coordinates.ToMapPos(_mapManager))) { Logger.WarningS("system.interaction", $"Player {player.Name} clicked {attacked.Name} outside of its bounding box component somehow"); return; } } // RangedAttack/AfterAttack: Check distance between user and clicked item, if too large parse it in the ranged function // TODO: have range based upon the item being used? or base it upon some variables of the player himself? var distance = (playerTransform.WorldPosition - attacked.Transform.WorldPosition).LengthSquared; if (distance > InteractionRangeSquared) { if (item != null) { RangedInteraction(player, item, attacked, coordinates); return; } return; // Add some form of ranged AttackHand here if you need it someday, or perhaps just ways to modify the range of AttackHand } // We are close to the nearby object and the object isn't contained in our active hand // AttackBy/AfterAttack: We will either use the item on the nearby object if (item != null) { Interaction(player, item, attacked, coordinates); } // AttackHand/Activate: Since our hand is empty we will use AttackHand/Activate else { Interaction(player, attacked); } }
private async void HandleStartItemConstruction(TryStartItemConstructionMessage ev, EntitySessionEventArgs args) { if (!_prototypeManager.TryIndex(ev.PrototypeName, out ConstructionPrototype constructionPrototype)) { Logger.Error($"Tried to start construction of invalid recipe '{ev.PrototypeName}'!"); return; } if (!_prototypeManager.TryIndex(constructionPrototype.Graph, out ConstructionGraphPrototype constructionGraph)) { Logger.Error($"Invalid construction graph '{constructionPrototype.Graph}' in recipe '{ev.PrototypeName}'!"); return; } var startNode = constructionGraph.Nodes[constructionPrototype.StartNode]; var targetNode = constructionGraph.Nodes[constructionPrototype.TargetNode]; var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name); var user = args.SenderSession.AttachedEntity; if (user == null || !ActionBlockerSystem.CanInteract(user)) { return; } if (!user.TryGetComponent(out HandsComponent? hands)) { return; } foreach (var condition in constructionPrototype.Conditions) { if (!condition.Condition(user, user.ToCoordinates(), Direction.South)) { return; } } if (pathFind == null) { throw new InvalidDataException($"Can't find path from starting node to target node in construction! Recipe: {ev.PrototypeName}"); } var edge = startNode.GetEdge(pathFind[0].Name); if (edge == null) { throw new InvalidDataException($"Can't find edge from starting node to the next node in pathfinding! Recipe: {ev.PrototypeName}"); } // No support for conditions here! foreach (var step in edge.Steps) { switch (step) { case ToolConstructionGraphStep _: case NestedConstructionGraphStep _: throw new InvalidDataException("Invalid first step for construction recipe!"); } } var item = await Construct(user, "item_construction", constructionGraph, edge, targetNode); if (item != null && item.TryGetComponent(out ItemComponent? itemComp)) { hands.PutInHandOrDrop(itemComp); } }
private void UserInteraction(IEntity player, GridCoordinates coordinates, EntityUid clickedUid) { //Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null if (!EntityManager.TryGetEntity(clickedUid, out var attacked)) { attacked = null; } //Verify player has a transform component if (!player.TryGetComponent <ITransformComponent>(out var playerTransform)) { return; } //Verify player is on the same map as the entity he clicked on else if (coordinates.MapID != playerTransform.MapID) { Logger.Warning(string.Format("Player named {0} clicked on a map he isn't located on", player.Name)); return; } //Verify player has a hand, and find what object he is currently holding in his active hand if (!player.TryGetComponent <IHandsComponent>(out var hands)) { return; } var item = hands.GetActiveHand?.Owner; if (!ActionBlockerSystem.CanInteract(player)) { return; } //TODO: Check if client should be able to see that object to click on it in the first place, prevent using locaters by firing a laser or something //Clicked on empty space behavior, try using ranged attack if (attacked == null && item != null) { //AFTERATTACK: Check if we clicked on an empty location, if so the only interaction we can do is afterattack InteractAfterattack(player, item, coordinates); return; } else if (attacked == null) { return; } //Verify attacked object is on the map if we managed to click on it somehow if (!attacked.GetComponent <ITransformComponent>().IsMapTransform) { Logger.Warning(string.Format("Player named {0} clicked on object {1} that isn't currently on the map somehow", player.Name, attacked.Name)); return; } //Check if ClickLocation is in object bounds here, if not lets log as warning and see why if (attacked.TryGetComponent(out BoundingBoxComponent boundingbox)) { if (!boundingbox.WorldAABB.Contains(coordinates.Position)) { Logger.Warning(string.Format("Player {0} clicked {1} outside of its bounding box component somehow", player.Name, attacked.Name)); return; } } //RANGEDATTACK/AFTERATTACK: Check distance between user and clicked item, if too large parse it in the ranged function //TODO: have range based upon the item being used? or base it upon some variables of the player himself? var distance = (playerTransform.WorldPosition - attacked.GetComponent <ITransformComponent>().WorldPosition).LengthSquared; if (distance > INTERACTION_RANGE_SQUARED) { if (item != null) { RangedInteraction(player, item, attacked, coordinates); return; } return; //Add some form of ranged attackhand here if you need it someday, or perhaps just ways to modify the range of attackhand } //We are close to the nearby object and the object isn't contained in our active hand //ATTACKBY/AFTERATTACK: We will either use the item on the nearby object if (item != null) { Interaction(player, item, attacked, coordinates); } //ATTACKHAND: Since our hand is empty we will use attackhand else { Interaction(player, attacked); } }
private void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, PhysicsComponent physics, CollidableComponent collider = null) { if (physics.Controller == null) { // Set up controller physics.SetController <MoverController>(); } var weightless = !transform.Owner.HasComponent <MovementIgnoreGravityComponent>() && _physicsManager.IsWeightless(transform.GridPosition); if (weightless && collider != null) { // No gravity: is our entity touching anything? var touching = IsAroundCollider(transform, mover, collider); if (!touching) { return; } } if (mover.VelocityDir.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner) && !weightless) { (physics.Controller as MoverController)?.StopMoving(); } else { if (weightless) { (physics.Controller as MoverController)?.Push(mover.VelocityDir, mover.CurrentPushSpeed); transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle(); return; } (physics.Controller as MoverController)?.Move(mover.VelocityDir, mover.Sprinting ? mover.CurrentSprintSpeed : mover.CurrentWalkSpeed); transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle(); // Handle footsteps. if (_mapManager.GridExists(mover.LastPosition.GridID)) { // Can happen when teleporting between grids. var distance = transform.GridPosition.Distance(_mapManager, mover.LastPosition); mover.StepSoundDistance += distance; } mover.LastPosition = transform.GridPosition; float distanceNeeded; if (mover.Sprinting) { distanceNeeded = StepSoundMoveDistanceRunning; } else { distanceNeeded = StepSoundMoveDistanceWalking; } if (mover.StepSoundDistance > distanceNeeded) { mover.StepSoundDistance = 0; if (!mover.Owner.HasComponent <FootstepSoundComponent>()) { return; } if (mover.Owner.TryGetComponent <InventoryComponent>(out var inventory) && inventory.TryGetSlotItem <ItemComponent>(EquipmentSlotDefines.Slots.SHOES, out var item) && item.Owner.TryGetComponent <FootstepModifierComponent>(out var modifier)) { modifier.PlayFootstep(); } else { PlayFootstepSound(transform.GridPosition); } } }
public bool TryPoint(ICommonSession?session, GridCoordinates coords, EntityUid uid) { var player = session?.AttachedEntity; if (player == null) { return(false); } if (_pointers.TryGetValue(session !, out var lastTime) && _gameTiming.CurTime < lastTime + PointDelay) { return(false); } if (!InRange(coords, player.Transform.GridPosition)) { player.PopupMessage(player, Loc.GetString("You can't reach there!")); return(false); } if (ActionBlockerSystem.CanChangeDirection(player)) { var diff = coords.ToMapPos(_mapManager) - player.Transform.MapPosition.Position; if (diff.LengthSquared > 0.01f) { player.Transform.LocalRotation = new Angle(diff); } } var viewers = _playerManager.GetPlayersInRange(player.Transform.GridPosition, 15); EntityManager.SpawnEntity("pointingarrow", coords); string selfMessage; string viewerMessage; string?viewerPointedAtMessage = null; if (EntityManager.TryGetEntity(uid, out var pointed)) { selfMessage = player == pointed ? Loc.GetString("You point at yourself.") : Loc.GetString("You point at {0:theName}.", pointed); viewerMessage = player == pointed ? $"{player.Name} {Loc.GetString("points at {0:themself}.", player)}" : $"{player.Name} {Loc.GetString("points at {0:theName}.", pointed)}"; viewerPointedAtMessage = $"{player.Name} {Loc.GetString("points at you.")}"; } else { var tileRef = _mapManager.GetGrid(coords.GridID).GetTileRef(coords); var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId]; selfMessage = Loc.GetString("You point at {0}.", tileDef.DisplayName); viewerMessage = $"{player.Name} {Loc.GetString("points at {0}.", tileDef.DisplayName)}"; } _pointers[session !] = _gameTiming.CurTime;
public bool TryPoint(ICommonSession?session, GridCoordinates coords, EntityUid uid) { var player = (session as IPlayerSession)?.ContentData()?.Mind?.CurrentEntity; if (player == null) { return(false); } if (_pointers.TryGetValue(session !, out var lastTime) && _gameTiming.CurTime < lastTime + PointDelay) { return(false); } if (EntityManager.TryGetEntity(uid, out var entity) && entity.HasComponent <PointingArrowComponent>()) { // this is a pointing arrow. no pointing here... return(false); } if (!InRange(coords, player.Transform.GridPosition)) { player.PopupMessage(Loc.GetString("You can't reach there!")); return(false); } if (ActionBlockerSystem.CanChangeDirection(player)) { var diff = coords.ToMapPos(_mapManager) - player.Transform.MapPosition.Position; if (diff.LengthSquared > 0.01f) { player.Transform.LocalRotation = new Angle(diff); } } var arrow = EntityManager.SpawnEntity("pointingarrow", coords); var layer = (int)VisibilityFlags.Normal; if (player.TryGetComponent(out VisibilityComponent? playerVisibility)) { var arrowVisibility = arrow.EnsureComponent <VisibilityComponent>(); layer = arrowVisibility.Layer = playerVisibility.Layer; } // Get players that are in range and whose visibility layer matches the arrow's. var viewers = _playerManager.GetPlayersBy((playerSession) => { if ((playerSession.VisibilityMask & layer) == 0) { return(false); } var ent = playerSession.ContentData()?.Mind?.CurrentEntity; return(ent != null && ent.Transform.MapPosition.InRange(player.Transform.MapPosition, PointingRange)); }); string selfMessage; string viewerMessage; string?viewerPointedAtMessage = null; if (EntityManager.TryGetEntity(uid, out var pointed)) { selfMessage = player == pointed ? Loc.GetString("You point at yourself.") : Loc.GetString("You point at {0:theName}.", pointed); viewerMessage = player == pointed ? $"{player.Name} {Loc.GetString("points at {0:themself}.", player)}" : $"{player.Name} {Loc.GetString("points at {0:theName}.", pointed)}"; viewerPointedAtMessage = $"{player.Name} {Loc.GetString("points at you.")}"; } else { var tileRef = _mapManager.GetGrid(coords.GridID).GetTileRef(coords); var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId]; selfMessage = Loc.GetString("You point at {0}.", tileDef.DisplayName); viewerMessage = $"{player.Name} {Loc.GetString("points at {0}.", tileDef.DisplayName)}"; } _pointers[session !] = _gameTiming.CurTime;
private void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, PhysicsComponent physics, CollidableComponent collider = null) { bool weightless = false; var tile = _mapManager.GetGrid(transform.GridID).GetTileRef(transform.GridPosition).Tile; if ((!_mapManager.GetGrid(transform.GridID).HasGravity || tile.IsEmpty) && collider != null) { weightless = true; // No gravity: is our entity touching anything? var touching = false; foreach (var entity in _entityManager.GetEntitiesInRange(transform.Owner, mover.GrabRange, true)) { if (entity.TryGetComponent <CollidableComponent>(out var otherCollider)) { if (otherCollider.Owner == transform.Owner) { continue; // Don't try to push off of yourself! } touching |= ((collider.CollisionMask & otherCollider.CollisionLayer) != 0x0 || (otherCollider.CollisionMask & collider.CollisionLayer) != 0x0) && // Ensure collision !entity.HasComponent <ItemComponent>(); // This can't be an item } } if (!touching) { return; } } if (mover.VelocityDir.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner)) { if (physics.LinearVelocity != Vector2.Zero) { physics.LinearVelocity = Vector2.Zero; } } else { if (weightless) { physics.LinearVelocity = mover.VelocityDir * mover.CurrentPushSpeed; transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle(); return; } physics.LinearVelocity = mover.VelocityDir * (mover.Sprinting ? mover.CurrentSprintSpeed : mover.CurrentWalkSpeed); transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle(); // Handle footsteps. if (_mapManager.GridExists(mover.LastPosition.GridID)) { // Can happen when teleporting between grids. var distance = transform.GridPosition.Distance(_mapManager, mover.LastPosition); mover.StepSoundDistance += distance; } mover.LastPosition = transform.GridPosition; float distanceNeeded; if (mover.Sprinting) { distanceNeeded = StepSoundMoveDistanceRunning; } else { distanceNeeded = StepSoundMoveDistanceWalking; } if (mover.StepSoundDistance > distanceNeeded) { mover.StepSoundDistance = 0; if (!mover.Owner.HasComponent <FootstepSoundComponent>()) { return; } if (mover.Owner.TryGetComponent <InventoryComponent>(out var inventory) && inventory.TryGetSlotItem <ItemComponent>(EquipmentSlotDefines.Slots.SHOES, out var item) && item.Owner.TryGetComponent <FootstepModifierComponent>(out var modifier)) { modifier.PlayFootstep(); } else { PlayFootstepSound(transform.GridPosition); } } }