예제 #1
0
        public override bool OnPicked(Character picker)
        {
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
            {
                return(false);
            }
            if (base.OnPicked(picker))
            {
                DeattachFromWall();

#if SERVER
                if (GameMain.Server != null && attachable)
                {
                    item.CreateServerEvent(this);
                    if (picker != null)
                    {
                        GameServer.Log(GameServer.CharacterLogName(picker) + " detached " + item.Name + " from a wall", ServerLog.MessageType.ItemInteraction);
                    }
                }
#endif
                return(true);
            }

            return(false);
        }
예제 #2
0
        private void SetActive(bool active, Character user = null)
        {
            PutItemsToLinkedContainer();

            if (inputContainer.Inventory.IsEmpty())
            {
                active = false;
            }

            IsActive             = active;
            currPowerConsumption = IsActive ? powerConsumption : 0.0f;
#if SERVER
            if (user != null)
            {
                GameServer.Log(GameServer.CharacterLogName(user) + (IsActive ? " activated " : " deactivated ") + item.Name, ServerLog.MessageType.ItemInteraction);
            }
#endif
            if (!IsActive)
            {
                progressTimer = 0.0f;
                progressState = 0.0f;
            }

#if CLIENT
            activateButton.Text = TextManager.Get(IsActive ? "DeconstructorCancel" : "DeconstructorDeconstruct");
#endif

            inputContainer.Inventory.Locked = IsActive;
        }
예제 #3
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            float newFlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
            bool  newIsActive       = msg.ReadBoolean();

            if (item.CanClientAccess(c))
            {
                if (newFlowPercentage != FlowPercentage)
                {
                    GameServer.Log(GameServer.CharacterLogName(c.Character) + " set the pumping speed of " + item.Name + " to " + (int)(newFlowPercentage) + " %", ServerLog.MessageType.ItemInteraction);
                }
                if (newIsActive != IsActive)
                {
                    GameServer.Log(GameServer.CharacterLogName(c.Character) + (newIsActive ? " turned on " : " turned off ") + item.Name, ServerLog.MessageType.ItemInteraction);
                }
                if (pumpSpeedLockTimer <= 0.0f)
                {
                    TargetLevel = null;
                }

                FlowPercentage = newFlowPercentage;
                IsActive       = newIsActive;
            }

            //notify all clients of the changed state
            item.CreateServerEvent(this);
        }
예제 #4
0
        partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log)
        {
            if (log)
            {
                if (causeOfDeath == CauseOfDeathType.Affliction)
                {
                    GameServer.Log(GameServer.CharacterLogName(this) + " has died (Cause of death: " + causeOfDeathAffliction.Prefab.Name + ")", ServerLog.MessageType.Attack);
                }
                else
                {
                    GameServer.Log(GameServer.CharacterLogName(this) + " has died (Cause of death: " + causeOfDeath + ")", ServerLog.MessageType.Attack);
                }
            }

            healthUpdateTimer = 0.0f;

            if (CauseOfDeath.Killer != null && CauseOfDeath.Killer.IsTraitor && CauseOfDeath.Killer != this)
            {
                var owner = GameMain.Server.ConnectedClients.Find(c => c.Character == this);
                if (owner != null)
                {
                    GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("KilledByTraitorNotification"), owner, ChatMessageType.ServerMessageBoxInGame);
                }
            }
            foreach (Client client in GameMain.Server.ConnectedClients)
            {
                if (client.InGame)
                {
                    client.PendingPositionUpdates.Enqueue(this);
                }
            }
        }
예제 #5
0
        private void SetActive(bool active, Character user = null)
        {
            PutItemsToLinkedContainer();

            this.user = user;

            if (inputContainer.Inventory.IsEmpty())
            {
                active = false;
            }

            IsActive                         = active;
            currPowerConsumption             = IsActive ? powerConsumption : 0.0f;
            userDeconstructorSpeedMultiplier = user != null ? 1f + user.GetStatValue(StatTypes.DeconstructorSpeedMultiplier) : 1f;

#if SERVER
            if (user != null)
            {
                GameServer.Log(GameServer.CharacterLogName(user) + (IsActive ? " activated " : " deactivated ") + item.Name, ServerLog.MessageType.ItemInteraction);
            }
#endif
            if (!IsActive)
            {
                progressTimer = 0.0f;
                progressState = 0.0f;
            }

            inputContainer.Inventory.Locked = IsActive;
        }
예제 #6
0
        public override void Equip(Character character)
        {
            picker = character;

            if (item.Removed)
            {
                DebugConsole.ThrowError($"Attempted to equip a removed item ({item.Name})\n" + Environment.StackTrace);
                return;
            }

            if (character != null)
            {
                item.Submarine = character.Submarine;
            }
            if (item.body == null)
            {
                if (body != null)
                {
                    item.body = body;
                }
                else
                {
                    return;
                }
            }

            if (!item.body.Enabled)
            {
                Limb hand = picker.AnimController.GetLimb(LimbType.RightHand) ?? picker.AnimController.GetLimb(LimbType.LeftHand);
                item.SetTransform(hand != null ? hand.SimPosition : character.SimPosition, 0.0f);
            }

            bool alreadyEquipped = character.HasEquippedItem(item);
            bool canSelect       = picker.TrySelectItem(item);

            if (canSelect || picker.HasEquippedItem(item))
            {
                if (!canSelect)
                {
                    character.DeselectItem(item);
                }

                item.body.Enabled     = true;
                item.body.PhysEnabled = false;
                IsActive = true;

#if SERVER
                if (!alreadyEquipped)
                {
                    GameServer.Log(GameServer.CharacterLogName(character) + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction);
                }
#endif
            }
        }
예제 #7
0
            public bool Start(GameServer server, TraitorManager traitorManager, Character.TeamType team)
            {
                var assignedCandidates = AssignTraitors(server, traitorManager, team, true);

                if (assignedCandidates == null)
                {
                    return(false);
                }

                foreach (Client client in server.ConnectedClients)
                {
                    client.RoundsSincePlayedAsTraitor++;
                }

                Traitors.Clear();
                foreach (var candidate in assignedCandidates)
                {
                    var traitor = new Traitor(this, candidate.Item1, candidate.Item2.Item1.Character);
                    Traitors.Add(candidate.Item1, traitor);
                    candidate.Item2.Item1.RoundsSincePlayedAsTraitor = 0;
                }
                CodeWords    = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
                CodeResponse = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);

                /**if (pendingObjectives.Count <= 0 || !pendingObjectives[0].CanBeStarted(Traitors.Values))
                 * {
                 *  Traitors.Clear();
                 *  return false;
                 * }**/

                var pendingMessages = new Dictionary <Traitor, List <string> >();

                pendingMessages.Clear();
                foreach (var traitor in Traitors.Values)
                {
                    pendingMessages.Add(traitor, new List <string>());
                }
                foreach (var traitor in Traitors.Values)
                {
                    traitor.Greet(server, CodeWords, CodeResponse, message => pendingMessages[traitor].Add(message));
                }
                pendingMessages.ForEach(traitor => traitor.Value.ForEach(message => traitor.Key.SendChatMessage(message, Identifier)));
                pendingMessages.ForEach(traitor => traitor.Value.ForEach(message => traitor.Key.SendChatMessageBox(message, Identifier)));

                Update(0.0f, () => { GameMain.Server.TraitorManager.ShouldEndRound = true; });
#if SERVER
                foreach (var traitor in Traitors.Values)
                {
                    GameServer.Log($"{GameServer.CharacterLogName(traitor.Character)} is a traitor and the current goals are:\n{(traitor.CurrentObjective?.GoalInfos != null ? TextManager.GetServerMessage(traitor.CurrentObjective?.GoalInfos) : "(empty)")}", ServerLog.MessageType.ServerMessage);
                }
#endif
                return(true);
            }
예제 #8
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            float newRechargeSpeed = msg.ReadRangedInteger(0, 10) / 10.0f * maxRechargeSpeed;

            if (item.CanClientAccess(c))
            {
                RechargeSpeed = newRechargeSpeed;
                GameServer.Log(GameServer.CharacterLogName(c.Character) + " set the recharge speed of " + item.Name + " to " + (int)((rechargeSpeed / maxRechargeSpeed) * 100.0f) + " %", ServerLog.MessageType.ItemInteraction);
            }

            item.CreateServerEvent(this);
        }
예제 #9
0
        public override void Unequip(Character character)
        {
            if (picker == null) return;

            picker.DeselectItem(item);
#if SERVER
            GameServer.Log(GameServer.CharacterLogName(character) + " unequipped " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif

            item.body.PhysEnabled = true;
            item.body.Enabled = false;
            IsActive = false;
        }
예제 #10
0
        public override void ReceiveSignal(Signal signal, Connection connection)
        {
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
            {
                return;
            }

            if (dockingCooldown > 0.0f)
            {
                return;
            }

            bool        wasDocked         = docked;
            DockingPort prevDockingTarget = DockingTarget;

            switch (connection.Name)
            {
            case "toggle":
                if (signal.value != "0")
                {
                    Docked = !docked;
                }
                break;

            case "set_active":
            case "set_state":
                Docked = signal.value != "0";
                break;
            }

#if SERVER
            if (signal.sender != null && docked != wasDocked)
            {
                if (docked)
                {
                    if (item.Submarine != null && DockingTarget?.item?.Submarine != null)
                    {
                        GameServer.Log(GameServer.CharacterLogName(signal.sender) + " docked " + item.Submarine.Info.Name + " to " + DockingTarget.item.Submarine.Info.Name, ServerLog.MessageType.ItemInteraction);
                    }
                }
                else
                {
                    if (item.Submarine != null && prevDockingTarget?.item?.Submarine != null)
                    {
                        GameServer.Log(GameServer.CharacterLogName(signal.sender) + " undocked " + item.Submarine.Info.Name + " from " + prevDockingTarget.item.Submarine.Info.Name, ServerLog.MessageType.ItemInteraction);
                    }
                }
            }
#endif
        }
예제 #11
0
        public override void Equip(Character character)
        {
            picker = character;

            if (character != null)
            {
                item.Submarine = character.Submarine;
            }

            if (item.body == null)
            {
                if (body != null)
                {
                    item.body = body;
                }
                else
                {
                    return;
                }
            }

            if (!item.body.Enabled)
            {
                Limb rightHand = picker.AnimController.GetLimb(LimbType.RightHand);
                item.SetTransform(rightHand.SimPosition, 0.0f);
            }

            bool alreadyEquipped = character.HasEquippedItem(item);
            bool canSelect       = picker.TrySelectItem(item);

            if (canSelect || picker.HasEquippedItem(item))
            {
                if (!canSelect)
                {
                    character.DeselectItem(item);
                }

                item.body.Enabled     = true;
                item.body.PhysEnabled = false;
                IsActive = true;

#if SERVER
                if (!alreadyEquipped)
                {
                    GameServer.Log(GameServer.CharacterLogName(character) + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction);
                }
#endif
            }
        }
예제 #12
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            int signalIndex = msg.ReadRangedInteger(0, Signals.Length - 1);

            if (!item.CanClientAccess(c))
            {
                return;
            }
            if (!SendSignal(signalIndex))
            {
                return;
            }
            GameServer.Log($"{GameServer.CharacterLogName(c.Character)} sent a signal \"{Signals[signalIndex]}\" from {item.Name}", ServerLog.MessageType.ItemInteraction);
            item.CreateServerEvent(this, new object[] { signalIndex });
        }
예제 #13
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            float newTargetForce = msg.ReadRangedInteger(-10, 10) * 10.0f;

            if (item.CanClientAccess(c))
            {
                if (Math.Abs(newTargetForce - targetForce) > 0.01f)
                {
                    GameServer.Log(GameServer.CharacterLogName(c.Character) + " set the force of " + item.Name + " to " + (int)(newTargetForce) + " %", ServerLog.MessageType.ItemInteraction);
                }

                targetForce = newTargetForce;
            }

            //notify all clients of the changed state
            item.CreateServerEvent(this);
        }
예제 #14
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            string newOutputValue = msg.ReadString();

            if (item.CanClientAccess(c))
            {
                if (newOutputValue.Length > MaxMessageLength)
                {
                    newOutputValue = newOutputValue.Substring(0, MaxMessageLength);
                }
                GameServer.Log(GameServer.CharacterLogName(c.Character) + " entered \"" + newOutputValue + "\" on " + item.Name,
                               ServerLog.MessageType.ItemInteraction);
                OutputValue = newOutputValue;
                item.SendSignal(0, newOutputValue, "signal_out", null);
                item.CreateServerEvent(this);
            }
        }
예제 #15
0
        public override void Unequip(Character character)
        {
#if SERVER
            if (prevEquipper != null)
            {
                GameServer.Log(GameServer.CharacterLogName(character) + " unequipped " + item.Name, ServerLog.MessageType.ItemInteraction);
            }
#endif
            prevEquipper = null;
            if (picker == null)
            {
                return;
            }
            item.body.PhysEnabled = true;
            item.body.Enabled     = false;
            IsActive = false;
        }
예제 #16
0
        public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
        {
#if CLIENT
            if (GameMain.Client != null)
            {
                return;
            }
#endif

            bool        wasDocked         = docked;
            DockingPort prevDockingTarget = DockingTarget;

            switch (connection.Name)
            {
            case "toggle":
                Docked = !docked;
                break;

            case "set_active":
            case "set_state":
                Docked = signal != "0";
                break;
            }

#if SERVER
            if (sender != null && docked != wasDocked)
            {
                if (docked)
                {
                    if (item.Submarine != null && DockingTarget?.item?.Submarine != null)
                    {
                        GameServer.Log(GameServer.CharacterLogName(sender) + " docked " + item.Submarine.Info.Name + " to " + DockingTarget.item.Submarine.Info.Name, ServerLog.MessageType.ItemInteraction);
                    }
                }
                else
                {
                    if (item.Submarine != null && prevDockingTarget?.item?.Submarine != null)
                    {
                        GameServer.Log(GameServer.CharacterLogName(sender) + " undocked " + item.Submarine.Info.Name + " from " + prevDockingTarget.item.Submarine.Info.Name, ServerLog.MessageType.ItemInteraction);
                    }
                }
            }
#endif
        }
예제 #17
0
        public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
        {
            if (IsStuck || IsJammed)
            {
                return;
            }

            bool wasOpen = PredictedState == null ? isOpen : PredictedState.Value;

            if (connection.Name == "toggle")
            {
                if (signal == "0")
                {
                    return;
                }
                if (toggleCooldownTimer > 0.0f && sender != lastUser)
                {
                    OnFailedToOpen(); return;
                }
                if (IsStuck)
                {
                    toggleCooldownTimer = 1.0f; OnFailedToOpen(); return;
                }
                toggleCooldownTimer = ToggleCoolDown;
                lastUser            = sender;
                SetState(!wasOpen, false, true, forcedOpen: false);
            }
            else if (connection.Name == "set_state")
            {
                bool signalOpen = signal != "0";
                if (IsStuck && signalOpen != wasOpen)
                {
                    toggleCooldownTimer = 1.0f; OnFailedToOpen(); return;
                }
                SetState(signalOpen, false, true, forcedOpen: false);
            }

#if SERVER
            if (sender != null && wasOpen != isOpen)
            {
                GameServer.Log(GameServer.CharacterLogName(sender) + (isOpen ? " opened " : " closed ") + item.Name, ServerLog.MessageType.ItemInteraction);
            }
#endif
        }
예제 #18
0
        private void StartFabricating(FabricationRecipe selectedItem, Character user)
        {
            if (selectedItem == null)
            {
                return;
            }
            if (!outputContainer.Inventory.IsEmpty())
            {
                return;
            }

#if CLIENT
            itemList.Enabled    = false;
            activateButton.Text = TextManager.Get("FabricatorCancel");
#endif

            IsActive       = true;
            this.user      = user;
            fabricatedItem = selectedItem;
            MoveIngredientsToInputContainer(selectedItem);

            requiredTime   = GetRequiredTime(fabricatedItem, user);
            timeUntilReady = requiredTime;

            inputContainer.Inventory.Locked  = true;
            outputContainer.Inventory.Locked = true;

            currPowerConsumption = powerConsumption;
            item.GetComponent <Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);

            if (GameMain.NetworkMember?.IsServer ?? true)
            {
                State = FabricatorState.Active;
            }
#if SERVER
            if (user != null)
            {
                GameServer.Log(GameServer.CharacterLogName(user) + " started fabricating " + selectedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
            }
#endif
        }
예제 #19
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());

            if (!item.CanClientAccess(c) || !Attachable || attached || !MathUtils.IsValid(simPosition))
            {
                return;
            }

            Vector2 offset = simPosition - c.Character.SimPosition;

            offset      = offset.ClampLength(MaxAttachDistance * 1.5f);
            simPosition = c.Character.SimPosition + offset;

            Drop(false, null);
            item.SetTransform(simPosition, 0.0f);
            AttachToWall();

            item.CreateServerEvent(this);
            GameServer.Log(GameServer.CharacterLogName(c.Character) + " attached " + item.Name + " to a wall", ServerLog.MessageType.ItemInteraction);
        }
예제 #20
0
        private void CancelFabricating(Character user = null)
        {
            if (fabricatedItem == null)
            {
                return;
            }

            IsActive       = false;
            fabricatedItem = null;
            this.user      = null;

            currPowerConsumption = 0.0f;

#if CLIENT
            itemList.Enabled = true;
            if (activateButton != null)
            {
                activateButton.Text = TextManager.Get("FabricatorCreate");
            }
#endif
            progressState = 0.0f;

            timeUntilReady = 0.0f;

            inputContainer.Inventory.Locked  = false;
            outputContainer.Inventory.Locked = false;

            if (GameMain.NetworkMember?.IsServer ?? true)
            {
                State = FabricatorState.Stopped;
            }
#if SERVER
            if (user != null)
            {
                GameServer.Log(GameServer.CharacterLogName(user) + " cancelled the fabrication of " + fabricatedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
            }
#endif
        }
예제 #21
0
        partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log)
        {
            if (log)
            {
                if (causeOfDeath == CauseOfDeathType.Affliction)
                {
                    GameServer.Log(GameServer.CharacterLogName(this) + " has died (Cause of death: " + causeOfDeathAffliction.Prefab.Name + ")", ServerLog.MessageType.Attack);
                }
                else
                {
                    GameServer.Log(GameServer.CharacterLogName(this) + " has died (Cause of death: " + causeOfDeath + ")", ServerLog.MessageType.Attack);
                }
            }

            healthUpdateTimer = 0.0f;

            foreach (Client client in GameMain.Server.ConnectedClients)
            {
                if (client.InGame)
                {
                    client.PendingPositionUpdates.Enqueue(this);
                }
            }
        }
예제 #22
0
        private bool TryLaunch(float deltaTime, Character character = null, bool ignorePower = false)
        {
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
            {
                return(false);
            }

            if (reload > 0.0f)
            {
                return(false);
            }

            if (MaxActiveProjectiles >= 0)
            {
                activeProjectiles.RemoveAll(it => it.Removed);
                if (activeProjectiles.Count >= MaxActiveProjectiles)
                {
                    return(false);
                }
            }

            if (!ignorePower)
            {
                if (GetAvailableBatteryPower() < powerConsumption)
                {
#if CLIENT
                    if (!flashLowPower && character != null && character == Character.Controlled)
                    {
                        flashLowPower = true;
                        GUI.PlayUISound(GUISoundType.PickItemFail);
                    }
#endif
                    return(false);
                }
            }

            Projectile launchedProjectile = null;
            for (int i = 0; i < ProjectileCount; i++)
            {
                foreach (MapEntity e in item.linkedTo)
                {
                    //use linked projectile containers in case they have to react to the turret being launched somehow
                    //(play a sound, spawn more projectiles)
                    if (!(e is Item linkedItem))
                    {
                        continue;
                    }
                    ItemContainer projectileContainer = linkedItem.GetComponent <ItemContainer>();
                    if (projectileContainer != null)
                    {
                        linkedItem.Use(deltaTime, null);
                        var repairable = linkedItem.GetComponent <Repairable>();
                        if (repairable != null && failedLaunchAttempts < 2)
                        {
                            repairable.LastActiveTime = (float)Timing.TotalTime + 1.0f;
                        }
                    }
                }
                var projectiles = GetLoadedProjectiles(true);
                if (projectiles.Count == 0 && !LaunchWithoutProjectile)
                {
                    //coilguns spawns ammo in the ammo boxes with the OnUse statuseffect when the turret is launched,
                    //causing a one frame delay before the gun can be launched (or more in multiplayer where there may be a longer delay)
                    //  -> attempt to launch the gun multiple times before showing the "no ammo" flash
                    failedLaunchAttempts++;
#if CLIENT
                    if (!flashNoAmmo && character != null && character == Character.Controlled && failedLaunchAttempts > 20)
                    {
                        flashNoAmmo          = true;
                        failedLaunchAttempts = 0;
                        GUI.PlayUISound(GUISoundType.PickItemFail);
                    }
#endif
                    return(false);
                }
                failedLaunchAttempts = 0;
                launchedProjectile   = projectiles.FirstOrDefault();

                if (!ignorePower)
                {
                    var   batteries   = item.GetConnectedComponents <PowerContainer>();
                    float neededPower = powerConsumption;
                    while (neededPower > 0.0001f && batteries.Count > 0)
                    {
                        batteries.RemoveAll(b => b.Charge <= 0.0001f || b.MaxOutPut <= 0.0001f);
                        float takePower = neededPower / batteries.Count;
                        takePower = Math.Min(takePower, batteries.Min(b => Math.Min(b.Charge * 3600.0f, b.MaxOutPut)));
                        foreach (PowerContainer battery in batteries)
                        {
                            neededPower    -= takePower;
                            battery.Charge -= takePower / 3600.0f;
#if SERVER
                            battery.Item.CreateServerEvent(battery);
#endif
                        }
                    }
                }

                if (launchedProjectile != null || LaunchWithoutProjectile)
                {
                    Launch(launchedProjectile?.Item, character);
                }
            }

#if SERVER
            if (character != null && launchedProjectile != null)
            {
                string msg            = GameServer.CharacterLogName(character) + " launched " + item.Name + " (projectile: " + launchedProjectile.Item.Name;
                var    containedItems = launchedProjectile.Item.ContainedItems;
                if (containedItems == null || !containedItems.Any())
                {
                    msg += ")";
                }
                else
                {
                    msg += ", contained items: " + string.Join(", ", containedItems.Select(i => i.Name)) + ")";
                }
                GameServer.Log(msg, ServerLog.MessageType.ItemInteraction);
            }
#endif

            return(true);
        }
예제 #23
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            List <Item> prevItems = new List <Item>(Items);

            byte itemCount = msg.ReadByte();

            ushort[] newItemIDs = new ushort[itemCount];
            for (int i = 0; i < itemCount; i++)
            {
                newItemIDs[i] = msg.ReadUInt16();
            }

            if (c == null || c.Character == null)
            {
                return;
            }

            bool accessible = c.Character.CanAccessInventory(this);

            if (this is CharacterInventory && accessible)
            {
                if (Owner == null || !(Owner is Character))
                {
                    accessible = false;
                }
                else if (!((CharacterInventory)this).AccessibleWhenAlive && !((Character)Owner).IsDead)
                {
                    accessible = false;
                }
            }

            if (!accessible)
            {
                //create a network event to correct the client's inventory state
                //otherwise they may have an item in their inventory they shouldn't have been able to pick up,
                //and receiving an event for that inventory later will cause the item to be dropped
                CreateNetworkEvent();
                for (int i = 0; i < capacity; i++)
                {
                    if (!(Entity.FindEntityByID(newItemIDs[i]) is Item item))
                    {
                        continue;
                    }
                    item.PositionUpdateInterval = 0.0f;
                    if (item.ParentInventory != null && item.ParentInventory != this)
                    {
                        item.ParentInventory.CreateNetworkEvent();
                    }
                }
                return;
            }

            List <Inventory> prevItemInventories = new List <Inventory>(Items.Select(i => i?.ParentInventory));

            for (int i = 0; i < capacity; i++)
            {
                Item newItem = newItemIDs[i] == 0 ? null : Entity.FindEntityByID(newItemIDs[i]) as Item;
                prevItemInventories.Add(newItem?.ParentInventory);

                if (newItemIDs[i] == 0 || (newItem != Items[i]))
                {
                    if (Items[i] != null)
                    {
                        Item   droppedItem = Items[i];
                        Entity prevOwner   = Owner;
                        droppedItem.Drop(null);

                        var previousInventory = prevOwner switch
                        {
                            Item itemInventory => (itemInventory.FindParentInventory(inventory => inventory is CharacterInventory) as CharacterInventory),
                            Character character => character.Inventory,
                            _ => null
                        };

                        if (previousInventory != null && previousInventory != c.Character?.Inventory)
                        {
                            GameMain.Server?.KarmaManager.OnItemTakenFromPlayer(previousInventory, c, droppedItem);
                        }

                        if (droppedItem.body != null && prevOwner != null)
                        {
                            droppedItem.body.SetTransform(prevOwner.SimPosition, 0.0f);
                        }
                    }
                    System.Diagnostics.Debug.Assert(Items[i] == null);
                }
            }

            for (int i = 0; i < capacity; i++)
            {
                if (newItemIDs[i] > 0)
                {
                    if (!(Entity.FindEntityByID(newItemIDs[i]) is Item item) || item == Items[i])
                    {
                        continue;
                    }

                    if (GameMain.Server != null)
                    {
                        var holdable = item.GetComponent <Holdable>();
                        if (holdable != null && !holdable.CanBeDeattached())
                        {
                            continue;
                        }

                        if (!prevItems.Contains(item) && !item.CanClientAccess(c))
                        {
#if DEBUG || UNSTABLE
                            DebugConsole.NewMessage($"Client {c.Name} failed to pick up item \"{item}\" (parent inventory: {(item.ParentInventory?.Owner.ToString() ?? "null")}). No access.", Color.Yellow);
#endif
                            if (item.body != null && !c.PendingPositionUpdates.Contains(item))
                            {
                                c.PendingPositionUpdates.Enqueue(item);
                            }
                            item.PositionUpdateInterval = 0.0f;
                            continue;
                        }
                    }
                    TryPutItem(item, i, true, true, c.Character, false);
                    for (int j = 0; j < capacity; j++)
                    {
                        if (Items[j] == item && newItemIDs[j] != item.ID)
                        {
                            Items[j] = null;
                        }
                    }
                }
            }

            CreateNetworkEvent();
            foreach (Inventory prevInventory in prevItemInventories.Distinct())
            {
                if (prevInventory != this)
                {
                    prevInventory?.CreateNetworkEvent();
                }
            }

            foreach (Item item in Items.Distinct())
            {
                if (item == null)
                {
                    continue;
                }
                if (!prevItems.Contains(item))
                {
                    if (Owner == c.Character)
                    {
                        GameServer.Log(GameServer.CharacterLogName(c.Character) + " picked up " + item.Name, ServerLog.MessageType.Inventory);
                    }
                    else
                    {
                        GameServer.Log(GameServer.CharacterLogName(c.Character) + " placed " + item.Name + " in " + Owner, ServerLog.MessageType.Inventory);
                    }
                }
            }
            foreach (Item item in prevItems.Distinct())
            {
                if (item == null)
                {
                    continue;
                }
                if (!Items.Contains(item))
                {
                    if (Owner == c.Character)
                    {
                        GameServer.Log(GameServer.CharacterLogName(c.Character) + " dropped " + item.Name, ServerLog.MessageType.Inventory);
                    }
                    else
                    {
                        GameServer.Log(GameServer.CharacterLogName(c.Character) + " removed " + item.Name + " from " + Owner, ServerLog.MessageType.Inventory);
                    }
                }
            }
        }
예제 #24
0
        public override void Update(float deltaTime, Camera cam)
        {
            if (!item.body.Enabled)
            {
                return;
            }
            if (midAir)
            {
                if (item.body.LinearVelocity.LengthSquared() < 0.01f)
                {
                    CurrentThrower = null;
                    if (statusEffectLists?.ContainsKey(ActionType.OnImpact) ?? false)
                    {
                        foreach (var statusEffect in statusEffectLists[ActionType.OnImpact])
                        {
                            statusEffect.SetUser(null);
                        }
                    }
                    if (statusEffectLists?.ContainsKey(ActionType.OnBroken) ?? false)
                    {
                        foreach (var statusEffect in statusEffectLists[ActionType.OnBroken])
                        {
                            statusEffect.SetUser(null);
                        }
                    }
                    item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform;
                    midAir = false;
                }
                return;
            }

            if (picker == null || picker.Removed || !picker.HasSelectedItem(item))
            {
                IsActive = false;
                return;
            }

            if (picker.IsKeyDown(InputType.Aim) && picker.IsKeyHit(InputType.Shoot))
            {
                throwing = true;
            }
            if (!picker.IsKeyDown(InputType.Aim) && !throwing)
            {
                throwPos = 0.0f;
            }
            bool aim = picker.IsKeyDown(InputType.Aim) && picker.CanAim;

            if (picker.IsDead || !picker.AllowInput)
            {
                throwing = false;
                aim      = false;
            }

            ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);

            if (item.body.Dir != picker.AnimController.Dir)
            {
                item.FlipX(relativeToSub: false);
            }

            AnimController ac = picker.AnimController;

            item.Submarine = picker.Submarine;

            if (!throwing)
            {
                if (aim)
                {
                    throwPos = MathUtils.WrapAnglePi(System.Math.Min(throwPos + deltaTime * 5.0f, MathHelper.PiOver2));
                    ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, false, throwPos);
                }
                else
                {
                    throwPos = 0;
                    ac.HoldItem(deltaTime, item, handlePos, holdPos, Vector2.Zero, false, holdAngle);
                }
            }
            else
            {
                throwPos = MathUtils.WrapAnglePi(throwPos - deltaTime * 15.0f);
                ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, false, throwPos);

                if (throwPos < 0)
                {
                    Vector2 throwVector = Vector2.Normalize(picker.CursorWorldPosition - picker.WorldPosition);
                    //throw upwards if cursor is at the position of the character
                    if (!MathUtils.IsValid(throwVector))
                    {
                        throwVector = Vector2.UnitY;
                    }

#if SERVER
                    GameServer.Log(GameServer.CharacterLogName(picker) + " threw " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif
                    CurrentThrower = picker;
                    if (statusEffectLists?.ContainsKey(ActionType.OnImpact) ?? false)
                    {
                        foreach (var statusEffect in statusEffectLists[ActionType.OnImpact])
                        {
                            statusEffect.SetUser(CurrentThrower);
                        }
                    }
                    if (statusEffectLists?.ContainsKey(ActionType.OnBroken) ?? false)
                    {
                        foreach (var statusEffect in statusEffectLists[ActionType.OnBroken])
                        {
                            statusEffect.SetUser(CurrentThrower);
                        }
                    }

                    item.Drop(CurrentThrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
                    item.body.ApplyLinearImpulse(throwVector * ThrowForce * item.body.Mass * 3.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);

                    //disable platform collisions until the item comes back to rest again
                    item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
                    midAir = true;

                    ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
                    ac.GetLimb(LimbType.Torso).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);

                    Limb rightHand = ac.GetLimb(LimbType.RightHand);
                    item.body.AngularVelocity = rightHand.body.AngularVelocity;
                    throwPos  = 0;
                    throwDone = true;

                    if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
                    {
                        GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnSecondaryUse, this, CurrentThrower.ID });
                    }
                    if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
                    {
                        //Stun grenades, flares, etc. all have their throw-related things handled in "onSecondaryUse"
                        ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, CurrentThrower, user: CurrentThrower);
                    }
                    throwing = false;
                }
            }
        }
예제 #25
0
        public void ClearConnections(Character user = null)
        {
            nodes.Clear();
            sections.Clear();

            foreach (Item item in Item.ItemList)
            {
                var connectionPanel = item.GetComponent <ConnectionPanel>();
                if (connectionPanel != null && connectionPanel.DisconnectedWires.Contains(this) && !item.Removed)
                {
#if SERVER
                    item.CreateServerEvent(connectionPanel);
#endif
                    connectionPanel.DisconnectedWires.Remove(this);
                }
            }

#if SERVER
            if (user != null)
            {
                if (connections[0] != null || connections[1] != null)
                {
                    GameMain.Server.KarmaManager.OnWireDisconnected(user, this);
                }

                if (connections[0] != null && connections[1] != null)
                {
                    GameServer.Log(GameServer.CharacterLogName(user) + " disconnected a wire from " +
                                   connections[0].Item.Name + " (" + connections[0].Name + ") to " +
                                   connections[1].Item.Name + " (" + connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
                }
                else if (connections[0] != null)
                {
                    GameServer.Log(GameServer.CharacterLogName(user) + " disconnected a wire from " +
                                   connections[0].Item.Name + " (" + connections[0].Name + ")", ServerLog.MessageType.ItemInteraction);
                }
                else if (connections[1] != null)
                {
                    GameServer.Log(GameServer.CharacterLogName(user) + " disconnected a wire from " +
                                   connections[1].Item.Name + " (" + connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
                }
            }
#endif

            SetConnectedDirty();

            for (int i = 0; i < 2; i++)
            {
                if (connections[i] == null)
                {
                    continue;
                }
                int wireIndex = connections[i].FindWireIndex(item);
                if (wireIndex == -1)
                {
                    continue;
                }
#if SERVER
                if (!connections[i].Item.Removed && (!connections[i].Item.Submarine?.Loading ?? true) && (!Level.Loaded?.Generating ?? true))
                {
                    connections[i].Item.CreateServerEvent(connections[i].Item.GetComponent <ConnectionPanel>());
                }
#endif
                connections[i].SetWire(wireIndex, null);
                connections[i] = null;
            }

            Drawable = sections.Count > 0;
        }
예제 #26
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            NetEntityEvent.Type eventType =
                (NetEntityEvent.Type)msg.ReadRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);

            c.KickAFKTimer = 0.0f;

            switch (eventType)
            {
            case NetEntityEvent.Type.ComponentState:
                int componentIndex = msg.ReadRangedInteger(0, components.Count - 1);
                (components[componentIndex] as IClientSerializable).ServerRead(type, msg, c);
                break;

            case NetEntityEvent.Type.InventoryState:
                int containerIndex = msg.ReadRangedInteger(0, components.Count - 1);
                (components[containerIndex] as ItemContainer).Inventory.ServerRead(type, msg, c);
                break;

            case NetEntityEvent.Type.Treatment:
                if (c.Character == null || !c.Character.CanInteractWith(this))
                {
                    return;
                }

                UInt16 characterID = msg.ReadUInt16();
                byte   limbIndex   = msg.ReadByte();

                Character targetCharacter = FindEntityByID(characterID) as Character;
                if (targetCharacter == null)
                {
                    break;
                }
                if (targetCharacter != c.Character && c.Character.SelectedCharacter != targetCharacter)
                {
                    break;
                }

                Limb targetLimb = limbIndex < targetCharacter.AnimController.Limbs.Length ? targetCharacter.AnimController.Limbs[limbIndex] : null;

                if (ContainedItems == null || ContainedItems.All(i => i == null))
                {
                    GameServer.Log(GameServer.CharacterLogName(c.Character) + " used item " + Name, ServerLog.MessageType.ItemInteraction);
                }
                else
                {
                    GameServer.Log(
                        GameServer.CharacterLogName(c.Character) + " used item " + Name + " (contained items: " + string.Join(", ", ContainedItems.Select(i => i.Name)) + ")",
                        ServerLog.MessageType.ItemInteraction);
                }

                ApplyTreatment(c.Character, targetCharacter, targetLimb);

                break;

            case NetEntityEvent.Type.ChangeProperty:
                ReadPropertyChange(msg, inGameEditableOnly: GameMain.NetworkMember.IsServer, sender: c);
                break;

            case NetEntityEvent.Type.Combine:
                UInt16 combineTargetID = msg.ReadUInt16();
                Item   combineTarget   = FindEntityByID(combineTargetID) as Item;
                if (combineTarget == null || !c.Character.CanInteractWith(this) || !c.Character.CanInteractWith(combineTarget))
                {
                    return;
                }
                Combine(combineTarget, c.Character);
                break;
            }
        }
예제 #27
0
        public override void Equip(Character character)
        {
            //if the item has multiple Pickable components (e.g. Holdable and Wearable, check that we don't equip it in hands when the item is worn or vice versa)
            if (item.GetComponents <Pickable>().Count() > 0)
            {
                bool inSuitableSlot = false;
                for (int i = 0; i < character.Inventory.Capacity; i++)
                {
                    if (character.Inventory.GetItemsAt(i).Contains(item))
                    {
                        if (character.Inventory.SlotTypes[i] != InvSlotType.Any &&
                            allowedSlots.Any(a => a.HasFlag(character.Inventory.SlotTypes[i])))
                        {
                            inSuitableSlot = true;
                            break;
                        }
                    }
                }
                if (!inSuitableSlot)
                {
                    return;
                }
            }

            picker = character;

            if (item.Removed)
            {
                DebugConsole.ThrowError($"Attempted to equip a removed item ({item.Name})\n" + Environment.StackTrace.CleanupStackTrace());
                return;
            }

            var wearable = item.GetComponent <Wearable>();

            if (wearable != null)
            {
                //cannot hold and wear an item at the same time
                wearable.Unequip(character);
            }

            if (character != null)
            {
                item.Submarine = character.Submarine;
            }
            if (item.body == null)
            {
                if (body != null)
                {
                    item.body = body;
                }
                else
                {
                    return;
                }
            }

            if (!item.body.Enabled)
            {
                Limb hand = picker.AnimController.GetLimb(LimbType.RightHand) ?? picker.AnimController.GetLimb(LimbType.LeftHand);
                item.SetTransform(hand != null ? hand.SimPosition : character.SimPosition, 0.0f);
            }

            bool alreadyEquipped = character.HasEquippedItem(item);

            if (picker.HasEquippedItem(item))
            {
                item.body.Enabled     = true;
                item.body.PhysEnabled = false;
                IsActive = true;

#if SERVER
                if (picker != prevEquipper)
                {
                    GameServer.Log(GameServer.CharacterLogName(character) + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction);
                }
#endif
                prevEquipper = picker;
            }
            else
            {
                prevEquipper = null;
            }
        }
예제 #28
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            List <Wire>[] wires = new List <Wire> [Connections.Count];

            //read wire IDs for each connection
            for (int i = 0; i < Connections.Count; i++)
            {
                wires[i] = new List <Wire>();
                for (int j = 0; j < Connection.MaxLinked; j++)
                {
                    ushort wireId = msg.ReadUInt16();

                    if (!(Entity.FindEntityByID(wireId) is Item wireItem))
                    {
                        continue;
                    }

                    Wire wireComponent = wireItem.GetComponent <Wire>();
                    if (wireComponent != null)
                    {
                        wires[i].Add(wireComponent);
                    }
                }
            }

            List <Wire> clientSideDisconnectedWires = new List <Wire>();
            ushort      disconnectedWireCount       = msg.ReadUInt16();

            for (int i = 0; i < disconnectedWireCount; i++)
            {
                ushort wireId = msg.ReadUInt16();
                if (!(Entity.FindEntityByID(wireId) is Item wireItem))
                {
                    continue;
                }
                Wire wireComponent = wireItem.GetComponent <Wire>();
                if (wireComponent == null)
                {
                    continue;
                }
                clientSideDisconnectedWires.Add(wireComponent);
            }

            //don't allow rewiring locked panels
            if (Locked || !GameMain.NetworkMember.ServerSettings.AllowRewiring)
            {
                return;
            }

            item.CreateServerEvent(this);

            //check if the character can access this connectionpanel
            //and all the wires they're trying to connect
            if (!item.CanClientAccess(c))
            {
                return;
            }
            for (int i = 0; i < Connections.Count; i++)
            {
                foreach (Wire wire in wires[i])
                {
                    //wire not found in any of the connections yet (client is trying to connect a new wire)
                    //  -> we need to check if the client has access to it
                    if (!Connections.Any(connection => connection.Wires.Contains(wire)) && !DisconnectedWires.Contains(wire))
                    {
                        if (!wire.Item.CanClientAccess(c))
                        {
                            return;
                        }
                    }
                }
            }

            if (!CheckCharacterSuccess(c.Character))
            {
                item.CreateServerEvent(this);
                c.Character.Inventory?.CreateNetworkEvent();
                for (int i = 0; i < 2; i++)
                {
                    var selectedWire = c.Character.SelectedItems[i]?.GetComponent <Wire>();
                    if (selectedWire == null)
                    {
                        continue;
                    }

                    selectedWire.CreateNetworkEvent();
                    var panel1 = selectedWire.Connections[0]?.ConnectionPanel;
                    if (panel1 != null && panel1 != this)
                    {
                        panel1.item.CreateServerEvent(panel1);
                    }
                    var panel2 = selectedWire.Connections[1]?.ConnectionPanel;
                    if (panel2 != null && panel2 != this)
                    {
                        panel2.item.CreateServerEvent(panel2);
                    }

                    CoroutineManager.InvokeAfter(() =>
                    {
                        item.CreateServerEvent(this);
                        if (panel1 != null && panel1 != this)
                        {
                            panel1.item.CreateServerEvent(panel1);
                        }
                        if (panel2 != null && panel2 != this)
                        {
                            panel2.item.CreateServerEvent(panel2);
                        }
                        if (!selectedWire.Item.Removed)
                        {
                            selectedWire.CreateNetworkEvent();
                        }
                    }, 1.0f);
                }
                GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, this, c.Character.ID });
                return;
            }

            //go through existing wire links
            for (int i = 0; i < Connections.Count; i++)
            {
                int j = -1;
                foreach (Wire existingWire in Connections[i].Wires)
                {
                    j++;
                    if (existingWire == null)
                    {
                        continue;
                    }

                    //existing wire not in the list of new wires -> disconnect it
                    if (!wires[i].Contains(existingWire))
                    {
                        if (existingWire.Locked)
                        {
                            //this should not be possible unless the client is running a modified version of the game
                            GameServer.Log(GameServer.CharacterLogName(c.Character) + " attempted to disconnect a locked wire from " +
                                           Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.Error);
                            continue;
                        }

                        existingWire.RemoveConnection(item);
                        if (existingWire.Item.ParentInventory == null)
                        {
                            item.GetComponent <ConnectionPanel>()?.DisconnectedWires.Add(existingWire);
                        }

                        if (!wires.Any(w => w.Contains(existingWire)))
                        {
                            GameMain.Server.KarmaManager.OnWireDisconnected(c.Character, existingWire);
                        }

                        if (existingWire.Connections[0] == null && existingWire.Connections[1] == null)
                        {
                            GameServer.Log(GameServer.CharacterLogName(c.Character) + " disconnected a wire from " +
                                           Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.Wiring);

                            if (existingWire.Item.ParentInventory != null)
                            {
                                //in an inventory and not connected to anything -> the wire cannot have any nodes
                                existingWire.ClearConnections();
                            }
                            else if (!clientSideDisconnectedWires.Contains(existingWire))
                            {
                                //not in an inventory, not connected to anything, not hanging loose from any panel -> must be dropped
                                existingWire.Item.Drop(c.Character);
                            }
                        }
                        else if (existingWire.Connections[0] != null)
                        {
                            GameServer.Log(GameServer.CharacterLogName(c.Character) + " disconnected a wire from " +
                                           Connections[i].Item.Name + " (" + Connections[i].Name + ") to " + existingWire.Connections[0].Item.Name + " (" + existingWire.Connections[0].Name + ")", ServerLog.MessageType.Wiring);

                            //wires that are not in anyone's inventory (i.e. not currently being rewired)
                            //can never be connected to only one connection
                            // -> the client must have dropped the wire from the connection panel

                            /*if (existingWire.Item.ParentInventory == null && !wires.Any(w => w.Contains(existingWire)))
                             * {
                             *  //let other clients know the item was also disconnected from the other connection
                             *  existingWire.Connections[0].Item.CreateServerEvent(existingWire.Connections[0].Item.GetComponent<ConnectionPanel>());
                             *  existingWire.Item.Drop(c.Character);
                             * }*/
                        }
                        else if (existingWire.Connections[1] != null)
                        {
                            GameServer.Log(GameServer.CharacterLogName(c.Character) + " disconnected a wire from " +
                                           Connections[i].Item.Name + " (" + Connections[i].Name + ") to " + existingWire.Connections[1].Item.Name + " (" + existingWire.Connections[1].Name + ")", ServerLog.MessageType.Wiring);

                            /*if (existingWire.Item.ParentInventory == null && !wires.Any(w => w.Contains(existingWire)))
                             * {
                             *  //let other clients know the item was also disconnected from the other connection
                             *  existingWire.Connections[1].Item.CreateServerEvent(existingWire.Connections[1].Item.GetComponent<ConnectionPanel>());
                             *  existingWire.Item.Drop(c.Character);
                             * }*/
                        }

                        Connections[i].SetWire(j, null);
                    }
                }
            }

            foreach (Wire disconnectedWire in DisconnectedWires.ToList())
            {
                if (disconnectedWire.Connections[0] == null &&
                    disconnectedWire.Connections[1] == null &&
                    !clientSideDisconnectedWires.Contains(disconnectedWire) &&
                    disconnectedWire.Item.ParentInventory == null)
                {
                    disconnectedWire.Item.Drop(c.Character);
                    GameServer.Log(GameServer.CharacterLogName(c.Character) + " dropped " + disconnectedWire.Name, ServerLog.MessageType.Inventory);
                }
            }

            //go through new wires
            for (int i = 0; i < Connections.Count; i++)
            {
                foreach (Wire newWire in wires[i])
                {
                    //already connected, no need to do anything
                    if (Connections[i].Wires.Contains(newWire))
                    {
                        continue;
                    }

                    Connections[i].TryAddLink(newWire);
                    newWire.Connect(Connections[i], true, true);

                    var otherConnection = newWire.OtherConnection(Connections[i]);

                    if (otherConnection == null)
                    {
                        GameServer.Log(GameServer.CharacterLogName(c.Character) + " connected a wire to " +
                                       Connections[i].Item.Name + " (" + Connections[i].Name + ")",
                                       ServerLog.MessageType.Wiring);
                    }
                    else
                    {
                        GameServer.Log(GameServer.CharacterLogName(c.Character) + " connected a wire from " +
                                       Connections[i].Item.Name + " (" + Connections[i].Name + ") to " +
                                       (otherConnection == null ? "none" : otherConnection.Item.Name + " (" + (otherConnection.Name) + ")"),
                                       ServerLog.MessageType.Wiring);
                    }
                }
            }
        }
예제 #29
0
        public override void Update(float deltaTime, Camera cam)
        {
#if SERVER
            if (GameMain.Server != null && nextServerLogWriteTime != null)
            {
                if (Timing.TotalTime >= (float)nextServerLogWriteTime)
                {
                    GameServer.Log(GameServer.CharacterLogName(lastUser) + " adjusted reactor settings: " +
                                   "Temperature: " + (int)(temperature * 100.0f) +
                                   ", Fission rate: " + (int)targetFissionRate +
                                   ", Turbine output: " + (int)targetTurbineOutput +
                                   (autoTemp ? ", Autotemp ON" : ", Autotemp OFF"),
                                   ServerLog.MessageType.ItemInteraction);

                    nextServerLogWriteTime = null;
                    lastServerLogWriteTime = (float)Timing.TotalTime;
                }
            }
#endif

            //if an AI character was using the item on the previous frame but not anymore, turn autotemp on
            // (= bots turn autotemp back on when leaving the reactor)
            if (LastAIUser != null)
            {
                if (LastAIUser.SelectedConstruction != item && LastAIUser.CanInteractWith(item))
                {
                    AutoTemp = true;
                    if (GameMain.NetworkMember?.IsServer ?? false)
                    {
                        unsentChanges = true;
                    }
                    LastAIUser = null;
                }
            }

#if CLIENT
            if (PowerOn && AvailableFuel < 1)
            {
                HintManager.OnReactorOutOfFuel(this);
            }
#endif

            prevAvailableFuel = AvailableFuel;
            ApplyStatusEffects(ActionType.OnActive, deltaTime, null);

            //use a smoothed "correct output" instead of the actual correct output based on the load
            //so the player doesn't have to keep adjusting the rate impossibly fast when the load fluctuates heavily
            if (!MathUtils.NearlyEqual(MaxPowerOutput, 0.0f))
            {
                correctTurbineOutput += MathHelper.Clamp((load / MaxPowerOutput * 100.0f) - correctTurbineOutput, -10.0f, 10.0f) * deltaTime;
            }

            //calculate tolerances of the meters based on the skills of the user
            //more skilled characters have larger "sweet spots", making it easier to keep the power output at a suitable level
            float tolerance = MathHelper.Lerp(2.5f, 10.0f, degreeOfSuccess);
            optimalTurbineOutput = new Vector2(correctTurbineOutput - tolerance, correctTurbineOutput + tolerance);
            tolerance            = MathHelper.Lerp(5.0f, 20.0f, degreeOfSuccess);
            allowedTurbineOutput = new Vector2(correctTurbineOutput - tolerance, correctTurbineOutput + tolerance);

            optimalTemperature = Vector2.Lerp(new Vector2(40.0f, 60.0f), new Vector2(30.0f, 70.0f), degreeOfSuccess);
            allowedTemperature = Vector2.Lerp(new Vector2(30.0f, 70.0f), new Vector2(10.0f, 90.0f), degreeOfSuccess);

            optimalFissionRate   = Vector2.Lerp(new Vector2(30, AvailableFuel - 20), new Vector2(20, AvailableFuel - 10), degreeOfSuccess);
            optimalFissionRate.X = Math.Min(optimalFissionRate.X, optimalFissionRate.Y - 10);
            allowedFissionRate   = Vector2.Lerp(new Vector2(20, AvailableFuel), new Vector2(10, AvailableFuel), degreeOfSuccess);
            allowedFissionRate.X = Math.Min(allowedFissionRate.X, allowedFissionRate.Y - 10);

            float heatAmount = GetGeneratedHeat(fissionRate);

            float temperatureDiff = (heatAmount - turbineOutput) - Temperature;
            Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff));
            //if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime;

            FissionRate = MathHelper.Lerp(fissionRate, Math.Min(targetFissionRate, AvailableFuel), deltaTime);

            TurbineOutput = MathHelper.Lerp(turbineOutput, targetTurbineOutput, deltaTime);

            float temperatureFactor = Math.Min(temperature / 50.0f, 1.0f);
            currPowerConsumption = -MaxPowerOutput *Math.Min(turbineOutput / 100.0f, temperatureFactor);

            //if the turbine output and coolant flow are the optimal range,
            //make the generated power slightly adjust according to the load
            //  (-> the reactor can automatically handle small changes in load as long as the values are roughly correct)
            if (turbineOutput > optimalTurbineOutput.X && turbineOutput < optimalTurbineOutput.Y &&
                temperature > optimalTemperature.X && temperature < optimalTemperature.Y)
            {
                float maxAutoAdjust = maxPowerOutput * 0.1f;
                autoAdjustAmount = MathHelper.Lerp(
                    autoAdjustAmount,
                    MathHelper.Clamp(-load - currPowerConsumption, -maxAutoAdjust, maxAutoAdjust),
                    deltaTime * 10.0f);
            }
            else
            {
                autoAdjustAmount = MathHelper.Lerp(autoAdjustAmount, 0.0f, deltaTime * 10.0f);
            }
            currPowerConsumption += autoAdjustAmount;

            if (!PowerOn)
            {
                targetFissionRate   = 0.0f;
                targetTurbineOutput = 0.0f;
            }
            else if (autoTemp)
            {
                UpdateAutoTemp(2.0f, deltaTime);
            }
            float             currentLoad = 0.0f;
            List <Connection> connections = item.Connections;
            if (connections != null && connections.Count > 0)
            {
                foreach (Connection connection in connections)
                {
                    if (!connection.IsPower)
                    {
                        continue;
                    }
                    foreach (Connection recipient in connection.Recipients)
                    {
                        if (!(recipient.Item is Item it))
                        {
                            continue;
                        }

                        PowerTransfer pt = it.GetComponent <PowerTransfer>();
                        if (pt == null)
                        {
                            continue;
                        }

                        //calculate how much external power there is in the grid
                        //(power coming from somewhere else than this reactor, e.g. batteries)
                        float externalPower = Math.Max(CurrPowerConsumption - pt.CurrPowerConsumption, 0) * 0.95f;
                        //reduce the external power from the load to prevent overloading the grid
                        currentLoad = Math.Max(currentLoad, pt.PowerLoad - externalPower);
                    }
                }
            }

            if (!loadQueue.Any() && PowerOn)
            {
                //loadQueue is empty, round must've just started
                //reset the fission rate, turbine output and
                //temperature to optimal levels to prevent fires
                //at the start of the round
                correctTurbineOutput = MathUtils.NearlyEqual(MaxPowerOutput, 0.0f) ? 0.0f : currentLoad / MaxPowerOutput * 100.0f;
                tolerance            = MathHelper.Lerp(2.5f, 10.0f, degreeOfSuccess);
                optimalTurbineOutput = new Vector2(correctTurbineOutput - tolerance, correctTurbineOutput + tolerance);
                tolerance            = MathHelper.Lerp(5.0f, 20.0f, degreeOfSuccess);
                allowedTurbineOutput = new Vector2(correctTurbineOutput - tolerance, correctTurbineOutput + tolerance);

                DebugConsole.Log($"Degree of success: {degreeOfSuccess}");
                DebugConsole.Log($"Current load: {currentLoad}");
                DebugConsole.Log($"Max power output: {MaxPowerOutput}");
                DebugConsole.Log($"Available fuel: {AvailableFuel}");

                float desiredTurbineOutput = MathHelper.Clamp(correctTurbineOutput, 0.0f, 100.0f);
                DebugConsole.Log($"Turbine output reset: {targetTurbineOutput}, {turbineOutput} -> {desiredTurbineOutput}");
                targetTurbineOutput = desiredTurbineOutput;
                turbineOutput       = desiredTurbineOutput;

                float desiredTemperature = (optimalTemperature.X + optimalTemperature.Y) / 2.0f;
                DebugConsole.Log($"Temperature reset: {temperature} -> {desiredTemperature}");
                temperature = desiredTemperature;

                float desiredFissionRate = GetFissionRateForTargetTemperatureAndTurbineOutput(desiredTemperature, desiredTurbineOutput);
                DebugConsole.Log($"Fission rate reset: {targetFissionRate}, {fissionRate} -> {desiredFissionRate}");
                targetFissionRate = desiredFissionRate;
                fissionRate       = desiredFissionRate;
            }

            loadQueue.Enqueue(currentLoad);
            while (loadQueue.Count() > 60.0f)
            {
                load = loadQueue.Average();
                loadQueue.Dequeue();
            }

            if (fissionRate > 0.0f)
            {
                var containedItems = item.OwnInventory?.AllItems;
                if (containedItems != null)
                {
                    foreach (Item item in containedItems)
                    {
                        if (!item.HasTag("reactorfuel"))
                        {
                            continue;
                        }
                        item.Condition -= fissionRate / 100.0f * fuelConsumptionRate * deltaTime;
                    }
                }
                if (item.AiTarget != null && MaxPowerOutput > 0)
                {
                    var   aiTarget = item.AiTarget;
                    float range    = Math.Abs(currPowerConsumption) / MaxPowerOutput;
                    aiTarget.SoundRange = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, range);
                    if (item.CurrentHull != null)
                    {
                        var hullAITarget = item.CurrentHull.AiTarget;
                        if (hullAITarget != null)
                        {
                            hullAITarget.SoundRange = Math.Max(hullAITarget.SoundRange, aiTarget.SoundRange);
                        }
                    }
                }
            }

            item.SendSignal(((int)(temperature * 100.0f)).ToString(), "temperature_out");
            item.SendSignal(((int)-CurrPowerConsumption).ToString(), "power_value_out");
            item.SendSignal(((int)load).ToString(), "load_value_out");
            item.SendSignal(((int)AvailableFuel).ToString(), "fuel_out");

            UpdateFailures(deltaTime);
#if CLIENT
            UpdateGraph(deltaTime);
#endif
            AvailableFuel = 0.0f;


            sendUpdateTimer -= deltaTime;
#if CLIENT
            if (unsentChanges && sendUpdateTimer <= 0.0f)
#else
            if (sendUpdateTimer < -NetworkUpdateIntervalLow || (unsentChanges && sendUpdateTimer <= 0.0f))
#endif
            {
#if SERVER
                if (GameMain.Server != null)
                {
                    item.CreateServerEvent(this);
                }
#endif
#if CLIENT
                if (GameMain.Client != null)
                {
                    item.CreateClientEvent(this);
                }
#endif
                sendUpdateTimer = NetworkUpdateIntervalHigh;
                unsentChanges   = false;
            }
        }