using Barotrauma.Items.Components; using Barotrauma.Networking; using Lidgren.Network; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; namespace Barotrauma { partial class CharacterHealth { private static bool toggledThisFrame; private static Sprite damageOverlay; private static string[] strengthTexts; private GUIButton cprButton; private Point screenResolution; private float uiScale, inventoryScale; private Alignment alignment = Alignment.Left; public Alignment Alignment { get { return alignment; } set { if (alignment == value) return; alignment = value; UpdateAlignment(); } } private GUIButton suicideButton; // healthbars private GUIProgressBar healthBar; private GUIProgressBar healthBarShadow; private GUIProgressBar healthWindowHealthBar; private GUIProgressBar healthWindowHealthBarShadow; private float healthShadowSize; private float healthShadowDelay; private float healthBarPulsateTimer; private float healthBarPulsatePhase; private GUITextBlock characterName; private GUIFrame afflictionInfoFrame; private GUIListBox afflictionInfoContainer; private GUIListBox recommendedTreatmentContainer; private float bloodParticleTimer; private GUIFrame healthWindow; private GUIComponent deadIndicator; private GUIComponent lowSkillIndicator; private SpriteSheet limbIndicatorOverlay; private float limbIndicatorOverlayAnimState; private GUIFrame dropItemArea; private float dropItemAnimDuration = 0.5f; private float dropItemAnimTimer; public Item DroppedItem { get { return droppedItem; } } private Item droppedItem; private GUIComponent draggingMed; private int highlightedLimbIndex = -1; private int selectedLimbIndex = -1; private float distortTimer; // 0-1 private float damageIntensity; private float damageIntensityDropdownRate = 0.1f; public float DamageOverlayTimer { get; private set; } private static CharacterHealth openHealthWindow; public static CharacterHealth OpenHealthWindow { get { return openHealthWindow; } set { if (openHealthWindow == value) return; if (value != null && !value.UseHealthWindow) return; openHealthWindow = value; toggledThisFrame = true; if (Character.Controlled == null) { return; } if (value == null && Character.Controlled?.SelectedCharacter?.CharacterHealth != null && Character.Controlled.SelectedCharacter.CharacterHealth == openHealthWindow && !Character.Controlled.SelectedCharacter.CanInventoryBeAccessed) { Character.Controlled.DeselectCharacter(); } Character.Controlled.ResetInteract = true; if (openHealthWindow != null) { openHealthWindow.characterName.Text = value.Character.Name; Character.Controlled.SelectedConstruction = null; } } } static CharacterHealth() { damageOverlay = new Sprite("Content/UI/damageOverlay.png", Vector2.Zero); } partial void InitProjSpecific(XElement element, Character character) { if (strengthTexts == null) { strengthTexts = new string[] { TextManager.Get("AfflictionStrengthLow"), TextManager.Get("AfflictionStrengthMedium"), TextManager.Get("AfflictionStrengthHigh") }; } character.OnAttacked += OnAttacked; bool horizontal = HUDLayoutSettings.HealthBarAreaLeft.Width > HUDLayoutSettings.HealthBarAreaLeft.Height; healthBar = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas), barSize: 1.0f, color: Color.Green, style: horizontal ? "GUIProgressBar" : "GUIProgressBarVertical") { IsHorizontal = horizontal }; healthBarShadow = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas), barSize: 1.0f, color: Color.Green, style: horizontal ? "GUIProgressBar" : "GUIProgressBarVertical") { IsHorizontal = horizontal }; healthShadowSize = 1.0f; afflictionInfoFrame = new GUIFrame(new RectTransform(new Point(HUDLayoutSettings.HealthWindowAreaLeft.Width / 2, 200), GUI.Canvas)); var paddedInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.9f), afflictionInfoFrame.RectTransform, Anchor.Center), style: null); new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.08f), paddedInfoFrame.RectTransform), "", font: GUI.LargeFont) { UserData = "selectedlimbname" }; afflictionInfoContainer = new GUIListBox(new RectTransform(new Vector2(0.7f, 0.85f), paddedInfoFrame.RectTransform, Anchor.BottomLeft)); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.08f), paddedInfoFrame.RectTransform), TextManager.Get("SuitableTreatments"), textAlignment: Alignment.TopRight); lowSkillIndicator = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.07f), paddedInfoFrame.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.08f) }, TextManager.Get("LowMedicalSkillWarning"), Color.Orange, textAlignment: Alignment.Center, font: GUI.SmallFont, wrap: true) { Visible = false }; recommendedTreatmentContainer = new GUIListBox(new RectTransform(new Vector2(0.28f, 0.5f), paddedInfoFrame.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.15f) }) { Spacing = 10 }; dropItemArea = new GUIFrame(new RectTransform(new Vector2(0.28f, 0.3f), paddedInfoFrame.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.0f) }, style: null) { ToolTip = TextManager.Get("HealthItemUseTip") }; dropItemArea.RectTransform.NonScaledSize = new Point(dropItemArea.Rect.Width); string[] healthCircleStyles = new string[] { "HealthCircleInner", "HealthCircleMid", "HealthCircleOuter" }; foreach (string healthCircleStyle in healthCircleStyles) { for (int i = 1; i < 4; i++) { var style = GUI.Style.GetComponentStyle(healthCircleStyle + i); if (style != null) { new GUIImage(new RectTransform(Vector2.One, dropItemArea.RectTransform), healthCircleStyle + i) { CanBeFocused = false }; } } } new GUIImage(new RectTransform(Vector2.One * 0.2f, dropItemArea.RectTransform, Anchor.Center), "HealthCross") { CanBeFocused = false }; healthWindow = new GUIFrame(new RectTransform(new Point(100, 200), GUI.Canvas)); var paddedHealthWindow = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), healthWindow.RectTransform, Anchor.Center)) { Stretch = true, RelativeSpacing = 0.03f }; var nameContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedHealthWindow.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true) { Stretch = true }; characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUI.LargeFont) { AutoScale = true }; new GUICustomComponent(new RectTransform(new Vector2(0.4f, 1.0f), nameContainer.RectTransform), onDraw: (spriteBatch, component) => { character.Info.DrawPortrait(spriteBatch, new Vector2(component.Rect.X, component.Rect.Center.Y - component.Rect.Width / 2), component.Rect.Width); }); new GUICustomComponent(new RectTransform(new Vector2(1.0f, 0.9f), paddedHealthWindow.RectTransform), (spriteBatch, component) => { DrawHealthWindow(spriteBatch, component.RectTransform.Rect, true, false); }, (deltaTime, component) => { UpdateLimbIndicators(deltaTime, component.RectTransform.Rect); } ); deadIndicator = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.1f), healthWindow.RectTransform, Anchor.Center), text: TextManager.Get("Deceased"), font: GUI.LargeFont, textAlignment: Alignment.Center, wrap: true, style: "GUIToolTip") { Visible = false, CanBeFocused = false }; healthWindowHealthBar = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas), barSize: 1.0f, color: Color.Green, style: "GUIProgressBarVertical") { IsHorizontal = false }; healthWindowHealthBarShadow = new GUIProgressBar(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAreaLeft, GUI.Canvas), barSize: 1.0f, color: Color.Green, style: "GUIProgressBarVertical") { IsHorizontal = false }; cprButton = new GUIButton(new RectTransform(new Point(80, 80), GUI.Canvas), text: "", style: "CPRButton") { OnClicked = (button, userData) => { Character selectedCharacter = Character.Controlled?.SelectedCharacter; if (selectedCharacter == null || (!selectedCharacter.IsUnconscious && selectedCharacter.Stun <= 0.0f)) return false; Character.Controlled.AnimController.Anim = (Character.Controlled.AnimController.Anim == AnimController.Animation.CPR) ? AnimController.Animation.None : AnimController.Animation.CPR; selectedCharacter.AnimController.ResetPullJoints(); if (GameMain.Client != null) { GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Treatment }); } return true; }, Visible = false }; UpdateAlignment(); suicideButton = new GUIButton(new RectTransform(new Vector2(0.06f, 0.02f), GUI.Canvas, Anchor.TopCenter) { MinSize = new Point(120, 20), RelativeOffset = new Vector2(0.0f, 0.01f) }, TextManager.Get("GiveInButton")) { ToolTip = TextManager.Get(GameMain.NetworkMember == null ? "GiveInHelpSingleplayer" : "GiveInHelpMultiplayer"), OnClicked = (button, userData) => { GUI.ForceMouseOn(null); if (Character.Controlled != null) { if (GameMain.Client != null) { GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Status }); } else { var causeOfDeath = GetCauseOfDeath(); Character.Controlled.Kill(causeOfDeath.First, causeOfDeath.Second); Character.Controlled = null; } } return true; } }; if (element != null) { foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "sprite": limbIndicatorOverlay = new SpriteSheet(subElement); break; } } } } private void OnAttacked(Character attacker, AttackResult attackResult) { if (Math.Abs(attackResult.Damage) < 0.01f && attackResult.Afflictions.Count == 0) return; DamageOverlayTimer = MathHelper.Clamp(attackResult.Damage / MaxVitality, DamageOverlayTimer, 1.0f); if (healthShadowDelay <= 0.0f) healthShadowDelay = 1.0f; if (healthBarPulsateTimer <= 0.0f) healthBarPulsatePhase = 0.0f; healthBarPulsateTimer = 1.0f; float additionalIntensity = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, 0.1f, attackResult.Damage / MaxVitality)); damageIntensity = MathHelper.Clamp(damageIntensity + additionalIntensity, 0, 1); } private void UpdateAlignment() { healthBar.RectTransform.RelativeOffset = healthBarShadow.RectTransform.RelativeOffset = Vector2.Zero; healthWindowHealthBar.RectTransform.RelativeOffset = healthWindowHealthBarShadow.RectTransform.RelativeOffset = Vector2.Zero; int healthWindowHealthBarWidth = (int)(40 * GUI.Scale); if (alignment == Alignment.Left) { healthBar.RectTransform.SetPosition(Anchor.BottomLeft); healthBarShadow.RectTransform.SetPosition(Anchor.BottomLeft); healthBar.RectTransform.AbsoluteOffset = healthBarShadow.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.HealthBarAreaLeft.X, GameMain.GraphicsHeight - HUDLayoutSettings.HealthBarAreaLeft.Bottom); healthBar.RectTransform.NonScaledSize = healthBarShadow.RectTransform.NonScaledSize = HUDLayoutSettings.HealthBarAreaLeft.Size; healthWindow.RectTransform.AbsoluteOffset = HUDLayoutSettings.HealthWindowAreaLeft.Location + new Point(healthWindowHealthBarWidth, 0); healthWindow.RectTransform.NonScaledSize = new Point( HUDLayoutSettings.HealthWindowAreaLeft.Width / 3 - healthWindowHealthBarWidth, HUDLayoutSettings.HealthWindowAreaLeft.Height); afflictionInfoFrame.RectTransform.AbsoluteOffset = new Point( healthWindow.Rect.Right, HUDLayoutSettings.HealthWindowAreaLeft.Y); afflictionInfoFrame.RectTransform.NonScaledSize = new Point( (int)(HUDLayoutSettings.HealthWindowAreaLeft.Width * 0.66f), (int)(HUDLayoutSettings.HealthWindowAreaLeft.Height)); healthWindowHealthBar.RectTransform.NonScaledSize = healthWindowHealthBarShadow.RectTransform.NonScaledSize = new Point(healthWindowHealthBarWidth, healthWindow.Rect.Height); healthWindowHealthBar.RectTransform.AbsoluteOffset = healthWindowHealthBarShadow.RectTransform.AbsoluteOffset = HUDLayoutSettings.HealthWindowAreaLeft.Location; int cprButtonSize = (int)(100 * GUI.Scale); cprButton.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.HealthWindowAreaLeft.Right, dropItemArea.Rect.Center.Y - cprButtonSize / 2); cprButton.RectTransform.NonScaledSize = new Point(cprButtonSize); } else { healthBar.RectTransform.SetPosition(Anchor.TopLeft); healthBarShadow.RectTransform.SetPosition(Anchor.TopLeft); healthBar.RectTransform.AbsoluteOffset = healthBarShadow.RectTransform.AbsoluteOffset = HUDLayoutSettings.HealthBarAreaRight.Location; healthBar.RectTransform.NonScaledSize = healthBarShadow.RectTransform.NonScaledSize = HUDLayoutSettings.HealthBarAreaRight.Size; healthWindow.RectTransform.AbsoluteOffset = new Point( HUDLayoutSettings.HealthWindowAreaRight.X + HUDLayoutSettings.HealthWindowAreaRight.Width / 3 * 2, HUDLayoutSettings.HealthWindowAreaRight.Y); healthWindow.RectTransform.NonScaledSize = new Point( HUDLayoutSettings.HealthWindowAreaRight.Width / 3 - healthWindowHealthBarWidth, HUDLayoutSettings.HealthWindowAreaRight.Height); afflictionInfoFrame.RectTransform.AbsoluteOffset = new Point( HUDLayoutSettings.HealthWindowAreaRight.X, HUDLayoutSettings.HealthWindowAreaLeft.Y); afflictionInfoFrame.RectTransform.NonScaledSize = new Point( (int)(HUDLayoutSettings.HealthWindowAreaLeft.Width * 0.66f), (int)(HUDLayoutSettings.HealthWindowAreaLeft.Height)); healthWindowHealthBar.RectTransform.NonScaledSize = healthWindowHealthBarShadow.RectTransform.NonScaledSize = new Point(healthWindowHealthBarWidth, healthWindow.Rect.Height); healthWindowHealthBar.RectTransform.AbsoluteOffset = healthWindowHealthBarShadow.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.HealthWindowAreaRight.Right - healthWindowHealthBarWidth, HUDLayoutSettings.HealthWindowAreaRight.Y); int cprButtonSize = (int)(100 * GUI.Scale); cprButton.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.HealthWindowAreaRight.X - cprButtonSize, dropItemArea.Rect.Center.Y - cprButtonSize / 2); cprButton.RectTransform.NonScaledSize = new Point(cprButtonSize); } dropItemArea.RectTransform.NonScaledSize = new Point(dropItemArea.Rect.Width); screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); inventoryScale = Inventory.UIScale; uiScale = GUI.Scale; } partial void UpdateOxygenProjSpecific(float prevOxygen) { if (prevOxygen > 0.0f && OxygenAmount <= 0.0f && Character.Controlled == Character) { SoundPlayer.PlaySound(Character.Info != null && Character.Info.Gender == Gender.Female ? "drownfemale" : "drownmale"); } } partial void UpdateBleedingProjSpecific(AfflictionBleeding affliction, Limb targetLimb, float deltaTime) { bloodParticleTimer -= deltaTime * (affliction.Strength / 10.0f); if (bloodParticleTimer <= 0.0f) { float bloodParticleSize = MathHelper.Lerp(0.5f, 1.0f, affliction.Strength / 100.0f); if (!Character.AnimController.InWater) bloodParticleSize *= 2.0f; var blood = GameMain.ParticleManager.CreateParticle( Character.AnimController.InWater ? "waterblood" : "blooddrop", targetLimb.WorldPosition, Rand.Vector(affliction.Strength), 0.0f, Character.AnimController.CurrentHull); if (blood != null) { blood.Size *= bloodParticleSize; } bloodParticleTimer = 1.0f; } } public void UpdateHUD(float deltaTime) { if (GUI.DisableHUD) return; if (openHealthWindow != null) { if (openHealthWindow != Character.Controlled?.CharacterHealth && openHealthWindow != Character.Controlled?.SelectedCharacter?.CharacterHealth) { openHealthWindow = null; return; } } if (DamageOverlayTimer > 0.0f) { DamageOverlayTimer -= deltaTime; } if (damageIntensity > 0) { damageIntensity -= deltaTime * damageIntensityDropdownRate; if (damageIntensity < 0) { damageIntensity = 0; } } if (healthShadowDelay > 0.0f) { healthShadowDelay -= deltaTime; } else { healthShadowSize = healthBar.BarSize > healthShadowSize ? Math.Min(healthShadowSize + deltaTime, healthBar.BarSize) : Math.Max(healthShadowSize - deltaTime, healthBar.BarSize); } dropItemArea.Visible = !Character.IsDead; float blurStrength = 0.0f; float distortStrength = 0.0f; float distortSpeed = 0.0f; float radialDistortStrength = 0.0f; float chromaticAberrationStrength = 0.0f; if (Character.IsUnconscious) { blurStrength = 1.0f; distortSpeed = 1.0f; } else if (OxygenAmount < 100.0f) { blurStrength = MathHelper.Lerp(0.5f, 1.0f, 1.0f - Vitality / MaxVitality); distortStrength = blurStrength; distortSpeed = (blurStrength + 1.0f); distortSpeed *= distortSpeed * distortSpeed * distortSpeed; } foreach (Affliction affliction in afflictions) { distortStrength = Math.Max(distortStrength, affliction.GetScreenDistortStrength()); blurStrength = Math.Max(blurStrength, affliction.GetScreenBlurStrength()); radialDistortStrength = Math.Max(radialDistortStrength, affliction.GetRadialDistortStrength()); chromaticAberrationStrength = Math.Max(chromaticAberrationStrength, affliction.GetChromaticAberrationStrength()); } foreach (LimbHealth limbHealth in limbHealths) { foreach (Affliction affliction in limbHealth.Afflictions) { distortStrength = Math.Max(distortStrength, affliction.GetScreenDistortStrength()); blurStrength = Math.Max(blurStrength, affliction.GetScreenBlurStrength()); radialDistortStrength = Math.Max(radialDistortStrength, affliction.GetRadialDistortStrength()); chromaticAberrationStrength = Math.Max(chromaticAberrationStrength, affliction.GetChromaticAberrationStrength()); } } Character.RadialDistortStrength = radialDistortStrength; Character.ChromaticAberrationStrength = chromaticAberrationStrength; if (blurStrength > 0.0f) { distortTimer = (distortTimer + deltaTime * distortSpeed) % MathHelper.TwoPi; Character.BlurStrength = (float)(Math.Sin(distortTimer) + 1.5f) * 0.25f * blurStrength; Character.DistortStrength = (float)(Math.Sin(distortTimer) + 1.0f) * 0.1f * distortStrength; } else { Character.BlurStrength = 0.0f; Character.DistortStrength = 0.0f; distortTimer = 0.0f; } if (PlayerInput.KeyHit(InputType.Health) && GUI.KeyboardDispatcher.Subscriber == null && Character.AllowInput && Character.FocusedCharacter == null && !toggledThisFrame) { if (openHealthWindow != null) OpenHealthWindow = null; else OpenHealthWindow = this; } else if (openHealthWindow == this) { if (Alignment == Alignment.Right ? HUD.CloseHUD(HUDLayoutSettings.HealthWindowAreaRight) : HUD.CloseHUD(HUDLayoutSettings.HealthWindowAreaLeft)) { //emulate a Health input to get the character to deselect the item server-side Character.Keys[(int)InputType.Health].Hit = true; OpenHealthWindow = null; } } toggledThisFrame = false; if (Character.IsDead) { healthBar.Color = healthWindowHealthBar.Color = Color.Black; healthBar.BarSize = healthWindowHealthBar.BarSize = 1.0f; } else { healthBar.Color = healthWindowHealthBar.Color = ToolBox.GradientLerp(Vitality / MaxVitality, Color.Red, Color.Orange, Color.Green); healthBar.HoverColor = healthWindowHealthBar.HoverColor = healthBar.Color * 2.0f; healthBar.BarSize = healthWindowHealthBar.BarSize = (Vitality > 0.0f) ? Vitality / MaxVitality : 1.0f - Vitality / MinVitality; if (healthBarPulsateTimer > 0.0f) { //0-1 float pulsateAmount = (float)(Math.Sin(healthBarPulsatePhase) + 1.0f) / 2.0f; healthBar.RectTransform.LocalScale = healthBarShadow.RectTransform.LocalScale = new Vector2(1.0f, (1.0f + pulsateAmount * healthBarPulsateTimer * 0.5f)); healthBarPulsatePhase += deltaTime * 5.0f; healthBarPulsateTimer -= deltaTime; } else { healthBar.RectTransform.LocalScale = Vector2.One; } } if (OpenHealthWindow == this) { if (Character == Character.Controlled && !Character.AllowInput) { openHealthWindow = null; } lowSkillIndicator.Visible = Timing.TotalTime % 1.0f < 0.8f && Character.Controlled != null && Character.Controlled.GetSkillLevel("medical") < 50.0f; float rotationSpeed = 0.25f; int i = 0; foreach (GUIComponent dropItemIndicator in dropItemArea.Children) { GUIImage img = dropItemIndicator as GUIImage; if (img == null) continue; img.State = GUI.MouseOn == dropItemArea ? GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None; byte alpha = img.Color.A; byte hoverAlpha = img.HoverColor.A; img.Color = ToolBox.GradientLerp(Vitality / MaxVitality, Color.Red, Color.Orange, Color.Green); img.Color = new Color(img.Color.R, img.Color.G, img.Color.B, alpha); img.HoverColor = new Color(img.Color.R, img.Color.G, img.Color.B, hoverAlpha); img.HoverColor = Color.Lerp(img.HoverColor, Color.White, 0.5f); if (img.State == GUIComponent.ComponentState.Hover && droppedItem == null) { dropItemAnimTimer = Math.Min(0.3f, dropItemAnimTimer + deltaTime * 0.5f); } if (i < 4) { img.Scale = 1.0f - (float)Math.Sin(dropItemAnimTimer / dropItemAnimDuration * MathHelper.TwoPi) * 0.3f; } if (dropItemIndicator == dropItemArea.Children.Last()) break; img.Rotation = (img.Rotation + (rotationSpeed + dropItemAnimTimer * 10.0f) * deltaTime) % MathHelper.TwoPi; rotationSpeed = (rotationSpeed + 0.3f) % 1.0f; i++; } Rectangle limbArea = healthWindow.Children.First().Rect; var highlightedLimb = highlightedLimbIndex < 0 ? null : limbHealths[highlightedLimbIndex]; if (highlightedLimb == null && selectedLimbIndex < 0) { // If no limb is selected or highlighted, select the one with the most critical afflictions. var affliction = GetAllAfflictions(a => a.Prefab.IndicatorLimb != LimbType.None) .OrderByDescending(a => a.DamagePerSecond) .ThenByDescending(a => a.Strength).FirstOrDefault(); var limbHealth = GetMathingLimbHealth(affliction); if (limbHealth != null) { selectedLimbIndex = limbHealths.IndexOf(limbHealth); } } UpdateAfflictionContainer(selectedLimbIndex < 0 ? highlightedLimb : limbHealths[selectedLimbIndex]); if (Inventory.draggingItem != null) { if (highlightedLimbIndex > -1) { selectedLimbIndex = highlightedLimbIndex; } } if (draggingMed != null) { if (!PlayerInput.LeftButtonHeld()) { OnItemDropped(draggingMed.UserData as Item, ignoreMousePos: false); draggingMed = null; } } /*if (GUI.MouseOn?.UserData is Affliction affliction) { ShowAfflictionInfo(affliction, afflictionInfoContainer); }*/ if (dropItemAnimTimer > 0.0f) { dropItemAnimTimer -= deltaTime; if (dropItemAnimTimer <= 0.0f) droppedItem = null; } } else { if (openHealthWindow != null && Character != Character.Controlled && Character != Character.Controlled?.SelectedCharacter) { openHealthWindow = null; } highlightedLimbIndex = -1; } Rectangle hoverArea = alignment == Alignment.Left ? Rectangle.Union(HUDLayoutSettings.AfflictionAreaLeft, HUDLayoutSettings.HealthBarAreaLeft) : Rectangle.Union(HUDLayoutSettings.AfflictionAreaRight, HUDLayoutSettings.HealthBarAreaRight); if (Character.AllowInput && UseHealthWindow && hoverArea.Contains(PlayerInput.MousePosition) && Inventory.SelectedSlot == null) { healthBar.State = GUIComponent.ComponentState.Hover; if (PlayerInput.LeftButtonClicked()) { OpenHealthWindow = openHealthWindow == this ? null : this; } } else { healthBar.State = GUIComponent.ComponentState.None; } suicideButton.Visible = Character == Character.Controlled && Character.IsUnconscious && !Character.IsDead; cprButton.Visible = Character == Character.Controlled?.SelectedCharacter && (Character.IsUnconscious || Character.Stun > 0.0f) && !Character.IsDead && openHealthWindow == this; deadIndicator.Visible = Character.IsDead; } public void AddToGUIUpdateList() { if (GUI.DisableHUD) return; if (OpenHealthWindow == this) { //afflictionContainer.AddToGUIUpdateList(); afflictionInfoFrame.AddToGUIUpdateList(); healthWindow.AddToGUIUpdateList(); healthWindowHealthBarShadow.AddToGUIUpdateList(); healthWindowHealthBar.AddToGUIUpdateList(); } else if (Character.Controlled == Character) { healthBarShadow.AddToGUIUpdateList(); healthBar.AddToGUIUpdateList(); } if (suicideButton.Visible && Character == Character.Controlled) suicideButton.AddToGUIUpdateList(); if (cprButton != null && cprButton.Visible) cprButton.AddToGUIUpdateList(); } public void DrawHUD(SpriteBatch spriteBatch) { if (GUI.DisableHUD) return; if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || Math.Abs(inventoryScale - Inventory.UIScale) > 0.01f || Math.Abs(uiScale - GUI.Scale) > 0.01f) { UpdateAlignment(); } float damageOverlayAlpha = DamageOverlayTimer; if (Vitality < MaxVitality * 0.1f) { damageOverlayAlpha = Math.Max(1.0f - (Vitality / maxVitality * 10.0f), damageOverlayAlpha); } else { float pulsateAmount = (float)(Math.Sin(healthBarPulsatePhase) + 1.0f) / 2.0f; damageOverlayAlpha = pulsateAmount * healthBarPulsateTimer * damageIntensity; } if (damageOverlayAlpha > 0.0f) { damageOverlay.Draw(spriteBatch, Vector2.Zero, Color.White * damageOverlayAlpha, Vector2.Zero, 0.0f, new Vector2(GameMain.GraphicsWidth / damageOverlay.size.X, GameMain.GraphicsHeight / damageOverlay.size.Y)); } if (Character.Inventory != null) { if (Character.Inventory.CurrentLayout == CharacterInventory.Layout.Right) { //move the healthbar on top of the inventory slots healthBar.RectTransform.ScreenSpaceOffset = new Point( (GameMain.GraphicsWidth - HUDLayoutSettings.Padding) - HUDLayoutSettings.HealthBarAreaRight.Right, HUDLayoutSettings.HealthBarAreaRight.Y - (int)(Character.Inventory.SlotPositions.Max(s => s.Y) + Inventory.EquipIndicator.size.Y * Inventory.UIScale * 2) - HUDLayoutSettings.HealthBarAreaRight.Height); healthBarShadow.RectTransform.ScreenSpaceOffset = healthBar.RectTransform.ScreenSpaceOffset; } else { healthBar.RectTransform.ScreenSpaceOffset = healthBarShadow.RectTransform.ScreenSpaceOffset = Point.Zero; } } DrawStatusHUD(spriteBatch); } public void DrawStatusHUD(SpriteBatch spriteBatch) { //Rectangle interactArea = healthBar.Rect; if (openHealthWindow != this) { List> statusIcons = new List>(); if (Character.CurrentHull == null || Character.CurrentHull.LethalPressure > 5.0f) statusIcons.Add(new Pair(pressureAffliction, TextManager.Get("PressureHUDWarning"))); if (Character.CurrentHull != null && Character.OxygenAvailable < LowOxygenThreshold && oxygenLowAffliction.Strength < oxygenLowAffliction.Prefab.ShowIconThreshold) statusIcons.Add(new Pair(oxygenLowAffliction, TextManager.Get("OxygenHUDWarning"))); var allAfflictions = GetAllAfflictions(true); foreach (Affliction affliction in allAfflictions) { if (affliction.Strength < affliction.Prefab.ShowIconThreshold || affliction.Prefab.Icon == null) continue; statusIcons.Add(new Pair(affliction, affliction.Prefab.Name)); } Pair highlightedIcon = null; Vector2 highlightedIconPos = Vector2.Zero; Rectangle afflictionArea = alignment == Alignment.Left ? HUDLayoutSettings.AfflictionAreaLeft : HUDLayoutSettings.AfflictionAreaRight; Point pos = afflictionArea.Location + healthBar.RectTransform.ScreenSpaceOffset; bool horizontal = afflictionArea.Width > afflictionArea.Height; int iconSize = horizontal ? afflictionArea.Height : afflictionArea.Width; foreach (Pair statusIcon in statusIcons) { Rectangle afflictionIconRect = new Rectangle(pos, new Point(iconSize)); if (afflictionIconRect.Contains(PlayerInput.MousePosition)) { highlightedIcon = statusIcon; highlightedIconPos = afflictionIconRect.Center.ToVector2(); } if (statusIcon.First.DamagePerSecond > 1.0f) { Rectangle glowRect = afflictionIconRect; glowRect.Inflate((int)(25 * GUI.Scale), (int)(25 * GUI.Scale)); var glow = GUI.Style.GetComponentStyle("OuterGlow"); glow.Sprites[GUIComponent.ComponentState.None][0].Draw( spriteBatch, glowRect, Color.Red * (float)((Math.Sin(statusIcon.First.DamagePerSecondTimer * MathHelper.TwoPi - MathHelper.PiOver2) + 1.0f) * 0.5f)); } var slot = GUI.Style.GetComponentStyle("AfflictionIconSlot"); slot.Sprites[highlightedIcon == statusIcon ? GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None][0].Draw( spriteBatch, afflictionIconRect, highlightedIcon == statusIcon ? slot.HoverColor : slot.Color); statusIcon.First.Prefab.Icon?.Draw(spriteBatch, pos.ToVector2(), highlightedIcon == statusIcon ? statusIcon.First.Prefab.IconColor : statusIcon.First.Prefab.IconColor * 0.8f, rotate: 0, scale: iconSize / statusIcon.First.Prefab.Icon.size.X); if (horizontal) pos.X += iconSize + (int)(5 * GUI.Scale); else pos.Y += iconSize + (int)(5 * GUI.Scale); } if (highlightedIcon != null) { GUI.DrawString(spriteBatch, alignment == Alignment.Left ? highlightedIconPos + new Vector2(60 * GUI.Scale, 5) : highlightedIconPos + new Vector2(-10.0f - GUI.Font.MeasureString(highlightedIcon.Second).X, 5), highlightedIcon.Second, Color.White * 0.8f, Color.Black * 0.5f); } if (Vitality > 0.0f) { float currHealth = healthBar.BarSize; Color prevColor = healthBar.Color; healthBarShadow.BarSize = healthShadowSize; healthBarShadow.Color = Color.Red; healthBarShadow.Visible = true; healthBar.BarSize = currHealth; healthBar.Color = prevColor; } else { healthBarShadow.Visible = false; } } else { if (Vitality > 0.0f) { float currHealth = healthWindowHealthBar.BarSize; Color prevColor = healthWindowHealthBar.Color; healthWindowHealthBarShadow.BarSize = healthShadowSize; healthWindowHealthBarShadow.Color = Color.Red; healthWindowHealthBarShadow.Visible = true; healthWindowHealthBar.BarSize = currHealth; healthWindowHealthBar.Color = prevColor; } else { healthWindowHealthBarShadow.Visible = false; } } } private void UpdateAfflictionContainer(LimbHealth selectedLimb) { ((GUITextBlock)afflictionInfoContainer.Parent.GetChildByUserData("selectedlimbname")).Text = selectedLimb == null ? "" : selectedLimb.Name; if (selectedLimb == null) { afflictionInfoContainer.Content.ClearChildren(); return; } var currentAfflictions = GetMatchingAfflictions(selectedLimb, a => a.Strength >= a.Prefab.ShowIconThreshold); var displayedAfflictions = afflictionInfoContainer.Content.Children.Select(c => c.UserData as Affliction); if (currentAfflictions.Any(a => !displayedAfflictions.Contains(a)) || displayedAfflictions.Any(a => !currentAfflictions.Contains(a))) { CreateAfflictionInfos(currentAfflictions); } UpdateAfflictionInfos(displayedAfflictions); } private void CreateAfflictionInfos(IEnumerable afflictions) { afflictionInfoContainer.Content.ClearChildren(); recommendedTreatmentContainer.Content.ClearChildren(); float characterSkillLevel = Character.Controlled == null ? 0.0f : Character.Controlled.GetSkillLevel("medical"); //random variance is 200% when the skill is 0 //no random variance if the skill is 50 or more float randomVariance = MathHelper.Lerp(2.0f, 0.0f, characterSkillLevel / 50.0f); //key = item identifier //float = suitability Dictionary treatmentSuitability = new Dictionary(); float minSuitability = -10, maxSuitability = 10; foreach (Affliction affliction in afflictions) { foreach (KeyValuePair treatment in affliction.Prefab.TreatmentSuitability) { if (!treatmentSuitability.ContainsKey(treatment.Key)) { treatmentSuitability[treatment.Key] = treatment.Value * affliction.Strength; } else { treatmentSuitability[treatment.Key] += treatment.Value * affliction.Strength; } minSuitability = Math.Min(treatmentSuitability[treatment.Key], minSuitability); maxSuitability = Math.Max(treatmentSuitability[treatment.Key], maxSuitability); } } //normalize the suitabilities to a range of 0 to 1 foreach (string treatment in treatmentSuitability.Keys.ToList()) { treatmentSuitability[treatment] = (treatmentSuitability[treatment] - minSuitability) / (maxSuitability - minSuitability); //lerp towards a random value if the medical skill is low treatmentSuitability[treatment] = MathHelper.Lerp(treatmentSuitability[treatment], Rand.Range(0.0f, 1.0f), randomVariance); } foreach (Affliction affliction in afflictions) { var child = new GUILayoutGroup(new RectTransform(Vector2.One * 0.95f, afflictionInfoContainer.Content.RectTransform, Anchor.TopCenter)) { Stretch = true, RelativeSpacing = 0.02f, UserData = affliction }; var headerContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), child.RectTransform), isHorizontal: true) { Stretch = true, UserData = "header" }; new GUIImage(new RectTransform(new Vector2(0.15f, 1.0f), headerContainer.RectTransform), affliction.Prefab.Icon, scaleToFit: true) { Color = affliction.Prefab.IconColor }; var labelContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), headerContainer.RectTransform), isHorizontal: true) { Stretch = true, AbsoluteSpacing = 10, UserData = "label" }; var afflictionName = new GUITextBlock(new RectTransform(new Vector2(0.65f, 1.0f), labelContainer.RectTransform), affliction.Prefab.Name, textAlignment: Alignment.CenterLeft, font: GUI.LargeFont); var afflictionStrength = new GUITextBlock(new RectTransform(new Vector2(0.35f, 0.6f), labelContainer.RectTransform), "", textAlignment: Alignment.TopRight, font: GUI.LargeFont) { Padding = Vector4.Zero, UserData = "strength" }; var vitality = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), labelContainer.RectTransform, Anchor.BottomRight), "", textAlignment: Alignment.BottomRight) { IgnoreLayoutGroups = true, UserData = "vitality" }; var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), child.RectTransform), affliction.Prefab.Description, textAlignment: Alignment.TopLeft, wrap: true); if (description.Font.MeasureString(description.WrappedText).Y > description.Rect.Height) { description.Font = GUI.SmallFont; } description.RectTransform.Resize(new Point(description.Rect.Width, (int)(description.TextSize.Y + 10))); child.RectTransform.Resize(new Point(child.Rect.Width, child.Children.Sum(c => c.Rect.Height))); child.Recalculate(); afflictionStrength.AutoScale = true; afflictionName.AutoScale = true; vitality.AutoDraw = true; } List> treatmentSuitabilities = treatmentSuitability.OrderByDescending(t => t.Value).ToList(); foreach (KeyValuePair treatment in treatmentSuitabilities) { ItemPrefab item = MapEntityPrefab.Find(name: null, identifier: treatment.Key, showErrorMessages: false) as ItemPrefab; if (item == null) continue; int slotSize = (int)(recommendedTreatmentContainer.Content.Rect.Width * 0.8f); var itemSlot = new GUIButton(new RectTransform(new Point(slotSize), recommendedTreatmentContainer.Content.RectTransform, Anchor.TopCenter), text: "", style: "InventorySlotSmall") { UserData = item }; itemSlot.Color = ToolBox.GradientLerp(treatment.Value, Color.Red, Color.White, Color.LightGreen); Sprite itemSprite = item.InventoryIcon ?? item.sprite; Color itemColor = itemSprite == item.sprite ? item.SpriteColor : item.InventoryIconColor; var itemIcon = new GUIImage(new RectTransform(new Vector2(0.8f, 0.8f), itemSlot.RectTransform, Anchor.Center), itemSprite, scaleToFit: true) { CanBeFocused = false, Color = itemColor, HoverColor = itemColor, SelectedColor = itemColor }; itemSlot.ToolTip = item.Name + "\n" + item.Description; } afflictionInfoContainer.Content.RectTransform.SortChildren((r1, r2) => { var first = r1.GUIComponent.UserData as Affliction; var second = r2.GUIComponent.UserData as Affliction; int dmgPerSecond = Math.Sign(second.DamagePerSecond - first.DamagePerSecond); return dmgPerSecond != 0 ? dmgPerSecond : Math.Sign(second.Strength - first.Strength); }); //afflictionInfoContainer.Content.RectTransform.SortChildren((r1, r2) => //{ // return Math.Sign(((Affliction)r2.GUIComponent.UserData).GetVitalityDecrease(this) - ((Affliction)r1.GUIComponent.UserData).GetVitalityDecrease(this)); //}); } private void UpdateAfflictionInfos(IEnumerable afflictions) { foreach (Affliction affliction in afflictions) { var child = afflictionInfoContainer.Content.FindChild(affliction); var headerContainer = child.GetChildByUserData("header"); var labelContainer = headerContainer.GetChildByUserData("label"); var strengthText = labelContainer.GetChildByUserData("strength") as GUITextBlock; strengthText.Text = strengthTexts[ MathHelper.Clamp((int)Math.Floor((affliction.Strength / affliction.Prefab.MaxStrength) * strengthTexts.Length), 0, strengthTexts.Length - 1)]; strengthText.TextColor = ToolBox.GradientLerp( affliction.Strength / affliction.Prefab.MaxStrength, Color.Yellow, Color.Orange, Color.Red); var vitalityText = labelContainer.GetChildByUserData("vitality") as GUITextBlock; int vitalityDecrease = (int)affliction.GetVitalityDecrease(this); if (vitalityDecrease == 0) { vitalityText.Visible = false; } else { vitalityText.Visible = true; vitalityText.Text = TextManager.Get("Vitality") + " -" + vitalityDecrease; vitalityText.TextColor = vitalityDecrease <= 0 ? Color.LightGreen : Color.Lerp(Color.Orange, Color.Red, affliction.Strength / affliction.Prefab.MaxStrength); } } } public bool OnItemDropped(Item item, bool ignoreMousePos) { //items can be dropped outside the health window if (!ignoreMousePos && !healthWindow.Rect.Contains(PlayerInput.MousePosition) && !afflictionInfoFrame.Rect.Contains(PlayerInput.MousePosition)) { return false; } //can't apply treatment to dead characters if (Character.IsDead) return true; if (item == null || !item.UseInHealthInterface) return true; if (!ignoreMousePos) { if (highlightedLimbIndex > -1) { selectedLimbIndex = highlightedLimbIndex; } else if (!dropItemArea.Rect.Contains(PlayerInput.MousePosition)) { return true; } } Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex); item.ApplyTreatment(Character.Controlled, Character, targetLimb); dropItemAnimTimer = dropItemAnimDuration; droppedItem = item; return true; } private List GetAvailableMedicalItems() { List allInventoryItems = new List(); allInventoryItems.AddRange(Character.Inventory.Items); if (Character.SelectedCharacter?.Inventory != null && Character.CanAccessInventory(Character.SelectedCharacter.Inventory)) { allInventoryItems.AddRange(Character.SelectedCharacter.Inventory.Items); } if (Character.SelectedBy?.Inventory != null) { allInventoryItems.AddRange(Character.SelectedBy.Inventory.Items); } List medicalItems = new List(); foreach (Item item in allInventoryItems) { if (item == null) continue; var containedItems = item.ContainedItems; if (containedItems != null) { foreach (Item containedItem in containedItems) { if (containedItem == null) continue; if (!containedItem.HasTag("medical") && !containedItem.HasTag("chem")) continue; medicalItems.Add(containedItem); } } if (!item.HasTag("medical") && !item.HasTag("chem")) continue; medicalItems.Add(item); } return medicalItems.Distinct().ToList(); } private void UpdateLimbIndicators(float deltaTime, Rectangle drawArea) { limbIndicatorOverlayAnimState += deltaTime * 8.0f; highlightedLimbIndex = -1; int i = 0; foreach (LimbHealth limbHealth in limbHealths) { if (limbHealth.IndicatorSprite == null) continue; float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); Rectangle highlightArea = GetLimbHighlightArea(limbHealth, drawArea); if (highlightArea.Contains(PlayerInput.MousePosition)) { highlightedLimbIndex = i; } i++; } if (PlayerInput.LeftButtonClicked() && highlightedLimbIndex > -1) { selectedLimbIndex = highlightedLimbIndex; //afflictionContainer.ClearChildren(); afflictionInfoContainer.ClearChildren(); } } private void DrawHealthWindow(SpriteBatch spriteBatch, Rectangle drawArea, bool allowHighlight, bool highlightAll) { if (Character.Removed) { return; } int i = 0; foreach (LimbHealth limbHealth in limbHealths) { if (limbHealth.IndicatorSprite == null) continue; float damageLerp = limbHealth.TotalDamage > 0.0f ? MathHelper.Lerp(0.2f, 1.0f, limbHealth.TotalDamage / 100.0f) : 0.0f; Color color = Character.IsDead ? Color.Lerp(Color.Black, new Color(150, 100, 100), damageLerp) : ToolBox.GradientLerp(damageLerp, Color.Green, Color.Orange, Color.Red); float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); if (((i == highlightedLimbIndex || i == selectedLimbIndex) && allowHighlight) || highlightAll) { color = Color.Lerp(color, Color.White, 0.5f); } limbHealth.IndicatorSprite.Draw(spriteBatch, drawArea.Center.ToVector2(), color, limbHealth.IndicatorSprite.Origin, 0, scale); i++; } spriteBatch.End(); spriteBatch.Begin(SpriteSortMode.Deferred, Lights.CustomBlendStates.Multiplicative); float overlayScale = Math.Min( drawArea.Width / (float)limbIndicatorOverlay.FrameSize.X, drawArea.Height / (float)limbIndicatorOverlay.FrameSize.Y); int frame = 0; int frameCount = 17; if (limbIndicatorOverlayAnimState >= frameCount * 2) limbIndicatorOverlayAnimState = 0.0f; if (limbIndicatorOverlayAnimState < frameCount) { frame = (int)limbIndicatorOverlayAnimState; } else { frame = frameCount - (int)(limbIndicatorOverlayAnimState - (frameCount - 1)); } limbIndicatorOverlay.Draw(spriteBatch, frame, drawArea.Center.ToVector2(), Color.Gray, origin: limbIndicatorOverlay.FrameSize.ToVector2() / 2, rotate: 0.0f, scale: Vector2.One * overlayScale); spriteBatch.End(); spriteBatch.Begin(SpriteSortMode.Deferred, blendState: BlendState.AlphaBlend, rasterizerState: GameMain.ScissorTestEnable); i = 0; foreach (LimbHealth limbHealth in limbHealths) { if (limbHealth.IndicatorSprite == null) continue; float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); Rectangle highlightArea = new Rectangle( (int)(drawArea.Center.X - (limbHealth.IndicatorSprite.Texture.Width / 2 - limbHealth.HighlightArea.X) * scale), (int)(drawArea.Center.Y - (limbHealth.IndicatorSprite.Texture.Height / 2 - limbHealth.HighlightArea.Y) * scale), (int)(limbHealth.HighlightArea.Width * scale), (int)(limbHealth.HighlightArea.Height * scale)); if (selectedLimbIndex == i) { if (alignment == Alignment.Left) { GUI.DrawLine(spriteBatch, highlightArea.Center.ToVector2(), afflictionInfoContainer.Parent.Rect.Location.ToVector2() + Vector2.UnitY * 20, Color.LightBlue * 0.3f, 0, 4); } else { GUI.DrawLine(spriteBatch, highlightArea.Center.ToVector2(), new Vector2(afflictionInfoContainer.Parent.Rect.Right, afflictionInfoContainer.Parent.Rect.Y + 20), Color.LightBlue * 0.3f, 0, 4); } } var slot = GUI.Style.GetComponentStyle("AfflictionIconSlot"); float iconScale = 0.3f * scale; Vector2 iconPos = highlightArea.Center.ToVector2(); foreach (Affliction affliction in limbHealth.Afflictions) { DrawLimbAfflictionIcon(spriteBatch, affliction, slot, iconScale, ref iconPos); } foreach (Affliction affliction in afflictions) { Limb indicatorLimb = Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb); if (indicatorLimb != null && indicatorLimb.HealthIndex == i) { DrawLimbAfflictionIcon(spriteBatch, affliction, slot, iconScale, ref iconPos); } } i++; } if (draggingMed != null) { GUIImage itemImage = draggingMed.GetChild(); float scale = Math.Min(40.0f / itemImage.Sprite.size.X, 40.0f / itemImage.Sprite.size.Y); itemImage.Sprite.Draw(spriteBatch, PlayerInput.MousePosition, itemImage.Color, 0, scale); } if (dropItemAnimTimer > 0.0f && droppedItem?.Prefab.InventoryIcon != null) { var droppedItemSprite = droppedItem.Prefab.InventoryIcon ?? droppedItem.Sprite; droppedItemSprite.Draw(spriteBatch, dropItemArea.Rect.Center.ToVector2(), droppedItemSprite == droppedItem.Sprite ? droppedItem.GetSpriteColor() : droppedItem.GetInventoryIconColor(), origin: droppedItemSprite.size / 2, scale: MathHelper.SmoothStep(0.0f, 100.0f / droppedItemSprite.size.Length(), dropItemAnimTimer / dropItemAnimDuration)); } } private void DrawLimbAfflictionIcon(SpriteBatch spriteBatch, Affliction affliction, GUIComponentStyle slotStyle, float iconScale, ref Vector2 iconPos) { if (affliction.Strength < affliction.Prefab.ShowIconThreshold) return; Vector2 iconSize = (affliction.Prefab.Icon.size * iconScale); //afflictions that have a strength of less than 10 are faded out slightly float alpha = MathHelper.Lerp(0.3f, 1.0f, (affliction.Strength - affliction.Prefab.ShowIconThreshold) / Math.Min(affliction.Prefab.MaxStrength - affliction.Prefab.ShowIconThreshold, 10.0f)); slotStyle.Sprites[GUIComponent.ComponentState.None][0].Draw( spriteBatch, new Rectangle((iconPos - iconSize / 2.0f).ToPoint(), iconSize.ToPoint()), slotStyle.Color * alpha); affliction.Prefab.Icon.Draw(spriteBatch, iconPos - iconSize / 2.0f, affliction.Prefab.IconColor * alpha, 0, iconScale); iconPos += new Vector2(10.0f, 20.0f) * iconScale; } private Rectangle GetLimbHighlightArea(LimbHealth limbHealth, Rectangle drawArea) { float scale = Math.Min(drawArea.Width / (float)limbHealth.IndicatorSprite.SourceRect.Width, drawArea.Height / (float)limbHealth.IndicatorSprite.SourceRect.Height); return new Rectangle( (int)(drawArea.Center.X - (limbHealth.IndicatorSprite.Texture.Width / 2 - limbHealth.HighlightArea.X) * scale), (int)(drawArea.Center.Y - (limbHealth.IndicatorSprite.Texture.Height / 2 - limbHealth.HighlightArea.Y) * scale), (int)(limbHealth.HighlightArea.Width * scale), (int)(limbHealth.HighlightArea.Height * scale)); } public void ClientRead(NetBuffer inc) { List> newAfflictions = new List>(); byte afflictionCount = inc.ReadByte(); for (int i = 0; i < afflictionCount; i++) { AfflictionPrefab afflictionPrefab = AfflictionPrefab.List[inc.ReadRangedInteger(0, AfflictionPrefab.List.Count - 1)]; float afflictionStrength = inc.ReadRangedSingle(0.0f, afflictionPrefab.MaxStrength, 8); newAfflictions.Add(new Pair(afflictionPrefab, afflictionStrength)); } foreach (Affliction affliction in afflictions) { //deactivate afflictions that weren't included in the network message if (!newAfflictions.Any(a => a.First == affliction.Prefab)) { affliction.Strength = 0.0f; } } foreach (Pair newAffliction in newAfflictions) { Affliction existingAffliction = afflictions.Find(a => a.Prefab == newAffliction.First); if (existingAffliction == null) { afflictions.Add(newAffliction.First.Instantiate(newAffliction.Second)); } else { existingAffliction.Strength = newAffliction.Second; if (existingAffliction == stunAffliction) Character.SetStun(existingAffliction.Strength, true, true); } } List> newLimbAfflictions = new List>(); byte limbAfflictionCount = inc.ReadByte(); for (int i = 0; i < limbAfflictionCount; i++) { int limbIndex = inc.ReadRangedInteger(0, limbHealths.Count - 1); AfflictionPrefab afflictionPrefab = AfflictionPrefab.List[inc.ReadRangedInteger(0, AfflictionPrefab.List.Count - 1)]; float afflictionStrength = inc.ReadRangedSingle(0.0f, afflictionPrefab.MaxStrength, 8); newLimbAfflictions.Add(new Triplet(limbHealths[limbIndex], afflictionPrefab, afflictionStrength)); } foreach (LimbHealth limbHealth in limbHealths) { foreach (Affliction affliction in limbHealth.Afflictions) { //deactivate afflictions that weren't included in the network message if (!newLimbAfflictions.Any(a => a.First == limbHealth && a.Second == affliction.Prefab)) { affliction.Strength = 0.0f; } } foreach (Triplet newAffliction in newLimbAfflictions) { if (newAffliction.First != limbHealth) continue; Affliction existingAffliction = limbHealth.Afflictions.Find(a => a.Prefab == newAffliction.Second); if (existingAffliction == null) { limbHealth.Afflictions.Add(newAffliction.Second.Instantiate(newAffliction.Third)); } else { existingAffliction.Strength = newAffliction.Third; } } } } partial void RemoveProjSpecific() { foreach (LimbHealth limbHealth in limbHealths) { if (limbHealth.IndicatorSprite != null) { limbHealth.IndicatorSprite.Remove(); limbHealth.IndicatorSprite = null; } } limbIndicatorOverlay?.Remove(); limbIndicatorOverlay = null; } } }