Exemplo n.º 1
0
        private void PlaceObject(LevelObjectPrefab prefab, SpawnPosition spawnPosition, Level level)
        {
            float rotation = 0.0f;

            if (prefab.AlignWithSurface && spawnPosition.Normal.LengthSquared() > 0.001f && spawnPosition != null)
            {
                rotation = MathUtils.VectorToAngle(new Vector2(spawnPosition.Normal.Y, spawnPosition.Normal.X));
            }
            rotation += Rand.Range(prefab.RandomRotationRad.X, prefab.RandomRotationRad.Y, Rand.RandSync.Server);

            Vector2 position = Vector2.Zero;
            Vector2 edgeDir  = Vector2.UnitX;

            if (spawnPosition == null)
            {
                position = new Vector2(
                    Rand.Range(0.0f, level.Size.X, Rand.RandSync.Server),
                    Rand.Range(0.0f, level.Size.Y, Rand.RandSync.Server));
            }
            else
            {
                edgeDir  = (spawnPosition.GraphEdge.Point1 - spawnPosition.GraphEdge.Point2) / spawnPosition.Length;
                position = spawnPosition.GraphEdge.Point2 + edgeDir * Rand.Range(prefab.MinSurfaceWidth / 2.0f, spawnPosition.Length - prefab.MinSurfaceWidth / 2.0f, Rand.RandSync.Server);
            }

            if (!MathUtils.NearlyEqual(prefab.RandomOffset.X, 0.0f) || !MathUtils.NearlyEqual(prefab.RandomOffset.Y, 0.0f))
            {
                Vector2 offsetDir = spawnPosition.Normal.LengthSquared() > 0.001f ? spawnPosition.Normal : Rand.Vector(1.0f, Rand.RandSync.Server);
                position += offsetDir * Rand.Range(prefab.RandomOffset.X, prefab.RandomOffset.Y, Rand.RandSync.Server);
            }

            var newObject = new LevelObject(prefab,
                                            new Vector3(position, Rand.Range(prefab.DepthRange.X, prefab.DepthRange.Y, Rand.RandSync.Server)), Rand.Range(prefab.MinSize, prefab.MaxSize, Rand.RandSync.Server), rotation);

            AddObject(newObject, level);

            foreach (LevelObjectPrefab.ChildObject child in prefab.ChildObjects)
            {
                int childCount = Rand.Range(child.MinCount, child.MaxCount, Rand.RandSync.Server);
                for (int j = 0; j < childCount; j++)
                {
                    var matchingPrefabs = LevelObjectPrefab.List.Where(p => child.AllowedNames.Contains(p.Name));
                    int prefabCount     = matchingPrefabs.Count();
                    var childPrefab     = prefabCount == 0 ? null : matchingPrefabs.ElementAt(Rand.Range(0, prefabCount, Rand.RandSync.Server));
                    if (childPrefab == null)
                    {
                        continue;
                    }

                    Vector2 childPos = position + edgeDir * Rand.Range(-0.5f, 0.5f, Rand.RandSync.Server) * prefab.MinSurfaceWidth;

                    var childObject = new LevelObject(childPrefab,
                                                      new Vector3(childPos, Rand.Range(childPrefab.DepthRange.X, childPrefab.DepthRange.Y, Rand.RandSync.Server)),
                                                      Rand.Range(childPrefab.MinSize, childPrefab.MaxSize, Rand.RandSync.Server),
                                                      rotation + Rand.Range(childPrefab.RandomRotationRad.X, childPrefab.RandomRotationRad.Y, Rand.RandSync.Server));

                    AddObject(childObject, level);
                }
            }
        }
Exemplo n.º 2
0
        public void ScrollToEnd(float duration)
        {
            CoroutineManager.StartCoroutine(ScrollCoroutine());

            IEnumerable <object> ScrollCoroutine()
            {
                if (BarSize >= 1.0f)
                {
                    yield return(CoroutineStatus.Success);
                }
                float t                = 0.0f;
                float startScroll      = BarScroll * BarSize;
                float distanceToTravel = ScrollBar.MaxValue - startScroll;
                float progress         = startScroll;
                float speed            = distanceToTravel / duration;

                while (t < duration && !MathUtils.NearlyEqual(ScrollBar.MaxValue, progress))
                {
                    t        += CoroutineManager.DeltaTime;
                    progress += speed * CoroutineManager.DeltaTime;
                    BarScroll = progress;
                    yield return(CoroutineStatus.Running);
                }

                yield return(CoroutineStatus.Success);
            }
        }
Exemplo n.º 3
0
 public bool CanBePut(Item item)
 {
     if (item == null)
     {
         return(false);
     }
     if (items.Count > 0)
     {
         if (item.IsFullCondition)
         {
             if (items.Any(it => !it.IsFullCondition))
             {
                 return(false);
             }
         }
         else if (MathUtils.NearlyEqual(item.Condition, 0.0f))
         {
             if (items.Any(it => !MathUtils.NearlyEqual(it.Condition, 0.0f)))
             {
                 return(false);
             }
         }
         else
         {
             return(false);
         }
         if (items[0].Prefab.Identifier != item.Prefab.Identifier ||
             items.Count + 1 > item.Prefab.MaxStackSize)
         {
             return(false);
         }
     }
     return(true);
 }
Exemplo n.º 4
0
        public float GetScreenGrainStrength()
        {
            if (Strength < Prefab.ActivationThreshold)
            {
                return(0.0f);
            }
            AfflictionPrefab.Effect currentEffect = GetActiveEffect();
            if (currentEffect == null)
            {
                return(0.0f);
            }
            if (MathUtils.NearlyEqual(currentEffect.MaxGrainStrength, 0f))
            {
                return(0.0f);
            }

            float amount = MathHelper.Lerp(
                currentEffect.MinGrainStrength,
                currentEffect.MaxGrainStrength,
                (Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);

            if (Prefab.GrainBurst > 0 && AdditionStrength > amount)
            {
                return(Math.Min(AdditionStrength, 1.0f));
            }

            return(amount);
        }
Exemplo n.º 5
0
        private bool?TryFloat(CampaignMode campaignMode, string value)
        {
            if (float.TryParse(value, out float f))
            {
                float target = GetFloat(campaignMode);
                value1 = target;
                value2 = f;
                switch (Operator)
                {
                case PropertyConditional.OperatorType.Equals:
                    return(MathUtils.NearlyEqual(target, f));

                case PropertyConditional.OperatorType.GreaterThan:
                    return(target > f);

                case PropertyConditional.OperatorType.GreaterThanEquals:
                    return(target >= f);

                case PropertyConditional.OperatorType.LessThan:
                    return(target < f);

                case PropertyConditional.OperatorType.LessThanEquals:
                    return(target <= f);

                case PropertyConditional.OperatorType.NotEquals:
                    return(!MathUtils.NearlyEqual(target, f));
                }
            }

            DebugConsole.Log($"{value} != float");
            return(null);
        }
 protected override bool Filter(Pump pump)
 {
     if (pump == null)
     {
         return(false);
     }
     if (pump.Item.HasTag("ballast"))
     {
         return(false);
     }
     if (pump.Item.Submarine == null)
     {
         return(false);
     }
     if (pump.Item.CurrentHull == null)
     {
         return(false);
     }
     if (pump.Item.Submarine.TeamID != character.TeamID)
     {
         return(false);
     }
     if (pump.Item.ConditionPercentage <= 0)
     {
         return(false);
     }
     if (pump.Item.CurrentHull.FireSources.Count > 0)
     {
         return(false);
     }
     if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(pump.Item, true))
     {
         return(false);
     }
     if (Character.CharacterList.Any(c => c.CurrentHull == pump.Item.CurrentHull && !HumanAIController.IsFriendly(c)))
     {
         return(false);
     }
     if (Option == "stoppumping")
     {
         if (!pump.IsActive || MathUtils.NearlyEqual(pump.FlowPercentage, 0))
         {
             return(false);
         }
     }
     else
     {
         if (!pump.Item.InWater)
         {
             return(false);
         }
         if (pump.IsActive && pump.FlowPercentage <= -99.9f)
         {
             return(false);
         }
     }
     return(true);
 }
Exemplo n.º 7
0
 private bool IsReady(Pump pump)
 {
     if (Option == "stoppumping")
     {
         return(!pump.IsActive || MathUtils.NearlyEqual(pump.FlowPercentage, 0));
     }
     else
     {
         return(!pump.Item.InWater || pump.IsActive && pump.FlowPercentage <= -99.9f);
     }
 }
Exemplo n.º 8
0
            public bool CanBePut(ItemPrefab itemPrefab, float?condition = null, int?quality = null)
            {
                if (itemPrefab == null)
                {
                    return(false);
                }
                if (items.Count > 0)
                {
                    if (condition.HasValue)
                    {
                        if (MathUtils.NearlyEqual(condition.Value, 0.0f))
                        {
                            if (items.Any(it => it.Condition > 0.0f))
                            {
                                return(false);
                            }
                        }
                        else if (MathUtils.NearlyEqual(condition.Value, itemPrefab.Health))
                        {
                            if (items.Any(it => !it.IsFullCondition))
                            {
                                return(false);
                            }
                        }
                        else
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        if (items.Any(it => !it.IsFullCondition))
                        {
                            return(false);
                        }
                    }

                    if (quality.HasValue)
                    {
                        if (items[0].Quality != quality.Value)
                        {
                            return(false);
                        }
                    }

                    if (items[0].Prefab.Identifier != itemPrefab.Identifier ||
                        items.Count + 1 > itemPrefab.MaxStackSize)
                    {
                        return(false);
                    }
                }
                return(true);
            }
Exemplo n.º 9
0
        private static bool SoundElementsEquivalent(XElement a, XElement b)
        {
            string filePathA = a.GetAttributeString("file", "").CleanUpPath();
            float  baseGainA = a.GetAttributeFloat("volume", 1.0f);
            float  rangeA    = a.GetAttributeFloat("range", 1000.0f);
            string filePathB = b.GetAttributeString("file", "").CleanUpPath();
            float  baseGainB = b.GetAttributeFloat("volume", 1.0f);
            float  rangeB    = b.GetAttributeFloat("range", 1000.0f);

            return(a.Name.ToString().Equals(b.Name.ToString(), StringComparison.OrdinalIgnoreCase) &&
                   filePathA == filePathB && MathUtils.NearlyEqual(baseGainA, baseGainB) &&
                   MathUtils.NearlyEqual(rangeA, rangeB));
        }
Exemplo n.º 10
0
 /// <param name="maxStackSize">Defaults to <see cref="ItemPrefab.MaxStackSize"/> if null</param>
 public int HowManyCanBePut(ItemPrefab itemPrefab, int?maxStackSize = null, float?condition = null)
 {
     if (itemPrefab == null)
     {
         return(0);
     }
     maxStackSize ??= itemPrefab.MaxStackSize;
     if (items.Count > 0)
     {
         if (condition.HasValue)
         {
             if (MathUtils.NearlyEqual(condition.Value, 0.0f))
             {
                 if (items.Any(it => it.Condition > 0.0f))
                 {
                     return(0);
                 }
             }
             else if (MathUtils.NearlyEqual(condition.Value, itemPrefab.Health))
             {
                 if (items.Any(it => !it.IsFullCondition))
                 {
                     return(0);
                 }
             }
             else
             {
                 return(0);
             }
         }
         else
         {
             if (items.Any(it => !it.IsFullCondition))
             {
                 return(0);
             }
         }
         if (items[0].Prefab.Identifier != itemPrefab.Identifier)
         {
             return(0);
         }
         return(maxStackSize.Value - items.Count);
     }
     else
     {
         return(maxStackSize.Value);
     }
 }
Exemplo n.º 11
0
        /// <summary>
        /// Convert a RGB value into a HSV value.
        /// </summary>
        /// <param name="color"></param>
        /// <see href="https://www.cs.rit.edu/~ncs/color/t_convert.html">Reference</see>
        /// <returns>
        /// Vector3 where X is the hue (0-360 or NaN)
        /// Y is the saturation (0-1)
        /// Z is the value (0-1)
        /// </returns>
        public static Vector3 RGBToHSV(Color color)
        {
            float r = color.R / 255f,
                  g = color.G / 255f,
                  b = color.B / 255f;

            float h, s;

            float min = Math.Min(r, Math.Min(g, b));
            float max = Math.Max(r, Math.Max(g, b));

            float v = max;

            float delta = max - min;

            if (max != 0)
            {
                s = delta / max;
            }
            else
            {
                s = 0;
                h = -1;
                return(new Vector3(h, s, v));
            }

            if (MathUtils.NearlyEqual(r, max))
            {
                h = (g - b) / delta;
            }
            else if (MathUtils.NearlyEqual(g, max))
            {
                h = 2 + (b - r) / delta;
            }
            else
            {
                h = 4 + (r - g) / delta;
            }

            h *= 60;
            if (h < 0)
            {
                h += 360;
            }

            return(new Vector3(h, s, v));
        }
Exemplo n.º 12
0
        public void Update()
        {
            float targetScroll =
                (float)currentPage / ((float)pageContainer.Content.CountChildren - 1);

            pageContainer.BarScroll = MathHelper.Lerp(pageContainer.BarScroll, targetScroll, 0.2f);
            if (MathUtils.NearlyEqual(pageContainer.BarScroll, targetScroll, 0.001f))
            {
                pageContainer.BarScroll = targetScroll;
            }

            for (int i = 0; i < CharacterMenus.Length; i++)
            {
                CharacterMenus[i]?.Update();
            }

            pageContainer.HoverCursor         = CursorState.Default;
            pageContainer.Content.HoverCursor = CursorState.Default;
        }
Exemplo n.º 13
0
        public void ReceiveTextInput(string input)
        {
            if (selectedCharacters > 0)
            {
                RemoveSelectedText();
            }
            Vector2 textPos = textBlock.TextPos;
            bool    wasOverflowClipActive = textBlock.OverflowClipActive;

            if (SetText(Text.Insert(CaretIndex, input)))
            {
                CaretIndex = Math.Min(Text.Length, CaretIndex + input.Length);
                OnTextChanged?.Invoke(this, Text);
                if (textBlock.OverflowClipActive && wasOverflowClipActive && !MathUtils.NearlyEqual(textBlock.TextPos, textPos))
                {
                    textBlock.TextPos = textPos + Vector2.UnitX * Font.MeasureString(input).X;
                }
            }
        }
 public void AddDamage(float damage, Vector2 worldPosition)
 {
     AddDamageProjSpecific(damage, worldPosition);
     if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
     {
         return;
     }
     if (Destroyed)
     {
         return;
     }
     if (!MathUtils.NearlyEqual(damage, 0.0f))
     {
         NetworkUpdatePending = true;
     }
     Damage += damage;
     if (Damage >= MaxHealth)
     {
         CreateFragments();
         Destroy();
     }
 }
Exemplo n.º 15
0
        public void AddDamage(float damage, float deltaTime, Entity attacker, bool isNetworkEvent = false)
        {
            if (Health <= 0.0f)
            {
                return;
            }
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && !isNetworkEvent)
            {
                return;
            }
            tookDamage |= !MathUtils.NearlyEqual(damage, 0.0f);
            Health     -= damage;
            if (Health <= 0.0f)
            {
#if CLIENT
                if (GameMain.GameSession?.Level?.LevelObjectManager != null)
                {
                    GameMain.GameSession.Level.LevelObjectManager.ForceRefreshVisibleObjects = true;
                }
#endif
                if (PhysicsBody != null)
                {
                    PhysicsBody.Enabled = false;
                }
                foreach (LevelTrigger trigger in Triggers)
                {
                    trigger.PhysicsBody.Enabled = false;
                    foreach (StatusEffect effect in trigger.StatusEffects)
                    {
                        if (effect.type != ActionType.OnBroken)
                        {
                            continue;
                        }
                        effect.Apply(effect.type, deltaTime, attacker, this, worldPosition: WorldPosition);
                    }
                }
            }
        }
Exemplo n.º 16
0
        public FabricationRecipe(XElement element, ItemPrefab itemPrefab)
        {
            TargetItem = itemPrefab;
            string displayName = element.GetAttributeString("displayname", "");

            DisplayName = string.IsNullOrEmpty(displayName) ? itemPrefab.Name : TextManager.Get($"DisplayName.{displayName}");

            SuitableFabricatorIdentifiers = element.GetAttributeStringArray("suitablefabricators", new string[0]);

            RequiredSkills = new List <Skill>();
            RequiredTime   = element.GetAttributeFloat("requiredtime", 1.0f);
            OutCondition   = element.GetAttributeFloat("outcondition", 1.0f);
            RequiredItems  = new List <RequiredItem>();
            Amount         = element.GetAttributeInt("amount", 1);

            foreach (XElement subElement in element.Elements())
            {
                switch (subElement.Name.ToString().ToLowerInvariant())
                {
                case "requiredskill":
                    if (subElement.Attribute("name") != null)
                    {
                        DebugConsole.ThrowError("Error in fabricable item " + itemPrefab.Name + "! Use skill identifiers instead of names.");
                        continue;
                    }

                    RequiredSkills.Add(new Skill(
                                           subElement.GetAttributeString("identifier", ""),
                                           subElement.GetAttributeInt("level", 0)));
                    break;

                case "item":
                case "requireditem":
                    string requiredItemIdentifier = subElement.GetAttributeString("identifier", "");
                    string requiredItemTag        = subElement.GetAttributeString("tag", "");
                    if (string.IsNullOrWhiteSpace(requiredItemIdentifier) && string.IsNullOrEmpty(requiredItemTag))
                    {
                        DebugConsole.ThrowError("Error in fabricable item " + itemPrefab.Name + "! One of the required items has no identifier or tag.");
                        continue;
                    }

                    float minCondition = subElement.GetAttributeFloat("mincondition", 1.0f);
                    //Substract mincondition from required item's condition or delete it regardless?
                    bool useCondition = subElement.GetAttributeBool("usecondition", true);
                    int  count        = subElement.GetAttributeInt("count", 1);

                    if (!string.IsNullOrEmpty(requiredItemIdentifier))
                    {
                        if (!(MapEntityPrefab.Find(null, requiredItemIdentifier.Trim()) is ItemPrefab requiredItem))
                        {
                            DebugConsole.ThrowError("Error in fabricable item " + itemPrefab.Name + "! Required item \"" + requiredItemIdentifier + "\" not found.");
                            continue;
                        }

                        var existing = RequiredItems.Find(r => r.ItemPrefabs.Count == 1 && r.ItemPrefabs[0] == requiredItem && MathUtils.NearlyEqual(r.MinCondition, minCondition));
                        if (existing == null)
                        {
                            RequiredItems.Add(new RequiredItem(requiredItem, count, minCondition, useCondition));
                        }
                        else
                        {
                            existing.Amount += count;
                        }
                    }
                    else
                    {
                        var matchingItems = ItemPrefab.Prefabs.Where(ip => ip.Tags.Any(t => t.Equals(requiredItemTag, StringComparison.OrdinalIgnoreCase)));
                        if (!matchingItems.Any())
                        {
                            DebugConsole.ThrowError("Error in fabricable item " + itemPrefab.Name + "! Could not find any items with the tag \"" + requiredItemTag + "\".");
                            continue;
                        }

                        var existing = RequiredItems.Find(r => r.ItemPrefabs.SequenceEqual(matchingItems) && MathUtils.NearlyEqual(r.MinCondition, minCondition));
                        if (existing == null)
                        {
                            RequiredItems.Add(new RequiredItem(matchingItems, count, minCondition, useCondition));
                        }
                        else
                        {
                            existing.Amount += count;
                        }
                    }
                    break;
                }
            }
        }
Exemplo n.º 17
0
        protected override void Update(float deltaTime)
        {
            if (!Visible)
            {
                return;
            }

            if (flashTimer > 0.0f)
            {
                flashTimer -= deltaTime;
            }
            if (!Enabled)
            {
                return;
            }

            if (skipUpdate)
            {
                skipUpdate = false;
                return;
            }

            if (MouseRect.Contains(PlayerInput.MousePosition) && (GUI.MouseOn == null || (!(GUI.MouseOn is GUIButton) && GUI.IsMouseOn(this))))
            {
                State = ComponentState.Hover;
                if (PlayerInput.PrimaryMouseButtonDown())
                {
                    mouseHeldInside = true;
                    Select();
                }
                else
                {
                    isSelecting = PlayerInput.PrimaryMouseButtonHeld();
                }
                if (PlayerInput.DoubleClicked())
                {
                    SelectAll();
                }
                if (isSelecting)
                {
                    if (!MathUtils.NearlyEqual(PlayerInput.MouseSpeed.X, 0))
                    {
                        CaretIndex = textBlock.GetCaretIndexFromScreenPos(PlayerInput.MousePosition);
                        CalculateCaretPos();
                        CalculateSelection();
                    }
                }
            }
            else
            {
                if ((PlayerInput.LeftButtonClicked() || PlayerInput.RightButtonClicked()) && selected)
                {
                    if (!mouseHeldInside)
                    {
                        Deselect();
                    }
                    mouseHeldInside = false;
                }
                isSelecting = false;
                State       = ComponentState.None;
            }
            if (!isSelecting)
            {
                isSelecting = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift);
            }

            if (CaretEnabled)
            {
                if (textBlock.OverflowClipActive)
                {
                    if (CaretScreenPos.X < textBlock.Rect.X + textBlock.Padding.X)
                    {
                        textBlock.TextPos = new Vector2(textBlock.TextPos.X + ((textBlock.Rect.X + textBlock.Padding.X) - CaretScreenPos.X), textBlock.TextPos.Y);
                        CalculateCaretPos();
                    }
                    else if (CaretScreenPos.X > textBlock.Rect.Right - textBlock.Padding.Z)
                    {
                        textBlock.TextPos = new Vector2(textBlock.TextPos.X - (CaretScreenPos.X - (textBlock.Rect.Right - textBlock.Padding.Z)), textBlock.TextPos.Y);
                        CalculateCaretPos();
                    }
                }
                caretTimer  += deltaTime;
                caretVisible = ((caretTimer * 1000.0f) % 1000) < 500;
                if (caretVisible && caretPosDirty)
                {
                    CalculateCaretPos();
                }
            }

            if (GUI.KeyboardDispatcher.Subscriber == this)
            {
                State = ComponentState.Selected;
                Character.DisableControls = true;
                if (OnEnterPressed != null && PlayerInput.KeyHit(Keys.Enter))
                {
                    OnEnterPressed(this, Text);
                }
            }
            else if (Selected)
            {
                Deselect();
            }

            textBlock.State = State;
        }
Exemplo n.º 18
0
        /// <summary>
        /// Returns a dictionary where the keys are the structures that took damage and the values are the amount of damage taken
        /// </summary>
        public static Dictionary <Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null)
        {
            List <Structure> structureList = new List <Structure>();
            float            dist          = 600.0f;

            foreach (MapEntity entity in MapEntity.mapEntityList)
            {
                if (!(entity is Structure structure))
                {
                    continue;
                }

                if (structure.HasBody &&
                    !structure.IsPlatform &&
                    Vector2.Distance(structure.WorldPosition, worldPosition) < dist * 3.0f)
                {
                    structureList.Add(structure);
                }
            }

            Dictionary <Structure, float> damagedStructures = new Dictionary <Structure, float>();

            foreach (Structure structure in structureList)
            {
                for (int i = 0; i < structure.SectionCount; i++)
                {
                    float distFactor = 1.0f - (Vector2.Distance(structure.SectionPosition(i, true), worldPosition) / worldRange);
                    if (distFactor <= 0.0f)
                    {
                        continue;
                    }

                    structure.AddDamage(i, damage * distFactor, attacker);

                    if (damagedStructures.ContainsKey(structure))
                    {
                        damagedStructures[structure] += damage * distFactor;
                    }
                    else
                    {
                        damagedStructures.Add(structure, damage * distFactor);
                    }
                }
            }

            if (Level.Loaded != null && !MathUtils.NearlyEqual(levelWallDamage, 0.0f))
            {
                for (int i = Level.Loaded.ExtraWalls.Count - 1; i >= 0; i--)
                {
                    if (!(Level.Loaded.ExtraWalls[i] is DestructibleLevelWall destructibleWall))
                    {
                        continue;
                    }
                    foreach (var cell in destructibleWall.Cells)
                    {
                        if (cell.IsPointInside(worldPosition))
                        {
                            destructibleWall.AddDamage(levelWallDamage, worldPosition);
                            continue;
                        }
                        foreach (var edge in cell.Edges)
                        {
                            if (!MathUtils.GetLineIntersection(worldPosition, cell.Center, edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, out Vector2 intersection))
                            {
                                continue;
                            }

                            float wallDist = Vector2.DistanceSquared(worldPosition, intersection);
                            if (wallDist < worldRange * worldRange)
                            {
                                destructibleWall.AddDamage(damage, worldPosition);
                                break;
                            }
                        }
                    }
                }
            }

            return(damagedStructures);
        }
Exemplo n.º 19
0
        public GUIComponent CreateInfoFrame(GUIFrame frame, bool returnParent, Sprite permissionIcon = null)
        {
            var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.874f, 0.58f), frame.RectTransform, Anchor.TopCenter)
            {
                RelativeOffset = new Vector2(0.0f, 0.05f)
            })
            {
                RelativeSpacing = 0.05f
                                  //Stretch = true
            };

            var headerArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.322f), paddedFrame.RectTransform), isHorizontal: true);

            new GUICustomComponent(new RectTransform(new Vector2(0.425f, 1.0f), headerArea.RectTransform),
                                   onDraw: (sb, component) => DrawInfoFrameCharacterIcon(sb, component.Rect));

            ScalableFont font = paddedFrame.Rect.Width < 280 ? GUI.SmallFont : GUI.Font;

            var headerTextArea = new GUILayoutGroup(new RectTransform(new Vector2(0.575f, 1.0f), headerArea.RectTransform))
            {
                RelativeSpacing = 0.02f,
                Stretch         = true
            };

            Color?nameColor = null;

            if (Job != null)
            {
                nameColor = Job.Prefab.UIColor;
            }

            GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform), ToolBox.LimitString(Name, GUI.Font, headerTextArea.Rect.Width), textColor: nameColor, font: GUI.Font)
            {
                ForceUpperCase = true,
                Padding        = Vector4.Zero
            };

            if (permissionIcon != null)
            {
                Point iconSize  = permissionIcon.SourceRect.Size;
                int   iconWidth = (int)((float)characterNameBlock.Rect.Height / iconSize.Y * iconSize.X);
                new GUIImage(new RectTransform(new Point(iconWidth, characterNameBlock.Rect.Height), characterNameBlock.RectTransform)
                {
                    AbsoluteOffset = new Point(-iconWidth - 2, 0)
                }, permissionIcon)
                {
                    IgnoreLayoutGroups = true
                };
            }

            if (Job != null)
            {
                new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform), Job.Name, textColor: Job.Prefab.UIColor, font: font)
                {
                    Padding = Vector4.Zero
                };
            }

            if (personalityTrait != null)
            {
                new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform), TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + personalityTrait.Name.Replace(" ", ""))), font: font)
                {
                    Padding = Vector4.Zero
                };
            }

            if (Job != null && (Character == null || !Character.IsDead))
            {
                var skillsArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.63f), paddedFrame.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter))
                {
                    Stretch = true
                };

                var skills = Job.Skills;
                skills.Sort((s1, s2) => - s1.Level.CompareTo(s2.Level));

                new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillsArea.RectTransform), TextManager.AddPunctuation(':', TextManager.Get("skills"), string.Empty), font: font)
                {
                    Padding = Vector4.Zero
                };

                foreach (Skill skill in skills)
                {
                    Color textColor = Color.White * (0.5f + skill.Level / 200.0f);

                    var skillName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillsArea.RectTransform), TextManager.Get("SkillName." + skill.Identifier), textColor: textColor, font: font)
                    {
                        Padding = Vector4.Zero
                    };

                    float modifiedSkillLevel = skill.Level;
                    if (Character != null)
                    {
                        modifiedSkillLevel = Character.GetSkillLevel(skill.Identifier);
                    }
                    if (!MathUtils.NearlyEqual(MathF.Round(modifiedSkillLevel), MathF.Round(skill.Level)))
                    {
                        int    skillChange = (int)MathF.Round(modifiedSkillLevel - skill.Level);
                        string changeText  = $"{(skillChange > 0 ? "+" : "") + skillChange}";
                        new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), skillName.RectTransform), $"{(int)skill.Level} ({changeText})", textColor: textColor, font: font, textAlignment: Alignment.CenterRight);
                    }
                    else
                    {
                        new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), skillName.RectTransform), ((int)skill.Level).ToString(), textColor: textColor, font: font, textAlignment: Alignment.CenterRight);
                    }
                }
            }
            else if (Character != null && Character.IsDead)
            {
                var deadArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.63f), paddedFrame.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter))
                {
                    Stretch = true
                };

                string deadDescription = TextManager.AddPunctuation(':', TextManager.Get("deceased") + "\n" + Character.CauseOfDeath.Affliction?.CauseOfDeathDescription ??
                                                                    TextManager.AddPunctuation(':', TextManager.Get("CauseOfDeath"), TextManager.Get("CauseOfDeath." + Character.CauseOfDeath.Type.ToString())));

                new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), deadArea.RectTransform), deadDescription, textColor: GUI.Style.Red, font: font, textAlignment: Alignment.TopLeft)
                {
                    Padding = Vector4.Zero
                };
            }

            if (returnParent)
            {
                return(frame);
            }
            else
            {
                return(paddedFrame);
            }
        }
Exemplo n.º 20
0
        public static List <VoronoiCell> GraphEdgesToCells(List <GraphEdge> graphEdges, Rectangle borders, float gridCellSize, out List <VoronoiCell>[,] cellGrid)
        {
            List <VoronoiCell> cells = new List <VoronoiCell>();

            cellGrid = new List <VoronoiCell> [(int)Math.Ceiling(borders.Width / gridCellSize), (int)Math.Ceiling(borders.Height / gridCellSize)];
            for (int x = 0; x < borders.Width / gridCellSize; x++)
            {
                for (int y = 0; y < borders.Height / gridCellSize; y++)
                {
                    cellGrid[x, y] = new List <VoronoiCell>();
                }
            }

            foreach (GraphEdge ge in graphEdges)
            {
                if (Vector2.DistanceSquared(ge.Point1, ge.Point2) < 0.001f)
                {
                    continue;
                }

                for (int i = 0; i < 2; i++)
                {
                    Site site = (i == 0) ? ge.Site1 : ge.Site2;

                    int x = (int)(Math.Floor((site.Coord.X - borders.X) / gridCellSize));
                    int y = (int)(Math.Floor((site.Coord.Y - borders.Y) / gridCellSize));

                    x = MathHelper.Clamp(x, 0, cellGrid.GetLength(0) - 1);
                    y = MathHelper.Clamp(y, 0, cellGrid.GetLength(1) - 1);

                    VoronoiCell cell = cellGrid[x, y].Find(c => c.Site == site);

                    if (cell == null)
                    {
                        cell = new VoronoiCell(site);
                        cellGrid[x, y].Add(cell);
                        cells.Add(cell);
                    }

                    if (ge.Cell1 == null)
                    {
                        ge.Cell1 = cell;
                    }
                    else
                    {
                        ge.Cell2 = cell;
                    }
                    cell.Edges.Add(ge);
                }
            }

            //add edges to the borders of the graph
            foreach (var cell in cells)
            {
                Vector2?point1 = null, point2 = null;
                foreach (GraphEdge ge in cell.Edges)
                {
                    if (MathUtils.NearlyEqual(ge.Point1.X, borders.X) || MathUtils.NearlyEqual(ge.Point1.X, borders.Right) ||
                        MathUtils.NearlyEqual(ge.Point1.Y, borders.Y) || MathUtils.NearlyEqual(ge.Point1.Y, borders.Bottom))
                    {
                        if (point1 == null)
                        {
                            point1 = ge.Point1;
                        }
                        else if (point2 == null)
                        {
                            if (MathUtils.NearlyEqual(point1.Value, ge.Point1))
                            {
                                continue;
                            }
                            point2 = ge.Point1;
                        }
                    }
                    if (MathUtils.NearlyEqual(ge.Point2.X, borders.X) || MathUtils.NearlyEqual(ge.Point2.X, borders.Right) ||
                        MathUtils.NearlyEqual(ge.Point2.Y, borders.Y) || MathUtils.NearlyEqual(ge.Point2.Y, borders.Bottom))
                    {
                        if (point1 == null)
                        {
                            point1 = ge.Point2;
                        }
                        else
                        {
                            if (MathUtils.NearlyEqual(point1.Value, ge.Point2))
                            {
                                continue;
                            }
                            point2 = ge.Point2;
                        }
                    }
                    if (point1.HasValue && point2.HasValue)
                    {
                        Debug.Assert(point1 != point2);
                        bool point1OnSide = MathUtils.NearlyEqual(point1.Value.X, borders.X) || MathUtils.NearlyEqual(point1.Value.X, borders.Right);
                        bool point2OnSide = MathUtils.NearlyEqual(point2.Value.X, borders.X) || MathUtils.NearlyEqual(point2.Value.X, borders.Right);
                        //one point is one the side, another on top/bottom
                        // -> the cell is in the corner of the level, we need 2 edges
                        if (point1OnSide != point2OnSide)
                        {
                            Vector2 cornerPos = new Vector2(
                                point1.Value.X < borders.Center.X ? borders.X : borders.Right,
                                point1.Value.Y < borders.Center.Y ? borders.Y : borders.Bottom);
                            cell.Edges.Add(
                                new GraphEdge(point1.Value, cornerPos)
                            {
                                Cell1        = cell,
                                IsSolid      = true,
                                Site1        = cell.Site,
                                OutsideLevel = true
                            });
                            cell.Edges.Add(
                                new GraphEdge(point2.Value, cornerPos)
                            {
                                Cell1        = cell,
                                IsSolid      = true,
                                Site1        = cell.Site,
                                OutsideLevel = true
                            });
                        }
                        else
                        {
                            cell.Edges.Add(
                                new GraphEdge(point1.Value, point2.Value)
                            {
                                Cell1        = cell,
                                IsSolid      = true,
                                Site1        = cell.Site,
                                OutsideLevel = true
                            });
                        }
                        break;
                    }
                }
            }

            return(cells);
        }
Exemplo n.º 21
0
        public void OnCharacterHealthChanged(Character target, Character attacker, float damage, IEnumerable <Affliction> appliedAfflictions = null)
        {
            if (target == null || attacker == null)
            {
                return;
            }
            if (target == attacker)
            {
                return;
            }

            //damaging dead characters doesn't affect karma
            if (target.IsDead || target.Removed)
            {
                return;
            }

            bool isEnemy = target.AIController is EnemyAIController || target.TeamID != attacker.TeamID;

            if (GameMain.Server.TraitorManager != null)
            {
                if (GameMain.Server.TraitorManager.TraitorList.Any(t => t.Character == target))
                {
                    //traitors always count as enemies
                    isEnemy = true;
                }
                if (GameMain.Server.TraitorManager.TraitorList.Any(t => t.Character == attacker && t.TargetCharacter == target))
                {
                    //target counts as an enemy to the traitor
                    isEnemy = true;
                }
            }

            if (appliedAfflictions != null)
            {
                foreach (Affliction affliction in appliedAfflictions)
                {
                    if (MathUtils.NearlyEqual(affliction.Prefab.KarmaChangeOnApplied, 0.0f))
                    {
                        continue;
                    }
                    damage -= affliction.Prefab.KarmaChangeOnApplied * affliction.Strength;
                }
            }

            if (target.AIController is EnemyAIController || target.TeamID != attacker.TeamID)
            {
                if (damage > 0)
                {
                    float karmaIncrease = damage * DamageEnemyKarmaIncrease;
                    if (attacker?.Info?.Job.Prefab.Identifier == "securityofficer")
                    {
                        karmaIncrease *= 2.0f;
                    }
                    AdjustKarma(attacker, karmaIncrease, "Damaged enemy");
                }
            }
            else
            {
                if (damage > 0)
                {
                    AdjustKarma(attacker, -damage * DamageFriendlyKarmaDecrease, "Damaged friendly");
                }
                else
                {
                    float karmaIncrease = -damage * HealFriendlyKarmaIncrease;
                    if (attacker?.Info?.Job.Prefab.Identifier == "medicaldoctor")
                    {
                        karmaIncrease *= 2.0f;
                    }
                    AdjustKarma(attacker, karmaIncrease, "Healed friendly");
                }
            }
        }
Exemplo n.º 22
0
        partial void InitProjSpecific()
        {
            Sprite?.EnsureLazyLoaded();
            Prefab.DeformableSprite?.EnsureLazyLoaded();

            CurrentSwingAmount      = Prefab.SwingAmountRad;
            CurrentScaleOscillation = Prefab.ScaleOscillation;

            SwingTimer          = Rand.Range(0.0f, MathHelper.TwoPi);
            ScaleOscillateTimer = Rand.Range(0.0f, MathHelper.TwoPi);

            if (Prefab.ParticleEmitterPrefabs != null)
            {
                ParticleEmitters        = new ParticleEmitter[Prefab.ParticleEmitterPrefabs.Count];
                ParticleEmitterTriggers = new LevelTrigger[Prefab.ParticleEmitterPrefabs.Count];
                for (int i = 0; i < Prefab.ParticleEmitterPrefabs.Count; i++)
                {
                    ParticleEmitters[i]        = new ParticleEmitter(Prefab.ParticleEmitterPrefabs[i]);
                    ParticleEmitterTriggers[i] = Prefab.ParticleEmitterTriggerIndex[i] > -1 ?
                                                 Triggers[Prefab.ParticleEmitterTriggerIndex[i]] : null;
                }
            }

            if (Prefab.LightSourceParams != null && Prefab.LightSourceParams.Count > 0)
            {
                LightSources        = new LightSource[Prefab.LightSourceParams.Count];
                LightSourceTriggers = new LevelTrigger[Prefab.LightSourceParams.Count];
                for (int i = 0; i < Prefab.LightSourceParams.Count; i++)
                {
                    LightSources[i] = new LightSource(Prefab.LightSourceParams[i])
                    {
                        Position     = new Vector2(Position.X, Position.Y),
                        IsBackground = true
                    };
                    LightSourceTriggers[i] = Prefab.LightSourceTriggerIndex[i] > -1 ?
                                             Triggers[Prefab.LightSourceTriggerIndex[i]] : null;
                }
            }

            Sounds        = new RoundSound[Prefab.Sounds.Count];
            SoundChannels = new SoundChannel[Prefab.Sounds.Count];
            SoundTriggers = new LevelTrigger[Prefab.Sounds.Count];
            for (int i = 0; i < Prefab.Sounds.Count; i++)
            {
                Sounds[i]        = Submarine.LoadRoundSound(Prefab.Sounds[i].SoundElement, false);
                SoundTriggers[i] = Prefab.Sounds[i].TriggerIndex > -1 ? Triggers[Prefab.Sounds[i].TriggerIndex] : null;
            }

            int j = 0;

            foreach (XElement subElement in Prefab.Config.Elements())
            {
                if (!subElement.Name.ToString().Equals("deformablesprite", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }
                foreach (XElement animationElement in subElement.Elements())
                {
                    var newDeformation = SpriteDeformation.Load(animationElement, Prefab.Name);
                    if (newDeformation != null)
                    {
                        newDeformation.Params = Prefab.SpriteDeformations[j].Params;
                        spriteDeformations.Add(newDeformation);
                        j++;
                    }
                }
            }

            VisibleOnSonar = Prefab.SonarDisruption > 0.0f || Prefab.OverrideProperties.Any(p => p != null && p.SonarDisruption > 0.0f) ||
                             (Triggers != null && Triggers.Any(t => !MathUtils.NearlyEqual(t.Force, Vector2.Zero) && t.ForceMode != LevelTrigger.TriggerForceMode.LimitVelocity || !string.IsNullOrWhiteSpace(t.InfectIdentifier)));
            if (VisibleOnSonar && Triggers.Any())
            {
                SonarRadius = Triggers.Select(t => t.ColliderRadius * 1.5f).Max();
            }
        }
        protected override void Update(float deltaTime)
        {
            if (Draggable)
            {
                GUIComponent parent = GUI.MouseOn?.Parent?.Parent;
                if ((GUI.MouseOn == InnerFrame || InnerFrame.IsParentOf(GUI.MouseOn)) && !(GUI.MouseOn is GUIButton || GUI.MouseOn is GUIColorPicker || GUI.MouseOn is GUITextBox || parent is GUITextBox))
                {
                    GUI.MouseCursor = CursorState.Move;
                    if (PlayerInput.PrimaryMouseButtonDown())
                    {
                        DraggingPosition = RectTransform.ScreenSpaceOffset.ToVector2() - PlayerInput.MousePosition;
                    }
                }

                if (PlayerInput.PrimaryMouseButtonHeld() && DraggingPosition != Vector2.Zero)
                {
                    GUI.MouseCursor = CursorState.Dragging;
                    RectTransform.ScreenSpaceOffset = (PlayerInput.MousePosition + DraggingPosition).ToPoint();
                }
                else
                {
                    DraggingPosition = Vector2.Zero;
                }
            }

            if (type == Type.InGame)
            {
                Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
                Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
                Vector2 endPos     = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);

                if (!closing)
                {
                    Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
                    InnerFrame.RectTransform.AbsoluteOffset = step;
                    if (BackgroundIcon != null)
                    {
                        BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2);
                        if (!MathUtils.NearlyEqual(openState, 1.0f))
                        {
                            BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White);
                        }
                    }
                    if (!(Screen.Selected is RoundSummaryScreen) && !MessageBoxes.Any(mb => mb.UserData is RoundSummary))
                    {
                        openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
                    }

                    if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose)
                    {
                        inGameCloseTimer += deltaTime;
                    }

                    if (inGameCloseTimer >= inGameCloseTime)
                    {
                        Close();
                    }
                }
                else
                {
                    openState += deltaTime * 2.0f;
                    Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
                    InnerFrame.RectTransform.AbsoluteOffset = step;
                    if (BackgroundIcon != null)
                    {
                        BackgroundIcon.Color *= 0.9f;
                    }
                    if (openState >= 2.0f)
                    {
                        if (Parent != null)
                        {
                            Parent.RemoveChild(this);
                        }
                        if (MessageBoxes.Contains(this))
                        {
                            MessageBoxes.Remove(this);
                        }
                    }
                }

                if (newBackgroundIcon != null)
                {
                    if (!iconSwitching)
                    {
                        if (BackgroundIcon != null)
                        {
                            BackgroundIcon.Color *= 0.9f;
                            if (BackgroundIcon.Color.A == 0)
                            {
                                BackgroundIcon = null;
                                iconSwitching  = true;
                                RemoveChild(BackgroundIcon);
                            }
                        }
                        else
                        {
                            iconSwitching = true;
                        }
                        iconState = 0;
                    }
                    else
                    {
                        newBackgroundIcon.SetAsFirstChild();
                        newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
                        newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White);
                        if (newBackgroundIcon.Color.A == 255)
                        {
                            BackgroundIcon = newBackgroundIcon;
                            BackgroundIcon.SetAsFirstChild();
                            newBackgroundIcon = null;
                            iconSwitching     = false;
                        }

                        iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f);
                    }
                }
            }
        }
Exemplo n.º 24
0
        public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource, Character attacker)
        {
            if (attack.Range <= 0.0f)
            {
                return;
            }

            //long range for the broad distance check, because large characters may still be in range even if their collider isn't
            float broadRange = Math.Max(attack.Range * 10.0f, 10000.0f);

            foreach (Character c in Character.CharacterList)
            {
                if (!c.Enabled ||
                    Math.Abs(c.WorldPosition.X - worldPosition.X) > broadRange ||
                    Math.Abs(c.WorldPosition.Y - worldPosition.Y) > broadRange)
                {
                    continue;
                }

                Vector2 explosionPos = worldPosition;
                if (c.Submarine != null)
                {
                    explosionPos -= c.Submarine.Position;
                }

                Hull hull       = Hull.FindHull(explosionPos, null, false);
                bool underWater = hull == null || explosionPos.Y < hull.Surface;

                explosionPos = ConvertUnits.ToSimUnits(explosionPos);

                Dictionary <Limb, float> distFactors         = new Dictionary <Limb, float>();
                Dictionary <Limb, float> damages             = new Dictionary <Limb, float>();
                List <Affliction>        modifiedAfflictions = new List <Affliction>();
                foreach (Limb limb in c.AnimController.Limbs)
                {
                    if (limb.IsSevered || limb.ignoreCollisions)
                    {
                        continue;
                    }

                    float dist = Vector2.Distance(limb.WorldPosition, worldPosition);

                    //calculate distance from the "outer surface" of the physics body
                    //doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
                    float limbRadius = limb.body.GetMaxExtent();
                    dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));

                    if (dist > attack.Range)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - dist / attack.Range;

                    //solid obstacles between the explosion and the limb reduce the effect of the explosion
                    var obstacles = Submarine.PickBodies(limb.SimPosition, explosionPos, collisionCategory: Physics.CollisionItem | Physics.CollisionItemBlocking | Physics.CollisionWall);
                    foreach (var body in obstacles)
                    {
                        if (body.UserData is Item item)
                        {
                            var door = item.GetComponent <Door>();
                            if (door != null && !door.IsBroken)
                            {
                                distFactor *= 0.01f;
                            }
                        }
                        else if (body.UserData is Structure structure)
                        {
                            int sectionIndex = structure.FindSectionIndex(worldPosition, world: true, clamp: true);
                            if (structure.SectionBodyDisabled(sectionIndex))
                            {
                                continue;
                            }
                            else if (structure.SectionIsLeaking(sectionIndex))
                            {
                                distFactor *= 0.1f;
                            }
                            else
                            {
                                distFactor *= 0.01f;
                            }
                        }
                        else
                        {
                            distFactor *= 0.1f;
                        }
                    }
                    if (distFactor <= 0.05f)
                    {
                        continue;
                    }

                    distFactors.Add(limb, distFactor);

                    modifiedAfflictions.Clear();
                    foreach (Affliction affliction in attack.Afflictions.Keys)
                    {
                        //previously the damage would be divided by the number of limbs (the intention was to prevent characters with more limbs taking more damage from explosions)
                        //that didn't work well on large characters like molochs and endworms: the explosions tend to only damage one or two of their limbs, and since the characters
                        //have lots of limbs, they tended to only take a fraction of the damage they should

                        //now we just divide by 10, which keeps the damage to normal-sized characters roughly the same as before and fixes the large characters
                        modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / 10));
                    }
                    c.LastDamageSource = damageSource;
                    if (attacker == null)
                    {
                        if (damageSource is Item item)
                        {
                            attacker = item.GetComponent <Projectile>()?.User;
                            if (attacker == null)
                            {
                                attacker = item.GetComponent <MeleeWeapon>()?.User;
                            }
                        }
                    }

                    //use a position slightly from the limb's position towards the explosion
                    //ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
                    Vector2      dir          = worldPosition - limb.WorldPosition;
                    Vector2      hitPos       = limb.WorldPosition + (dir.LengthSquared() <= 0.001f ? Rand.Vector(1.0f) : Vector2.Normalize(dir)) * 0.01f;
                    AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
                    damages.Add(limb, attackResult.Damage);

                    if (attack.StatusEffects != null && attack.StatusEffects.Any())
                    {
                        attack.SetUser(attacker);
                        var statusEffectTargets = new List <ISerializableEntity>()
                        {
                            c, limb
                        };
                        foreach (StatusEffect statusEffect in attack.StatusEffects)
                        {
                            statusEffect.Apply(ActionType.OnUse, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(ActionType.Always, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(underWater ? ActionType.InWater : ActionType.NotInWater, 1.0f, damageSource, statusEffectTargets);
                        }
                    }

                    if (limb.WorldPosition != worldPosition && !MathUtils.NearlyEqual(force, 0.0f))
                    {
                        Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
                        if (!MathUtils.IsValid(limbDiff))
                        {
                            limbDiff = Rand.Vector(1.0f);
                        }
                        Vector2 impulse      = limbDiff * distFactor * force;
                        Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
                        limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.2f);
                    }
                }

                //sever joints
                if (attack.SeverLimbsProbability > 0.0f)
                {
                    foreach (Limb limb in c.AnimController.Limbs)
                    {
                        if (limb.character.Removed || limb.Removed)
                        {
                            continue;
                        }
                        if (limb.IsSevered)
                        {
                            continue;
                        }
                        if (!c.IsDead && !limb.CanBeSeveredAlive)
                        {
                            continue;
                        }
                        if (distFactors.TryGetValue(limb, out float distFactor))
                        {
                            if (damages.TryGetValue(limb, out float damage))
                            {
                                c.TrySeverLimbJoints(limb, attack.SeverLimbsProbability * distFactor, damage, allowBeheading: true);
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 25
0
        public void Explode(Vector2 worldPosition, Entity damageSource, Character attacker = null)
        {
            prevExplosions.Add(new Triplet <Explosion, Vector2, float>(this, worldPosition, (float)Timing.TotalTime));
            if (prevExplosions.Count > 100)
            {
                prevExplosions.RemoveAt(0);
            }

            Hull hull = Hull.FindHull(worldPosition);

            ExplodeProjSpecific(worldPosition, hull);

            if (hull != null && !string.IsNullOrWhiteSpace(decal) && decalSize > 0.0f)
            {
                hull.AddDecal(decal, worldPosition, decalSize, true);
            }

            float displayRange = attack.Range;

            Vector2 cameraPos  = Character.Controlled != null ? Character.Controlled.WorldPosition : GameMain.GameScreen.Cam.Position;
            float   cameraDist = Vector2.Distance(cameraPos, worldPosition) / 2.0f;

            GameMain.GameScreen.Cam.Shake = cameraShake * Math.Max((cameraShakeRange - cameraDist) / cameraShakeRange, 0.0f);
#if CLIENT
            if (screenColor != Color.Transparent)
            {
                Color flashColor = Color.Lerp(Color.Transparent, screenColor, Math.Max((screenColorRange - cameraDist) / screenColorRange, 0.0f));
                Screen.Selected.ColorFade(flashColor, Color.Transparent, screenColorDuration);
            }
#endif

            if (displayRange < 0.1f)
            {
                return;
            }

            if (attack.GetStructureDamage(1.0f) > 0.0f)
            {
                RangedStructureDamage(worldPosition, displayRange, attack.GetStructureDamage(1.0f), attacker);
            }

            if (EmpStrength > 0.0f)
            {
                float displayRangeSqr = displayRange * displayRange;
                foreach (Item item in Item.ItemList)
                {
                    float distSqr = Vector2.DistanceSquared(item.WorldPosition, worldPosition);
                    if (distSqr > displayRangeSqr)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange;

                    //damage repairable power-consuming items
                    var powered = item.GetComponent <Powered>();
                    if (powered == null || !powered.VulnerableToEMP)
                    {
                        continue;
                    }
                    if (item.Repairables.Any())
                    {
                        item.Condition -= item.MaxCondition * EmpStrength * distFactor;
                    }

                    //discharge batteries
                    var powerContainer = item.GetComponent <PowerContainer>();
                    if (powerContainer != null)
                    {
                        powerContainer.Charge -= powerContainer.Capacity * EmpStrength * distFactor;
                    }
                }
            }

            if (MathUtils.NearlyEqual(force, 0.0f) && MathUtils.NearlyEqual(attack.Stun, 0.0f) && MathUtils.NearlyEqual(attack.GetTotalDamage(false), 0.0f))
            {
                return;
            }

            DamageCharacters(worldPosition, attack, force, damageSource, attacker);

            if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
            {
                foreach (Item item in Item.ItemList)
                {
                    if (item.Condition <= 0.0f)
                    {
                        continue;
                    }
                    if (Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.5f)
                    {
                        continue;
                    }
                    if (applyFireEffects && !item.FireProof)
                    {
                        //don't apply OnFire effects if the item is inside a fireproof container
                        //(or if it's inside a container that's inside a fireproof container, etc)
                        Item container = item.Container;
                        bool fireProof = false;
                        while (container != null)
                        {
                            if (container.FireProof)
                            {
                                fireProof = true;
                                break;
                            }
                            container = container.Container;
                        }
                        if (!fireProof)
                        {
                            item.ApplyStatusEffects(ActionType.OnFire, 1.0f);
                            if (item.Condition <= 0.0f && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
                            {
                                GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire });
                            }
                        }
                    }

                    if (item.Prefab.DamagedByExplosions && !item.Indestructible)
                    {
                        float limbRadius = item.body == null ? 0.0f : item.body.GetMaxExtent();
                        float dist       = Vector2.Distance(item.WorldPosition, worldPosition);
                        dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));
                        if (dist > attack.Range)
                        {
                            continue;
                        }
                        float distFactor   = 1.0f - dist / attack.Range;
                        float damageAmount = attack.GetItemDamage(1.0f) * item.Prefab.ExplosionDamageMultiplier;
                        item.Condition -= damageAmount * distFactor;
                    }
                }
            }
        }
Exemplo n.º 26
0
        public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource, Character attacker)
        {
            if (attack.Range <= 0.0f)
            {
                return;
            }

            //long range for the broad distance check, because large characters may still be in range even if their collider isn't
            float broadRange = Math.Max(attack.Range * 10.0f, 10000.0f);

            foreach (Character c in Character.CharacterList)
            {
                if (!c.Enabled ||
                    Math.Abs(c.WorldPosition.X - worldPosition.X) > broadRange ||
                    Math.Abs(c.WorldPosition.Y - worldPosition.Y) > broadRange)
                {
                    continue;
                }

                Vector2 explosionPos = worldPosition;
                if (c.Submarine != null)
                {
                    explosionPos -= c.Submarine.Position;
                }

                Hull hull       = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false);
                bool underWater = hull == null || explosionPos.Y < hull.Surface;

                explosionPos = ConvertUnits.ToSimUnits(explosionPos);

                Dictionary <Limb, float> distFactors = new Dictionary <Limb, float>();
                foreach (Limb limb in c.AnimController.Limbs)
                {
                    float dist = Vector2.Distance(limb.WorldPosition, worldPosition);

                    //calculate distance from the "outer surface" of the physics body
                    //doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
                    float limbRadius = Math.Max(Math.Max(limb.body.width * 0.5f, limb.body.height * 0.5f), limb.body.radius);
                    dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));

                    if (dist > attack.Range)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - dist / attack.Range;

                    //solid obstacles between the explosion and the limb reduce the effect of the explosion by 90%
                    if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null)
                    {
                        distFactor *= 0.1f;
                    }

                    distFactors.Add(limb, distFactor);

                    List <Affliction> modifiedAfflictions = new List <Affliction>();
                    foreach (Affliction affliction in attack.Afflictions.Keys)
                    {
                        modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / c.AnimController.Limbs.Length));
                    }
                    c.LastDamageSource = damageSource;
                    if (attacker == null)
                    {
                        if (damageSource is Item item)
                        {
                            attacker = item.GetComponent <Projectile>()?.User;
                            if (attacker == null)
                            {
                                attacker = item.GetComponent <MeleeWeapon>()?.User;
                            }
                        }
                    }

                    //use a position slightly from the limb's position towards the explosion
                    //ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
                    Vector2 hitPos = limb.WorldPosition + (worldPosition - limb.WorldPosition) / dist * 0.01f;
                    c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);

                    if (attack.StatusEffects != null && attack.StatusEffects.Any())
                    {
                        attack.SetUser(attacker);
                        var statusEffectTargets = new List <ISerializableEntity>()
                        {
                            c, limb
                        };
                        foreach (StatusEffect statusEffect in attack.StatusEffects)
                        {
                            statusEffect.Apply(ActionType.OnUse, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(ActionType.Always, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(underWater ? ActionType.InWater : ActionType.NotInWater, 1.0f, damageSource, statusEffectTargets);
                        }
                    }

                    if (limb.WorldPosition != worldPosition && !MathUtils.NearlyEqual(force, 0.0f))
                    {
                        Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
                        if (!MathUtils.IsValid(limbDiff))
                        {
                            limbDiff = Rand.Vector(1.0f);
                        }
                        Vector2 impulse      = limbDiff * distFactor * force;
                        Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
                        limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
                    }
                }

                //sever joints
                if (c.IsDead && attack.SeverLimbsProbability > 0.0f)
                {
                    foreach (Limb limb in c.AnimController.Limbs)
                    {
                        if (!distFactors.ContainsKey(limb))
                        {
                            continue;
                        }
                        if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb])
                        {
                            c.TrySeverLimbJoints(limb, 1.0f);
                        }
                    }
                }
            }
        }
Exemplo n.º 27
0
        public void OnCharacterHealthChanged(Character target, Character attacker, float damage, IEnumerable <Affliction> appliedAfflictions = null)
        {
            if (target == null || attacker == null)
            {
                return;
            }
            if (target == attacker)
            {
                return;
            }

            //damaging dead characters doesn't affect karma
            if (target.IsDead || target.Removed)
            {
                return;
            }

            bool isEnemy = target.AIController is EnemyAIController || target.TeamID != attacker.TeamID;

            if (GameMain.Server.TraitorManager?.Traitors != null)
            {
                if (GameMain.Server.TraitorManager.Traitors.Any(t => t.Character == target))
                {
                    //traitors always count as enemies
                    isEnemy = true;
                }
                if (GameMain.Server.TraitorManager.Traitors.Any(t =>
                                                                t.Character == attacker &&
                                                                t.CurrentObjective != null &&
                                                                t.CurrentObjective.IsEnemy(target)))
                {
                    //target counts as an enemy to the traitor
                    isEnemy = true;
                }
            }

            bool targetIsHusk   = target.CharacterHealth?.GetAffliction <AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active;
            bool attackerIsHusk = attacker.CharacterHealth?.GetAffliction <AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active;

            //huskified characters count as enemies to healthy characters and vice versa
            if (targetIsHusk != attackerIsHusk)
            {
                isEnemy = true;
            }

            if (appliedAfflictions != null)
            {
                foreach (Affliction affliction in appliedAfflictions)
                {
                    if (MathUtils.NearlyEqual(affliction.Prefab.KarmaChangeOnApplied, 0.0f))
                    {
                        continue;
                    }
                    damage -= affliction.Prefab.KarmaChangeOnApplied * affliction.Strength;
                }
            }

            Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == target);

            if (damage > 0 && targetClient != null)
            {
                var targetMemory = GetClientMemory(targetClient);
                targetMemory.LastAttackTime[attacker] = Timing.TotalTime;
            }

            Client attackerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == attacker);

            if (attackerClient != null)
            {
                //if the attacker has been attacked by the target within the last x seconds, ignore the damage
                //(= no karma penalty from retaliating against someone who attacked you)
                var attackerMemory = GetClientMemory(attackerClient);
                if (attackerMemory.LastAttackTime.ContainsKey(target) &&
                    attackerMemory.LastAttackTime[target] > Timing.TotalTime - AllowedRetaliationTime)
                {
                    damage = Math.Min(damage, 0);
                }
            }

            //attacking/healing clowns has a smaller effect on karma
            if (target.HasEquippedItem("clownmask") &&
                target.HasEquippedItem("clowncostume"))
            {
                damage *= 0.5f;
            }

            //smaller karma penalty for attacking someone who's aiming with a weapon
            if (damage > 0.0f &&
                target.IsKeyDown(InputType.Aim) &&
                target.SelectedItems.Any(it => it != null && (it.GetComponent <MeleeWeapon>() != null || it.GetComponent <RangedWeapon>() != null)))
            {
                damage *= 0.5f;
            }

            //damage scales according to the karma of the target
            //(= smaller karma penalty from attacking someone who has a low karma)
            if (damage > 0 && targetClient != null)
            {
                damage *= MathUtils.InverseLerp(0.0f, 50.0f, targetClient.Karma);
            }

            if (isEnemy)
            {
                if (damage > 0)
                {
                    float karmaIncrease = damage * DamageEnemyKarmaIncrease;
                    if (attacker?.Info?.Job.Prefab.Identifier == "securityofficer")
                    {
                        karmaIncrease *= 2.0f;
                    }
                    AdjustKarma(attacker, karmaIncrease, "Damaged enemy");
                }
            }
            else
            {
                if (damage > 0)
                {
                    AdjustKarma(attacker, -damage * DamageFriendlyKarmaDecrease, "Damaged friendly");
                }
                else
                {
                    float karmaIncrease = -damage * HealFriendlyKarmaIncrease;
                    if (attacker?.Info?.Job.Prefab.Identifier == "medicaldoctor")
                    {
                        karmaIncrease *= 2.0f;
                    }
                    AdjustKarma(attacker, karmaIncrease, "Healed friendly");
                }
            }
        }
Exemplo n.º 28
0
        protected override void Update(float deltaTime)
        {
            if (type != Type.InGame)
            {
                return;
            }

            Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
            Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
            Vector2 endPos     = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);

            if (!closing)
            {
                Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
                InnerFrame.RectTransform.AbsoluteOffset = step;
                if (BackgroundIcon != null)
                {
                    BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2);
                    if (!MathUtils.NearlyEqual(openState, 1.0f))
                    {
                        BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White);
                    }
                }
                if (!(Screen.Selected is RoundSummaryScreen) && !MessageBoxes.Any(mb => mb.UserData is RoundSummary))
                {
                    openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
                }

                if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose)
                {
                    inGameCloseTimer += deltaTime;
                }

                if (inGameCloseTimer >= inGameCloseTime)
                {
                    Close();
                }
            }
            else
            {
                openState += deltaTime * 2.0f;
                Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
                InnerFrame.RectTransform.AbsoluteOffset = step;
                if (BackgroundIcon != null)
                {
                    BackgroundIcon.Color *= 0.9f;
                }
                if (openState >= 2.0f)
                {
                    if (Parent != null)
                    {
                        Parent.RemoveChild(this);
                    }
                    if (MessageBoxes.Contains(this))
                    {
                        MessageBoxes.Remove(this);
                    }
                }
            }

            if (newBackgroundIcon != null)
            {
                if (!iconSwitching)
                {
                    if (BackgroundIcon != null)
                    {
                        BackgroundIcon.Color *= 0.9f;
                        if (BackgroundIcon.Color.A == 0)
                        {
                            BackgroundIcon = null;
                            iconSwitching  = true;
                            RemoveChild(BackgroundIcon);
                        }
                    }
                    else
                    {
                        iconSwitching = true;
                    }
                    iconState = 0;
                }
                else
                {
                    newBackgroundIcon.SetAsFirstChild();
                    newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
                    newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White);
                    if (newBackgroundIcon.Color.A == 255)
                    {
                        BackgroundIcon = newBackgroundIcon;
                        BackgroundIcon.SetAsFirstChild();
                        newBackgroundIcon = null;
                        iconSwitching     = false;
                    }

                    iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f);
                }
            }
        }
Exemplo n.º 29
0
        protected override void Update(float deltaTime)
        {
            if (!Visible)
            {
                return;
            }

            UpdateChildrenRect();
            RepositionChildren();

            if (scrollBarNeedsRecalculation)
            {
                UpdateScrollBarSize();
            }


            if (FadeElements)
            {
                foreach (var(component, _) in childVisible)
                {
                    float lerp     = 0;
                    float y        = component.Rect.Y;
                    float contentY = Content.Rect.Y;
                    float height   = component.Rect.Height;
                    if (y < Content.Rect.Y)
                    {
                        float distance = (contentY - y) / height;
                        lerp = distance;
                    }

                    float centerY = Content.Rect.Y + Content.Rect.Height / 2.0f;
                    if (y > centerY)
                    {
                        float distance = (y - centerY) / (centerY - height);
                        lerp = distance;
                    }

                    component.Color         = component.HoverColor = ToolBox.GradientLerp(lerp, component.DefaultColor, Color.Transparent);
                    component.DisabledColor = ToolBox.GradientLerp(lerp, component.Style.DisabledColor, Color.Transparent);
                    component.HoverColor    = ToolBox.GradientLerp(lerp, component.Style.HoverColor, Color.Transparent);

                    foreach (var child in component.GetAllChildren())
                    {
                        Color gradient = ToolBox.GradientLerp(lerp, child.DefaultColor, Color.Transparent);
                        child.Color = child.HoverColor = gradient;
                        if (child is GUITextBlock block)
                        {
                            block.TextColor = block.HoverTextColor = gradient;
                        }
                    }
                }
            }

            if (SmoothScroll)
            {
                if (targetScroll > -1)
                {
                    float distance = Math.Abs(targetScroll - BarScroll);
                    float speed    = Math.Max(distance * BarSize, 0.1f);
                    BarScroll = (1.0f - speed) * BarScroll + speed * targetScroll;
                    if (MathUtils.NearlyEqual(BarScroll, targetScroll) || GUIScrollBar.DraggingBar != null)
                    {
                        targetScroll  = -1;
                        pendingScroll = null;
                    }
                }
            }

            if ((GUI.IsMouseOn(this) || GUI.IsMouseOn(ScrollBar)) && AllowMouseWheelScroll && PlayerInput.ScrollWheelSpeed != 0)
            {
                float speed = PlayerInput.ScrollWheelSpeed / 500.0f * BarSize;
                if (SmoothScroll)
                {
                    if (ClampScrollToElements)
                    {
                        bool scrollDown = Math.Clamp(PlayerInput.ScrollWheelSpeed, 0, 1) > 0;

                        if (scrollDown)
                        {
                            SelectPrevious(takeKeyBoardFocus: true);
                        }
                        else
                        {
                            SelectNext(takeKeyBoardFocus: true);
                        }
                    }
                    else
                    {
                        pendingScroll = null;
                        if (targetScroll < 0)
                        {
                            targetScroll = BarScroll;
                        }
                        targetScroll -= speed;
                        targetScroll  = Math.Clamp(targetScroll, ScrollBar.MinValue, ScrollBar.MaxValue);
                    }
                }
                else
                {
                    ScrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * BarSize;
                }
            }


            ScrollBar.Enabled = ScrollBarEnabled && BarSize < 1.0f;
            if (AutoHideScrollBar)
            {
                ScrollBarVisible = ScrollBar.Enabled;
            }
            if (dimensionsNeedsRecalculation)
            {
                UpdateDimensions();
            }
        }
Exemplo n.º 30
0
        protected override void Update(float deltaTime)
        {
            if (!Visible)
            {
                return;
            }

            if (flashTimer > 0.0f)
            {
                flashTimer -= deltaTime;
            }
            if (!Enabled)
            {
                return;
            }
            if (MouseRect.Contains(PlayerInput.MousePosition) && (GUI.MouseOn == null || GUI.IsMouseOn(this)))
            {
                state = ComponentState.Hover;
                if (PlayerInput.LeftButtonDown())
                {
                    Select();
                }
                else
                {
                    isSelecting = PlayerInput.LeftButtonHeld();
                }
                if (PlayerInput.DoubleClicked())
                {
                    SelectAll();
                }
                if (isSelecting)
                {
                    if (!MathUtils.NearlyEqual(PlayerInput.MouseSpeed.X, 0))
                    {
                        CaretIndex = GetCaretIndexFromScreenPos(PlayerInput.MousePosition);
                        CalculateCaretPos();
                        CalculateSelection();
                    }
                }
            }
            else
            {
                if (PlayerInput.LeftButtonClicked() && selected)
                {
                    Deselect();
                }
                isSelecting = false;
                state       = ComponentState.None;
            }
            if (!isSelecting)
            {
                isSelecting = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift);
            }

            if (CaretEnabled)
            {
                caretTimer  += deltaTime;
                caretVisible = ((caretTimer * 1000.0f) % 1000) < 500;
                if (caretVisible && caretPosDirty)
                {
                    CalculateCaretPos();
                }
            }

            if (GUI.KeyboardDispatcher.Subscriber == this)
            {
                state = ComponentState.Selected;
                Character.DisableControls = true;
                if (OnEnterPressed != null && PlayerInput.KeyHit(Keys.Enter))
                {
                    OnEnterPressed(this, Text);
                }
            }
            else if (Selected)
            {
                Deselect();
            }

            textBlock.State = state;
        }