//Where is the target? Is there something in the way we can break //to get to the target? private bool CheckForAttackTarget() { if (followTarget != null) { if (mobAI.IsDead || mobAI.IsUnconscious) { Deactivate(); followTarget = null; return(false); } var followLivingBehaviour = followTarget.GetComponent <LivingHealthBehaviour>(); var distanceToTarget = Vector3.Distance(followTarget.transform.position, transform.position); if (followLivingBehaviour != null) { //When to stop following on the server: if (followLivingBehaviour.IsDead || distanceToTarget > 30f) { Deactivate(); followTarget = null; return(false); } } var dirToTarget = (followTarget.position - transform.position).normalized; var hitInfo = MatrixManager.Linecast(transform.position + dirToTarget, LayerTypeSelection.Windows, checkMask, followTarget.position); // Debug.DrawLine(transform.position + dirToTarget, followTarget.position, Color.blue, 10f); if (hitInfo.CollisionHit.GameObject != null) { if (Vector3.Distance(transform.position, hitInfo.TileHitWorld) < 1.5f) { var dir = (hitInfo.TileHitWorld - transform.position).normalized; //Only hit target if (onlyHitTarget) { var healthBehaviour = hitInfo.CollisionHit.GameObject.transform.GetComponent <LivingHealthBehaviour>(); if (hitInfo.CollisionHit.GameObject.transform != followTarget || healthBehaviour.IsDead) { return(false); } else { AttackFlesh(dir, healthBehaviour); return(true); } } //What to do with player hit? if (hitInfo.CollisionHit.GameObject.transform.gameObject.layer == playersLayer) { var healthBehaviour = hitInfo.CollisionHit.GameObject.transform.GetComponent <LivingHealthBehaviour>(); if (healthBehaviour.IsDead) { return(false); } AttackFlesh(dir, healthBehaviour); if (followTarget.gameObject.layer == playersLayer) { if (followTarget != hitInfo.CollisionHit.GameObject.transform) { if (targetOtherPlayersWhoGetInWay) { followTarget = hitInfo.CollisionHit.GameObject.transform; } } } return(true); } //What to do with NPC hit? if (hitInfo.CollisionHit.GameObject.transform.gameObject.layer == npcLayer) { var mobAi = hitInfo.CollisionHit.GameObject.transform.GetComponent <MobAI>(); if (mobAi != null && mobAI != null) { if (mobAi.mobName.Equals(mobAI.mobName, StringComparison.OrdinalIgnoreCase)) { return(false); } } var healthBehaviour = hitInfo.CollisionHit.GameObject.transform.GetComponent <LivingHealthBehaviour>(); if (healthBehaviour != null) { if (healthBehaviour.IsDead) { return(false); } AttackFlesh(dir, healthBehaviour); return(true); } } //What to do with Tile hits? if (distanceToTarget > 4.5f) { //Don't bother, the target is too far away to warrant a decision to break a tile return(false); } AttackTile(hitInfo.TileHitWorld.RoundToInt(), dir); return(true); } } } return(false); }
/// Same as ExposeHotspot but allows providing a world position and handles the conversion public void ExposeHotspotWorldPosition(Vector2Int tileWorldPosition, float temperature, float volume) { ExposeHotspot(MatrixManager.WorldToLocalInt(tileWorldPosition.To3Int(), MatrixManager.Get(matrix)), temperature, volume); }
public void ServerPerformInteraction(ConnectionApply interaction) { var cableCoil = interaction.HandObject.GetComponent <CableCoil>(); if (cableCoil != null) { Vector3Int worldPosInt = Vector3Int.RoundToInt(interaction.WorldPositionTarget); MatrixInfo matrix = MatrixManager.AtPoint(worldPosInt, true); var localPosInt = MatrixManager.WorldToLocalInt(worldPosInt, matrix); // if there is no matrix or IsClearUnderfloor == false - return if (matrix.Matrix == null || !matrix.Matrix.IsClearUnderfloorConstruction(localPosInt, true)) { return; } Connection WireEndB = interaction.WireEndB; Connection WireEndA = interaction.WireEndA; if (WireEndB != Connection.NA) { // high voltage cables can't connect diagonally if (CableType == WiringColor.high) { switch (WireEndB) { case Connection.NorthEast: return; case Connection.NorthWest: return; case Connection.SouthWest: return; case Connection.SouthEast: return; } switch (WireEndA) { case Connection.NorthEast: return; case Connection.NorthWest: return; case Connection.SouthWest: return; case Connection.SouthEast: return; } } var econs = interaction.Performer.GetComponentInParent <Matrix>().GetElectricalConnections(localPosInt); foreach (var con in econs) { if (con.WireEndA == Connection.Overlap || con.WireEndB == Connection.Overlap) { if (con.WireEndA == WireEndB || con.WireEndB == WireEndB) { Chat.AddExamineMsgToClient("There is already a cable at that position"); econs.Clear(); ElectricalPool.PooledFPCList.Add(econs); return; } foreach (var Econ in econs) { if (Econ.WireEndA == WireEndB || Econ.WireEndB == WireEndB) { if (con.WireEndA == Econ.WireEndA || con.WireEndB == Econ.WireEndA) { Chat.AddExamineMsgToClient("There is already a cable at that position"); econs.Clear(); ElectricalPool.PooledFPCList.Add(econs); return; } else if (con.WireEndA == Econ.WireEndB || con.WireEndB == Econ.WireEndB) { Chat.AddExamineMsgToClient("There is already a cable at that position"); econs.Clear(); ElectricalPool.PooledFPCList.Add(econs); return; } } } } } econs.Clear(); ElectricalPool.PooledFPCList.Add(econs); BuildCable(localPosInt, interaction.Performer.transform.parent, WireEndA, WireEndB, interaction); Inventory.ServerConsume(interaction.HandSlot, 1); } } }
/// <Summary> /// Use World positions /// </Summary> private bool CanDriftTo(Vector3Int originPos, Vector3Int targetPos) { return(MatrixManager.IsPassableAt(originPos, targetPos, false)); }
private void SyncMatrix() { registerTile.ParentNetId = MatrixManager.Get(serverState.MatrixId).NetId; }
/// <summary> /// Spawns a ghost for the indicated mind's body and transfers the connection's control to it. /// </summary> /// <param name="conn"></param> /// <param name="oldBody"></param> /// <param name="characterSettings"></param> /// <param name="occupation"></param> /// <returns></returns> public static void ServerSpawnGhost(Mind forMind) { if (forMind == null) { Logger.LogError("Mind was null for ServerSpawnGhost", Category.Ghosts); return; } //determine where to spawn the ghost var body = forMind.GetCurrentMob(); if (body == null) { Logger.LogError("Body was null for ServerSpawnGhost", Category.Ghosts); return; } var settings = body.GetComponent <PlayerScript>().characterSettings; var connection = body.GetComponent <NetworkIdentity>().connectionToClient; var registerTile = body.GetComponent <RegisterTile>(); if (registerTile == null) { Logger.LogErrorFormat("Cannot spawn ghost for body {0} because it has no registerTile", Category.Ghosts, body.name); return; } Vector3Int spawnPosition = TransformState.HiddenPos; var objBeh = body.GetComponent <ObjectBehaviour>(); if (objBeh != null) { spawnPosition = objBeh.AssumedWorldPositionServer(); } if (spawnPosition == TransformState.HiddenPos) { //spawn ghost at occupation location if we can't determine where their body is Transform spawnTransform = SpawnPoint.GetRandomPointForJob(forMind.occupation.JobType, true); if (spawnTransform == null) { Logger.LogErrorFormat("Unable to determine spawn position for occupation {1}. Cannot spawn ghost.", Category.Ghosts, forMind.occupation.DisplayName); return; } spawnPosition = spawnTransform.transform.position.CutToInt(); } var matrixInfo = MatrixManager.AtPoint(spawnPosition, true); var parentTransform = matrixInfo.Objects; //using parentTransform.rotation rather than Quaternion.identity because objects should always //be upright w.r.t. localRotation, NOT world rotation var ghost = UnityEngine.Object.Instantiate(CustomNetworkManager.Instance.ghostPrefab, spawnPosition, parentTransform.rotation, parentTransform); forMind.Ghosting(ghost); ServerTransferPlayer(connection, ghost, body, Event.GhostSpawned, settings); //fire all hooks var info = SpawnInfo.Ghost(forMind.occupation, settings, CustomNetworkManager.Instance.ghostPrefab, SpawnDestination.At(spawnPosition, parentTransform)); Spawn._ServerFireClientServerSpawnHooks(SpawnResult.Single(info, ghost)); if (PlayerList.Instance.IsAdmin(forMind.ghost.connectedPlayer)) { var adminItemStorage = AdminManager.Instance.GetItemSlotStorage(forMind.ghost.connectedPlayer); adminItemStorage.ServerAddObserverPlayer(ghost); ghost.GetComponent <GhostSprites>().SetAdminGhost(); } }
public LightMatrixManager(MatrixManager manager) { this.manager = manager; Camera = new BasicCamera(new Vector3(0, 0, -20), new Vector3(0, 0, 0), new Vector3(0, 1, 0)); Projection = new BasicProjectionMatrixProvider(); }
public void ServerPerformInteraction(PositionalHandApply interaction) { var roundTargetWorldPosition = interaction.WorldPositionTarget.RoundToInt(); MatrixInfo matrix = MatrixManager.AtPoint(roundTargetWorldPosition, true); if (matrix?.Matrix == null) { return; } if (!MatrixManager.IsWallAt(roundTargetWorldPosition, true)) { return; } Vector3Int PlaceDirection = interaction.Performer.Player().Script.WorldPos - roundTargetWorldPosition; OrientationEnum FaceDirection; //is there a wall in the direction of the new wallmount? taking into account diagonal clicking var tileInFront = roundTargetWorldPosition + new Vector3Int(PlaceDirection.x, 0, 0); if (!MatrixManager.IsWallAt(tileInFront, true)) { if (PlaceDirection.x > 0) { FaceDirection = OrientationEnum.Right_By90; } else { FaceDirection = OrientationEnum.Left_By270; } } else { tileInFront = roundTargetWorldPosition + new Vector3Int(0, PlaceDirection.y, 0); if (!MatrixManager.IsWallAt(tileInFront, true)) { if (PlaceDirection.y > 0) { FaceDirection = OrientationEnum.Up_By0; } else { FaceDirection = OrientationEnum.Down_By180; } } else { return; } } if (IsWallProtrusion) { roundTargetWorldPosition = tileInFront; } GameObject WallMount = Spawn.ServerPrefab(WallMountToSpawn, roundTargetWorldPosition, interaction.Performer.transform.parent, spawnItems: false).GameObject; var Directional = WallMount.GetComponent <Rotatable>(); if (Directional != null) { Directional.FaceDirection(FaceDirection); } Inventory.ServerConsume(interaction.HandSlot, 1); var construction = WallMount.GetComponent <LightFixtureConstruction>(); if (construction != null) { construction.ServerSetState(LightFixtureConstruction.State.initial); } }
private PlayerState NextStateServer(PlayerState state, PlayerAction action) { //Check if there is a bump interaction according to the server BumpType serverBump = CheckSlideAndBump(state, ref action); //Client only needs to check whether movement was prevented, specific type of bump doesn't matter bool isClientBump = action.isBump; //we only lerp back if the client thinks it's passable but server does not...if client //thinks it's not passable and server thinks it's passable, then it's okay to let the client continue if (!isClientBump && serverBump != BumpType.None) { Logger.LogWarningFormat("isBump mismatch, resetting: C={0} S={1}", Category.Movement, isClientBump, serverBump != BumpType.None); RollbackPosition(); } if (isClientBump || serverBump != BumpType.None) { // we bumped something, an interaction might occur // try pushing things / opening doors BumpInteract(state.WorldPosition, (Vector2)action.Direction()); playerSprites.FaceDirection(Orientation.From(action.Direction())); return(state); } if (IsNonStickyServer) { PushPull pushable; if (IsAroundPushables(serverState, out pushable)) { StartCoroutine(InteractSpacePushable(pushable, action.Direction())); } return(state); } if (action.isNonPredictive) { Logger.Log("Ignored action marked as Non-predictive while being indoors", Category.Movement); return(state); } bool matrixChangeDetected; PlayerState nextState = NextState(state, action, out matrixChangeDetected); if (!matrixChangeDetected) { return(nextState); } //todo: subscribe to current matrix rotations on spawn var newMatrix = MatrixManager.Get(nextState.MatrixId); Logger.Log($"Matrix will change to {newMatrix}", Category.Movement); if (newMatrix.MatrixMove) { //Subbing to new matrix rotations newMatrix.MatrixMove.OnRotate.AddListener(OnRotation); // Logger.Log( $"Registered rotation listener to {newMatrix.MatrixMove}" ); } //Unsubbing from old matrix rotations MatrixMove oldMatrixMove = MatrixManager.Get(Matrix).MatrixMove; if (oldMatrixMove) { // Logger.Log( $"Unregistered rotation listener from {oldMatrixMove}" ); oldMatrixMove.OnRotate.RemoveListener(OnRotation); } return(nextState); }
/// <summary> /// Release reagents at provided coordinates, making them react with world /// </summary> public void ReagentReact(ReagentMix reagents, Vector3Int worldPosInt, Vector3Int localPosInt) { if (MatrixManager.IsTotallyImpassable(worldPosInt, true)) { return; } bool didSplat = false; foreach (var reagent in reagents.reagents) { if (reagent.Value < 1) { continue; } switch (reagent.Key.Name) { case "Water": { matrix.ReactionManager.ExtinguishHotspot(localPosInt); foreach (var livingHealthBehaviour in matrix.Get <LivingHealthBehaviour>(localPosInt, true)) { livingHealthBehaviour.Extinguish(); } Clean(worldPosInt, localPosInt, true); break; } case "SpaceCleaner": Clean(worldPosInt, localPosInt, false); break; case "WeldingFuel": //temporary: converting spilled fuel to plasma Get(localPosInt).GasMix.AddGas(Gas.Plasma, reagent.Value); break; case "Lube": { //( ͡° ͜ʖ ͡°) if (!Get(localPosInt).IsSlippery) { EffectsFactory.WaterSplat(worldPosInt); MakeSlipperyAt(localPosInt, false); } break; } default: { //for all other things leave a chem splat if (!didSplat) { EffectsFactory.ChemSplat(worldPosInt); didSplat = true; } break; } } } }
private static Transform DefaultParent(Transform parent, Vector3?worldPos) { return(parent != null ? parent : MatrixManager.GetDefaultParent(worldPos, true)); }
private void LoadTurf() { metaDataLayer = MatrixManager.AtPoint(registerTile.WorldPositionServer, true).MetaDataLayer; metaNode = metaDataLayer.Get(registerTile.WorldPositionServer, false); }
private void CreateHotSpot() { var reactionManager = MatrixManager.AtPoint(currentTileWorldPos, true).ReactionManager; reactionManager.ExposeHotspotWorldPosition(currentTileWorldPos.To2Int()); }
private static Vector3 WorldToLocal(Component source, Vector3 position) { return(MatrixManager.WorldToLocal(position, MatrixManager.Get(source.GetComponent <Matrix>()))); }
public void Process() { float Damagedealt = AngleAndIntensity.magnitude; float EnergyExpended = 0; var v3int = new Vector3Int(Location.x, Location.y, 0); var metaTileMap = matrix.MetaTileMap; if (metaTileMap == null) { return; } EnergyExpended = metaTileMap.ApplyDamage(v3int, Damagedealt, MatrixManager.LocalToWorldInt(v3int, matrix.MatrixInfo), AttackType.Bomb) * 0.375f; // Prevents a perpetual motion explosion if (EnergyExpended <= 0.375f) { EnergyExpended = 0.375f; } if (Damagedealt > 100) { var Node = matrix.GetMetaDataNode(v3int); if (Node != null) { foreach (var electricalData in Node.ElectricalData) { electricalData.InData.DestroyThisPlease(); } SavedPipes.Clear(); SavedPipes.AddRange(Node.PipeData); foreach (var Pipe in SavedPipes) { Pipe.pipeData.DestroyThis(); } } } foreach (var integrity in matrix.Get <Integrity>(v3int, true)) { //Throw items //And do damage to objects integrity.ApplyDamage(Damagedealt, AttackType.Bomb, DamageType.Brute); } foreach (var player in matrix.Get <ObjectBehaviour>(v3int, ObjectType.Player, true)) { // do damage player.GetComponent <PlayerHealthV2>().ApplyDamageAll(null, Damagedealt, AttackType.Bomb, DamageType.Brute); } foreach (var line in PresentLines) { line.ExplosionStrength -= EnergyExpended * (line.ExplosionStrength / Damagedealt); } AngleAndIntensity = Vector2.zero; }
public void DestroyThis() { if (MonoPipe == null) { var Transform = matrix.UnderFloorLayer.GetMatrix4x4(pipeNode.NodeLocation, pipeNode.RelatedTile); var pipe = Spawn.ServerPrefab(pipeNode.RelatedTile.SpawnOnDeconstruct, MatrixManager.LocalToWorld(pipeNode.NodeLocation, matrix), localRotation: PipeDeconstruction.QuaternionFromMatrix(Transform)).GameObject; var itempipe = pipe.GetComponent <PipeItemTile>(); itempipe.Colour = matrix.UnderFloorLayer.GetColour(pipeNode.NodeLocation, pipeNode.RelatedTile); itempipe.Setsprite(); //matrix.RemoveUnderFloorTile(pipeNode.NodeLocation,pipeNode.RelatedTile); pipeNode.LocatedOn.RemoveUnderFloorTile(pipeNode.NodeLocation, pipeNode.RelatedTile); pipeNode.IsOn.PipeData.Remove(pipeNode); this.OnDisable(); } }
/// <summary> /// Server: /// Allow items to be stored by clicking on bags with item in hand /// and clicking items with bag in hand if CanClickPickup is enabled /// /// </summary> public void ServerPerformInteraction(PositionalHandApply interaction) { // See which item needs to be stored if (Validations.IsTarget(gameObject, interaction)) { // Add hand item to this storage Inventory.ServerTransfer(interaction.HandSlot, itemStorage.GetBestSlotFor(interaction.HandObject)); } // See if this item can click pickup else if (canClickPickup) { switch (pickupMode) { case PickupMode.Single: // Store the clicked item var slot = itemStorage.GetBestSlotFor(interaction.TargetObject); if (slot == null) { Chat.AddExamineMsgFromServer(interaction.Performer, $"The {interaction.TargetObject.ExpensiveName()} doesn't fit!"); return; } Inventory.ServerAdd(interaction.TargetObject, slot); break; case PickupMode.Same: if (interaction.TargetObject == null || interaction.TargetObject.Item() == null) { Chat.AddExamineMsgFromServer(interaction.Performer, "There's nothing to pickup!"); return; } // Get all items of the same type on the tile and try to store them var itemsOnTileSame = MatrixManager.GetAt <ItemAttributesV2>(interaction.WorldPositionTarget.To2Int().To3Int(), true); if (itemsOnTileSame.Count == 0) { Chat.AddExamineMsgFromServer(interaction.Performer, "There's nothing to pickup!"); return; } foreach (var item in itemsOnTileSame) { // Only try to add it if it matches the target object's traits if (item.HasAllTraits(interaction.TargetObject.Item().GetTraits())) { // Try to add each item to the storage // Can't break this loop when it fails because some items might not fit and // there might be stacks with space still Inventory.ServerAdd(item.gameObject, itemStorage.GetBestSlotFor(item.gameObject)); } } Chat.AddExamineMsgFromServer(interaction.Performer, $"You put everything you could in the {gameObject.ExpensiveName()}."); break; case PickupMode.All: // Get all items on the tile and try to store them var itemsOnTileAll = MatrixManager.GetAt <ItemAttributesV2>(interaction.WorldPositionTarget.To2Int().To3Int(), true); if (itemsOnTileAll.Count == 0) { Chat.AddExamineMsgFromServer(interaction.Performer, "There's nothing to pickup!"); return; } foreach (var item in itemsOnTileAll) { // Try to add each item to the storage // Can't break this loop when it fails because some items might not fit and // there might be stacks with space still Inventory.ServerAdd(item.gameObject, itemStorage.GetBestSlotFor(item.gameObject)); } Chat.AddExamineMsgFromServer(interaction.Performer, $"You put everything you could in the {gameObject.ExpensiveName()}."); break; } } }
T GetAtRelative <T>(Orientation orientation) where T : MonoBehaviour { return(MatrixManager.GetAt <T>(player.ClientPosition + Vector3Int.FloorToInt(orientation.Vector), true).First()); }
private void PropagateChatToClients(ChatEvent chatEvent) { List <ConnectedPlayer> players = PlayerList.Instance.AllPlayers; //Local chat range checks: if (chatEvent.channels.HasFlag(ChatChannel.Local) || chatEvent.channels.HasFlag(ChatChannel.Combat) || chatEvent.channels.HasFlag(ChatChannel.Action)) { for (int i = players.Count - 1; i >= 0; i--) { if (players[i].Script == null) { //joined viewer, don't message them players.RemoveAt(i); continue; } if (players[i].Script.IsGhost) { //send all to ghosts continue; } if (chatEvent.position == TransformState.HiddenPos) { //show messages with no provided position to everyone continue; } if (Vector2.Distance(chatEvent.position, (Vector3)players[i].Script.WorldPos) > 14f) { //Player in the list is too far away for local chat, remove them: players.RemoveAt(i); } else { //within range, but check if they are in another room or hiding behind a wall if (MatrixManager.Linecast(chatEvent.position, LayerTypeSelection.Walls , layerMask, (Vector3)players[i].Script.WorldPos).ItHit) { //if it hit a wall remove that player players.RemoveAt(i); } } } //Get NPCs in vicinity var npcs = Physics2D.OverlapCircleAll(chatEvent.position, 14f, npcMask); foreach (Collider2D coll in npcs) { if (MatrixManager.Linecast(chatEvent.position, LayerTypeSelection.Walls, layerMask, coll.transform.position).ItHit == false) { //NPC is in hearing range, pass the message on: var mobAi = coll.GetComponent <MobAI>(); if (mobAi != null) { mobAi.LocalChatReceived(chatEvent); } } } } for (var i = 0; i < players.Count; i++) { ChatChannel channels = chatEvent.channels; if (channels.HasFlag(ChatChannel.Combat) || channels.HasFlag(ChatChannel.Local) || channels.HasFlag(ChatChannel.System) || channels.HasFlag(ChatChannel.Examine) || channels.HasFlag(ChatChannel.Action)) { if (!channels.HasFlag(ChatChannel.Binary) || players[i].Script.IsGhost) { UpdateChatMessage.Send(players[i].GameObject, channels, chatEvent.modifiers, chatEvent.message, chatEvent.messageOthers, chatEvent.originator, chatEvent.speaker); continue; } } if (players[i].Script == null) { channels &= ChatChannel.OOC; } else { channels &= players[i].Script.GetAvailableChannelsMask(false); } //if the mask ends up being a big fat 0 then don't do anything if (channels != ChatChannel.None) { UpdateChatMessage.Send(players[i].GameObject, channels, chatEvent.modifiers, chatEvent.message, chatEvent.messageOthers, chatEvent.originator, chatEvent.speaker); } } if (rconManager != null) { string name = ""; if ((namelessChannels & chatEvent.channels) != chatEvent.channels) { name = "<b>[" + chatEvent.channels + "]</b> "; } RconManager.AddChatLog(name + chatEvent.message); } }
public void OnHover() { var wallMount = CheckWallMountOverlay(); if (wallMount) { Vector2 cameraPos = Camera.main.ScreenToWorldPoint(CommonInput.mousePosition); var tilePos = cameraPos.RoundToInt(); OrientationEnum orientation = OrientationEnum.Down; Vector3Int PlaceDirection = PlayerManager.LocalPlayerScript.WorldPos - tilePos; bool isWallBlocked = false; if (PlaceDirection.x != 0 && !MatrixManager.IsWallAtAnyMatrix(tilePos + new Vector3Int(PlaceDirection.x > 0 ? 1 : -1, 0, 0), true)) { if (PlaceDirection.x > 0) { orientation = OrientationEnum.Right; } else { orientation = OrientationEnum.Left; } } else { if (PlaceDirection.y != 0 && !MatrixManager.IsWallAtAnyMatrix(tilePos + new Vector3Int(0, PlaceDirection.y > 0 ? 1 : -1, 0), true)) { if (PlaceDirection.y > 0) { orientation = OrientationEnum.Up; } else { orientation = OrientationEnum.Down; } } else { isWallBlocked = true; } } if (!MatrixManager.IsWallAtAnyMatrix(tilePos, false) || isWallBlocked) { if (instanceActive) { instanceActive = false; Highlight.DeHighlight(); } return; } if (!instanceActive) { instanceActive = true; Highlight.ShowHighlight(UIManager.Hands.CurrentSlot.ItemObject, true); } Vector3 spritePos = tilePos; if (wallMount.IsWallProtrusion) //for light bulbs, tubes, cameras, etc. move the sprite towards the floor { if (orientation == OrientationEnum.Right) { spritePos.x += 0.5f; Highlight.instance.spriteRenderer.transform.rotation = Quaternion.Euler(0, 0, 270); } else if (orientation == OrientationEnum.Left) { spritePos.x -= 0.5f; Highlight.instance.spriteRenderer.transform.rotation = Quaternion.Euler(0, 0, 90); } else if (orientation == OrientationEnum.Up) { spritePos.y += 0.5f; Highlight.instance.spriteRenderer.transform.rotation = Quaternion.Euler(0, 0, 0); } else { spritePos.y -= 0.5f; Highlight.instance.spriteRenderer.transform.rotation = Quaternion.Euler(0, 0, 0); } } Highlight.instance.spriteRenderer.transform.position = spritePos; } }
void Start() { MatrixManager.RegisterMatrix(this, IsSpaceMatrix, IsMainStation, IsLavaLand); }
/// <summary> /// Server: /// Allow items to be stored by clicking on bags with item in hand /// and clicking items with bag in hand if CanClickPickup is enabled /// /// </summary> public void ServerPerformInteraction(PositionalHandApply interaction) { if (!allowedToInteract) { return; } // See which item needs to be stored if (Validations.IsTarget(gameObject, interaction)) { // Add hand item to this storage Inventory.ServerTransfer(interaction.HandSlot, itemStorage.GetBestSlotFor(interaction.HandObject)); } // See if this item can click pickup else if (canClickPickup) { bool pickedUpSomething = false; Pickupable pickup; switch (pickupMode) { case PickupMode.Single: // Don't pick up items which aren't set as CanPickup pickup = interaction.TargetObject.GetComponent <Pickupable>(); if (pickup == null || pickup.CanPickup == false) { Chat.AddExamineMsgFromServer(interaction.Performer, "There's nothing to pickup!"); return; } // Store the clicked item var slot = itemStorage.GetBestSlotFor(interaction.TargetObject); if (slot == null) { Chat.AddExamineMsgFromServer(interaction.Performer, $"The {interaction.TargetObject.ExpensiveName()} doesn't fit!"); return; } Inventory.ServerAdd(interaction.TargetObject, slot); break; case PickupMode.Same: if (interaction.TargetObject == null || interaction.TargetObject.Item() == null) { Chat.AddExamineMsgFromServer(interaction.Performer, "There's nothing to pickup!"); return; } // Get all items of the same type on the tile and try to store them var itemsOnTileSame = MatrixManager.GetAt <ItemAttributesV2>(interaction.WorldPositionTarget.To2Int().To3Int(), true); if (itemsOnTileSame.Count == 0) { Chat.AddExamineMsgFromServer(interaction.Performer, "There's nothing to pickup!"); return; } foreach (var item in itemsOnTileSame) { // Don't pick up items which aren't set as CanPickup pickup = item.gameObject.GetComponent <Pickupable>(); if (pickup == null || pickup.CanPickup == false) { continue; } // Only try to add it if it matches the target object's traits if (item.HasAllTraits(interaction.TargetObject.Item().GetTraits())) { // Try to add each item to the storage // Can't break this loop when it fails because some items might not fit and // there might be stacks with space still if (Inventory.ServerAdd(item.gameObject, itemStorage.GetBestSlotFor(item.gameObject))) { pickedUpSomething = true; } } } Chat.AddExamineMsgFromServer(interaction.Performer, $"You put everything you could in the {gameObject.ExpensiveName()}."); break; case PickupMode.All: // Get all items on the tile and try to store them var itemsOnTileAll = MatrixManager.GetAt <ItemAttributesV2>(interaction.WorldPositionTarget.To2Int().To3Int(), true); if (itemsOnTileAll.Count == 0) { Chat.AddExamineMsgFromServer(interaction.Performer, "There's nothing to pickup!"); return; } foreach (var item in itemsOnTileAll) { // Don't pick up items which aren't set as CanPickup pickup = item.gameObject.GetComponent <Pickupable>(); if (pickup == null || pickup.CanPickup == false) { continue; } // Try to add each item to the storage // Can't break this loop when it fails because some items might not fit and // there might be stacks with space still if (Inventory.ServerAdd(item.gameObject, itemStorage.GetBestSlotFor(item.gameObject))) { pickedUpSomething = true; } } if (pickedUpSomething) { Chat.AddExamineMsgFromServer(interaction.Performer, $"You put everything you could in the {gameObject.ExpensiveName()}."); } else { Chat.AddExamineMsgFromServer(interaction.Performer, "There's nothing to pickup!"); } break; case PickupMode.DropClick: if (canQuickEmpty) { // Drop all items that are inside this storage var slots = itemStorage.GetItemSlots(); if (slots == null) { Chat.AddExamineMsgFromServer(interaction.Performer, "It's already empty!"); return; } if (PlayerManager.PlayerScript == null) { return; } if (Validations.IsInReachDistanceByPositions(PlayerManager.PlayerScript.registerTile.WorldPosition, interaction.WorldPositionTarget) == false) { return; } if (MatrixManager.IsPassableAtAllMatricesOneTile(interaction.WorldPositionTarget.RoundToInt(), CustomNetworkManager.Instance._isServer) == false) { return; } PlayerManager.PlayerScript.playerNetworkActions.CmdDropAllItems(itemStorage.GetIndexedItemSlot(0) .ItemStorageNetID, interaction.WorldPositionTarget); Chat.AddExamineMsgFromServer(interaction.Performer, $"You start dumping out the {gameObject.ExpensiveName()}."); } break; } } }
private bool CheckFloatingServer(Vector3 goal) { bool isRecursive = goal != TransformState.HiddenPos; if (!IsFloatingServer || matrix == null) { return(isRecursive); } Vector3 worldPosition = serverState.WorldPosition; Vector3 moveDelta; if (!isRecursive) { //Normal delta if not recursive moveDelta = (Vector3)serverState.WorldImpulse * serverState.Speed * Time.deltaTime; } else { //Artificial delta if recursive moveDelta = goal - worldPosition; } Vector3Int intOrigin = Vector3Int.RoundToInt(worldPosition); if (intOrigin.x > 18000 || intOrigin.x < -18000 || intOrigin.y > 18000 || intOrigin.y < -18000) { Stop(); Logger.Log($"ITEM {transform.name} was forced to stop at {intOrigin}", Category.Movement); return(true); } float distance = moveDelta.magnitude; Vector3 newGoal; if (distance > 1) { //limit goal to just one tile away and run this method recursively afterwards newGoal = worldPosition + (Vector3)serverState.WorldImpulse; } else { newGoal = worldPosition + moveDelta; } Vector3Int intGoal = Vector3Int.RoundToInt(newGoal); bool isWithinTile = intOrigin == intGoal; //same tile, no need to validate stuff if (isWithinTile || ValidateFloating(worldPosition, newGoal)) { AdvanceMovement(worldPosition, newGoal); } else { if (serverState.Speed >= PushPull.HIGH_SPEED_COLLISION_THRESHOLD && IsTileSnap) { //Stop first (reach tile), then inform about collision var collisionInfo = new CollisionInfo { Speed = serverState.Speed, Size = this.Size, CollisionTile = intGoal }; Stop(); OnHighSpeedCollision().Invoke(collisionInfo); } else { Stop(); } //Process any objects that we might have bumped into foreach (var objectBehaviour in MatrixManager.GetAt <ObjectBehaviour>(intGoal, true)) { foreach (var bump in objectBehaviour.GetComponents <IBumpableObject>()) { bump.OnBump(gameObject); } } } if (distance > 1) { CheckFloatingServer(isRecursive ? goal : newGoal); } return(true); }
private void Update() { if (state == State.SELECTING) { // ignore when we are over UI if (EventSystem.current.IsPointerOverGameObject()) { return; } //check which objects we are over, pick the top one to spawn if (CommonInput.GetMouseButtonDown(0)) { //NOTE: Avoiding multiple enumeration by converting IEnumerables to lists. var hitGOs = MouseUtils.GetOrderedObjectsUnderMouse(layerMask, go => go.GetComponent <CustomNetTransform>() != null) .Select(r => r.GetComponentInParent <CustomNetTransform>().gameObject).ToList(); //warn about objects which cannot be cloned var nonPooledHits = hitGOs .Where(go => PoolManager.DeterminePrefab(go) == null).ToList(); if (nonPooledHits.Any()) { foreach (GameObject nonPooled in nonPooledHits) { Logger.LogWarningFormat("Object {0} does not have a PoolPrefabTracker component and its name" + " did not match one of our existing prefabs " + "therefore cannot be cloned (because we wouldn't know which prefab to instantiate). " + "Please attach this component to the object and specify the prefab" + " to allow it to be cloned.", Category.ItemSpawn, nonPooled.name); } } var pooledHits = hitGOs.Where(go => PoolManager.DeterminePrefab(go) != null).ToList(); if (pooledHits.Any()) { toClone = pooledHits.First(); ToState(State.DRAWING); } } } else if (state == State.DRAWING) { cursorObject.transform.position = Camera.main.ScreenToWorldPoint(CommonInput.mousePosition); if (CommonInput.GetMouseButtonDown(0)) { Vector3Int position = cursorObject.transform.position.RoundToInt(); position.z = 0; if (MatrixManager.IsPassableAt(position)) { if (CustomNetworkManager.IsServer) { PoolManager.NetworkClone(toClone, position); } else { DevCloneMessage.Send(toClone, (Vector3)position); } } } } }
private PlayerState NextStateServer(PlayerState state, PlayerAction action) { bool isServerBump = !CanMoveThere(state, action); bool isClientBump = action.isBump; if (!isClientBump && isServerBump) { Logger.LogWarningFormat("isBump mismatch, resetting: C={0} S={1}", Category.Movement, isClientBump, isServerBump); RollbackPosition(); } if (isClientBump || isServerBump) { //gotta try pushing things BumpInteract(state.WorldPosition, (Vector2)action.Direction()); playerSprites.FaceDirection(Orientation.From(action.Direction())); return(state); } if (IsNonStickyServer) { PushPull pushable; if (IsAroundPushables(serverState, out pushable)) { StartCoroutine(InteractSpacePushable(pushable, action.Direction())); } return(state); } if (action.isNonPredictive) { Logger.Log("Ignored action marked as Non-predictive while being indoors", Category.Movement); return(state); } bool matrixChangeDetected; PlayerState nextState = NextState(state, action, out matrixChangeDetected); if (!matrixChangeDetected) { return(nextState); } //todo: subscribe to current matrix rotations on spawn var newMatrix = MatrixManager.Get(nextState.MatrixId); Logger.Log($"Matrix will change to {newMatrix}", Category.Movement); if (newMatrix.MatrixMove) { //Subbing to new matrix rotations newMatrix.MatrixMove.OnRotate.AddListener(OnRotation); // Logger.Log( $"Registered rotation listener to {newMatrix.MatrixMove}" ); } //Unsubbing from old matrix rotations MatrixMove oldMatrixMove = MatrixManager.Get(matrix).MatrixMove; if (oldMatrixMove) { // Logger.Log( $"Unregistered rotation listener from {oldMatrixMove}" ); oldMatrixMove.OnRotate.RemoveListener(OnRotation); } return(nextState); }
/// Same as ExposeHotspot but allows providing a world position and handles the conversion public void ExposeHotspotWorldPosition(Vector2Int tileWorldPosition) { ExposeHotspot(MatrixManager.WorldToLocalInt(tileWorldPosition.To3Int(), MatrixManager.Get(matrix))); }
private void Expose(Vector3Int hotspotPosition, Vector3Int atLocalPosition) { var isSideExposure = hotspotPosition != atLocalPosition; //calculate world position var hotspotWorldPosition = MatrixManager.LocalToWorldInt(hotspotPosition, MatrixManager.Get(matrix)); var atWorldPosition = MatrixManager.LocalToWorldInt(atLocalPosition, MatrixManager.Get(matrix)); if (!hotspots.ContainsKey(hotspotPosition)) { Logger.LogError("Hotspot position key was not found in the hotspots dictionary", Category.Atmos); return; } var exposure = FireExposure.FromMetaDataNode(hotspots[hotspotPosition], hotspotWorldPosition.To2Int(), atLocalPosition.To2Int(), atWorldPosition.To2Int()); if (isSideExposure) { //side exposure logic //already exposed by a different hotspot if (hotspots.ContainsKey(atLocalPosition)) { return; } var metadata = metaDataLayer.Get(atLocalPosition); if (!metadata.IsOccupied) { //atmos can pass here, so no need to check side exposure (nothing to brush up against) return; } //only expose to atmos impassable objects, since those are the things the flames would //actually brush up against var regTiles = matrix.Get <RegisterTile>(atLocalPosition, true); foreach (var regTile in regTiles) { if (!regTile.IsAtmosPassable(exposure.HotspotLocalPosition.To3Int(), true)) { var exposable = regTile.GetComponent <IFireExposable>(); exposable.OnExposed(exposure); } } //expose the tiles there foreach (var tilemapDamage in tilemapDamages) { tilemapDamage.OnExposed(exposure); } } else { //direct exposure logic var fireExposables = matrix.Get <IFireExposable>(atLocalPosition, true); foreach (var exposable in fireExposables) { exposable.OnExposed(exposure); } //expose the tiles foreach (var tilemapDamage in tilemapDamages) { tilemapDamage.OnExposed(exposure); } } }
private void Expose(Vector3Int hotspotPosition, Vector3Int atLocalPosition) { Profiler.BeginSample("ExposureInit"); var isSideExposure = hotspotPosition != atLocalPosition; //calculate world position var hotspotWorldPosition = MatrixManager.LocalToWorldInt(hotspotPosition, MatrixManager.Get(matrix)); var atWorldPosition = MatrixManager.LocalToWorldInt(atLocalPosition, MatrixManager.Get(matrix)); if (!hotspots.ContainsKey(hotspotPosition)) { Logger.LogError("Hotspot position key was not found in the hotspots dictionary", Category.Atmos); return; } //update fire exposure, reusing it to avoid creating GC. applyExposure.Update(isSideExposure, hotspots[hotspotPosition], hotspotWorldPosition, atLocalPosition, atWorldPosition); Profiler.EndSample(); if (isSideExposure) { Profiler.BeginSample("SideExposure"); //side exposure logic //already exposed by a different hotspot if (hotspots.ContainsKey(atLocalPosition)) { Profiler.EndSample(); return; } var metadata = metaDataLayer.Get(atLocalPosition); if (!metadata.IsOccupied) { //atmos can pass here, so no need to check side exposure (nothing to brush up against) Profiler.EndSample(); return; } //only expose to atmos impassable objects, since those are the things the flames would //actually brush up against matrix.ServerObjects.InvokeOnObjects(applyExposure, atLocalPosition); //expose the tiles there foreach (var tilemapDamage in tilemapDamages) { tilemapDamage.OnExposed(applyExposure.FireExposure); } Profiler.EndSample(); } else { Profiler.BeginSample("DirectExposure"); //direct exposure logic matrix.ServerObjects.InvokeOnObjects(applyExposure, atLocalPosition); //expose the tiles foreach (var tilemapDamage in tilemapDamages) { tilemapDamage.OnExposed(applyExposure.FireExposure); } Profiler.EndSample(); } }
public override void Process(NetMessage msg) { var clientStorage = SentByPlayer.Script.DynamicItemStorage; var usedSlot = clientStorage.GetActiveHandSlot(); if (usedSlot == null || usedSlot.ItemObject == null) { return; } var hasConstructionMenu = usedSlot.ItemObject.GetComponent <BuildingMaterial>(); if (hasConstructionMenu == null) { return; } var entry = hasConstructionMenu.BuildList.Entries.ToArray()[msg.EntryIndex]; if (!entry.CanBuildWith(hasConstructionMenu)) { return; } //check if the space to construct on is passable if (!MatrixManager.IsPassableAtAllMatricesOneTile((Vector3Int)SentByPlayer.GameObject.TileWorldPosition(), true, includingPlayers: false)) { Chat.AddExamineMsg(SentByPlayer.GameObject, "It won't fit here."); return; } //if we are building something impassable, check if there is anything on the space other than the performer. var atPosition = MatrixManager.GetAt <RegisterTile>((Vector3Int)SentByPlayer.GameObject.TileWorldPosition(), true); if (entry.Prefab == null) { //requires immediate attention, show it regardless of log filter: Logger.Log($"Construction entry is missing prefab for {entry.Name}", Category.Construction); return; } var registerTile = entry.Prefab.GetComponent <RegisterTile>(); if (registerTile == null) { Logger.LogWarningFormat("Buildable prefab {0} has no registerTile, no idea if it's passable", Category.Construction, entry.Prefab); } var builtObjectIsImpassable = registerTile == null || !registerTile.IsPassable(true); foreach (var thingAtPosition in atPosition) { if (entry.OnePerTile) { //can only build one of this on a given tile if (entry.Prefab.Equals(Spawn.DeterminePrefab(thingAtPosition.gameObject))) { Chat.AddExamineMsg(SentByPlayer.GameObject, $"There's already one here."); return; } } if (builtObjectIsImpassable) { //if the object we are building is itself impassable, we should check if anything blocks construciton. //otherwise it's fine to add it to the pile on the tile if (ServerValidations.IsConstructionBlocked(SentByPlayer.GameObject, null, SentByPlayer.GameObject.TileWorldPosition())) { return; } } } //build and consume void ProgressComplete() { var spawnedObj = entry.ServerBuild(SpawnDestination.At(SentByPlayer.Script.registerTile), hasConstructionMenu); if (spawnedObj) { var conveyorBelt = spawnedObj.GetComponent <ConveyorBelt>(); if (conveyorBelt != null) { conveyorBelt.SetBeltFromBuildMenu(msg.Direction); } Chat.AddActionMsgToChat(SentByPlayer.GameObject, $"You finish building the {entry.Name}.", $"{SentByPlayer.GameObject.ExpensiveName()} finishes building the {entry.Name}."); } } Chat.AddActionMsgToChat(SentByPlayer.GameObject, $"You begin building the {entry.Name}...", $"{SentByPlayer.GameObject.ExpensiveName()} begins building the {entry.Name}..."); ToolUtils.ServerUseTool(SentByPlayer.GameObject, usedSlot.ItemObject, ActionTarget.Tile(SentByPlayer.Script.registerTile.WorldPositionServer), entry.BuildTime, ProgressComplete); }
public void ServerPerformInteraction(HandApply interaction) { if (interaction.TargetObject != gameObject) { return; } if (Validations.HasItemTrait(interaction.HandObject, CommonTraits.Instance.MetalSheet)) { if (objectBehaviour.IsPushable) { if (!Validations.HasAtLeast(interaction.HandObject, 2)) { Chat.AddExamineMsg(interaction.Performer, "You need two sheets of metal to finish a false wall!"); return; } ToolUtils.ServerUseToolWithActionMessages(interaction, 4f, "You start adding plating...", $"{interaction.Performer.ExpensiveName()} begins adding plating...", "You create a false wall.", $"{interaction.Performer.ExpensiveName()} creates a false wall.", () => ConstructFalseWall(interaction)); } else { if (!Validations.HasAtLeast(interaction.HandObject, 2)) { Chat.AddExamineMsg(interaction.Performer, "You need two sheets of metal to finish a wall!"); return; } ToolUtils.ServerUseToolWithActionMessages(interaction, 4f, "You start adding plating...", $"{interaction.Performer.ExpensiveName()} begins adding plating...", "You create a wall.", $"{interaction.Performer.ExpensiveName()} creates a wall.", () => ConstructWall(interaction)); } } else if (Validations.HasItemTrait(interaction.HandObject, CommonTraits.Instance.PlasteelSheet)) { if (objectBehaviour.IsPushable) { if (!Validations.HasAtLeast(interaction.HandObject, 2)) { Chat.AddExamineMsg(interaction.Performer, "You need at least two sheets to create a reinforced false wall!"); return; } ToolUtils.ServerUseToolWithActionMessages(interaction, 4f, "You start adding plating...", $"{interaction.Performer.ExpensiveName()} begins adding plating...", "You add the plating.", $"{interaction.Performer.ExpensiveName()} adds the plating.", () => ConstructReinforcedFalseWall(interaction)); } else { //add plasteel for constructing reinforced girder ToolUtils.ServerUseToolWithActionMessages(interaction, 6f, "You start reinforcing the girder...", $"{interaction.Performer.ExpensiveName()} starts reinforcing the girder...", "You reinforce the girder.", $"{interaction.Performer.ExpensiveName()} reinforces the girder.", () => ReinforceGirder(interaction)); } } else if (Validations.HasItemTrait(interaction.HandObject, CommonTraits.Instance.Wrench)) { if (objectBehaviour.IsPushable) { //secure it if there's floor if (MatrixManager.IsSpaceAt(registerObject.WorldPositionServer, true)) { Chat.AddExamineMsg(interaction.Performer, "A floor must be present to secure the girder!"); return; } if (!ServerValidations.IsAnchorBlocked(interaction)) { ToolUtils.ServerUseToolWithActionMessages(interaction, 4f, "You start securing the girder...", $"{interaction.Performer.ExpensiveName()} starts securing the girder...", "You secure the girder.", $"{interaction.Performer.ExpensiveName()} secures the girder.", () => objectBehaviour.ServerSetAnchored(true, interaction.Performer)); } } else { //unsecure it ToolUtils.ServerUseToolWithActionMessages(interaction, 4f, "You start unsecuring the girder...", $"{interaction.Performer.ExpensiveName()} starts unsecuring the girder...", "You unsecure the girder.", $"{interaction.Performer.ExpensiveName()} unsecures the girder.", () => objectBehaviour.ServerSetAnchored(false, interaction.Performer)); } } else if (Validations.HasItemTrait(interaction.HandObject, CommonTraits.Instance.Screwdriver)) { //disassemble if it's unanchored if (objectBehaviour.IsPushable) { ToolUtils.ServerUseToolWithActionMessages(interaction, 4f, "You start to disassemble the girder...", $"{interaction.Performer.ExpensiveName()} starts to disassemble the girder...", "You disassemble the girder.", $"{interaction.Performer.ExpensiveName()} disassembles the girder.", () => Disassemble(interaction)); } else { Chat.AddExamineMsg(interaction.Performer, "You must unsecure it first."); } } }