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); }
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; }
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); }
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); } } }
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; }
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 } }
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); }
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); }
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; }
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 }
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 } }
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 }); }
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); }
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); } }
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; }
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 }
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 }
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 }
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); }
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 }
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); } } }
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); }
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); } } } }
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; } } }
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; }
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; } }
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; } }
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); } } } }
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; } }