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); } } }
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); } }
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); }
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); }
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); }
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); } }
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); }
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)); }
/// <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); } }
/// <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)); }
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; }
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(); } }
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); } } } }
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; } } }
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; }
/// <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); }
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); } }
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); }
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"); } } }
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); } } } }
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); } } } } } }
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; } } } }
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); } } } } }
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"); } } }
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); } } }
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(); } }
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; }