From 5eb01d4c50a607fc16a9b19de7ebc42bce69e098 Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 24 Apr 2017 19:08:51 +0300 Subject: [PATCH 1/4] EnemyAIController tweaking: - removed the "distance accumulator" which was used to make characters reset their attack target if they haven't moved enough within a specific interval. The intention was to make characters less likely to get stuck but it seems to cause more problems than it solves. - more frequent target updates - characters can target entities they're previously targeted from further away (even if the target isn't within range anymore) --- Subsurface/Content/Characters/Husk/husk.xml | 4 +-- .../Source/Characters/AI/EnemyAIController.cs | 33 +++++-------------- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/Subsurface/Content/Characters/Husk/husk.xml b/Subsurface/Content/Characters/Husk/husk.xml index 0376a0262..9ea4a67d2 100644 --- a/Subsurface/Content/Characters/Husk/husk.xml +++ b/Subsurface/Content/Characters/Husk/husk.xml @@ -26,7 +26,7 @@ - + @@ -110,6 +110,6 @@ + attackcooldown="1.0"/> diff --git a/Subsurface/Source/Characters/AI/EnemyAIController.cs b/Subsurface/Source/Characters/AI/EnemyAIController.cs index 73b0bee77..8198a43e2 100644 --- a/Subsurface/Source/Characters/AI/EnemyAIController.cs +++ b/Subsurface/Source/Characters/AI/EnemyAIController.cs @@ -13,7 +13,7 @@ namespace Barotrauma class EnemyAIController : AIController { - private const float UpdateTargetsInterval = 5.0f; + private const float UpdateTargetsInterval = 0.5f; private const float RaycastInterval = 1.0f; @@ -32,7 +32,6 @@ namespace Barotrauma private float raycastTimer; private Vector2 prevPosition; - private float distanceAccumulator; //a timer for attacks such as biting that last for a specific amount of time //the duration is determined by the attackDuration of the attacking limb @@ -108,8 +107,6 @@ namespace Barotrauma public override void Update(float deltaTime) { - UpdateDistanceAccumulator(); - bool ignorePlatforms = (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X)); if (steeringManager is IndoorsSteeringManager) @@ -187,14 +184,6 @@ namespace Barotrauma coolDownTimer -= deltaTime; } - - private void UpdateDistanceAccumulator() - { - Limb limb = Character.AnimController.Limbs[0]; - distanceAccumulator += (limb.SimPosition - prevPosition).Length(); - - prevPosition = limb.body.SimPosition; - } private void UpdateAttack(float deltaTime) { @@ -214,6 +203,11 @@ namespace Barotrauma if (selectedAiTarget.Entity != null && Character.Submarine==null && selectedAiTarget.Entity.Submarine != null) attackSimPosition += ConvertUnits.ToSimUnits(selectedAiTarget.Entity.Submarine.Position); } + if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater) + { + Character.AnimController.TargetDir = Character.SimPosition.X < attackSimPosition.X ? Direction.Right : Direction.Left; + } + if (coolDownTimer>0.0f) { UpdateCoolDown(attackSimPosition, deltaTime); @@ -374,15 +368,6 @@ namespace Barotrauma //sight/hearing range public void UpdateTargets(Character character) { - if (distanceAccumulator<5.0f && Rand.Range(1,3)==1) - { - selectedAiTarget = null; - character.AnimController.TargetMovement = -character.AnimController.TargetMovement; - state = AiState.None; - return; - } - distanceAccumulator = 0.0f; - wallAttackPos = Vector2.Zero; selectedAiTarget = null; @@ -423,13 +408,11 @@ namespace Barotrauma valueModifier = attackRooms; } - dist = Vector2.Distance( - character.WorldPosition, - target.WorldPosition); + dist = Vector2.Distance(character.WorldPosition, target.WorldPosition); //if the target has been within range earlier, the character will notice it more easily //(i.e. remember where the target was) - if (targetMemories.ContainsKey(target)) dist *= 0.5f; + if (targetMemories.ContainsKey(target)) dist *= 0.1f; //ignore target if it's too far to see or hear if (dist > target.SightRange * sight && dist > target.SoundRange * hearing) continue; From 1dad5d9bf4c9fd688ae356cf170f9e79ab6d2ac0 Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 24 Apr 2017 21:13:54 +0300 Subject: [PATCH 2/4] Passive sonar: when not active, the sonar shows nearby sources of sound and a faint outline of the structures around them. Now it's much easier to monitor how much noise the sub is making and to hide from enemies. + Changed the visuals of the sonar a bit. The current rendering method is very inefficient though, todo: write a shader --- .../Source/Items/Components/Machines/Radar.cs | 358 +++++++++++------- .../Items/Components/Machines/Reactor.cs | 14 +- 2 files changed, 215 insertions(+), 157 deletions(-) diff --git a/Subsurface/Source/Items/Components/Machines/Radar.cs b/Subsurface/Source/Items/Components/Machines/Radar.cs index 661f7de83..f42c92563 100644 --- a/Subsurface/Source/Items/Components/Machines/Radar.cs +++ b/Subsurface/Source/Items/Components/Machines/Radar.cs @@ -20,14 +20,20 @@ namespace Barotrauma.Items.Components private List radarBlips; private float prevPingRadius; - + + float prevPassivePingRadius; + + private Vector2 center; + private float displayRadius; + private float displayScale; + [HasDefaultValue(10000.0f, false)] public float Range { get { return range; } set { range = MathHelper.Clamp(value, 0.0f, 100000.0f); } } - + [HasDefaultValue(false, false)] public bool DetectSubmarineWalls { @@ -35,6 +41,20 @@ namespace Barotrauma.Items.Components set; } + public override bool IsActive + { + get + { + return base.IsActive; + } + + set + { + base.IsActive = value; + if (isActiveTickBox != null) isActiveTickBox.Selected = value; + } + } + public Radar(Item item, XElement element) : base(item, element) { @@ -52,8 +72,8 @@ namespace Barotrauma.Items.Components break; } } - - isActiveTickBox = new GUITickBox(new Rectangle(0, 0, 20, 20), "Sonar", Alignment.TopLeft, GuiFrame); + + isActiveTickBox = new GUITickBox(new Rectangle(0, 0, 20, 20), "Active Sonar", Alignment.TopLeft, GuiFrame); isActiveTickBox.OnSelected = (GUITickBox box) => { IsActive = box.Selected; @@ -61,8 +81,10 @@ namespace Barotrauma.Items.Components return true; }; - + GuiFrame.CanBeFocused = false; + + IsActive = false; } public override void Update(float deltaTime, Camera cam) @@ -70,23 +92,16 @@ namespace Barotrauma.Items.Components currPowerConsumption = powerConsumption; base.Update(deltaTime, cam); - - for (int i = radarBlips.Count - 1; i >= 0; i-- ) - { - radarBlips[i].FadeTimer -= deltaTime*0.5f; - if (radarBlips[i].FadeTimer <= 0.0f) radarBlips.RemoveAt(i); - } - + if (voltage >= minVoltage || powerConsumption <= 0.0f) { pingState = pingState + deltaTime * 0.5f; - if (pingState>1.0f) + if (pingState > 1.0f) { + if (item.CurrentHull != null) item.CurrentHull.AiTarget.SoundRange = Math.Max(Range * pingState, item.CurrentHull.AiTarget.SoundRange); item.Use(deltaTime); pingState = 0.0f; } - - if (item.CurrentHull != null) item.CurrentHull.AiTarget.SoundRange = Math.Max(Range * pingState, item.CurrentHull.AiTarget.SoundRange); } else { @@ -109,56 +124,60 @@ namespace Barotrauma.Items.Components public override void UpdateHUD(Character character) { GuiFrame.Update((float)Timing.Step); + + for (int i = radarBlips.Count - 1; i >= 0; i--) + { + radarBlips[i].FadeTimer -= (float)Timing.Step * 0.5f; + if (radarBlips[i].FadeTimer <= 0.0f) radarBlips.RemoveAt(i); + } + + float radius = (GuiFrame.Rect.Height / 2 - 30); + + if (IsActive) + { + float pingRadius = displayRadius * pingState; + Ping(item.WorldPosition, pingRadius, prevPingRadius, displayScale, range, 2.0f); + prevPingRadius = pingRadius; + } + + float passivePingRadius = (float)Math.Sin(GameMain.Instance.TotalElapsedTime*10); + if (passivePingRadius > 0.0f) + { + foreach (AITarget t in AITarget.List) + { + if (t.SoundRange <= 0.0f) continue; + + if (Vector2.Distance(t.WorldPosition, item.WorldPosition) < t.SoundRange) + { + Ping(t.WorldPosition, t.SoundRange * passivePingRadius * 0.2f, t.SoundRange * prevPassivePingRadius * 0.2f, displayScale, t.SoundRange, 0.5f); + + radarBlips.Add(new RadarBlip(t.WorldPosition, 1.0f)); + } + } + } + prevPassivePingRadius = passivePingRadius; + } public override void DrawHUD(SpriteBatch spriteBatch, Character character) { GuiFrame.Draw(spriteBatch); - if (voltage < minVoltage && powerConsumption > 0.0f) return; - int radius = GuiFrame.Rect.Height / 2 - 30; DrawRadar(spriteBatch, new Rectangle((int)GuiFrame.Center.X - radius, (int)GuiFrame.Center.Y - radius, radius * 2, radius * 2)); } private void DrawRadar(SpriteBatch spriteBatch, Rectangle rect) { - Vector2 center = new Vector2(rect.X + rect.Width*0.5f, rect.Center.Y); + center = new Vector2(rect.X + rect.Width * 0.5f, rect.Center.Y); + displayRadius = rect.Width / 2.0f; + displayScale = displayRadius / range; - if (!IsActive) return; - - float pingRadius = (rect.Width / 2.0f) * pingState; - pingCircle.Draw(spriteBatch, center, Color.White * (1.0f - pingState), 0.0f, (rect.Width / pingCircle.size.X) * pingState); - - float radius = rect.Width / 2.0f; - - float displayScale = radius / range; - - foreach (Submarine submarine in Submarine.Loaded) + if (IsActive) { - if (item.Submarine == submarine && !DetectSubmarineWalls) continue; - if (item.Submarine != null && item.Submarine.DockedTo.Contains(submarine)) continue; - if (submarine.HullVertices == null) continue; - - for (int i = 0; i < submarine.HullVertices.Count; i++) - { - Vector2 start = ConvertUnits.ToDisplayUnits(submarine.HullVertices[i]); - Vector2 end = ConvertUnits.ToDisplayUnits(submarine.HullVertices[(i + 1) % submarine.HullVertices.Count]); - - if (item.Submarine == submarine) - { - start += Rand.Vector(500.0f); - end += Rand.Vector(500.0f); - } - - CreateBlipsForLine( - start + submarine.WorldPosition, - end + submarine.WorldPosition, - radius, displayScale, 200.0f, 2.0f); - } + pingCircle.Draw(spriteBatch, center, Color.White * (1.0f - pingState), 0.0f, (rect.Width / pingCircle.size.X) * pingState); } - - + if (item.Submarine != null && !DetectSubmarineWalls) { float simScale = displayScale * Physics.DisplayToSimRation; @@ -181,93 +200,18 @@ namespace Barotrauma.Items.Components } } - - if (Level.Loaded != null && (item.CurrentHull == null || !DetectSubmarineWalls)) - { - if (Level.Loaded.Size.Y - item.WorldPosition.Y < range) - { - CreateBlipsForLine( - new Vector2(item.WorldPosition.X - range, Level.Loaded.Size.Y), - new Vector2(item.WorldPosition.X + range, Level.Loaded.Size.Y), - radius, displayScale, 500.0f, 10.0f); - } - - List cells = Level.Loaded.GetCells(item.WorldPosition, 7); - foreach (VoronoiCell cell in cells) - { - foreach (GraphEdge edge in cell.edges) - { - if (!edge.isSolid) continue; - float cellDot = Vector2.Dot(cell.Center - item.WorldPosition, (edge.Center + cell.Translation) - cell.Center); - if (cellDot > 0) continue; - - float facingDot = Vector2.Dot( - Vector2.Normalize(edge.point1 - edge.point2), - Vector2.Normalize(cell.Center - item.WorldPosition)); - - CreateBlipsForLine(edge.point1 + cell.Translation, edge.point2 + cell.Translation, radius, displayScale, 350.0f, 3.0f * (Math.Abs(facingDot) + 1.0f)); - } - } - - foreach (RuinGeneration.Ruin ruin in Level.Loaded.Ruins) - { - if (!MathUtils.CircleIntersectsRectangle(item.WorldPosition, range, ruin.Area)) continue; - - foreach (var ruinShape in ruin.RuinShapes) - { - foreach (RuinGeneration.Line wall in ruinShape.Walls) - { - - float cellDot = Vector2.Dot( - Vector2.Normalize(ruinShape.Center - item.WorldPosition), - Vector2.Normalize((wall.A+wall.B)/2.0f - ruinShape.Center)); - if (cellDot > 0) continue; - - CreateBlipsForLine(wall.A, wall.B, radius, displayScale, 100.0f, 1000.0f); - } - } - } - } - - foreach (Character c in Character.CharacterList) - { - if (c.AnimController.CurrentHull != null) continue; - if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) continue; - - foreach (Limb limb in c.AnimController.Limbs) - { - float pointDist = (limb.WorldPosition - item.WorldPosition).Length() * displayScale; - - if (limb.SimPosition == Vector2.Zero || pointDist > radius) continue; - - if (pointDist > prevPingRadius && pointDist < pingRadius) - { - for (int i = 0; i<=limb.Mass/100.0f; i++) - { - var blip = new RadarBlip(limb.WorldPosition+Rand.Vector(limb.Mass/10.0f), 1.0f); - radarBlips.Add(blip); - } - } - } - } - foreach (RadarBlip radarBlip in radarBlips) { - DrawBlip(spriteBatch, radarBlip, center, Color.Green * radarBlip.FadeTimer, radius); + DrawBlip(spriteBatch, radarBlip, center, radarBlip.FadeTimer / 2.0f); } - prevPingRadius = pingRadius; - if (screenOverlay != null) { screenOverlay.Draw(spriteBatch, center, 0.0f, rect.Width / screenOverlay.size.X); } - - //prevPingRadius = pingRadius; - + if (GameMain.GameSession == null) return; - DrawMarker(spriteBatch, GameMain.GameSession.StartLocation.Name, (Level.Loaded.StartPosition - item.WorldPosition), displayScale, center, (rect.Width * 0.5f)); @@ -307,7 +251,7 @@ namespace Barotrauma.Items.Components foreach (WayPoint wp in steering.SteeringPath.Nodes) { Vector2 pos = (wp.Position - item.WorldPosition) * displayScale; - if (pos.Length() > radius) continue; + if (pos.Length() > displayRadius) continue; pos.Y = -pos.Y; pos += center; @@ -323,14 +267,119 @@ namespace Barotrauma.Items.Components } } - private void CreateBlipsForLine(Vector2 point1, Vector2 point2, float radius, float displayScale, float lineStep, float zStep) + private void Ping(Vector2 pingSource, float pingRadius, float prevPingRadius, float displayScale, float range, float pingStrength = 1.0f) { - float pingRadius = radius * pingState; + foreach (Submarine submarine in Submarine.Loaded) + { + if (item.Submarine == submarine && !DetectSubmarineWalls) continue; + if (item.Submarine != null && item.Submarine.DockedTo.Contains(submarine)) continue; + if (submarine.HullVertices == null) continue; + for (int i = 0; i < submarine.HullVertices.Count; i++) + { + Vector2 start = ConvertUnits.ToDisplayUnits(submarine.HullVertices[i]); + Vector2 end = ConvertUnits.ToDisplayUnits(submarine.HullVertices[(i + 1) % submarine.HullVertices.Count]); + + if (item.Submarine == submarine) + { + start += Rand.Vector(500.0f); + end += Rand.Vector(500.0f); + } + + CreateBlipsForLine( + start + submarine.WorldPosition, + end + submarine.WorldPosition, + pingRadius, prevPingRadius, + 200.0f, 2.0f, range, 1.0f); + } + } + + if (Level.Loaded != null && (item.CurrentHull == null || !DetectSubmarineWalls)) + { + if (Level.Loaded.Size.Y - pingSource.Y < range) + { + CreateBlipsForLine( + new Vector2(pingSource.X - range, Level.Loaded.Size.Y), + new Vector2(pingSource.X + range, Level.Loaded.Size.Y), + pingRadius, prevPingRadius, + 250.0f, 150.0f, range, pingStrength); + } + + List cells = Level.Loaded.GetCells(pingSource, 7); + foreach (VoronoiCell cell in cells) + { + foreach (GraphEdge edge in cell.edges) + { + if (!edge.isSolid) continue; + float cellDot = Vector2.Dot(cell.Center - pingSource, (edge.Center + cell.Translation) - cell.Center); + if (cellDot > 0) continue; + + float facingDot = Vector2.Dot( + Vector2.Normalize(edge.point1 - edge.point2), + Vector2.Normalize(cell.Center - pingSource)); + + CreateBlipsForLine( + edge.point1 + cell.Translation, + edge.point2 + cell.Translation, + pingRadius, prevPingRadius, + 350.0f, 3.0f * (Math.Abs(facingDot) + 1.0f), range, pingStrength); + } + } + + foreach (RuinGeneration.Ruin ruin in Level.Loaded.Ruins) + { + if (!MathUtils.CircleIntersectsRectangle(pingSource, range, ruin.Area)) continue; + + foreach (var ruinShape in ruin.RuinShapes) + { + foreach (RuinGeneration.Line wall in ruinShape.Walls) + { + float cellDot = Vector2.Dot( + Vector2.Normalize(ruinShape.Center - pingSource), + Vector2.Normalize((wall.A + wall.B) / 2.0f - ruinShape.Center)); + if (cellDot > 0) continue; + + CreateBlipsForLine( + wall.A, wall.B, + pingRadius, prevPingRadius, + 100.0f, 1000.0f, range, pingStrength); + } + } + } + } + + foreach (Character c in Character.CharacterList) + { + if (c.AnimController.CurrentHull != null || !c.Enabled) continue; + if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) continue; + + foreach (Limb limb in c.AnimController.Limbs) + { + float pointDist = (limb.WorldPosition - pingSource).Length() * displayScale; + + if (limb.SimPosition == Vector2.Zero || pointDist > displayRadius) continue; + + if (pointDist > prevPingRadius && pointDist < pingRadius) + { + for (int i = 0; i <= limb.Mass / 100.0f; i++) + { + var blip = new RadarBlip(limb.WorldPosition + Rand.Vector(limb.Mass / 10.0f), MathHelper.Clamp(limb.Mass, 0.1f, pingStrength)); + radarBlips.Add(blip); + } + } + } + } + } + + private void CreateBlipsForLine(Vector2 point1, Vector2 point2, float pingRadius, float prevPingRadius, + float lineStep, float zStep, float range, float pingStrength) + { float length = (point1 - point2).Length(); Vector2 lineDir = (point2 - point1) / length; + range *= displayScale; + for (float x = 0; x < length; x += lineStep*Rand.Range(0.8f,1.2f)) { Vector2 point = point1 + lineDir * x; @@ -338,44 +387,62 @@ namespace Barotrauma.Items.Components float pointDist = Vector2.Distance(item.WorldPosition, point) * displayScale; - if (pointDist > radius) continue; + if (pointDist > displayRadius) continue; if (pointDist < prevPingRadius || pointDist > pingRadius) continue; - - - //float step = 3.0f * (Math.Abs(facingDot) + 1.0f); - float alpha = Rand.Range(1.5f, 2.0f); - for (float z = 0; z < radius - pointDist; z += zStep) + + float alpha = pingStrength * Rand.Range(1.5f, 2.0f); + for (float z = 0; z < displayRadius - pointDist * displayScale; z += zStep) { - var blip = new RadarBlip( point + Rand.Vector(150.0f) + Vector2.Normalize(point - item.WorldPosition) * z / displayScale, - alpha); + alpha * (1.0f - pointDist/range)); radarBlips.Add(blip); zStep += 0.5f; alpha -= (z == 0) ? 0.5f : 0.1f; + if (alpha < 0) break; } } } - private void DrawBlip(SpriteBatch spriteBatch, RadarBlip blip, Vector2 center, Color color, float radius) + private void DrawBlip(SpriteBatch spriteBatch, RadarBlip blip, Vector2 center, float strength) { - float displayScale = radius / range; + strength = MathHelper.Clamp(strength, 0.0f, 1.0f); + Color[] colors = new Color[] { + Color.TransparentBlack, + new Color(0, 50, 160), + new Color(0, 133, 166), + new Color(2, 159, 30), + new Color(255, 255, 255) }; + + float scaledT = strength * (colors.Length - 1); + Color color = Color.Lerp(colors[(int)scaledT], colors[(int)scaledT], (scaledT - (int)scaledT)); + Vector2 pos = (blip.Position - item.WorldPosition) * displayScale; pos.Y = -pos.Y; - if (pos.Length() > radius) + if (pos.Length() > displayRadius) { blip.FadeTimer = 0.0f; return; } - pos.X = MathUtils.Round(pos.X, 4); - pos.Y = MathUtils.Round(pos.Y, 2); + /*pos.X = MathUtils.Round(pos.X, 4); + pos.Y = MathUtils.Round(pos.Y, 2);*/ - GUI.DrawRectangle(spriteBatch, center + pos, new Vector2(4, 2), color, true); + + float posDist = pos.Length(); + Vector2 dir = pos / posDist; + float distFactor = (posDist / displayRadius); + + Vector2 normal = new Vector2(dir.Y, -dir.X) * (strength + 1.0f) * distFactor * 5.0f; + + GUI.DrawLine(spriteBatch, center + pos - normal, center + pos + normal, color * (1.0f - distFactor), 0, 2); + + pos += Rand.Range(0.0f, 1.0f) * dir + Rand.Range(-1.0f, 1.0f) * normal; + GUI.DrawLine(spriteBatch, center + pos - normal, center + pos + normal, color * 0.2f, 0, 3); } private void DrawMarker(SpriteBatch spriteBatch, string label, Vector2 position, float scale, Vector2 center, float radius) @@ -431,7 +498,6 @@ namespace Barotrauma.Items.Components try { IsActive = message.ReadBoolean(); - isActiveTickBox.Selected = IsActive; } catch { @@ -448,7 +514,7 @@ namespace Barotrauma.Items.Components public RadarBlip(Vector2 pos, float fadeTimer) { Position = pos; - FadeTimer = fadeTimer; + FadeTimer = Math.Max(fadeTimer, 0.0f); } } } diff --git a/Subsurface/Source/Items/Components/Machines/Reactor.cs b/Subsurface/Source/Items/Components/Machines/Reactor.cs index 9cf9462d7..54da51335 100644 --- a/Subsurface/Source/Items/Components/Machines/Reactor.cs +++ b/Subsurface/Source/Items/Components/Machines/Reactor.cs @@ -295,22 +295,14 @@ namespace Barotrauma.Items.Components FissionRate += (MathHelper.Clamp(load - target, -10.0f, 10.0f) - 1.0f) * deltaTime; CoolingRate += (MathHelper.Clamp(target - load, -5.0f, 5.0f) - 1.0f) * deltaTime; } - - //fission rate can't be lowered below a certain amount if the core is too hot - //FissionRate = Math.Max(fissionRate, heat / 200.0f); //the power generated by the reactor is equal to the temperature currPowerConsumption = -temperature*powerPerTemp; - - //foreach (Item i in item.ContainedItems) - //{ - // i.Condition = 5.0f; - //} - + if (item.CurrentHull != null) { - //the sound can be heard from 20 000 display units away when everything running at 100% - item.CurrentHull.SoundRange = Math.Max((coolingRate + fissionRate) * 100, item.CurrentHull.AiTarget.SoundRange); + //the sound can be heard from 20 000 display units away when running at full power + item.CurrentHull.SoundRange = Math.Max(temperature * 2, item.CurrentHull.AiTarget.SoundRange); } UpdateGraph(deltaTime); From 58de86a98f3a19059444c07ddddb996e38165aac Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 24 Apr 2017 21:34:36 +0300 Subject: [PATCH 3/4] Heal and revive debug commands can also be used on other characters than the controlled one --- Subsurface/Source/DebugConsole.cs | 124 +++++++++++++++++++----------- 1 file changed, 81 insertions(+), 43 deletions(-) diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 40d26342f..7145393e9 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -220,7 +220,10 @@ namespace Barotrauma NewMessage(" ", Color.Cyan); NewMessage("heal: restore the controlled character to full health", Color.Cyan); + NewMessage("heal [character name]: restore the specified character to full health", Color.Cyan); NewMessage("revive: bring the controlled character back from the dead", Color.Cyan); + NewMessage("revive [character name]: bring the specified character back from the dead", Color.Cyan); + NewMessage("killmonsters: immediately kills all AI-controlled enemies in the level", Color.Cyan); NewMessage(" ", Color.Cyan); @@ -464,44 +467,11 @@ namespace Barotrauma case "control": if (commands.Length < 2) break; - int characterIndex; - string characterName; - if (int.TryParse(commands.Last(), out characterIndex)) - { - characterName = string.Join(" ", commands.Skip(1).Take(commands.Length-2)).ToLowerInvariant(); - } - else - { - characterName = string.Join(" ", commands.Skip(1)).ToLowerInvariant(); - characterIndex = -1; - } + var character = FindMatchingCharacter(commands); - var matchingCharacters = Character.CharacterList.FindAll(c => !c.IsRemotePlayer && c.Name.ToLowerInvariant() == characterName); - - if (!matchingCharacters.Any()) + if (character != null) { - ThrowError("Matching characters not found"); - return; - } - - if (characterIndex==-1) - { - Character.Controlled = matchingCharacters.First(); - if (matchingCharacters.Count > 1) - { - NewMessage( - "Found multiple matching characters. "+ - "Use \"control [charactername] [0-"+(matchingCharacters.Count-1)+"]\" to choose which character to control.", - Color.LightGray); - } - } - else if (characterIndex<0 || characterIndex>= matchingCharacters.Count) - { - ThrowError("Character index out of range. Select an index between 0 and " + (matchingCharacters.Count - 1)); - } - else - { - Character.Controlled = matchingCharacters[characterIndex]; + Character.Controlled = character; } break; @@ -522,18 +492,39 @@ namespace Barotrauma Entity.DumpIds(count); break; case "heal": - if (Character.Controlled != null) + Character healedCharacter = null; + if (commands.Length == 1) { - Character.Controlled.AddDamage(CauseOfDeath.Damage, -Character.Controlled.MaxHealth, null); - Character.Controlled.Oxygen = 100.0f; - Character.Controlled.Bleeding = 0.0f; - Character.Controlled.AnimController.StunTimer = 0.0f; + healedCharacter = Character.Controlled; } + else + { + healedCharacter = FindMatchingCharacter(commands); + } + + if (healedCharacter != null) + { + healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null); + healedCharacter.Oxygen = 100.0f; + healedCharacter.Bleeding = 0.0f; + healedCharacter.AnimController.StunTimer = 0.0f; + } + break; case "revive": - if (Character.Controlled != null) + Character reveivedCharacter = null; + if (commands.Length == 1) { - Character.Controlled.Revive(false); + reveivedCharacter = Character.Controlled; + } + else + { + reveivedCharacter = FindMatchingCharacter(commands); + } + + if (reveivedCharacter != null) + { + reveivedCharacter.Revive(false); } break; case "freecamera": @@ -807,6 +798,53 @@ namespace Barotrauma break; } } + + private static Character FindMatchingCharacter(string[] commands) + { + if (commands.Length < 2) return null; + + int characterIndex; + string characterName; + if (int.TryParse(commands.Last(), out characterIndex)) + { + characterName = string.Join(" ", commands.Skip(1).Take(commands.Length - 2)).ToLowerInvariant(); + } + else + { + characterName = string.Join(" ", commands.Skip(1)).ToLowerInvariant(); + characterIndex = -1; + } + + var matchingCharacters = Character.CharacterList.FindAll(c => !c.IsRemotePlayer && c.Name.ToLowerInvariant() == characterName); + + if (!matchingCharacters.Any()) + { + NewMessage("Matching characters not found", Color.Red); + return null; + } + + if (characterIndex == -1) + { + if (matchingCharacters.Count > 1) + { + NewMessage( + "Found multiple matching characters. " + + "Use \"" + commands[0] + " [charactername] [0-" + (matchingCharacters.Count - 1) + "]\" to choose a specific character.", + Color.LightGray); + } + return matchingCharacters[0]; + } + else if (characterIndex < 0 || characterIndex >= matchingCharacters.Count) + { + ThrowError("Character index out of range. Select an index between 0 and " + (matchingCharacters.Count - 1)); + } + else + { + return matchingCharacters[characterIndex]; + } + + return null; + } public static void NewMessage(string msg, Color color) { From 6397bc432e13c529719146f0ed627b8a6a8a7730 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 27 Apr 2017 19:38:04 +0300 Subject: [PATCH 4/4] UI tweaks --- .../Source/Characters/Jobs/JobPrefab.cs | 1 + Subsurface/Source/GUI/GUIComponent.cs | 10 ++-- Subsurface/Source/GUI/GUITickBox.cs | 14 ++++++ Subsurface/Source/Items/Item.cs | 47 +++++++++++-------- Subsurface/Source/Screens/NetLobbyScreen.cs | 7 ++- 5 files changed, 54 insertions(+), 25 deletions(-) diff --git a/Subsurface/Source/Characters/Jobs/JobPrefab.cs b/Subsurface/Source/Characters/Jobs/JobPrefab.cs index 7fb814592..f75993dc0 100644 --- a/Subsurface/Source/Characters/Jobs/JobPrefab.cs +++ b/Subsurface/Source/Characters/Jobs/JobPrefab.cs @@ -107,6 +107,7 @@ namespace Barotrauma int width = 500, height = 400; GUIFrame backFrame = new GUIFrame(Rectangle.Empty, Color.Black*0.5f); + backFrame.Padding = Vector4.Zero; GUIFrame frame = new GUIFrame(new Rectangle(GameMain.GraphicsWidth / 2 - width / 2, GameMain.GraphicsHeight / 2 - height / 2, width, height), "", backFrame); frame.Padding = new Vector4(30.0f, 30.0f, 30.0f, 30.0f); diff --git a/Subsurface/Source/GUI/GUIComponent.cs b/Subsurface/Source/GUI/GUIComponent.cs index d91b81cf6..77cd79f21 100644 --- a/Subsurface/Source/GUI/GUIComponent.cs +++ b/Subsurface/Source/GUI/GUIComponent.cs @@ -310,15 +310,19 @@ namespace Barotrauma int centerWidth = Math.Max(rect.Width - uiSprite.Slices[0].Width - uiSprite.Slices[2].Width, 0); int centerHeight = Math.Max(rect.Height - uiSprite.Slices[0].Height - uiSprite.Slices[8].Height, 0); + Vector2 scale = new Vector2( + MathHelper.Clamp((float)rect.Width / (uiSprite.Slices[0].Width + uiSprite.Slices[2].Width),0, 1), + MathHelper.Clamp((float)rect.Height / (uiSprite.Slices[0].Height + uiSprite.Slices[6].Height), 0, 1)); + for (int x = 0; x < 3; x++) { - int width = x == 1 ? centerWidth : uiSprite.Slices[x].Width; + float width = (x == 1 ? centerWidth : uiSprite.Slices[x].Width) * scale.X; for (int y = 0; y < 3; y++) { - int height = y == 1 ? centerHeight : uiSprite.Slices[x + y * 3].Height; + float height = (y == 1 ? centerHeight : uiSprite.Slices[x + y * 3].Height) * scale.Y; spriteBatch.Draw(uiSprite.Sprite.Texture, - new Rectangle((int)pos.X, (int)pos.Y, width, height), + new Rectangle((int)pos.X, (int)pos.Y, (int)width, (int)height), uiSprite.Slices[x + y * 3], currColor * (currColor.A / 255.0f)); diff --git a/Subsurface/Source/GUI/GUITickBox.cs b/Subsurface/Source/GUI/GUITickBox.cs index ae1f3c69f..c2037d65b 100644 --- a/Subsurface/Source/GUI/GUITickBox.cs +++ b/Subsurface/Source/GUI/GUITickBox.cs @@ -59,6 +59,20 @@ namespace Barotrauma get { return box.Rect; } } + public override ScalableFont Font + { + get + { + return base.Font; + } + + set + { + base.Font = value; + if (text != null) text.Font = value; + } + } + public GUITickBox(Rectangle rect, string label, Alignment alignment, GUIComponent parent) : this(rect, label, alignment, GUI.Font, parent) { diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 3896ab768..d20b99748 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -1037,9 +1037,6 @@ namespace Barotrauma private GUIComponent CreateEditingHUD(bool inGame=false) { - int width = 450; - int x = GameMain.GraphicsWidth/2-width/2, y = 10; - List editableProperties = inGame ? GetProperties() : GetProperties(); int requiredItemCount = 0; @@ -1047,33 +1044,42 @@ namespace Barotrauma { foreach (ItemComponent ic in components) { - requiredItemCount += ic.requiredItems.Count; + requiredItemCount += ic.requiredItems.Count; } } - editingHUD = new GUIFrame(new Rectangle(x, y, width, 70 + (editableProperties.Count + requiredItemCount) * 30), ""); + int width = 450; + int height = 80 + requiredItemCount * 20; + int x = GameMain.GraphicsWidth / 2 - width / 2, y = 10; + foreach (var objectProperty in editableProperties) + { + var editable = objectProperty.Attributes.OfType().FirstOrDefault(); + if (editable != null) height += (int)(Math.Ceiling(editable.MaxLength / 40.0f) * 18.0f) + 5; + } + + editingHUD = new GUIFrame(new Rectangle(x, y, width, height), ""); editingHUD.Padding = new Vector4(10, 10, 0, 0); editingHUD.UserData = this; new GUITextBlock(new Rectangle(0, 0, 100, 20), prefab.Name, "", Alignment.TopLeft, Alignment.TopLeft, editingHUD, false, GUI.LargeFont); - y += 20; + y += 25; if (!inGame) { if (prefab.IsLinkable) { - new GUITextBlock(new Rectangle(0, 0, 0, 20), "Hold space to link to another item", + new GUITextBlock(new Rectangle(0, 5, 0, 20), "Hold space to link to another item", "", Alignment.TopRight, Alignment.TopRight, editingHUD).Font = GUI.SmallFont; - y += 25; } foreach (ItemComponent ic in components) { foreach (RelatedItem relatedItem in ic.requiredItems) { - new GUITextBlock(new Rectangle(0, y, 100, 20), ic.Name + ": " + relatedItem.Type.ToString() + " required", "", editingHUD); - GUITextBox namesBox = new GUITextBox(new Rectangle(-10, y, 160, 20), Alignment.Right, "", editingHUD); + new GUITextBlock(new Rectangle(0, y, 100, 15), ic.Name + ": " + relatedItem.Type.ToString() + " required", "", Alignment.TopLeft, Alignment.CenterLeft, editingHUD, false, GUI.SmallFont); + GUITextBox namesBox = new GUITextBox(new Rectangle(-10, y, 160, 15), Alignment.Right, "", editingHUD); + namesBox.Font = GUI.SmallFont; PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (relatedItem); PropertyDescriptor property = properties.Find("JoinedNames", false); @@ -1083,24 +1089,25 @@ namespace Barotrauma namesBox.OnEnterPressed = EnterProperty; namesBox.OnTextChanged = PropertyChanged; - y += 30; + y += 20; } } - + if (requiredItemCount > 0) y += 10; } foreach (var objectProperty in editableProperties) { - int height = 20; + int boxHeight = 18; var editable = objectProperty.Attributes.OfType().FirstOrDefault(); - if (editable != null) height = (int)(Math.Ceiling(editable.MaxLength / 20.0f) * 20.0f); + if (editable != null) boxHeight = (int)(Math.Ceiling(editable.MaxLength / 40.0f) * 18.0f); object value = objectProperty.GetValue(); if (value is bool) { - GUITickBox propertyTickBox = new GUITickBox(new Rectangle(10, y, 20, 20), objectProperty.Name, + GUITickBox propertyTickBox = new GUITickBox(new Rectangle(10, y, 18, 18), objectProperty.Name, Alignment.Left, editingHUD); + propertyTickBox.Font = GUI.SmallFont; propertyTickBox.Selected = (bool)value; @@ -1109,11 +1116,11 @@ namespace Barotrauma } else { + new GUITextBlock(new Rectangle(0, y, 100, 18), objectProperty.Name, "", Alignment.TopLeft, Alignment.Left, editingHUD, false, GUI.SmallFont); - new GUITextBlock(new Rectangle(0, y, 100, 20), objectProperty.Name, Color.Transparent, Color.White, Alignment.Left, "", editingHUD); - - GUITextBox propertyBox = new GUITextBox(new Rectangle(180, y, 250, height), "", editingHUD); - if (height > 20) propertyBox.Wrap = true; + GUITextBox propertyBox = new GUITextBox(new Rectangle(180, y, 250, boxHeight), "", editingHUD); + propertyBox.Font = GUI.SmallFont; + if (boxHeight > 18) propertyBox.Wrap = true; if (value != null) { @@ -1133,7 +1140,7 @@ namespace Barotrauma propertyBox.OnTextChanged = PropertyChanged; } - y = y + height + 10; + y = y + boxHeight + 5; } return editingHUD; diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index c003a3c6d..bebdfabe7 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -260,7 +260,8 @@ namespace Barotrauma //mission type ------------------------------------------------------------------ - missionTypeBlock = new GUITextBlock(new Rectangle(columnX, 0, 300, 20), "Mission type:", "", Alignment.BottomLeft, Alignment.TopLeft, infoFrame); + missionTypeBlock = new GUITextBlock(new Rectangle(columnX, -10, 300, 20), "Mission type:", "", Alignment.BottomLeft, Alignment.CenterLeft, infoFrame); + missionTypeBlock.Padding = Vector4.Zero; missionTypeBlock.UserData = 0; missionTypeButtons = new GUIButton[2]; @@ -268,7 +269,7 @@ namespace Barotrauma missionTypeButtons[0] = new GUIButton(new Rectangle(100, 0, 20, 20), "<", Alignment.BottomLeft, "", missionTypeBlock); missionTypeButtons[0].UserData = -1; - new GUITextBlock(new Rectangle(120, 0, 80, 20), "Random", "", Alignment.BottomLeft, Alignment.TopCenter, missionTypeBlock).UserData = 0; + new GUITextBlock(new Rectangle(120, 0, 80, 20), "Random", "", Alignment.BottomLeft, Alignment.Center, missionTypeBlock).UserData = 0; missionTypeButtons[1] = new GUIButton(new Rectangle(200, 0, 20, 20), ">", Alignment.BottomLeft, "", missionTypeBlock); missionTypeButtons[1].UserData = 1; @@ -342,6 +343,8 @@ namespace Barotrauma public override void Select() { + if (GameMain.NetworkMember == null) return; + GameMain.LightManager.LosEnabled = false; textBox.Select();