(d9829ac) v0.9.4.0

This commit is contained in:
Regalis
2019-10-24 18:05:42 +02:00
parent 9aa12bcac2
commit b39922a074
319 changed files with 12516 additions and 6815 deletions
@@ -45,9 +45,6 @@ namespace Barotrauma
case AIState.Eat:
stateColor = Color.Brown;
break;
case AIState.GoTo:
stateColor = Color.Magenta;
break;
}
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black);
@@ -10,7 +10,7 @@ namespace Barotrauma
{
/*if (GameMain.GameSession != null && GameMain.GameSession.CrewManager != null)
{
CurrentOrder = Order.PrefabList.Find(o => o.AITag == "dismissed");
CurrentOrder = Order.GetPrefab("dismissed");
objectiveManager.SetOrder(CurrentOrder, "", null);
GameMain.GameSession.CrewManager.SetCharacterOrder(Character, CurrentOrder, null, null);
}*/
@@ -369,12 +369,12 @@ namespace Barotrauma
LimbJoints.ForEach(j => j.UpdateDeformations(deltaTime));
foreach (var deformation in SpriteDeformations)
{
if (character.IsDead && deformation.DeformationParams.StopWhenHostIsDead) { continue; }
if (deformation.DeformationParams.UseMovementSine)
if (character.IsDead && deformation.Params.StopWhenHostIsDead) { continue; }
if (deformation.Params.UseMovementSine)
{
if (this is AnimController animator)
{
deformation.Phase = MathUtils.WrapAngleTwoPi(animator.WalkPos * deformation.DeformationParams.Frequency + MathHelper.Pi * deformation.DeformationParams.SineOffset);
deformation.Phase = MathUtils.WrapAngleTwoPi(animator.WalkPos * deformation.Params.Frequency + MathHelper.Pi * deformation.Params.SineOffset);
}
}
else
@@ -7,10 +7,8 @@ namespace Barotrauma
{
partial class Attack
{
public string StructureSoundType
{
get; private set;
}
[Serialize("StructureBlunt", true), Editable()]
public string StructureSoundType { get; private set; }
private RoundSound sound;
@@ -23,8 +21,6 @@ namespace Barotrauma
DebugConsole.ThrowError("Error in attack ("+element+") - sounds should be defined as child elements, not as attributes.");
return;
}
StructureSoundType = element.GetAttributeString("structuresoundtype", "StructureBlunt");
foreach (XElement subElement in element.Elements())
{
@@ -86,11 +86,7 @@ namespace Barotrauma
set { chromaticAberrationStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public string BloodDecalName
{
get;
private set;
}
public string BloodDecalName => Params.BloodDecal;
private List<ParticleEmitter> bloodEmitters = new List<ParticleEmitter>();
public IEnumerable<ParticleEmitter> BloodEmitters
@@ -137,22 +133,18 @@ namespace Barotrauma
get { return activeObjectiveEntities; }
}
partial void InitProjSpecific(XDocument doc)
partial void InitProjSpecific(XElement mainElement)
{
soundInterval = doc.Root.GetAttributeFloat("soundinterval", 10.0f);
soundInterval = mainElement.GetAttributeFloat("soundinterval", 10.0f);
soundTimer = Rand.Range(0.0f, soundInterval);
BloodDecalName = doc.Root.GetAttributeString("blooddecal", "");
sounds = new List<CharacterSound>();
foreach (XElement subElement in doc.Root.Elements())
Params.Sounds.ForEach(s => sounds.Add(new CharacterSound(s)));
foreach (XElement subElement in mainElement.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sound":
var characterSound = new CharacterSound(subElement);
if (characterSound.Sound != null) { sounds.Add(characterSound); }
break;
case "damageemitter":
damageEmitters.Add(new ParticleEmitter(subElement));
break;
@@ -216,7 +208,7 @@ namespace Barotrauma
float targetOffsetAmount = 0.0f;
if (moveCam)
{
if (needsAir &&
if (NeedsAir &&
pressureProtection < 80.0f &&
(AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 0.0f))
{
@@ -351,7 +343,7 @@ namespace Barotrauma
partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult)
{
if (attackResult.Damage <= 0) { return; }
if (attackResult.Damage <= 1.0f) { return; }
if (soundTimer < soundInterval * 0.5f)
{
PlaySound(CharacterSound.SoundType.Damage);
@@ -542,7 +534,7 @@ namespace Barotrauma
{
switch (AIController.State)
{
case AIController.AIState.Attack:
case AIState.Attack:
PlaySound(CharacterSound.SoundType.Attack);
break;
default:
@@ -577,6 +569,7 @@ namespace Barotrauma
}
}
CharacterHealth.UpdateClientSpecific(deltaTime);
if (controlled == this)
{
CharacterHealth.UpdateHUD(deltaTime);
@@ -612,7 +605,7 @@ namespace Barotrauma
CharacterHUD.Draw(spriteBatch, this, cam);
if (drawHealth) CharacterHealth.DrawHUD(spriteBatch);
}
public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam)
{
if (!Enabled) return;
@@ -713,12 +706,14 @@ namespace Barotrauma
if (IsDead) return;
if (Vitality < MaxVitality * 0.98f && hudInfoVisible)
if (CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
{
hudInfoAlpha = Math.Max(hudInfoAlpha, Math.Min(CharacterHealth.DamageOverlayTimer, 1.0f));
Vector2 healthBarPos = new Vector2(pos.X - 50, -pos.Y);
GUI.DrawProgressBar(spriteBatch, healthBarPos, new Vector2(100.0f, 15.0f),
Vitality / MaxVitality,
Color.Lerp(Color.Red, Color.Green, Vitality / MaxVitality) * 0.8f * hudInfoAlpha,
CharacterHealth.DisplayedVitality / MaxVitality,
Color.Lerp(Color.Red, Color.Green, CharacterHealth.DisplayedVitality / MaxVitality) * 0.8f * hudInfoAlpha,
new Color(0.5f, 0.57f, 0.6f, 1.0f) * hudInfoAlpha);
}
}
@@ -220,7 +220,7 @@ namespace Barotrauma
}
public static CharacterInfo ClientRead(string configPath, IReadMessage inc)
public static CharacterInfo ClientRead(string speciesName, IReadMessage inc)
{
ushort infoID = inc.ReadUInt16();
string newName = inc.ReadString();
@@ -238,7 +238,7 @@ namespace Barotrauma
Dictionary<string, float> skillLevels = new Dictionary<string, float>();
if (!string.IsNullOrEmpty(jobIdentifier))
{
jobPrefab = JobPrefab.List.Find(jp => jp.Identifier == jobIdentifier);
jobPrefab = JobPrefab.Get(jobIdentifier);
byte skillCount = inc.ReadByte();
for (int i = 0; i < skillCount; i++)
{
@@ -249,7 +249,7 @@ namespace Barotrauma
}
// TODO: animations
CharacterInfo ch = new CharacterInfo(configPath, newName, jobPrefab, ragdollFile)
CharacterInfo ch = new CharacterInfo(speciesName, newName, jobPrefab, ragdollFile)
{
ID = infoID,
};
@@ -20,13 +20,13 @@ namespace Barotrauma
return;
}
//freeze AI characters if more than 1 seconds have passed since last update from the server
if (lastRecvPositionUpdateTime < Lidgren.Network.NetTime.Now - 1.0f)
//freeze AI characters if more than x seconds have passed since last update from the server
if (lastRecvPositionUpdateTime < Lidgren.Network.NetTime.Now - NetConfig.FreezeCharacterIfPositionDataMissingDelay)
{
AnimController.Frozen = true;
memState.Clear();
//hide after 2 seconds
if (lastRecvPositionUpdateTime < Lidgren.Network.NetTime.Now - 2.0f)
//hide after y seconds
if (lastRecvPositionUpdateTime < Lidgren.Network.NetTime.Now - NetConfig.DisableCharacterIfPositionDataMissingDelay)
{
Enabled = false;
return;
@@ -74,7 +74,7 @@ namespace Barotrauma
states = newInput,
intAim = intAngle
};
if (focusedItem != null && !CharacterInventory.DraggingItemToWorld &&
if (focusedItem != null && !CharacterInventory.DraggingItemToWorld &&
(!newMem.states.HasFlag(InputNetFlags.Grab) && !newMem.states.HasFlag(InputNetFlags.Health)))
{
newMem.interact = focusedItem.ID;
@@ -133,9 +133,9 @@ namespace Barotrauma
{
msg.WriteRangedInteger((int)memInput[i].states, 0, (int)InputNetFlags.MaxVal);
msg.Write(memInput[i].intAim);
if (memInput[i].states.HasFlag(InputNetFlags.Select) ||
if (memInput[i].states.HasFlag(InputNetFlags.Select) ||
memInput[i].states.HasFlag(InputNetFlags.Deselect) ||
memInput[i].states.HasFlag(InputNetFlags.Use) ||
memInput[i].states.HasFlag(InputNetFlags.Use) ||
memInput[i].states.HasFlag(InputNetFlags.Health) ||
memInput[i].states.HasFlag(InputNetFlags.Grab))
{
@@ -187,11 +187,11 @@ namespace Barotrauma
bool attackInput = msg.ReadBoolean();
keys[(int)InputType.Attack].Held = attackInput;
keys[(int)InputType.Attack].SetState(false, attackInput);
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
TransformCursorPos();
bool ragdollInput = msg.ReadBoolean();
keys[(int)InputType.Ragdoll].Held = ragdollInput;
keys[(int)InputType.Ragdoll].SetState(false, ragdollInput);
@@ -225,7 +225,7 @@ namespace Barotrauma
msg.ReadSingle());
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
Vector2 linearVelocity = new Vector2(
msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);
@@ -252,9 +252,9 @@ namespace Barotrauma
if (GameMain.Client.Character == this && AllowInput)
{
var posInfo = new CharacterStateInfo(
pos, rotation,
networkUpdateID,
facingRight ? Direction.Right : Direction.Left,
pos, rotation,
networkUpdateID,
facingRight ? Direction.Right : Direction.Left,
selectedCharacter, selectedItem, animation);
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
@@ -264,11 +264,11 @@ namespace Barotrauma
else
{
var posInfo = new CharacterStateInfo(
pos, rotation,
linearVelocity, angularVelocity,
sendingTime, facingRight ? Direction.Right : Direction.Left,
pos, rotation,
linearVelocity, angularVelocity,
sendingTime, facingRight ? Direction.Right : Direction.Left,
selectedCharacter, selectedItem, animation);
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
index++;
memState.Insert(index, posInfo);
@@ -359,18 +359,12 @@ namespace Barotrauma
DebugConsole.Log("Received spawn data for " + speciesName);
string configPath = GetConfigFile(speciesName);
if (string.IsNullOrEmpty(configPath))
{
throw new Exception("Error in character spawn data - could not find a config file for the character \"" + configPath + "\"!");
}
Character character = null;
if (noInfo)
{
if (!spawn) return null;
character = Create(configPath, position, seed, null, true);
character = Create(speciesName, position, seed, null, true);
character.ID = id;
}
else
@@ -383,19 +377,13 @@ namespace Barotrauma
if (!spawn) return null;
string infoConfigPath = GetConfigFile(infoSpeciesName);
if (string.IsNullOrEmpty(infoConfigPath))
{
throw new Exception("Error in character spawn data - could not find a config file for the character info \"" + configPath + "\"!");
}
CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc);
CharacterInfo info = CharacterInfo.ClientRead(infoConfigPath, inc);
character = Create(configPath, position, seed, info, GameMain.Client.ID != ownerId, hasAi);
character = Create(infoSpeciesName, position, seed, info, GameMain.Client.ID != ownerId, hasAi);
character.ID = id;
character.TeamID = (TeamType)teamID;
if (configPath == HumanConfigFile && character.TeamID != TeamType.FriendlyNPC)
if (character.IsHuman && character.TeamID != TeamType.FriendlyNPC)
{
CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID);
GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo);
@@ -421,15 +409,6 @@ namespace Barotrauma
return character;
}
private void ReadTraitorStatus(IReadMessage msg)
{
IsTraitor = msg.ReadBoolean();
if (IsTraitor)
{
TraitorCurrentObjective = msg.ReadString();
}
}
private void ReadStatus(IReadMessage msg)
{
bool isDead = msg.ReadBoolean();
@@ -1,6 +1,4 @@
using System;
using System.Xml.Linq;
using Barotrauma.Sounds;
using Barotrauma.Sounds;
namespace Barotrauma
{
@@ -12,29 +10,18 @@ namespace Barotrauma
}
private readonly RoundSound roundSound;
public readonly CharacterParams.SoundParams Params;
public readonly SoundType Type;
public SoundType Type => Params.State;
public Gender Gender => Params.Gender;
public float Volume => roundSound.Volume;
public float Range => roundSound.Range;
public Sound Sound => roundSound?.Sound;
public float Volume
public CharacterSound(CharacterParams.SoundParams soundParams)
{
get { return roundSound.Volume; }
}
public float Range
{
get { return roundSound.Range; }
}
public Sound Sound
{
get { return roundSound?.Sound; }
}
public readonly Gender Gender;
public CharacterSound(XElement element)
{
roundSound = Submarine.LoadRoundSound(element);
Enum.TryParse(element.GetAttributeString("state", "Idle"), true, out Type);
Enum.TryParse(element.GetAttributeString("gender", "None"), true, out Gender);
Params = soundParams;
roundSound = Submarine.LoadRoundSound(soundParams.Element);
}
}
}
@@ -95,6 +95,8 @@ namespace Barotrauma
private const float UpdateDisplayedAfflictionsInterval = 0.5f;
private List<Affliction> currentDisplayedAfflictions = new List<Affliction>();
public float DisplayedVitality, DisplayVitalityDelay;
public bool MouseOnElement
{
get { return highlightedLimbIndex > -1 || GUI.MouseOn == dropItemArea; }
@@ -159,6 +161,8 @@ namespace Barotrauma
partial void InitProjSpecific(XElement element, Character character)
{
DisplayedVitality = MaxVitality;
if (strengthTexts == null)
{
strengthTexts = new string[]
@@ -349,15 +353,17 @@ namespace Barotrauma
private void OnAttacked(Character attacker, AttackResult attackResult)
{
if (Math.Abs(attackResult.Damage) < 0.01f && attackResult.Afflictions.Count == 0) return;
if (Math.Abs(attackResult.Damage) < 0.01f && attackResult.Afflictions.Count == 0) { return; }
DamageOverlayTimer = MathHelper.Clamp(attackResult.Damage / MaxVitality, DamageOverlayTimer, 1.0f);
if (healthShadowDelay <= 0.0f) healthShadowDelay = 1.0f;
if (healthShadowDelay <= 0.0f) { healthShadowDelay = 1.0f; }
if (healthBarPulsateTimer <= 0.0f) healthBarPulsatePhase = 0.0f;
if (healthBarPulsateTimer <= 0.0f) { healthBarPulsatePhase = 0.0f; }
healthBarPulsateTimer = 1.0f;
float additionalIntensity = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, 0.1f, attackResult.Damage / MaxVitality));
damageIntensity = MathHelper.Clamp(damageIntensity + additionalIntensity, 0, 1);
DisplayVitalityDelay = 0.5f;
}
private void UpdateAlignment()
@@ -435,6 +441,35 @@ namespace Barotrauma
uiScale = GUI.Scale;
}
public void UpdateClientSpecific(float deltaTime)
{
if (GameMain.NetworkMember == null)
{
DisplayedVitality = Vitality;
}
else
{
DisplayVitalityDelay -= deltaTime;
if (DisplayVitalityDelay <= 0.0f)
{
DisplayedVitality = Vitality;
}
}
if (damageIntensity > 0)
{
damageIntensity -= deltaTime * damageIntensityDropdownRate;
if (damageIntensity < 0)
{
damageIntensity = 0;
}
}
if (DamageOverlayTimer > 0.0f)
{
DamageOverlayTimer -= deltaTime;
}
}
partial void UpdateOxygenProjSpecific(float prevOxygen)
{
if (prevOxygen > 0.0f && OxygenAmount <= 0.0f &&
@@ -492,20 +527,7 @@ namespace Barotrauma
});
updateDisplayedAfflictionsTimer = UpdateDisplayedAfflictionsInterval;
}
if (DamageOverlayTimer > 0.0f)
{
DamageOverlayTimer -= deltaTime;
}
if (damageIntensity > 0)
{
damageIntensity -= deltaTime * damageIntensityDropdownRate;
if (damageIntensity < 0)
{
damageIntensity = 0;
}
}
if (healthShadowDelay > 0.0f)
{
healthShadowDelay -= deltaTime;
@@ -639,12 +661,12 @@ namespace Barotrauma
}
else
{
healthBar.Color = healthWindowHealthBar.Color = ToolBox.GradientLerp(Vitality / MaxVitality, Color.Red, Color.Orange, Color.Green);
healthBar.Color = healthWindowHealthBar.Color = ToolBox.GradientLerp(DisplayedVitality / MaxVitality, Color.Red, Color.Orange, Color.Green);
healthBar.HoverColor = healthWindowHealthBar.HoverColor = healthBar.Color * 2.0f;
healthBar.BarSize = healthWindowHealthBar.BarSize =
(Vitality > 0.0f) ?
(MaxVitality > 0.0f ? Vitality / MaxVitality : 0.0f) :
(Math.Abs(MinVitality) > 0.0f ? 1.0f - Vitality / MinVitality : 0.0f);
(DisplayedVitality > 0.0f) ?
(MaxVitality > 0.0f ? DisplayedVitality / MaxVitality : 0.0f) :
(Math.Abs(MinVitality) > 0.0f ? 1.0f - DisplayedVitality / MinVitality : 0.0f);
if (healthBarPulsateTimer > 0.0f)
{
@@ -1446,6 +1468,9 @@ namespace Barotrauma
}
}
}
CalculateVitality();
DisplayedVitality = Vitality;
}
partial void UpdateLimbAfflictionOverlays()
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using System.Linq;
namespace Barotrauma
{
@@ -29,13 +30,18 @@ namespace Barotrauma
}
var itemContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform, Anchor.TopRight)
{ RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) });
{ RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) })
{
Stretch = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform),
TextManager.Get("Items", fallBackTag: "mapentitycategory.equipment"), font: GUI.LargeFont);
foreach (string itemName in ItemNames)
foreach (string itemName in ItemNames.Distinct())
{
int count = ItemNames.Count(i => i == itemName);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform),
" - " + itemName, font: GUI.SmallFont);
" - " + (count == 1 ? itemName : itemName + " x" + count),
font: GUI.SmallFont);
}
return backFrame;
@@ -11,6 +11,8 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using SpriteParams = Barotrauma.RagdollParams.SpriteParams;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -18,8 +20,9 @@ namespace Barotrauma
{
public void UpdateDeformations(float deltaTime)
{
float jointMidAngle = (LowerLimit + UpperLimit) / 2.0f;
float jointAngle = this.JointAngle - jointMidAngle;
float diff = Math.Abs(UpperLimit - LowerLimit);
float strength = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, MathHelper.Pi, diff));
float jointAngle = this.JointAngle * strength;
JointBendDeformation limbADeformation = LimbA.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation;
JointBendDeformation limbBDeformation = LimbB.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation;
@@ -28,7 +31,6 @@ namespace Barotrauma
{
UpdateBend(LimbA, limbADeformation, this.LocalAnchorA, -jointAngle);
UpdateBend(LimbB, limbBDeformation, this.LocalAnchorB, jointAngle);
}
void UpdateBend(Limb limb, JointBendDeformation deformation, Vector2 localAnchor, float angle)
@@ -74,6 +76,14 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch)
{
// TODO: move this into the character editor
//var mouthPos = ragdoll.GetMouthPosition();
//if (mouthPos != null)
//{
// var pos = ConvertUnits.ToDisplayUnits(mouthPos.Value);
// pos.Y = -pos.Y;
// ShapeExtensions.DrawPoint(spriteBatch, pos, Color.Red, size: 5);
//}
return;
// A debug visualisation on the bezier curve between limbs.
var start = LimbA.WorldPosition;
@@ -110,6 +120,9 @@ namespace Barotrauma
public Sprite Sprite { get; protected set; }
public DeformableSprite DeformSprite { get; protected set; }
public List<DecorativeSprite> DecorativeSprites { get; private set; } = new List<DecorativeSprite>();
public Sprite ActiveSprite
{
get
@@ -130,32 +143,23 @@ namespace Barotrauma
public WearableSprite HuskSprite { get; private set; }
public WearableSprite HerpesSprite { get; private set; }
public void LoadHuskSprite()
{
var info = character.Info;
if (info == null) { return; }
var element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), WearableType.Husk).FirstOrDefault();
if (element != null)
{
HuskSprite = new WearableSprite(element.Element("sprite"), WearableType.Husk);
}
}
public void LoadHerpesSprite()
{
var info = character.Info;
if (info == null) { return; }
var element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), WearableType.Herpes).FirstOrDefault();
if (element != null)
{
HerpesSprite = new WearableSprite(element.Element("sprite"), WearableType.Herpes);
}
}
public void LoadHuskSprite() => HuskSprite = GetWearableSprite(WearableType.Husk);
public void LoadHerpesSprite() => HerpesSprite = GetWearableSprite(WearableType.Herpes);
public float TextureScale => limbParams.Ragdoll.TextureScale;
public float TextureScale => Params.Ragdoll.TextureScale;
public Sprite DamagedSprite { get; private set; }
public List<ConditionalSprite> ConditionalSprites { get; private set; } = new List<ConditionalSprite>();
private Dictionary<DecorativeSprite, SpriteState> spriteAnimState = new Dictionary<DecorativeSprite, SpriteState>();
private Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
class SpriteState
{
public float RotationState;
public float OffsetState;
public bool IsActive = true;
}
public Color InitialLightSourceColor
{
@@ -183,25 +187,38 @@ namespace Barotrauma
set { burnOverLayStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public string HitSoundTag { get; private set; }
public string HitSoundTag => Params?.Sound?.Tag;
partial void InitProjSpecific(XElement element)
{
for (int i = 0; i < Params.decorativeSpriteParams.Count; i++)
{
var param = Params.decorativeSpriteParams[i];
var decorativeSprite = new DecorativeSprite(param.Element, file: GetSpritePath(param.Element, param));
DecorativeSprites.Add(decorativeSprite);
int groupID = decorativeSprite.RandomGroupID;
if (!DecorativeSpriteGroups.ContainsKey(groupID))
{
DecorativeSpriteGroups.Add(groupID, new List<DecorativeSprite>());
}
DecorativeSpriteGroups[groupID].Add(decorativeSprite);
spriteAnimState.Add(decorativeSprite, new SpriteState());
}
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
Sprite = new Sprite(subElement, "", GetSpritePath(subElement));
Sprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.normalSpriteParams));
break;
case "damagedsprite":
DamagedSprite = new Sprite(subElement, "", GetSpritePath(subElement));
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams));
break;
case "conditionalsprite":
ConditionalSprites.Add(new ConditionalSprite(subElement, character, file: GetSpritePath(subElement)));
ConditionalSprites.Add(new ConditionalSprite(subElement, character, file: GetSpritePath(subElement, null)));
break;
case "deformablesprite":
DeformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement));
DeformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement, Params.deformSpriteParams));
foreach (XElement animationElement in subElement.Elements())
{
int sync = animationElement.GetAttributeInt("sync", -1);
@@ -231,32 +248,63 @@ namespace Barotrauma
LightSource = new LightSource(subElement);
InitialLightSourceColor = LightSource.Color;
break;
case "sound":
HitSoundTag = subElement.GetAttributeString("tag", "");
if (string.IsNullOrWhiteSpace(HitSoundTag))
{
//legacy support
HitSoundTag = subElement.GetAttributeString("file", "");
}
break;
}
}
}
public void RecreateSprite()
public void RecreateSprites()
{
if (Sprite == null) { return; }
Sprite.Remove();
var source = Sprite.SourceElement;
Sprite = new Sprite(source, file: GetSpritePath(source));
if (Sprite != null)
{
Sprite.Remove();
var source = Sprite.SourceElement;
Sprite = new Sprite(source, file: GetSpritePath(source, Params.normalSpriteParams));
}
if (DeformSprite != null)
{
DeformSprite.Remove();
var source = DeformSprite.Sprite.SourceElement;
DeformSprite = new DeformableSprite(source, filePath: GetSpritePath(source, Params.deformSpriteParams));
}
if (DamagedSprite != null)
{
DamagedSprite.Remove();
var source = DamagedSprite.SourceElement;
DamagedSprite = new Sprite(source, file: GetSpritePath(source, Params.damagedSpriteParams));
}
for (int i = 0; i < ConditionalSprites.Count; i++)
{
var conditionalSprite = ConditionalSprites[i];
conditionalSprite.Remove();
var source = conditionalSprite.SourceElement;
// TODO: lazy load?
ConditionalSprites[i] = new ConditionalSprite(source, character, file: GetSpritePath(source, null));
}
for (int i = 0; i < DecorativeSprites.Count; i++)
{
var decorativeSprite = DecorativeSprites[i];
decorativeSprite.Remove();
var source = decorativeSprite.Sprite.SourceElement;
DecorativeSprites[i] = new DecorativeSprite(source, file: GetSpritePath(source, Params.decorativeSpriteParams[i]));
}
}
private string GetSpritePath(XElement element, SpriteParams spriteParams)
{
string texturePath = element.GetAttributeString("texture", null);
if (string.IsNullOrWhiteSpace(texturePath) && spriteParams != null)
{
texturePath = spriteParams.Ragdoll.Texture;
}
return GetSpritePath(texturePath);
}
/// <summary>
/// Get the full path of a limb sprite, taking into account tags, gender and head id
/// </summary>
private string GetSpritePath(XElement element)
private string GetSpritePath(string texturePath)
{
string spritePath = element.Attribute("texture")?.Value ?? "";
string spritePath = texturePath;
string spritePathWithTags = spritePath;
if (character.Info != null && character.IsHumanoid)
{
@@ -274,16 +322,19 @@ namespace Barotrauma
Path.GetFileNameWithoutExtension(spritePath) + tags + Path.GetExtension(spritePath));
}
}
return File.Exists(spritePathWithTags) ? spritePathWithTags : spritePath;
}
partial void LoadParamsProjSpecific()
{
bool isFlipped = dir == Direction.Left;
Sprite?.LoadParams(limbParams.normalSpriteParams, isFlipped);
DamagedSprite?.LoadParams(limbParams.damagedSpriteParams, isFlipped);
DeformSprite?.Sprite.LoadParams(limbParams.deformSpriteParams, isFlipped);
Sprite?.LoadParams(Params.normalSpriteParams, isFlipped);
DamagedSprite?.LoadParams(Params.damagedSpriteParams, isFlipped);
DeformSprite?.Sprite.LoadParams(Params.deformSpriteParams, isFlipped);
for (int i = 0; i < DecorativeSprites.Count; i++)
{
DecorativeSprites[i].Sprite?.LoadParams(Params.decorativeSpriteParams[i], isFlipped);
}
}
partial void AddDamageProjSpecific(Vector2 simPosition, List<Affliction> afflictions, bool playSound, List<DamageModifier> appliedDamageModifiers)
@@ -378,6 +429,8 @@ namespace Barotrauma
LightSource.ParentSub = body.Submarine;
LightSource.Rotation = (dir == Direction.Right) ? body.Rotation : body.Rotation - MathHelper.Pi;
}
UpdateSpriteStates(deltaTime);
}
public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null)
@@ -401,7 +454,14 @@ namespace Barotrauma
body.Dir = Dir;
bool hideLimb = wearingItems.Any(w => w != null && w.HideLimb);
bool enableHuskSprite = character.IsHusk || character.CharacterHealth.GetAffliction<AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active;
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
bool hideLimb = Params.Hide ||
enableHuskSprite && HuskSprite != null && HuskSprite.HideLimb ||
OtherWearables.Any(w => w.HideLimb) ||
wearingItems.Any(w => w != null && w.HideLimb);
// TODO: there's now two calls to this, because body.Draw() method calls this too -> is this an issue?
body.UpdateDrawPosition();
if (!hideLimb)
@@ -418,44 +478,68 @@ namespace Barotrauma
{
DeformSprite.Reset();
}
body.Draw(DeformSprite, cam, Vector2.One * Scale * TextureScale, color);
body.Draw(DeformSprite, cam, Vector2.One * Scale * TextureScale, color, Params.MirrorHorizontally);
}
else
{
body.Draw(spriteBatch, activeSprite, color, null, Scale * TextureScale);
body.Draw(spriteBatch, activeSprite, color, null, Scale * TextureScale, Params.MirrorHorizontally, Params.MirrorVertically);
}
}
SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
if (LightSource != null)
{
LightSource.Position = body.DrawPosition;
LightSource.LightSpriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipVertically;
}
if (damageOverlayStrength > 0.0f && DamagedSprite != null && !hideLimb)
{
DamagedSprite.Draw(spriteBatch,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color * Math.Min(damageOverlayStrength, 1.0f), ActiveSprite.Origin,
-body.DrawRotation,
Scale, spriteEffect, ActiveSprite.Depth - 0.0000015f);
}
foreach (var decorativeSprite in DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
var ca = (float)Math.Cos(-body.Rotation);
var sa = (float)Math.Sin(-body.Rotation);
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), color,
-body.Rotation + rotation, Scale, spriteEffect,
depth: decorativeSprite.Sprite.Depth);
}
float depthStep = 0.000001f;
float step = depthStep;
WearableSprite onlyDrawable = wearingItems.Find(w => w.HideOtherWearables);
SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
if (Params.MirrorHorizontally)
{
spriteEffect = spriteEffect == SpriteEffects.None ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
}
if (Params.MirrorVertically)
{
spriteEffect |= SpriteEffects.FlipVertically;
}
if (onlyDrawable == null)
{
if (HerpesSprite != null)
{
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
if (herpesStrength > 0.0f)
{
DrawWearable(HerpesSprite, depthStep, spriteBatch, color * Math.Min(herpesStrength / 10.0f, 1.0f), spriteEffect);
depthStep += 0.000001f;
}
DrawWearable(HerpesSprite, depthStep, spriteBatch, color * Math.Min(herpesStrength / 10.0f, 1.0f), spriteEffect);
depthStep += step;
}
if (HuskSprite != null && (character.SpeciesName == "Humanhusk" || (character.SpeciesName == "Human" &&
character.CharacterHealth.GetAffliction<AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active)))
if (HuskSprite != null && enableHuskSprite)
{
DrawWearable(HuskSprite, depthStep, spriteBatch, color, spriteEffect);
depthStep += 0.000001f;
depthStep += step;
}
foreach (WearableSprite wearable in OtherWearables)
{
if (wearable.Type == WearableType.Beard && enableHuskSprite && HuskSprite != null) { continue; }
DrawWearable(wearable, depthStep, spriteBatch, color, spriteEffect);
//if there are multiple sprites on this limb, make the successive ones be drawn in front
depthStep += 0.000001f;
depthStep += step;
}
}
foreach (WearableSprite wearable in WearingItems)
@@ -463,18 +547,7 @@ namespace Barotrauma
if (onlyDrawable != null && onlyDrawable != wearable) continue;
DrawWearable(wearable, depthStep, spriteBatch, color, spriteEffect);
//if there are multiple sprites on this limb, make the successive ones be drawn in front
depthStep += 0.000001f;
}
if (damageOverlayStrength > 0.0f && DamagedSprite != null && !hideLimb)
{
float depth = ActiveSprite.Depth - 0.0000015f;
DamagedSprite.Draw(spriteBatch,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color * Math.Min(damageOverlayStrength, 1.0f), ActiveSprite.Origin,
-body.DrawRotation,
1.0f, spriteEffect, depth);
depthStep += step;
}
if (GameMain.DebugDraw)
@@ -492,7 +565,7 @@ namespace Barotrauma
from.Y = -from.Y;
Vector2 to = ConvertUnits.ToDisplayUnits(attachJoint.WorldAnchorB);
to.Y = -to.Y;
var localFront = body.GetLocalFront(MathHelper.ToRadians(limbParams.Ragdoll.SpritesheetOrientation));
var localFront = body.GetLocalFront(Params.GetSpriteOrientation());
var front = ConvertUnits.ToDisplayUnits(body.FarseerBody.GetWorldPoint(localFront));
front.Y = -front.Y;
GUI.DrawLine(spriteBatch, bodyDrawPos, front, Color.Yellow, width: 2);
@@ -503,25 +576,89 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, new Rectangle((int)to.X, (int)to.Y, 10, 10), Color.Red, true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)front.X, (int)front.Y, 10, 10), Color.Yellow, true);
//Vector2 mainLimbFront = ConvertUnits.ToDisplayUnits(ragdoll.MainLimb.body.FarseerBody.GetWorldPoint(ragdoll.MainLimb.body.GetFrontLocal(MathHelper.ToRadians(ragdoll.RagdollParams.SpritesheetOrientation))));
//Vector2 mainLimbFront = ConvertUnits.ToDisplayUnits(ragdoll.MainLimb.body.FarseerBody.GetWorldPoint(ragdoll.MainLimb.body.GetFrontLocal(MathHelper.ToRadians(limbParams.Orientation))));
//mainLimbFront.Y = -mainLimbFront.Y;
//var mainLimbDrawPos = ragdoll.MainLimb.body.DrawPosition;
//mainLimbDrawPos.Y = -mainLimbDrawPos.Y;
//GUI.DrawLine(spriteBatch, mainLimbDrawPos, mainLimbFront, Color.White, width: 5);
//GUI.DrawRectangle(spriteBatch, new Rectangle((int)mainLimbFront.X, (int)mainLimbFront.Y, 10, 10), Color.Yellow, true);
}
foreach (var modifier in damageModifiers)
DrawDamageModifiers(spriteBatch, cam, bodyDrawPos, isScreenSpace: false);
}
}
private void UpdateSpriteStates(float deltaTime)
{
foreach (int spriteGroup in DecorativeSpriteGroups.Keys)
{
for (int i = 0; i < DecorativeSpriteGroups[spriteGroup].Count; i++)
{
float rotation = -body.TransformedRotation + GetArmorSectorRotationOffset(modifier.ArmorSector) * Dir;
Vector2 forward = VectorExtensions.Forward(rotation);
float size = ConvertUnits.ToDisplayUnits(body.GetSize().Length() / 2);
color = modifier.DamageMultiplier > 1 ? Color.Red : Color.GreenYellow;
GUI.DrawLine(spriteBatch, bodyDrawPos, bodyDrawPos + Vector2.Normalize(forward) * size, color, width: (int)Math.Round(4 / cam.Zoom));
ShapeExtensions.DrawSector(spriteBatch, bodyDrawPos, size, GetArmorSectorSize(modifier.ArmorSector) * Dir, 40, color, rotation + MathHelper.Pi, thickness: 2 / cam.Zoom);
var decorativeSprite = DecorativeSpriteGroups[spriteGroup][i];
if (decorativeSprite == null) { continue; }
if (spriteGroup > 0)
{
// TODO
//int activeSpriteIndex = ID % DecorativeSpriteGroups[spriteGroup].Count;
//if (i != activeSpriteIndex)
//{
// spriteAnimState[decorativeSprite].IsActive = false;
// continue;
//}
}
//check if the sprite is active (whether it should be drawn or not)
var spriteState = spriteAnimState[decorativeSprite];
spriteState.IsActive = true;
foreach (PropertyConditional conditional in decorativeSprite.IsActiveConditionals)
{
if (!conditional.Matches(this))
{
spriteState.IsActive = false;
break;
}
}
if (!spriteState.IsActive) { continue; }
//check if the sprite should be animated
bool animate = true;
foreach (PropertyConditional conditional in decorativeSprite.AnimationConditionals)
{
if (!conditional.Matches(this)) { animate = false; break; }
}
if (!animate) { continue; }
spriteState.OffsetState += deltaTime;
spriteState.RotationState += deltaTime;
}
}
}
public void DrawDamageModifiers(SpriteBatch spriteBatch, Camera cam, Vector2 startPos, bool isScreenSpace)
{
foreach (var modifier in damageModifiers)
{
float rotation = -body.TransformedRotation + GetArmorSectorRotationOffset(modifier.ArmorSectorInRadians) * Dir;
Vector2 forward = VectorExtensions.Forward(rotation);
float size = ConvertUnits.ToDisplayUnits(body.GetSize().Length() / 2);
if (isScreenSpace)
{
size *= cam.Zoom;
}
Color color = modifier.DamageMultiplier > 1 ? Color.Red : Color.GreenYellow;
int width = 4;
if (!isScreenSpace)
{
width = (int)Math.Round(width / cam.Zoom);
}
GUI.DrawLine(spriteBatch, startPos, startPos + Vector2.Normalize(forward) * size, color, width: width);
int thickness = 2;
if (!isScreenSpace)
{
thickness = (int)Math.Round(thickness / cam.Zoom);
}
ShapeExtensions.DrawSector(spriteBatch, startPos, size, GetArmorSectorSize(modifier.ArmorSectorInRadians) * Dir, 40, color, rotation + MathHelper.Pi, thickness);
}
}
private void DrawWearable(WearableSprite wearable, float depthStep, SpriteBatch spriteBatch, Color color, SpriteEffects spriteEffect)
{
if (wearable.InheritSourceRect)
@@ -584,6 +721,26 @@ namespace Barotrauma
Scale * textureScale, spriteEffect, depth);
}
private WearableSprite GetWearableSprite(WearableType type, bool random = false)
{
var info = character.Info;
if (info == null) { return null; }
XElement element;
if (random)
{
element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), type)?.FirstOrDefault();
}
else
{
element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), type)?.GetRandom(Rand.RandSync.ClientOnly);
}
if (element != null)
{
return new WearableSprite(element.Element("sprite"), type);
}
return null;
}
partial void RemoveProjSpecific()
{
Sprite?.Remove();
@@ -595,6 +752,9 @@ namespace Barotrauma
DeformSprite?.Sprite?.Remove();
DeformSprite = null;
DecorativeSprites.ForEach(s => s.Remove());
ConditionalSprites.Clear();
ConditionalSprites.ForEach(s => s.Remove());
ConditionalSprites.Clear();