Unstable 0.1500.5.0 (almost forgor edition 💀)
This commit is contained in:
@@ -81,10 +81,10 @@ namespace Barotrauma
|
|||||||
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), GUI.Style.Green, 0, 4);
|
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), GUI.Style.Green, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LatchOntoAI.WallAttachPos.HasValue)
|
if (LatchOntoAI.AttachPos.HasValue)
|
||||||
{
|
{
|
||||||
//GUI.DrawLine(spriteBatch, pos,
|
GUI.DrawLine(spriteBatch, pos,
|
||||||
// ConvertUnits.ToDisplayUnits(new Vector2(LatchOntoAI.WallAttachPos.Value.X, -LatchOntoAI.WallAttachPos.Value.Y)), GUI.Style.Green, 0, 3);
|
ConvertUnits.ToDisplayUnits(new Vector2(LatchOntoAI.AttachPos.Value.X, -LatchOntoAI.AttachPos.Value.Y)), GUI.Style.Green, 0, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -562,8 +562,10 @@ namespace Barotrauma
|
|||||||
if (this is HumanoidAnimController humanoid)
|
if (this is HumanoidAnimController humanoid)
|
||||||
{
|
{
|
||||||
Vector2 pos = ConvertUnits.ToDisplayUnits(humanoid.RightHandIKPos);
|
Vector2 pos = ConvertUnits.ToDisplayUnits(humanoid.RightHandIKPos);
|
||||||
|
if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.Position; }
|
||||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
|
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
|
||||||
pos = ConvertUnits.ToDisplayUnits(humanoid.LeftHandIKPos);
|
pos = ConvertUnits.ToDisplayUnits(humanoid.LeftHandIKPos);
|
||||||
|
if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.Position; }
|
||||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
|
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool shouldRecreateHudTexts = true;
|
public static bool ShouldRecreateHudTexts { get; set; } = true;
|
||||||
private static bool heldDownShiftWhenGotHudTexts;
|
private static bool heldDownShiftWhenGotHudTexts;
|
||||||
|
|
||||||
public static bool IsCampaignInterfaceOpen =>
|
public static bool IsCampaignInterfaceOpen =>
|
||||||
@@ -150,7 +150,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (character.IsHumanoid && character.SelectedCharacter != null)
|
if (character.Params.CanInteract && character.SelectedCharacter != null)
|
||||||
{
|
{
|
||||||
character.SelectedCharacter.CharacterHealth.AddToGUIUpdateList();
|
character.SelectedCharacter.CharacterHealth.AddToGUIUpdateList();
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
|
if (character.Params.CanInteract && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
|
||||||
{
|
{
|
||||||
if (character.SelectedCharacter.CanInventoryBeAccessed)
|
if (character.SelectedCharacter.CanInventoryBeAccessed)
|
||||||
{
|
{
|
||||||
@@ -219,7 +219,7 @@ namespace Barotrauma
|
|||||||
if (focusedItemOverlayTimer <= 0.0f)
|
if (focusedItemOverlayTimer <= 0.0f)
|
||||||
{
|
{
|
||||||
focusedItem = null;
|
focusedItem = null;
|
||||||
shouldRecreateHudTexts = true;
|
ShouldRecreateHudTexts = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,6 +285,21 @@ namespace Barotrauma
|
|||||||
i.GetRootInventoryOwner() == i);
|
i.GetRootInventoryOwner() == i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GameMain.GameSession != null)
|
||||||
|
{
|
||||||
|
foreach (var mission in GameMain.GameSession.Missions)
|
||||||
|
{
|
||||||
|
if (!mission.DisplayTargetHudIcons) { continue; }
|
||||||
|
foreach (var target in mission.HudIconTargets)
|
||||||
|
{
|
||||||
|
if (target.Submarine != character.Submarine) { continue; }
|
||||||
|
float alpha = GetDistanceBasedIconAlpha(target, maxDistance: mission.Prefab.HudIconMaxDistance);
|
||||||
|
if (alpha <= 0.0f) { continue; }
|
||||||
|
GUI.DrawIndicator(spriteBatch, target.DrawPosition, cam, 100.0f, mission.Prefab.HudIcon, mission.Prefab.HudIconColor * alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (Character.ObjectiveEntity objectiveEntity in character.ActiveObjectiveEntities)
|
foreach (Character.ObjectiveEntity objectiveEntity in character.ActiveObjectiveEntities)
|
||||||
{
|
{
|
||||||
DrawObjectiveIndicator(spriteBatch, cam, character, objectiveEntity, 1.0f);
|
DrawObjectiveIndicator(spriteBatch, cam, character, objectiveEntity, 1.0f);
|
||||||
@@ -317,7 +332,7 @@ namespace Barotrauma
|
|||||||
if (focusedItem != character.FocusedItem)
|
if (focusedItem != character.FocusedItem)
|
||||||
{
|
{
|
||||||
focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer);
|
focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer);
|
||||||
shouldRecreateHudTexts = true;
|
ShouldRecreateHudTexts = true;
|
||||||
}
|
}
|
||||||
focusedItem = character.FocusedItem;
|
focusedItem = character.FocusedItem;
|
||||||
}
|
}
|
||||||
@@ -342,13 +357,13 @@ namespace Barotrauma
|
|||||||
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
|
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
|
||||||
{
|
{
|
||||||
bool shiftDown = PlayerInput.IsShiftDown();
|
bool shiftDown = PlayerInput.IsShiftDown();
|
||||||
if (shouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown)
|
if (ShouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown)
|
||||||
{
|
{
|
||||||
shouldRecreateHudTexts = true;
|
ShouldRecreateHudTexts = true;
|
||||||
heldDownShiftWhenGotHudTexts = shiftDown;
|
heldDownShiftWhenGotHudTexts = shiftDown;
|
||||||
}
|
}
|
||||||
var hudTexts = focusedItem.GetHUDTexts(character, shouldRecreateHudTexts);
|
var hudTexts = focusedItem.GetHUDTexts(character, ShouldRecreateHudTexts);
|
||||||
shouldRecreateHudTexts = false;
|
ShouldRecreateHudTexts = false;
|
||||||
|
|
||||||
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
|
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
|
||||||
|
|
||||||
@@ -490,7 +505,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (!character.IsIncapacitated && character.Stun <= 0.0f)
|
if (!character.IsIncapacitated && character.Stun <= 0.0f)
|
||||||
{
|
{
|
||||||
if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
|
if (character.Params.CanInteract && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
|
||||||
{
|
{
|
||||||
if (character.SelectedCharacter.CanInventoryBeAccessed)
|
if (character.SelectedCharacter.CanInventoryBeAccessed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,11 +17,15 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public bool LastControlled;
|
public bool LastControlled;
|
||||||
|
|
||||||
|
#warning TODO: Refactor
|
||||||
private Sprite disguisedPortrait;
|
private Sprite disguisedPortrait;
|
||||||
private List<WearableSprite> disguisedAttachmentSprites;
|
private List<WearableSprite> disguisedAttachmentSprites;
|
||||||
private Vector2? disguisedSheetIndex;
|
private Vector2? disguisedSheetIndex;
|
||||||
private Sprite disguisedJobIcon;
|
private Sprite disguisedJobIcon;
|
||||||
private Color disguisedJobColor;
|
private Color disguisedJobColor;
|
||||||
|
private Color disguisedHairColor;
|
||||||
|
private Color disguisedFacialHairColor;
|
||||||
|
private Color disguisedSkinColor;
|
||||||
|
|
||||||
private Sprite tintMask;
|
private Sprite tintMask;
|
||||||
private float tintHighlightThreshold;
|
private float tintHighlightThreshold;
|
||||||
@@ -161,10 +165,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void DrawInfoFrameCharacterIcon(SpriteBatch sb, Rectangle componentRect)
|
private void DrawInfoFrameCharacterIcon(SpriteBatch sb, Rectangle componentRect)
|
||||||
{
|
{
|
||||||
if (headSprite == null) { return; }
|
if (_headSprite == null) { return; }
|
||||||
Vector2 targetAreaSize = componentRect.Size.ToVector2();
|
Vector2 targetAreaSize = componentRect.Size.ToVector2();
|
||||||
float scale = Math.Min(targetAreaSize.X / headSprite.size.X, targetAreaSize.Y / headSprite.size.Y);
|
float scale = Math.Min(targetAreaSize.X / _headSprite.size.X, targetAreaSize.Y / _headSprite.size.Y);
|
||||||
DrawIcon(sb, componentRect.Location.ToVector2() + headSprite.size / 2 * scale, targetAreaSize);
|
DrawIcon(sb, componentRect.Location.ToVector2() + _headSprite.size / 2 * scale, targetAreaSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GUIFrame CreateCharacterFrame(GUIComponent parent, string text, object userData)
|
public GUIFrame CreateCharacterFrame(GUIComponent parent, string text, object userData)
|
||||||
@@ -227,193 +231,36 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (idCard.Item.Tags == string.Empty) return;
|
if (idCard.Item.Tags == string.Empty) return;
|
||||||
|
|
||||||
if (idCard.StoredJobPrefab == null || idCard.StoredPortrait == null)
|
if (idCard.StoredOwnerAppearance.JobPrefab == null || idCard.StoredOwnerAppearance.Portrait == null)
|
||||||
{
|
{
|
||||||
string[] readTags = idCard.Item.Tags.Split(',');
|
string[] readTags = idCard.Item.Tags.Split(',');
|
||||||
|
|
||||||
if (readTags.Length == 0) return;
|
if (readTags.Length == 0) { return; }
|
||||||
|
|
||||||
if (idCard.StoredJobPrefab == null)
|
if (idCard.StoredOwnerAppearance.JobPrefab == null)
|
||||||
{
|
{
|
||||||
string jobIdTag = readTags.FirstOrDefault(s => s.StartsWith("jobid:"));
|
idCard.StoredOwnerAppearance.ExtractJobPrefab(readTags);
|
||||||
|
|
||||||
if (jobIdTag != null && jobIdTag.Length > 6)
|
|
||||||
{
|
|
||||||
string jobId = jobIdTag.Substring(6);
|
|
||||||
if (jobId != string.Empty)
|
|
||||||
{
|
|
||||||
idCard.StoredJobPrefab = JobPrefab.Get(jobId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idCard.StoredPortrait == null)
|
if (idCard.StoredOwnerAppearance.Portrait == null)
|
||||||
{
|
{
|
||||||
string disguisedGender = string.Empty;
|
idCard.StoredOwnerAppearance.ExtractAppearance(this, readTags);
|
||||||
string disguisedRace = string.Empty;
|
|
||||||
string disguisedHeadSpriteId = string.Empty;
|
|
||||||
int disguisedHairIndex = -1;
|
|
||||||
int disguisedBeardIndex = -1;
|
|
||||||
int disguisedMoustacheIndex = -1;
|
|
||||||
int disguisedFaceAttachmentIndex = -1;
|
|
||||||
|
|
||||||
foreach (string tag in readTags)
|
|
||||||
{
|
|
||||||
string[] s = tag.Split(':');
|
|
||||||
|
|
||||||
switch (s[0])
|
|
||||||
{
|
|
||||||
case "gender":
|
|
||||||
disguisedGender = s[1];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "race":
|
|
||||||
disguisedRace = s[1];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "headspriteid":
|
|
||||||
disguisedHeadSpriteId = s[1];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "hairindex":
|
|
||||||
disguisedHairIndex = int.Parse(s[1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "beardindex":
|
|
||||||
disguisedBeardIndex = int.Parse(s[1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "moustacheindex":
|
|
||||||
disguisedMoustacheIndex = int.Parse(s[1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "faceattachmentindex":
|
|
||||||
disguisedFaceAttachmentIndex = int.Parse(s[1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "sheetindex":
|
|
||||||
string[] vectorValues = s[1].Split(";");
|
|
||||||
idCard.StoredSheetIndex = new Vector2(float.Parse(vectorValues[0]), float.Parse(vectorValues[1]));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disguisedGender == string.Empty || disguisedRace == string.Empty || disguisedHeadSpriteId == string.Empty)
|
|
||||||
{
|
|
||||||
idCard.StoredPortrait = null;
|
|
||||||
idCard.StoredAttachments = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (XElement limbElement in Ragdoll.MainElement.Elements())
|
|
||||||
{
|
|
||||||
if (!limbElement.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
|
|
||||||
|
|
||||||
XElement spriteElement = limbElement.Element("sprite");
|
|
||||||
if (spriteElement == null) { continue; }
|
|
||||||
|
|
||||||
string spritePath = spriteElement.Attribute("texture").Value;
|
|
||||||
|
|
||||||
spritePath = spritePath.Replace("[GENDER]", disguisedGender);
|
|
||||||
spritePath = spritePath.Replace("[RACE]", disguisedRace.ToLowerInvariant());
|
|
||||||
spritePath = spritePath.Replace("[HEADID]", disguisedHeadSpriteId);
|
|
||||||
|
|
||||||
string fileName = Path.GetFileNameWithoutExtension(spritePath);
|
|
||||||
|
|
||||||
//go through the files in the directory to find a matching sprite
|
|
||||||
foreach (string file in Directory.GetFiles(Path.GetDirectoryName(spritePath)))
|
|
||||||
{
|
|
||||||
if (!file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
string fileWithoutTags = Path.GetFileNameWithoutExtension(file);
|
|
||||||
fileWithoutTags = fileWithoutTags.Split('[', ']').First();
|
|
||||||
if (fileWithoutTags != fileName) { continue; }
|
|
||||||
idCard.StoredPortrait = new Sprite(spriteElement, "", file) { RelativeOrigin = Vector2.Zero };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Wearables != null)
|
|
||||||
{
|
|
||||||
XElement disguisedHairElement, disguisedBeardElement, disguisedMoustacheElement, disguisedFaceAttachmentElement;
|
|
||||||
List<XElement> disguisedHairs, disguisedBeards, disguisedMoustaches, disguisedFaceAttachments;
|
|
||||||
|
|
||||||
Gender disguisedGenderEnum = disguisedGender == "female" ? Gender.Female : Gender.Male;
|
|
||||||
Race disguisedRaceEnum = (Race)Enum.Parse(typeof(Race), disguisedRace);
|
|
||||||
int headSpriteId = int.Parse(disguisedHeadSpriteId);
|
|
||||||
|
|
||||||
float commonness = disguisedGenderEnum == Gender.Female ? 0.05f : 0.2f;
|
|
||||||
disguisedHairs = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Hair, headSpriteId), WearableType.Hair, commonness);
|
|
||||||
disguisedBeards = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Beard, headSpriteId), WearableType.Beard);
|
|
||||||
disguisedMoustaches = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Moustache, headSpriteId), WearableType.Moustache);
|
|
||||||
disguisedFaceAttachments = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.FaceAttachment, headSpriteId), WearableType.FaceAttachment);
|
|
||||||
|
|
||||||
if (IsValidIndex(disguisedHairIndex, disguisedHairs))
|
|
||||||
{
|
|
||||||
disguisedHairElement = disguisedHairs[disguisedHairIndex];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
disguisedHairElement = GetRandomElement(disguisedHairs);
|
|
||||||
}
|
|
||||||
if (IsValidIndex(disguisedBeardIndex, disguisedBeards))
|
|
||||||
{
|
|
||||||
disguisedBeardElement = disguisedBeards[disguisedBeardIndex];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
disguisedBeardElement = GetRandomElement(disguisedBeards);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsValidIndex(disguisedMoustacheIndex, disguisedMoustaches))
|
|
||||||
{
|
|
||||||
disguisedMoustacheElement = disguisedMoustaches[disguisedMoustacheIndex];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
disguisedMoustacheElement = GetRandomElement(disguisedMoustaches);
|
|
||||||
}
|
|
||||||
if (IsValidIndex(disguisedFaceAttachmentIndex, disguisedFaceAttachments))
|
|
||||||
{
|
|
||||||
disguisedFaceAttachmentElement = disguisedFaceAttachments[disguisedFaceAttachmentIndex];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
disguisedFaceAttachmentElement = GetRandomElement(disguisedFaceAttachments);
|
|
||||||
}
|
|
||||||
|
|
||||||
idCard.StoredAttachments = new List<WearableSprite>();
|
|
||||||
|
|
||||||
disguisedFaceAttachmentElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.FaceAttachment)));
|
|
||||||
disguisedBeardElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Beard)));
|
|
||||||
disguisedMoustacheElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Moustache)));
|
|
||||||
disguisedHairElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Hair)));
|
|
||||||
|
|
||||||
if (OmitJobInPortraitClothing)
|
|
||||||
{
|
|
||||||
JobPrefab.NoJobElement?.Element("PortraitClothing")?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.JobIndicator)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
idCard.StoredJobPrefab?.ClothingElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.JobIndicator)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idCard.StoredJobPrefab != null)
|
if (idCard.StoredOwnerAppearance.JobPrefab != null)
|
||||||
{
|
{
|
||||||
disguisedJobIcon = idCard.StoredJobPrefab.Icon;
|
disguisedJobIcon = idCard.StoredOwnerAppearance.JobPrefab.Icon;
|
||||||
disguisedJobColor = idCard.StoredJobPrefab.UIColor;
|
disguisedJobColor = idCard.StoredOwnerAppearance.JobPrefab.UIColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
disguisedPortrait = idCard.StoredPortrait;
|
disguisedPortrait = idCard.StoredOwnerAppearance.Portrait;
|
||||||
disguisedSheetIndex = idCard.StoredSheetIndex;
|
disguisedSheetIndex = idCard.StoredOwnerAppearance.SheetIndex;
|
||||||
disguisedAttachmentSprites = idCard.StoredAttachments;
|
disguisedAttachmentSprites = idCard.StoredOwnerAppearance.Attachments;
|
||||||
|
|
||||||
|
disguisedHairColor = idCard.StoredOwnerAppearance.HairColor;
|
||||||
|
disguisedFacialHairColor = idCard.StoredOwnerAppearance.FacialHairColor;
|
||||||
|
disguisedSkinColor = idCard.StoredOwnerAppearance.SkinColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void LoadAttachmentSprites(bool omitJob)
|
partial void LoadAttachmentSprites(bool omitJob)
|
||||||
@@ -462,24 +309,35 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false, bool evaluateDisguise = false)
|
public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false, bool evaluateDisguise = false)
|
||||||
{
|
{
|
||||||
if (evaluateDisguise && IsDisguised) return;
|
if (evaluateDisguise && IsDisguised) { return; }
|
||||||
|
|
||||||
Vector2? sheetIndex;
|
Vector2? sheetIndex;
|
||||||
Sprite portraitToDraw;
|
Sprite portraitToDraw;
|
||||||
List<WearableSprite> attachmentsToDraw;
|
List<WearableSprite> attachmentsToDraw;
|
||||||
|
|
||||||
|
Color hairColor;
|
||||||
|
Color facialHairColor;
|
||||||
|
Color skinColor;
|
||||||
|
|
||||||
if (!IsDisguisedAsAnother || !evaluateDisguise)
|
if (!IsDisguisedAsAnother || !evaluateDisguise)
|
||||||
{
|
{
|
||||||
sheetIndex = Head.SheetIndex;
|
sheetIndex = Head.SheetIndex;
|
||||||
portraitToDraw = Portrait;
|
portraitToDraw = Portrait;
|
||||||
attachmentsToDraw = AttachmentSprites;
|
attachmentsToDraw = AttachmentSprites;
|
||||||
|
|
||||||
|
hairColor = Head.HairColor;
|
||||||
|
facialHairColor = Head.FacialHairColor;
|
||||||
|
skinColor = Head.SkinColor;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//TODO: disguise skin and hair colors
|
|
||||||
sheetIndex = disguisedSheetIndex;
|
sheetIndex = disguisedSheetIndex;
|
||||||
portraitToDraw = disguisedPortrait;
|
portraitToDraw = disguisedPortrait;
|
||||||
attachmentsToDraw = disguisedAttachmentSprites;
|
attachmentsToDraw = disguisedAttachmentSprites;
|
||||||
|
|
||||||
|
hairColor = disguisedHairColor;
|
||||||
|
facialHairColor = disguisedFacialHairColor;
|
||||||
|
skinColor = disguisedSkinColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (portraitToDraw != null)
|
if (portraitToDraw != null)
|
||||||
@@ -492,14 +350,14 @@ namespace Barotrauma
|
|||||||
SetHeadEffect(spriteBatch);
|
SetHeadEffect(spriteBatch);
|
||||||
portraitToDraw.SourceRect = new Rectangle(CalculateOffset(portraitToDraw, sheetIndex.Value.ToPoint()), portraitToDraw.SourceRect.Size);
|
portraitToDraw.SourceRect = new Rectangle(CalculateOffset(portraitToDraw, sheetIndex.Value.ToPoint()), portraitToDraw.SourceRect.Size);
|
||||||
}
|
}
|
||||||
portraitToDraw.Draw(spriteBatch, screenPos + offset, SkinColor, portraitToDraw.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
|
portraitToDraw.Draw(spriteBatch, screenPos + offset, skinColor, portraitToDraw.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
|
||||||
if (attachmentsToDraw != null)
|
if (attachmentsToDraw != null)
|
||||||
{
|
{
|
||||||
float depthStep = 0.000001f;
|
float depthStep = 0.000001f;
|
||||||
foreach (var attachment in attachmentsToDraw)
|
foreach (var attachment in attachmentsToDraw)
|
||||||
{
|
{
|
||||||
SetAttachmentEffect(spriteBatch, attachment);
|
SetAttachmentEffect(spriteBatch, attachment);
|
||||||
DrawAttachmentSprite(spriteBatch, attachment, portraitToDraw, sheetIndex, screenPos + offset, scale, depthStep, GetAttachmentColor(attachment), flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
|
DrawAttachmentSprite(spriteBatch, attachment, portraitToDraw, sheetIndex, screenPos + offset, scale, depthStep, GetAttachmentColor(attachment, hairColor, facialHairColor), flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
|
||||||
depthStep += depthStep;
|
depthStep += depthStep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -516,7 +374,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
headEffectParameters.Effect ??= GameMain.GameScreen.ThresholdTintEffect;
|
headEffectParameters.Effect ??= GameMain.GameScreen.ThresholdTintEffect;
|
||||||
headEffectParameters.Params ??= new Dictionary<string, object>();
|
headEffectParameters.Params ??= new Dictionary<string, object>();
|
||||||
headEffectParameters.Params["xBaseTexture"] = headSprite.Texture;
|
headEffectParameters.Params["xBaseTexture"] = HeadSprite.Texture;
|
||||||
headEffectParameters.Params["xTintMaskTexture"] = tintMask?.Texture ?? GUI.WhiteTexture;
|
headEffectParameters.Params["xTintMaskTexture"] = tintMask?.Texture ?? GUI.WhiteTexture;
|
||||||
headEffectParameters.Params["xCutoffTexture"] = GUI.WhiteTexture;
|
headEffectParameters.Params["xCutoffTexture"] = GUI.WhiteTexture;
|
||||||
headEffectParameters.Params["baseToCutoffSizeRatio"] = 1.0f;
|
headEffectParameters.Params["baseToCutoffSizeRatio"] = 1.0f;
|
||||||
@@ -541,15 +399,15 @@ namespace Barotrauma
|
|||||||
spriteBatch.SwapEffect(attachmentEffectParameters[attachment.Type]);
|
spriteBatch.SwapEffect(attachmentEffectParameters[attachment.Type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color GetAttachmentColor(WearableSprite attachment)
|
private Color GetAttachmentColor(WearableSprite attachment, Color hairColor, Color facialHairColor)
|
||||||
{
|
{
|
||||||
switch (attachment.Type)
|
switch (attachment.Type)
|
||||||
{
|
{
|
||||||
case WearableType.Hair:
|
case WearableType.Hair:
|
||||||
return HairColor;
|
return hairColor;
|
||||||
case WearableType.Beard:
|
case WearableType.Beard:
|
||||||
case WearableType.Moustache:
|
case WearableType.Moustache:
|
||||||
return FacialHairColor;
|
return facialHairColor;
|
||||||
default:
|
default:
|
||||||
return Color.White;
|
return Color.White;
|
||||||
}
|
}
|
||||||
@@ -574,7 +432,7 @@ namespace Barotrauma
|
|||||||
foreach (var attachment in AttachmentSprites)
|
foreach (var attachment in AttachmentSprites)
|
||||||
{
|
{
|
||||||
SetAttachmentEffect(spriteBatch, attachment);
|
SetAttachmentEffect(spriteBatch, attachment);
|
||||||
DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, depthStep, GetAttachmentColor(attachment));
|
DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, depthStep, GetAttachmentColor(attachment, HairColor, FacialHairColor));
|
||||||
depthStep += depthStep;
|
depthStep += depthStep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -718,6 +576,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private readonly GUIComponent parentComponent;
|
private readonly GUIComponent parentComponent;
|
||||||
private readonly List<Sprite> characterSprites = new List<Sprite>();
|
private readonly List<Sprite> characterSprites = new List<Sprite>();
|
||||||
|
public GUIButton RandomizeButton;
|
||||||
|
|
||||||
public AppearanceCustomizationMenu(CharacterInfo info, GUIComponent parent, bool hasIcon = true)
|
public AppearanceCustomizationMenu(CharacterInfo info, GUIComponent parent, bool hasIcon = true)
|
||||||
{
|
{
|
||||||
@@ -737,13 +596,12 @@ namespace Barotrauma
|
|||||||
ClearSprites();
|
ClearSprites();
|
||||||
|
|
||||||
float contentWidth = HasIcon ? 0.75f : 1.0f;
|
float contentWidth = HasIcon ? 0.75f : 1.0f;
|
||||||
var content =
|
var listBox = new GUIListBox(
|
||||||
new GUIListBox(
|
new RectTransform(new Vector2(contentWidth, 1.0f), parentComponent.RectTransform,
|
||||||
new RectTransform(new Vector2(contentWidth, 1.0f), parentComponent.RectTransform,
|
Anchor.CenterLeft))
|
||||||
Anchor.CenterLeft))
|
{ CanBeFocused = false, CanTakeKeyBoardFocus = false };
|
||||||
{ CanBeFocused = false, CanTakeKeyBoardFocus = false }
|
var content = listBox.Content;
|
||||||
.Content;
|
|
||||||
|
|
||||||
info.LoadHeadAttachments();
|
info.LoadHeadAttachments();
|
||||||
if (HasIcon)
|
if (HasIcon)
|
||||||
{
|
{
|
||||||
@@ -754,7 +612,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
RectTransform createItemRectTransform(string labelTag, float width = 0.6f)
|
RectTransform createItemRectTransform(string labelTag, float width = 0.6f)
|
||||||
{
|
{
|
||||||
var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), content.RectTransform));
|
var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.166f), content.RectTransform));
|
||||||
|
|
||||||
var label = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), layoutGroup.RectTransform),
|
var label = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), layoutGroup.RectTransform),
|
||||||
TextManager.Get(labelTag), font: GUI.SubHeadingFont);
|
TextManager.Get(labelTag), font: GUI.SubHeadingFont);
|
||||||
@@ -793,6 +651,7 @@ namespace Barotrauma
|
|||||||
info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race),
|
info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race),
|
||||||
wearableType, info.HeadSpriteId).Count();
|
wearableType, info.HeadSpriteId).Count();
|
||||||
|
|
||||||
|
List<GUIScrollBar> attachmentSliders = new List<GUIScrollBar>();
|
||||||
void createAttachmentSlider(int initialValue, WearableType wearableType)
|
void createAttachmentSlider(int initialValue, WearableType wearableType)
|
||||||
{
|
{
|
||||||
int attachmentCount = countAttachmentsOfType(wearableType);
|
int attachmentCount = countAttachmentsOfType(wearableType);
|
||||||
@@ -812,6 +671,7 @@ namespace Barotrauma
|
|||||||
BarSize = 1.0f / (float)(attachmentCount + 1)
|
BarSize = 1.0f / (float)(attachmentCount + 1)
|
||||||
};
|
};
|
||||||
slider.BarScrollValue = initialValue;
|
slider.BarScrollValue = initialValue;
|
||||||
|
attachmentSliders.Add(slider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -869,6 +729,36 @@ namespace Barotrauma
|
|||||||
CanBeFocused = false
|
CanBeFocused = false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var childToSelect = dropdown.ListBox.Content.FindChild(c => (Color)c.UserData == getter());
|
||||||
|
dropdown.Select(dropdown.ListBox.Content.GetChildIndex(childToSelect));
|
||||||
|
|
||||||
|
//The following exists to track mouseover to preview colors before selecting them
|
||||||
|
bool previewingColor = false;
|
||||||
|
new GUICustomComponent(new RectTransform(Vector2.One, buttonFrame.RectTransform),
|
||||||
|
onUpdate: (deltaTime, component) =>
|
||||||
|
{
|
||||||
|
if (GUI.MouseOn is GUIFrame { Parent: { } p } hoveredFrame && dropdown.ListBox.Content.IsParentOf(hoveredFrame))
|
||||||
|
{
|
||||||
|
previewingColor = true;
|
||||||
|
Color color = (Color)(dropdown.ListBox.Content.FindChild(c =>
|
||||||
|
c == hoveredFrame || c.IsParentOf(hoveredFrame))?.UserData ?? dropdown.SelectedData);
|
||||||
|
setter(color);
|
||||||
|
buttonFrame.Color = getter();
|
||||||
|
buttonFrame.HoverColor = getter();
|
||||||
|
}
|
||||||
|
else if (previewingColor)
|
||||||
|
{
|
||||||
|
setter((Color)dropdown.SelectedData);
|
||||||
|
buttonFrame.Color = getter();
|
||||||
|
buttonFrame.HoverColor = getter();
|
||||||
|
previewingColor = false;
|
||||||
|
}
|
||||||
|
}, onDraw: null)
|
||||||
|
{
|
||||||
|
CanBeFocused = false,
|
||||||
|
Visible = true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (countAttachmentsOfType(WearableType.Hair) > 0)
|
if (countAttachmentsOfType(WearableType.Hair) > 0)
|
||||||
@@ -886,28 +776,54 @@ namespace Barotrauma
|
|||||||
|
|
||||||
createColorSelector($"Customization.{nameof(info.SkinColor)}", info.SkinColors, () => info.SkinColor,
|
createColorSelector($"Customization.{nameof(info.SkinColor)}", info.SkinColors, () => info.SkinColor,
|
||||||
(color) => info.SkinColor = color);
|
(color) => info.SkinColor = color);
|
||||||
|
|
||||||
|
RandomizeButton = new GUIButton(new RectTransform(Vector2.One * 0.12f,
|
||||||
|
parentComponent.RectTransform,
|
||||||
|
anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest)
|
||||||
|
{ RelativeOffset = new Vector2(0.01f, 0.005f) }, style: "RandomizeButton")
|
||||||
|
{
|
||||||
|
OnClicked = (button, o) =>
|
||||||
|
{
|
||||||
|
var headPreset = info.Heads.Keys.GetRandom(Rand.RandSync.Unsynced);
|
||||||
|
info.Head.gender = headPreset.Gender;
|
||||||
|
info.Head.race = headPreset.Race;
|
||||||
|
info.Head.HeadSpriteId = headPreset.ID;
|
||||||
|
|
||||||
|
info.Head.HairIndex = Rand.Int(countAttachmentsOfType(WearableType.Hair), Rand.RandSync.Unsynced);
|
||||||
|
info.Head.BeardIndex = Rand.Int(countAttachmentsOfType(WearableType.Beard), Rand.RandSync.Unsynced);
|
||||||
|
info.Head.MoustacheIndex = Rand.Int(countAttachmentsOfType(WearableType.Moustache), Rand.RandSync.Unsynced);
|
||||||
|
info.Head.FaceAttachmentIndex = Rand.Int(countAttachmentsOfType(WearableType.FaceAttachment), Rand.RandSync.Unsynced);
|
||||||
|
|
||||||
|
info.Head.HairColor = info.HairColors.GetRandom(Rand.RandSync.Unsynced);
|
||||||
|
info.Head.FacialHairColor = info.FacialHairColors.GetRandom(Rand.RandSync.Unsynced);
|
||||||
|
info.Head.SkinColor = info.SkinColors.GetRandom(Rand.RandSync.Unsynced);
|
||||||
|
|
||||||
|
RecreateFrameContents();
|
||||||
|
info.RefreshHead();
|
||||||
|
OnHeadSwitch?.Invoke(this);
|
||||||
|
attachmentSliders.ForEach(s => OnSliderMoved?.Invoke(s, s.BarScroll));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//force update twice because the listbox is insanely janky
|
||||||
|
//TODO: fix all of the UI :)
|
||||||
|
listBox.ForceUpdate();
|
||||||
|
listBox.ForceUpdate();
|
||||||
|
foreach (var childLayoutGroup in listBox.Content.GetAllChildren<GUILayoutGroup>())
|
||||||
|
{
|
||||||
|
childLayoutGroup.Recalculate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool OpenHeadSelection(GUIButton button, object userData)
|
private bool OpenHeadSelection(GUIButton button, object userData)
|
||||||
{
|
{
|
||||||
Gender selectedGender = (Gender)userData;
|
Gender selectedGender = (Gender)userData;
|
||||||
if (HeadSelectionList != null)
|
|
||||||
{
|
|
||||||
HeadSelectionList.Visible = true;
|
|
||||||
foreach (GUIComponent child in HeadSelectionList.Content.Children)
|
|
||||||
{
|
|
||||||
child.Visible = (Gender)child.UserData == selectedGender;
|
|
||||||
child.Children.ForEach(c =>
|
|
||||||
c.Visible = ((Tuple<Gender, Race, int>)c.UserData).Item1 == selectedGender);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var info = CharacterInfo;
|
var info = CharacterInfo;
|
||||||
|
|
||||||
float characterHeightWidthRatio = info.HeadSprite.size.Y / info.HeadSprite.size.X;
|
float characterHeightWidthRatio = info.HeadSprite.size.Y / info.HeadSprite.size.X;
|
||||||
HeadSelectionList = new GUIListBox(
|
HeadSelectionList ??= new GUIListBox(
|
||||||
new RectTransform(
|
new RectTransform(
|
||||||
new Point(parentComponent.Rect.Width,
|
new Point(parentComponent.Rect.Width,
|
||||||
(int)(parentComponent.Rect.Width * characterHeightWidthRatio * 0.6f)), GUI.Canvas)
|
(int)(parentComponent.Rect.Width * characterHeightWidthRatio * 0.6f)), GUI.Canvas)
|
||||||
@@ -915,6 +831,9 @@ namespace Barotrauma
|
|||||||
AbsoluteOffset = new Point(parentComponent.Rect.Right - parentComponent.Rect.Width,
|
AbsoluteOffset = new Point(parentComponent.Rect.Right - parentComponent.Rect.Width,
|
||||||
button.Rect.Bottom)
|
button.Rect.Bottom)
|
||||||
});
|
});
|
||||||
|
HeadSelectionList.Visible = true;
|
||||||
|
HeadSelectionList.Content.ClearChildren();
|
||||||
|
ClearSprites();
|
||||||
|
|
||||||
parentComponent.RectTransform.SizeChanged += () =>
|
parentComponent.RectTransform.SizeChanged += () =>
|
||||||
{
|
{
|
||||||
@@ -952,15 +871,14 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
row = null;
|
row = null;
|
||||||
itemsInRow = 0;
|
itemsInRow = 0;
|
||||||
foreach (var head in heads)
|
foreach (var kvp in heads.Where(kv => kv.Key.Gender == selectedGender))
|
||||||
{
|
{
|
||||||
var headPreset = head.Key;
|
var headPreset = kvp.Key;
|
||||||
Gender gender = headPreset.Gender;
|
|
||||||
Race race = headPreset.Race;
|
Race race = headPreset.Race;
|
||||||
int headIndex = headPreset.ID;
|
int headIndex = headPreset.ID;
|
||||||
|
|
||||||
string spritePath = spritePathWithTags
|
string spritePath = spritePathWithTags
|
||||||
.Replace("[GENDER]", gender.ToString().ToLowerInvariant())
|
.Replace("[GENDER]", selectedGender.ToString().ToLowerInvariant())
|
||||||
.Replace("[RACE]", race.ToString().ToLowerInvariant());
|
.Replace("[RACE]", race.ToString().ToLowerInvariant());
|
||||||
|
|
||||||
if (!File.Exists(spritePath))
|
if (!File.Exists(spritePath))
|
||||||
@@ -970,18 +888,18 @@ namespace Barotrauma
|
|||||||
|
|
||||||
Sprite headSprite = new Sprite(headSpriteElement, "", spritePath);
|
Sprite headSprite = new Sprite(headSpriteElement, "", spritePath);
|
||||||
headSprite.SourceRect =
|
headSprite.SourceRect =
|
||||||
new Rectangle(CharacterInfo.CalculateOffset(headSprite, head.Value.ToPoint()),
|
new Rectangle(CalculateOffset(headSprite, kvp.Value.ToPoint()),
|
||||||
headSprite.SourceRect.Size);
|
headSprite.SourceRect.Size);
|
||||||
characterSprites.Add(headSprite);
|
characterSprites.Add(headSprite);
|
||||||
|
|
||||||
if (itemsInRow >= 4 || row == null || gender != (Gender)row.UserData)
|
if (itemsInRow >= 4 || row == null)
|
||||||
{
|
{
|
||||||
row = new GUILayoutGroup(
|
row = new GUILayoutGroup(
|
||||||
new RectTransform(new Vector2(1.0f, 0.333f), HeadSelectionList.Content.RectTransform),
|
new RectTransform(new Vector2(1.0f, 0.333f), HeadSelectionList.Content.RectTransform),
|
||||||
true)
|
true)
|
||||||
{
|
{
|
||||||
UserData = gender,
|
UserData = selectedGender,
|
||||||
Visible = gender == selectedGender
|
Visible = true
|
||||||
};
|
};
|
||||||
itemsInRow = 0;
|
itemsInRow = 0;
|
||||||
}
|
}
|
||||||
@@ -991,10 +909,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
OutlineColor = Color.White * 0.5f,
|
OutlineColor = Color.White * 0.5f,
|
||||||
PressedColor = Color.White * 0.5f,
|
PressedColor = Color.White * 0.5f,
|
||||||
UserData = new Tuple<Gender, Race, int>(gender, race, headIndex),
|
UserData = new Tuple<Gender, Race, int>(selectedGender, race, headIndex),
|
||||||
OnClicked = SwitchHead,
|
OnClicked = SwitchHead,
|
||||||
Selected = gender == info.Gender && race == info.Race && headIndex == info.HeadSpriteId,
|
Selected = selectedGender == info.Gender && race == info.Race && headIndex == info.HeadSpriteId,
|
||||||
Visible = gender == selectedGender
|
Visible = true
|
||||||
};
|
};
|
||||||
|
|
||||||
new GUIImage(new RectTransform(Vector2.One, btn.RectTransform), headSprite, scaleToFit: true);
|
new GUIImage(new RectTransform(Vector2.One, btn.RectTransform), headSprite, scaleToFit: true);
|
||||||
@@ -1013,7 +931,7 @@ namespace Barotrauma
|
|||||||
int id = ((Tuple<Gender, Race, int>)obj).Item3;
|
int id = ((Tuple<Gender, Race, int>)obj).Item3;
|
||||||
info.Gender = gender;
|
info.Gender = gender;
|
||||||
info.Race = race;
|
info.Race = race;
|
||||||
info.HeadSpriteId = id;
|
info.Head.HeadSpriteId = id;
|
||||||
RecreateFrameContents();
|
RecreateFrameContents();
|
||||||
OnHeadSwitch?.Invoke(this);
|
OnHeadSwitch?.Invoke(this);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -66,8 +66,6 @@ namespace Barotrauma
|
|||||||
private SpriteSheet medUIExtra;
|
private SpriteSheet medUIExtra;
|
||||||
private float medUIExtraAnimState;
|
private float medUIExtraAnimState;
|
||||||
|
|
||||||
private GUIComponent draggingMed;
|
|
||||||
|
|
||||||
private int highlightedLimbIndex = -1;
|
private int highlightedLimbIndex = -1;
|
||||||
private int selectedLimbIndex = -1;
|
private int selectedLimbIndex = -1;
|
||||||
private LimbHealth currentDisplayedLimb;
|
private LimbHealth currentDisplayedLimb;
|
||||||
@@ -118,7 +116,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (prevOpenHealthWindow != null)
|
if (prevOpenHealthWindow != null)
|
||||||
{
|
{
|
||||||
prevOpenHealthWindow.selectedLimbIndex = -1;
|
|
||||||
prevOpenHealthWindow.highlightedLimbIndex = -1;
|
prevOpenHealthWindow.highlightedLimbIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +204,7 @@ namespace Barotrauma
|
|||||||
new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform, Anchor.CenterLeft),
|
new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform, Anchor.CenterLeft),
|
||||||
onDraw: (spriteBatch, component) =>
|
onDraw: (spriteBatch, component) =>
|
||||||
{
|
{
|
||||||
character.Info?.DrawPortrait(spriteBatch, new Vector2(component.Rect.X, component.Rect.Center.Y - component.Rect.Width / 2), Vector2.Zero, component.Rect.Width, false, openHealthWindow?.Character != Character.Controlled);
|
character.Info?.DrawPortrait(spriteBatch, new Vector2(component.Rect.X, component.Rect.Center.Y - component.Rect.Width / 2), Vector2.Zero, component.Rect.Width, false, character != Character.Controlled);
|
||||||
});
|
});
|
||||||
characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont)
|
characterName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), nameContainer.RectTransform), "", textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont)
|
||||||
{
|
{
|
||||||
@@ -216,7 +213,7 @@ namespace Barotrauma
|
|||||||
new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform),
|
new GUICustomComponent(new RectTransform(new Vector2(0.2f, 1.0f), nameContainer.RectTransform),
|
||||||
onDraw: (spriteBatch, component) =>
|
onDraw: (spriteBatch, component) =>
|
||||||
{
|
{
|
||||||
character.Info?.DrawJobIcon(spriteBatch, component.Rect, openHealthWindow?.Character != Character.Controlled);
|
character.Info?.DrawJobIcon(spriteBatch, component.Rect, character != Character.Controlled);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -275,6 +272,34 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
cprButton = new GUIButton(new RectTransform(new Vector2(0.17f, 0.17f), characterIndicatorArea.RectTransform, Anchor.BottomLeft, scaleBasis: ScaleBasis.Smallest), text: "", style: "CPRButton")
|
||||||
|
{
|
||||||
|
OnClicked = (button, userData) =>
|
||||||
|
{
|
||||||
|
Character selectedCharacter = Character.Controlled?.SelectedCharacter;
|
||||||
|
if (selectedCharacter == null || (!selectedCharacter.IsUnconscious && selectedCharacter.Stun <= 0.0f))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Character.Controlled.AnimController.Anim = (Character.Controlled.AnimController.Anim == AnimController.Animation.CPR) ?
|
||||||
|
AnimController.Animation.None : AnimController.Animation.CPR;
|
||||||
|
|
||||||
|
selectedCharacter.AnimController.ResetPullJoints();
|
||||||
|
|
||||||
|
if (GameMain.Client != null)
|
||||||
|
{
|
||||||
|
GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Treatment });
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
ToolTip = TextManager.Get("doctor.cprobjective"),
|
||||||
|
IgnoreLayoutGroups = true,
|
||||||
|
Visible = false
|
||||||
|
};
|
||||||
|
|
||||||
var limbSelection = new GUICustomComponent(new RectTransform(new Vector2(0.4f, 1.0f), characterIndicatorArea.RectTransform),
|
var limbSelection = new GUICustomComponent(new RectTransform(new Vector2(0.4f, 1.0f), characterIndicatorArea.RectTransform),
|
||||||
(spriteBatch, component) =>
|
(spriteBatch, component) =>
|
||||||
{
|
{
|
||||||
@@ -300,7 +325,7 @@ namespace Barotrauma
|
|||||||
deadIndicator.AutoScaleHorizontal = true;
|
deadIndicator.AutoScaleHorizontal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
afflictionIconContainer = new GUIListBox(new RectTransform(new Vector2(0.25f, 0.7f), characterIndicatorArea.RectTransform), style: null);
|
afflictionIconContainer = new GUIListBox(new RectTransform(new Vector2(0.25f, 1.0f), characterIndicatorArea.RectTransform), style: null);
|
||||||
|
|
||||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), healthWindowVerticalLayout.RectTransform),
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), healthWindowVerticalLayout.RectTransform),
|
||||||
TextManager.Get("SuitableTreatments"), font: GUI.SubHeadingFont, textAlignment: Alignment.BottomCenter);
|
TextManager.Get("SuitableTreatments"), font: GUI.SubHeadingFont, textAlignment: Alignment.BottomCenter);
|
||||||
@@ -324,33 +349,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
characterIndicatorArea.Recalculate();
|
characterIndicatorArea.Recalculate();
|
||||||
|
|
||||||
cprButton = new GUIButton(new RectTransform(new Vector2(afflictionIconContainer.RectTransform.RelativeSize.X, 0.3f), characterIndicatorArea.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest), text: "", style: "CPRButton")
|
|
||||||
{
|
|
||||||
OnClicked = (button, userData) =>
|
|
||||||
{
|
|
||||||
Character selectedCharacter = Character.Controlled?.SelectedCharacter;
|
|
||||||
if (selectedCharacter == null || (!selectedCharacter.IsUnconscious && selectedCharacter.Stun <= 0.0f))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Character.Controlled.AnimController.Anim = (Character.Controlled.AnimController.Anim == AnimController.Animation.CPR) ?
|
|
||||||
AnimController.Animation.None : AnimController.Animation.CPR;
|
|
||||||
|
|
||||||
selectedCharacter.AnimController.ResetPullJoints();
|
|
||||||
|
|
||||||
if (GameMain.Client != null)
|
|
||||||
{
|
|
||||||
GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Treatment });
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
ToolTip = TextManager.Get("doctor.cprobjective"),
|
|
||||||
IgnoreLayoutGroups = true,
|
|
||||||
Visible = false
|
|
||||||
};
|
|
||||||
|
|
||||||
healthBarHolder = new GUIFrame(new RectTransform(Point.Zero, GUI.Canvas), style: null)
|
healthBarHolder = new GUIFrame(new RectTransform(Point.Zero, GUI.Canvas), style: null)
|
||||||
{
|
{
|
||||||
HoverCursor = CursorState.Hand
|
HoverCursor = CursorState.Hand
|
||||||
@@ -721,20 +719,19 @@ namespace Barotrauma
|
|||||||
foreach (GUIComponent afflictionIcon in afflictionIconContainer.Content.Children)
|
foreach (GUIComponent afflictionIcon in afflictionIconContainer.Content.Children)
|
||||||
{
|
{
|
||||||
if (!(afflictionIcon.UserData is Affliction affliction)) { continue; }
|
if (!(afflictionIcon.UserData is Affliction affliction)) { continue; }
|
||||||
var btn = afflictionIcon.GetChild<GUIButton>();
|
if (affliction.AppliedAsFailedTreatmentTime > Timing.TotalTime - 1.0 && afflictionIcon.FlashTimer <= 0.0f)
|
||||||
if (affliction.AppliedAsFailedTreatmentTime > Timing.TotalTime - 1.0 && btn.FlashTimer <= 0.0f)
|
|
||||||
{
|
{
|
||||||
btn.Flash(GUI.Style.Red);
|
afflictionIcon.Flash(GUI.Style.Red);
|
||||||
}
|
}
|
||||||
else if (affliction.AppliedAsSuccessfulTreatmentTime > Timing.TotalTime - 1.0 && btn.FlashTimer <= 0.0f)
|
else if (affliction.AppliedAsSuccessfulTreatmentTime > Timing.TotalTime - 1.0 && afflictionIcon.FlashTimer <= 0.0f)
|
||||||
{
|
{
|
||||||
btn.Flash(GUI.Style.Green);
|
afflictionIcon.Flash(GUI.Style.Green);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GUI.MouseOn != null && GUI.MouseOn.UserData is string str && str == "selectaffliction")
|
if (GUI.MouseOn?.UserData is Affliction)
|
||||||
{
|
{
|
||||||
Affliction affliction = GUI.MouseOn.Parent.UserData as Affliction;
|
Affliction affliction = GUI.MouseOn?.UserData as Affliction;
|
||||||
|
|
||||||
if (afflictionTooltip == null || afflictionTooltip.UserData != affliction)
|
if (afflictionTooltip == null || afflictionTooltip.UserData != affliction)
|
||||||
{
|
{
|
||||||
@@ -802,6 +799,8 @@ namespace Barotrauma
|
|||||||
currentDisplayedLimb = selectedLimb;
|
currentDisplayedLimb = selectedLimb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateAfflictionInfos(displayedAfflictions.Select(d => d.affliction));
|
||||||
|
|
||||||
foreach (GUIComponent component in recommendedTreatmentContainer.Content.Children)
|
foreach (GUIComponent component in recommendedTreatmentContainer.Content.Children)
|
||||||
{
|
{
|
||||||
var treatmentButton = component.GetChild<GUIButton>();
|
var treatmentButton = component.GetChild<GUIButton>();
|
||||||
@@ -857,15 +856,6 @@ namespace Barotrauma
|
|||||||
selectedLimbIndex = highlightedLimbIndex;
|
selectedLimbIndex = highlightedLimbIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (draggingMed != null)
|
|
||||||
{
|
|
||||||
if (!PlayerInput.PrimaryMouseButtonHeld())
|
|
||||||
{
|
|
||||||
OnItemDropped(draggingMed.UserData as Item, ignoreMousePos: false);
|
|
||||||
draggingMed = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1157,8 +1147,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
CreateRecommendedTreatments();
|
CreateRecommendedTreatments();
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateAfflictionInfos(displayedAfflictions.Select(d => d.affliction));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateAfflictionInfos(IEnumerable<Affliction> afflictions)
|
private void CreateAfflictionInfos(IEnumerable<Affliction> afflictions)
|
||||||
@@ -1173,28 +1161,40 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
displayedAfflictions.Add((affliction, affliction.Strength));
|
displayedAfflictions.Add((affliction, affliction.Strength));
|
||||||
|
|
||||||
var child = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), afflictionIconContainer.Content.RectTransform, Anchor.TopCenter))
|
var frame = new GUIButton(new RectTransform(new Vector2(1.0f, 0.25f), afflictionIconContainer.Content.RectTransform), style: "ListBoxElement")
|
||||||
{
|
{
|
||||||
Stretch = true,
|
UserData = affliction,
|
||||||
UserData = affliction
|
|
||||||
};
|
|
||||||
|
|
||||||
var button = new GUIButton(new RectTransform(new Vector2(1.0f, 0.9f), child.RectTransform), style: null)
|
|
||||||
{
|
|
||||||
Color = Color.Gray.Multiply(0.1f).Opaque(),
|
|
||||||
HoverColor = Color.Gray.Multiply(0.4f).Opaque(),
|
|
||||||
SelectedColor = Color.Gray.Multiply(0.25f).Opaque(),
|
|
||||||
PressedColor = Color.Gray.Multiply(0.2f).Opaque(),
|
|
||||||
UserData = "selectaffliction",
|
|
||||||
OnClicked = SelectAffliction
|
OnClicked = SelectAffliction
|
||||||
};
|
};
|
||||||
|
|
||||||
|
new GUIFrame(new RectTransform(Vector2.One, frame.RectTransform), style: "GUIFrameListBox") { CanBeFocused = false };
|
||||||
|
|
||||||
|
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), frame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||||
|
{
|
||||||
|
Stretch = true,
|
||||||
|
CanBeFocused = false
|
||||||
|
};
|
||||||
|
|
||||||
|
var progressbarBg = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.18f), content.RectTransform), 0.0f, GUI.Style.Green, style: "GUIAfflictionBar")
|
||||||
|
{
|
||||||
|
UserData = "afflictionstrengthprediction",
|
||||||
|
CanBeFocused = false
|
||||||
|
};
|
||||||
|
new GUIProgressBar(new RectTransform(Vector2.One, progressbarBg.RectTransform), 0.0f, Color.Transparent, showFrame: false, style: "GUIAfflictionBar")
|
||||||
|
{
|
||||||
|
UserData = "afflictionstrength",
|
||||||
|
CanBeFocused = false
|
||||||
|
};
|
||||||
|
|
||||||
|
//spacing
|
||||||
|
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), content.RectTransform), style: null) { CanBeFocused = false };
|
||||||
|
|
||||||
if (affliction == mostSevereAffliction)
|
if (affliction == mostSevereAffliction)
|
||||||
{
|
{
|
||||||
buttonToSelect = button;
|
buttonToSelect = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
var afflictionIcon = new GUIImage(new RectTransform(Vector2.One * 0.8f, button.RectTransform, Anchor.Center), affliction.Prefab.Icon, scaleToFit: true)
|
var afflictionIcon = new GUIImage(new RectTransform(Vector2.One * 0.8f, content.RectTransform), affliction.Prefab.Icon, scaleToFit: true)
|
||||||
{
|
{
|
||||||
Color = GetAfflictionIconColor(affliction),
|
Color = GetAfflictionIconColor(affliction),
|
||||||
CanBeFocused = false
|
CanBeFocused = false
|
||||||
@@ -1203,32 +1203,22 @@ namespace Barotrauma
|
|||||||
afflictionIcon.HoverColor = Color.Lerp(afflictionIcon.Color, Color.White, 0.6f);
|
afflictionIcon.HoverColor = Color.Lerp(afflictionIcon.Color, Color.White, 0.6f);
|
||||||
afflictionIcon.SelectedColor = Color.Lerp(afflictionIcon.Color, Color.White, 0.5f);
|
afflictionIcon.SelectedColor = Color.Lerp(afflictionIcon.Color, Color.White, 0.5f);
|
||||||
|
|
||||||
float afflictionVitalityDecrease = affliction.GetVitalityDecrease(this);
|
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.1f, 0.0f), content.RectTransform),
|
||||||
|
affliction.Prefab.Name, font: GUI.SmallFont, textAlignment: Alignment.BottomCenter)
|
||||||
Color afflictionEffectColor = Color.White;
|
|
||||||
if (afflictionVitalityDecrease > 0.0f)
|
|
||||||
{
|
{
|
||||||
afflictionEffectColor = GUI.Style.Red;
|
CanBeFocused = false
|
||||||
}
|
};
|
||||||
else if (afflictionVitalityDecrease < 0.0f)
|
|
||||||
{
|
|
||||||
afflictionEffectColor = GUI.Style.Green;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), child.RectTransform),
|
|
||||||
affliction.Prefab.Name, font: GUI.SmallFont, textAlignment: Alignment.Center, style: "GUIToolTip");
|
|
||||||
nameText.Text = ToolBox.LimitString(nameText.Text, nameText.Font, nameText.Rect.Width);
|
nameText.Text = ToolBox.LimitString(nameText.Text, nameText.Font, nameText.Rect.Width);
|
||||||
nameText.RectTransform.MinSize = new Point(0, (int)(nameText.TextSize.Y * 1.25f));
|
nameText.RectTransform.MinSize = new Point(0, (int)(nameText.TextSize.Y));
|
||||||
|
nameText.RectTransform.SizeChanged += () =>
|
||||||
new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.15f), child.RectTransform), 0.0f, afflictionEffectColor, style: "GUIAfflictionBar")
|
|
||||||
{
|
{
|
||||||
UserData = "afflictionstrength"
|
nameText.Text = ToolBox.LimitString(nameText.Text, nameText.Font, nameText.Rect.Width);
|
||||||
};
|
};
|
||||||
|
|
||||||
child.Recalculate();
|
content.Recalculate();
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonToSelect?.OnClicked(buttonToSelect, "selectaffliction");
|
buttonToSelect?.OnClicked(buttonToSelect, buttonToSelect.UserData);
|
||||||
afflictionIconContainer.RecalculateChildren();
|
afflictionIconContainer.RecalculateChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1448,11 +1438,52 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void UpdateAfflictionInfos(IEnumerable<Affliction> afflictions)
|
private void UpdateAfflictionInfos(IEnumerable<Affliction> afflictions)
|
||||||
{
|
{
|
||||||
|
var potentialTreatment = Inventory.DraggingItems.FirstOrDefault();
|
||||||
|
if (potentialTreatment == null && GUI.MouseOn?.UserData is ItemPrefab itemPrefab)
|
||||||
|
{
|
||||||
|
potentialTreatment = Character.Controlled.Inventory.AllItems.FirstOrDefault(it => it.prefab == itemPrefab);
|
||||||
|
}
|
||||||
|
potentialTreatment ??= Inventory.SelectedSlot?.Item;
|
||||||
|
|
||||||
foreach (Affliction affliction in afflictions)
|
foreach (Affliction affliction in afflictions)
|
||||||
{
|
{
|
||||||
|
float afflictionVitalityDecrease = affliction.GetVitalityDecrease(this);
|
||||||
|
Color afflictionEffectColor = Color.White;
|
||||||
|
if (afflictionVitalityDecrease > 0.0f)
|
||||||
|
{
|
||||||
|
afflictionEffectColor = GUI.Style.Red;
|
||||||
|
}
|
||||||
|
else if (afflictionVitalityDecrease < 0.0f)
|
||||||
|
{
|
||||||
|
afflictionEffectColor = GUI.Style.Green;
|
||||||
|
}
|
||||||
|
|
||||||
var child = afflictionIconContainer.Content.FindChild(affliction);
|
var child = afflictionIconContainer.Content.FindChild(affliction);
|
||||||
var afflictionStrengthBar = child.GetChildByUserData("afflictionstrength") as GUIProgressBar;
|
|
||||||
|
var afflictionStrengthPredictionBar = child.GetChild<GUILayoutGroup>().GetChildByUserData("afflictionstrengthprediction") as GUIProgressBar;
|
||||||
|
afflictionStrengthPredictionBar.BarSize = 0.0f;
|
||||||
|
var afflictionStrengthBar = afflictionStrengthPredictionBar.GetChildByUserData("afflictionstrength") as GUIProgressBar;
|
||||||
afflictionStrengthBar.BarSize = affliction.Strength / affliction.Prefab.MaxStrength;
|
afflictionStrengthBar.BarSize = affliction.Strength / affliction.Prefab.MaxStrength;
|
||||||
|
afflictionStrengthBar.Color = afflictionEffectColor;
|
||||||
|
|
||||||
|
float afflictionStrengthPrediction = GetAfflictionStrengthPrediction(potentialTreatment, affliction);
|
||||||
|
if (!MathUtils.NearlyEqual(afflictionStrengthPrediction, affliction.Strength))
|
||||||
|
{
|
||||||
|
float t = (float)Math.Max(0.5f, (Math.Sin(Timing.TotalTime * 5) + 1.0f) / 2.0f);
|
||||||
|
if (afflictionStrengthPrediction < affliction.Strength)
|
||||||
|
{
|
||||||
|
afflictionStrengthBar.Color = afflictionEffectColor;
|
||||||
|
afflictionStrengthPredictionBar.Color = GUI.Style.Blue * t;
|
||||||
|
afflictionStrengthPredictionBar.BarSize = afflictionStrengthBar.BarSize;
|
||||||
|
afflictionStrengthBar.BarSize = afflictionStrengthPrediction / affliction.Prefab.MaxStrength;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
afflictionStrengthPredictionBar.Color = Color.Red * t;
|
||||||
|
afflictionStrengthPredictionBar.BarSize = afflictionStrengthPrediction / affliction.Prefab.MaxStrength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (afflictionTooltip != null && afflictionTooltip.UserData == affliction)
|
if (afflictionTooltip != null && afflictionTooltip.UserData == affliction)
|
||||||
{
|
{
|
||||||
UpdateAfflictionInfo(afflictionTooltip.Content, affliction);
|
UpdateAfflictionInfo(afflictionTooltip.Content, affliction);
|
||||||
@@ -1460,6 +1491,32 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float GetAfflictionStrengthPrediction(Item item, Affliction affliction)
|
||||||
|
{
|
||||||
|
float strength = affliction.Strength;
|
||||||
|
if (item == null) { return strength; }
|
||||||
|
|
||||||
|
foreach (ItemComponent ic in item.Components)
|
||||||
|
{
|
||||||
|
if (ic.statusEffectLists == null) { continue; }
|
||||||
|
if (!ic.statusEffectLists.TryGetValue(ActionType.OnUse, out List<StatusEffect> statusEffects)) { continue; }
|
||||||
|
foreach (StatusEffect effect in statusEffects)
|
||||||
|
{
|
||||||
|
foreach (var reduceAffliction in effect.ReduceAffliction)
|
||||||
|
{
|
||||||
|
if (reduceAffliction.affliction != affliction.Identifier && reduceAffliction.affliction != affliction.Prefab.AfflictionType) { continue; }
|
||||||
|
strength -= reduceAffliction.amount * (effect.Duration > 0 ? effect.Duration : 1.0f);
|
||||||
|
}
|
||||||
|
foreach (var addAffliction in effect.Afflictions)
|
||||||
|
{
|
||||||
|
if (addAffliction.Prefab != affliction.Prefab) { continue; }
|
||||||
|
strength += addAffliction.Strength * (effect.Duration > 0 ? effect.Duration : 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strength;
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateAfflictionInfo(GUIComponent parent, Affliction affliction)
|
private void UpdateAfflictionInfo(GUIComponent parent, Affliction affliction)
|
||||||
{
|
{
|
||||||
var labelContainer = parent.GetChildByUserData("label");
|
var labelContainer = parent.GetChildByUserData("label");
|
||||||
@@ -1722,13 +1779,6 @@ namespace Barotrauma
|
|||||||
Color.LightGray * 0.5f, width: 4);
|
Color.LightGray * 0.5f, width: 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (draggingMed != null)
|
|
||||||
{
|
|
||||||
GUIImage itemImage = draggingMed.GetChild<GUIImage>();
|
|
||||||
float scale = Math.Min(40.0f / itemImage.Sprite.size.X, 40.0f / itemImage.Sprite.size.Y);
|
|
||||||
itemImage.Sprite.Draw(spriteBatch, PlayerInput.MousePosition, itemImage.Color, 0, scale);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawLimbAfflictionIcon(SpriteBatch spriteBatch, Affliction affliction, float iconScale, ref Vector2 iconPos)
|
private void DrawLimbAfflictionIcon(SpriteBatch spriteBatch, Affliction affliction, float iconScale, ref Vector2 iconPos)
|
||||||
|
|||||||
@@ -679,6 +679,14 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
clr = clr.Multiply(character.Info.SkinColor);
|
clr = clr.Multiply(character.Info.SkinColor);
|
||||||
}
|
}
|
||||||
|
if (character.CharacterHealth.FaceTint.A > 0 && type == LimbType.Head)
|
||||||
|
{
|
||||||
|
clr = Color.Lerp(clr, character.CharacterHealth.FaceTint.Opaque(), character.CharacterHealth.FaceTint.A / 255.0f);
|
||||||
|
}
|
||||||
|
if (character.CharacterHealth.BodyTint.A > 0)
|
||||||
|
{
|
||||||
|
clr = Color.Lerp(clr, character.CharacterHealth.BodyTint.Opaque(), character.CharacterHealth.BodyTint.A / 255.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Color color = new Color((byte)(clr.R * brightness), (byte)(clr.G * brightness), (byte)(clr.B * brightness), clr.A);
|
Color color = new Color((byte)(clr.R * brightness), (byte)(clr.G * brightness), (byte)(clr.B * brightness), clr.A);
|
||||||
Color blankColor = new Color(brightness, brightness, brightness, 1);
|
Color blankColor = new Color(brightness, brightness, brightness, 1);
|
||||||
@@ -720,6 +728,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.UpdateDrawPosition();
|
body.UpdateDrawPosition();
|
||||||
|
float depthStep = 0.000001f;
|
||||||
|
|
||||||
if (!hideLimb)
|
if (!hideLimb)
|
||||||
{
|
{
|
||||||
@@ -794,7 +803,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
body.Draw(spriteBatch, conditionalSprite.Sprite, color, null, Scale * TextureScale, Params.MirrorHorizontally, Params.MirrorVertically);
|
body.Draw(spriteBatch, conditionalSprite.Sprite, color, depth: activeSprite.Depth - (depthStep * 50), Scale * TextureScale, Params.MirrorHorizontally, Params.MirrorVertically);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -809,7 +818,7 @@ namespace Barotrauma
|
|||||||
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
|
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
|
||||||
color * Math.Min(damageOverlayStrength, 1.0f), activeSprite.Origin,
|
color * Math.Min(damageOverlayStrength, 1.0f), activeSprite.Origin,
|
||||||
-body.DrawRotation,
|
-body.DrawRotation,
|
||||||
Scale, spriteEffect, activeSprite.Depth - 0.0000015f);
|
Scale, spriteEffect, activeSprite.Depth - (depthStep * 90));
|
||||||
}
|
}
|
||||||
foreach (var decorativeSprite in DecorativeSprites)
|
foreach (var decorativeSprite in DecorativeSprites)
|
||||||
{
|
{
|
||||||
@@ -827,9 +836,8 @@ namespace Barotrauma
|
|||||||
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
|
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)), c,
|
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), c,
|
||||||
-body.Rotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffect,
|
-body.Rotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffect,
|
||||||
depth: decorativeSprite.Sprite.Depth);
|
depth: activeSprite.Depth - (depthStep * 100));
|
||||||
}
|
}
|
||||||
float depthStep = 0.000001f;
|
|
||||||
float step = depthStep;
|
float step = depthStep;
|
||||||
WearableSprite onlyDrawable = wearingItems.Find(w => w.HideOtherWearables);
|
WearableSprite onlyDrawable = wearingItems.Find(w => w.HideOtherWearables);
|
||||||
if (Params.MirrorHorizontally)
|
if (Params.MirrorHorizontally)
|
||||||
|
|||||||
@@ -690,6 +690,7 @@ namespace Barotrauma
|
|||||||
AssignRelayToServer("readycheck", true);
|
AssignRelayToServer("readycheck", true);
|
||||||
|
|
||||||
AssignRelayToServer("givetalent", true);
|
AssignRelayToServer("givetalent", true);
|
||||||
|
AssignRelayToServer("unlocktalents", true);
|
||||||
AssignRelayToServer("giveexperience", true);
|
AssignRelayToServer("giveexperience", true);
|
||||||
|
|
||||||
AssignOnExecute("control", (string[] args) =>
|
AssignOnExecute("control", (string[] args) =>
|
||||||
@@ -1099,9 +1100,35 @@ namespace Barotrauma
|
|||||||
|
|
||||||
commands.Add(new Command("load|loadsub", "load [submarine name]: Load a submarine.", (string[] args) =>
|
commands.Add(new Command("load|loadsub", "load [submarine name]: Load a submarine.", (string[] args) =>
|
||||||
{
|
{
|
||||||
if (args.Length == 0) return;
|
if (args.Length == 0) { return; }
|
||||||
SubmarineInfo subInfo = new SubmarineInfo(string.Join(" ", args));
|
|
||||||
|
if (GameMain.GameSession != null)
|
||||||
|
{
|
||||||
|
ThrowError("The loadsub command cannot be used when a round is running. You should probably be using spawnsub instead.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = string.Join(" ", args);
|
||||||
|
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => name.Equals(s.Name, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (subInfo == null)
|
||||||
|
{
|
||||||
|
string path = Path.Combine(SubmarineInfo.SavePath, name);
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
ThrowError($"Could not find a submarine with the name \"{name}\" or in the path {path}.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subInfo = new SubmarineInfo(path);
|
||||||
|
}
|
||||||
|
|
||||||
Submarine.Load(subInfo, true);
|
Submarine.Load(subInfo, true);
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
return new string[][]
|
||||||
|
{
|
||||||
|
SubmarineInfo.SavedSubmarines.Select(s => s.Name).ToArray()
|
||||||
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
commands.Add(new Command("cleansub", "", (string[] args) =>
|
commands.Add(new Command("cleansub", "", (string[] args) =>
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Barotrauma.Networking;
|
||||||
|
|
||||||
|
namespace Barotrauma
|
||||||
|
{
|
||||||
|
partial class AlienRuinMission : Mission
|
||||||
|
{
|
||||||
|
public override void ClientReadInitial(IReadMessage msg)
|
||||||
|
{
|
||||||
|
existingTargets.Clear();
|
||||||
|
spawnedTargets.Clear();
|
||||||
|
allTargets.Clear();
|
||||||
|
ushort existingTargetsCount = msg.ReadUInt16();
|
||||||
|
for (int i = 0; i < existingTargetsCount; i++)
|
||||||
|
{
|
||||||
|
ushort targetId = msg.ReadUInt16();
|
||||||
|
if (targetId == Entity.NullEntityID) { continue; }
|
||||||
|
Entity target = Entity.FindEntityByID(targetId);
|
||||||
|
if (target == null) { continue; }
|
||||||
|
existingTargets.Add(target);
|
||||||
|
allTargets.Add(target);
|
||||||
|
}
|
||||||
|
ushort spawnedTargetsCount = msg.ReadUInt16();
|
||||||
|
for (int i = 0; i < spawnedTargetsCount; i++)
|
||||||
|
{
|
||||||
|
var enemy = Character.ReadSpawnData(msg);
|
||||||
|
existingTargets.Add(enemy);
|
||||||
|
allTargets.Add(enemy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -14,6 +15,10 @@ namespace Barotrauma
|
|||||||
get { return shownMessages; }
|
get { return shownMessages; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool DisplayTargetHudIcons => Prefab.DisplayTargetHudIcons;
|
||||||
|
|
||||||
|
public virtual IEnumerable<Entity> HudIconTargets => Enumerable.Empty<Entity>();
|
||||||
|
|
||||||
public Color GetDifficultyColor()
|
public Color GetDifficultyColor()
|
||||||
{
|
{
|
||||||
int v = Difficulty ?? MissionPrefab.MinDifficulty;
|
int v = Difficulty ?? MissionPrefab.MinDifficulty;
|
||||||
@@ -92,7 +97,7 @@ namespace Barotrauma
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClientRead(IReadMessage msg)
|
public virtual void ClientRead(IReadMessage msg)
|
||||||
{
|
{
|
||||||
State = msg.ReadInt16();
|
State = msg.ReadInt16();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,54 @@ namespace Barotrauma
|
|||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool DisplayTargetHudIcons
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float HudIconMaxDistance
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sprite HudIcon
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return hudIcon ?? Icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color HudIconColor
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return hudIconColor ?? IconColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Sprite hudIcon;
|
||||||
|
private Color? hudIconColor;
|
||||||
|
|
||||||
partial void InitProjSpecific(XElement element)
|
partial void InitProjSpecific(XElement element)
|
||||||
{
|
{
|
||||||
|
DisplayTargetHudIcons = element.GetAttributeBool("displaytargethudicons", false);
|
||||||
|
HudIconMaxDistance = element.GetAttributeFloat("hudiconmaxdistance", 1000.0f);
|
||||||
foreach (XElement subElement in element.Elements())
|
foreach (XElement subElement in element.Elements())
|
||||||
{
|
{
|
||||||
if (!subElement.Name.ToString().Equals("icon", StringComparison.OrdinalIgnoreCase)) { continue; }
|
string name = subElement.Name.ToString();
|
||||||
Icon = new Sprite(subElement);
|
if (name.Equals("icon", StringComparison.OrdinalIgnoreCase))
|
||||||
IconColor = subElement.GetAttributeColor("color", Color.White);
|
{
|
||||||
|
Icon = new Sprite(subElement);
|
||||||
|
IconColor = subElement.GetAttributeColor("color", Color.White);
|
||||||
|
}
|
||||||
|
else if (name.Equals("hudicon", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
hudIcon = new Sprite(subElement);
|
||||||
|
hudIconColor = subElement.GetAttributeColor("color");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using Barotrauma.Networking;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Barotrauma
|
||||||
|
{
|
||||||
|
partial class ScanMission : Mission
|
||||||
|
{
|
||||||
|
public override IEnumerable<Entity> HudIconTargets
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (State == 0)
|
||||||
|
{
|
||||||
|
return scanTargets.Where(kvp => !kvp.Value).Select(kvp => kvp.Key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<Entity>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ClientReadInitial(IReadMessage msg)
|
||||||
|
{
|
||||||
|
startingItems.Clear();
|
||||||
|
ushort itemCount = msg.ReadUInt16();
|
||||||
|
for (int i = 0; i < itemCount; i++)
|
||||||
|
{
|
||||||
|
startingItems.Add(Item.ReadSpawnData(msg));
|
||||||
|
}
|
||||||
|
if (startingItems.Contains(null))
|
||||||
|
{
|
||||||
|
throw new Exception($"Error in ScanMission.ClientReadInitial: item list contains null (mission: {Prefab.Identifier})");
|
||||||
|
}
|
||||||
|
if (startingItems.Count != itemCount)
|
||||||
|
{
|
||||||
|
throw new Exception($"Error in ScanMission.ClientReadInitial: item count does not match the server count ({itemCount} != {startingItems.Count}, mission: {Prefab.Identifier})");
|
||||||
|
}
|
||||||
|
scanners.Clear();
|
||||||
|
GetScanners();
|
||||||
|
ClientReadScanTargetStatus(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ClientRead(IReadMessage msg)
|
||||||
|
{
|
||||||
|
base.ClientRead(msg);
|
||||||
|
ClientReadScanTargetStatus(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClientReadScanTargetStatus(IReadMessage msg)
|
||||||
|
{
|
||||||
|
scanTargets.Clear();
|
||||||
|
byte targetsToScan = msg.ReadByte();
|
||||||
|
for (int i = 0; i < targetsToScan; i++)
|
||||||
|
{
|
||||||
|
ushort id = msg.ReadUInt16();
|
||||||
|
bool scanned = msg.ReadBoolean();
|
||||||
|
Entity entity = Entity.FindEntityByID(id);
|
||||||
|
scanTargets.Add(entity as WayPoint, scanned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -627,7 +627,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (child == Content || child == ScrollBar || child == ContentBackground) { continue; }
|
if (child == Content || child == ScrollBar || child == ContentBackground) { continue; }
|
||||||
child.AddToGUIUpdateList(ignoreChildren, order);
|
child.AddToGUIUpdateList(ignoreChildren, order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (GUIComponent child in Content.Children)
|
foreach (GUIComponent child in Content.Children)
|
||||||
@@ -656,7 +656,7 @@ namespace Barotrauma
|
|||||||
OnAddedToGUIUpdateList?.Invoke(this);
|
OnAddedToGUIUpdateList?.Invoke(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lastVisible = 0;
|
int lastVisible = 0;
|
||||||
for (int i = 0; i < Content.CountChildren; i++)
|
for (int i = 0; i < Content.CountChildren; i++)
|
||||||
{
|
{
|
||||||
@@ -700,6 +700,8 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ForceUpdate() => Update((float)Timing.Step);
|
||||||
|
|
||||||
protected override void Update(float deltaTime)
|
protected override void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
if (!Visible) { return; }
|
if (!Visible) { return; }
|
||||||
|
|||||||
@@ -595,10 +595,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private string GetPlayerBalanceText() => GetCurrencyFormatted(PlayerMoney);
|
private string GetPlayerBalanceText() => GetCurrencyFormatted(PlayerMoney);
|
||||||
|
|
||||||
private GUILayoutGroup CreateDealsGroup(GUIListBox parentList)
|
private GUILayoutGroup CreateDealsGroup(GUIListBox parentList, int elementCount = 4)
|
||||||
{
|
{
|
||||||
var elementHeight = (int)(GUI.yScale * 80);
|
var elementHeight = (int)(GUI.yScale * 80);
|
||||||
var frame = new GUIFrame(new RectTransform(new Point(parentList.Content.Rect.Width, 4 * elementHeight + 3), parent: parentList.Content.RectTransform), style: null);
|
var frame = new GUIFrame(new RectTransform(new Point(parentList.Content.Rect.Width, elementCount * elementHeight + 3), parent: parentList.Content.RectTransform), style: null);
|
||||||
frame.UserData = "deals";
|
frame.UserData = "deals";
|
||||||
var dealsGroup = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter);
|
var dealsGroup = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter);
|
||||||
var dealsHeader = new GUILayoutGroup(new RectTransform(new Point((int)(0.95f * parentList.Content.Rect.Width), elementHeight), parent: dealsGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
var dealsHeader = new GUILayoutGroup(new RectTransform(new Point((int)(0.95f * parentList.Content.Rect.Width), elementHeight), parent: dealsGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||||
@@ -726,6 +726,8 @@ namespace Barotrauma
|
|||||||
FilterStoreItems(category, searchBox.Text);
|
FilterStoreItems(category, searchBox.Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int prevDailySpecialCount;
|
||||||
|
|
||||||
private void RefreshStoreBuyList()
|
private void RefreshStoreBuyList()
|
||||||
{
|
{
|
||||||
float prevBuyListScroll = storeBuyList.BarScroll;
|
float prevBuyListScroll = storeBuyList.BarScroll;
|
||||||
@@ -734,11 +736,14 @@ namespace Barotrauma
|
|||||||
bool hasPermissions = HasPermissions;
|
bool hasPermissions = HasPermissions;
|
||||||
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
||||||
|
|
||||||
if ((storeDailySpecialsGroup != null) != CurrentLocation.DailySpecials.Any())
|
int dailySpecialCount = CurrentLocation?.DailySpecials.Count() ?? 3;
|
||||||
|
|
||||||
|
if ((storeDailySpecialsGroup != null) != CurrentLocation.DailySpecials.Any() || dailySpecialCount != prevDailySpecialCount)
|
||||||
{
|
{
|
||||||
if (storeDailySpecialsGroup == null)
|
if (storeDailySpecialsGroup == null || dailySpecialCount != prevDailySpecialCount)
|
||||||
{
|
{
|
||||||
storeDailySpecialsGroup = CreateDealsGroup(storeBuyList);
|
storeBuyList.RemoveChild(storeDailySpecialsGroup?.Parent);
|
||||||
|
storeDailySpecialsGroup = CreateDealsGroup(storeBuyList, 1 + dailySpecialCount);
|
||||||
storeDailySpecialsGroup.Parent.SetAsFirstChild();
|
storeDailySpecialsGroup.Parent.SetAsFirstChild();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -747,6 +752,7 @@ namespace Barotrauma
|
|||||||
storeDailySpecialsGroup = null;
|
storeDailySpecialsGroup = null;
|
||||||
}
|
}
|
||||||
storeBuyList.RecalculateChildren();
|
storeBuyList.RecalculateChildren();
|
||||||
|
prevDailySpecialCount = dailySpecialCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (PurchasedItem item in CurrentLocation.StoreStock)
|
foreach (PurchasedItem item in CurrentLocation.StoreStock)
|
||||||
|
|||||||
@@ -1320,7 +1320,7 @@ namespace Barotrauma
|
|||||||
if (!(characterLayout is null))
|
if (!(characterLayout is null))
|
||||||
{
|
{
|
||||||
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomRight);
|
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomRight);
|
||||||
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("close"))
|
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
|
||||||
{
|
{
|
||||||
OnClicked = (button, o) =>
|
OnClicked = (button, o) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -922,8 +922,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
//open the pause menu if not controlling a character OR if the character has no UIs active that can be closed with ESC
|
//open the pause menu if not controlling a character OR if the character has no UIs active that can be closed with ESC
|
||||||
else if ((Character.Controlled == null || !itemHudActive())
|
else if ((Character.Controlled == null || !itemHudActive())
|
||||||
//TODO: do we need to check Inventory.SelectedSlot?
|
&& CharacterHealth.OpenHealthWindow == null
|
||||||
&& Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null
|
|
||||||
&& !CrewManager.IsCommandInterfaceOpen
|
&& !CrewManager.IsCommandInterfaceOpen
|
||||||
&& !(Screen.Selected is SubEditorScreen editor && !editor.WiringMode && Character.Controlled?.SelectedConstruction != null))
|
&& !(Screen.Selected is SubEditorScreen editor && !editor.WiringMode && Character.Controlled?.SelectedConstruction != null))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1837,6 +1837,7 @@ namespace Barotrauma
|
|||||||
ic.ParseMsg();
|
ic.ParseMsg();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CharacterHUD.ShouldRecreateHudTexts = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplySettings()
|
private void ApplySettings()
|
||||||
|
|||||||
@@ -1,13 +1,201 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using System;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Barotrauma.Extensions;
|
||||||
|
using Barotrauma.IO;
|
||||||
|
|
||||||
namespace Barotrauma.Items.Components
|
namespace Barotrauma.Items.Components
|
||||||
{
|
{
|
||||||
partial class IdCard
|
partial class IdCard
|
||||||
{
|
{
|
||||||
public Sprite StoredPortrait;
|
public struct OwnerAppearance
|
||||||
public Vector2 StoredSheetIndex;
|
{
|
||||||
public JobPrefab StoredJobPrefab;
|
public Sprite Portrait;
|
||||||
public List<WearableSprite> StoredAttachments;
|
public Vector2 SheetIndex;
|
||||||
|
public JobPrefab JobPrefab;
|
||||||
|
public List<WearableSprite> Attachments;
|
||||||
|
public Color HairColor;
|
||||||
|
public Color FacialHairColor;
|
||||||
|
public Color SkinColor;
|
||||||
|
|
||||||
|
public void ExtractJobPrefab(string[] tags)
|
||||||
|
{
|
||||||
|
string jobIdTag = tags.FirstOrDefault(s => s.StartsWith("jobid:"));
|
||||||
|
|
||||||
|
if (jobIdTag != null && jobIdTag.Length > 6)
|
||||||
|
{
|
||||||
|
string jobId = jobIdTag.Substring(6);
|
||||||
|
if (jobId != string.Empty)
|
||||||
|
{
|
||||||
|
JobPrefab = JobPrefab.Get(jobId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExtractAppearance(CharacterInfo characterInfo, string[] tags)
|
||||||
|
{
|
||||||
|
Gender disguisedGender = Gender.None;
|
||||||
|
Race disguisedRace = Race.None;
|
||||||
|
int disguisedHeadSpriteId = -1;
|
||||||
|
int disguisedHairIndex = -1;
|
||||||
|
int disguisedBeardIndex = -1;
|
||||||
|
int disguisedMoustacheIndex = -1;
|
||||||
|
int disguisedFaceAttachmentIndex = -1;
|
||||||
|
Color hairColor = Color.Black;
|
||||||
|
Color facialHairColor = Color.Black;
|
||||||
|
Color skinColor = Color.Black;
|
||||||
|
|
||||||
|
foreach (string tag in tags)
|
||||||
|
{
|
||||||
|
string[] s = tag.Split(':');
|
||||||
|
|
||||||
|
switch (s[0].ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "haircolor":
|
||||||
|
hairColor = XMLExtensions.ParseColor(s[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "facialhaircolor":
|
||||||
|
facialHairColor = XMLExtensions.ParseColor(s[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "skincolor":
|
||||||
|
skinColor = XMLExtensions.ParseColor(s[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "gender":
|
||||||
|
Enum.TryParse(s[1], ignoreCase: true, out disguisedGender);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "race":
|
||||||
|
Enum.TryParse(s[1], ignoreCase: true, out disguisedRace);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "headspriteid":
|
||||||
|
int.TryParse(s[1], NumberStyles.Any, CultureInfo.InvariantCulture, out disguisedHeadSpriteId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "hairindex":
|
||||||
|
disguisedHairIndex = int.Parse(s[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "beardindex":
|
||||||
|
disguisedBeardIndex = int.Parse(s[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "moustacheindex":
|
||||||
|
disguisedMoustacheIndex = int.Parse(s[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "faceattachmentindex":
|
||||||
|
disguisedFaceAttachmentIndex = int.Parse(s[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "sheetindex":
|
||||||
|
string[] vectorValues = s[1].Split(";");
|
||||||
|
SheetIndex = new Vector2(float.Parse(vectorValues[0]), float.Parse(vectorValues[1]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((characterInfo.HasGenders && disguisedGender == Gender.None)
|
||||||
|
|| (characterInfo.HasRaces && disguisedRace == Race.None)
|
||||||
|
|| disguisedHeadSpriteId <= 0)
|
||||||
|
{
|
||||||
|
Portrait = null;
|
||||||
|
Attachments = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (XElement limbElement in characterInfo.Ragdoll.MainElement.Elements())
|
||||||
|
{
|
||||||
|
if (!limbElement.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||||
|
|
||||||
|
XElement spriteElement = limbElement.Element("sprite");
|
||||||
|
if (spriteElement == null) { continue; }
|
||||||
|
|
||||||
|
string spritePath = spriteElement.Attribute("texture").Value;
|
||||||
|
|
||||||
|
spritePath = spritePath.Replace("[GENDER]", disguisedGender.ToString().ToLowerInvariant());
|
||||||
|
spritePath = spritePath.Replace("[RACE]", disguisedRace.ToString().ToLowerInvariant());
|
||||||
|
spritePath = spritePath.Replace("[HEADID]", disguisedHeadSpriteId.ToString());
|
||||||
|
|
||||||
|
string fileName = Path.GetFileNameWithoutExtension(spritePath);
|
||||||
|
|
||||||
|
//go through the files in the directory to find a matching sprite
|
||||||
|
foreach (string file in Directory.GetFiles(Path.GetDirectoryName(spritePath)))
|
||||||
|
{
|
||||||
|
if (!file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
string fileWithoutTags = Path.GetFileNameWithoutExtension(file);
|
||||||
|
fileWithoutTags = fileWithoutTags.Split('[', ']').First();
|
||||||
|
if (fileWithoutTags != fileName) { continue; }
|
||||||
|
Portrait = new Sprite(spriteElement, "", file) { RelativeOrigin = Vector2.Zero };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (characterInfo.Wearables != null)
|
||||||
|
{
|
||||||
|
float baldnessChance = disguisedGender == Gender.Female ? 0.05f : 0.2f;
|
||||||
|
|
||||||
|
List<XElement> createElementList(WearableType wearableType, float emptyCommonness = 1.0f)
|
||||||
|
=> CharacterInfo.AddEmpty(
|
||||||
|
characterInfo.FilterByTypeAndHeadID(
|
||||||
|
characterInfo.FilterElementsByGenderAndRace(characterInfo.Wearables, disguisedGender, disguisedRace),
|
||||||
|
wearableType, disguisedHeadSpriteId),
|
||||||
|
wearableType, emptyCommonness);
|
||||||
|
|
||||||
|
var disguisedHairs = createElementList(WearableType.Hair, baldnessChance);
|
||||||
|
var disguisedBeards = createElementList(WearableType.Beard);
|
||||||
|
var disguisedMoustaches = createElementList(WearableType.Moustache);
|
||||||
|
var disguisedFaceAttachments = createElementList(WearableType.FaceAttachment);
|
||||||
|
|
||||||
|
XElement getElementFromList(List<XElement> list, int index)
|
||||||
|
=> CharacterInfo.IsValidIndex(index, list)
|
||||||
|
? list[index]
|
||||||
|
: characterInfo.GetRandomElement(list);
|
||||||
|
|
||||||
|
var disguisedHairElement = getElementFromList(disguisedHairs, disguisedHairIndex);
|
||||||
|
var disguisedBeardElement = getElementFromList(disguisedBeards, disguisedBeardIndex);
|
||||||
|
var disguisedMoustacheElement = getElementFromList(disguisedMoustaches, disguisedMoustacheIndex);
|
||||||
|
var disguisedFaceAttachmentElement = getElementFromList(disguisedFaceAttachments, disguisedFaceAttachmentIndex);
|
||||||
|
|
||||||
|
Attachments = new List<WearableSprite>();
|
||||||
|
|
||||||
|
void loadAttachments(List<WearableSprite> attachments, XElement element, WearableType wearableType)
|
||||||
|
{
|
||||||
|
foreach (var s in element?.Elements("sprite") ?? Enumerable.Empty<XElement>())
|
||||||
|
{
|
||||||
|
attachments.Add(new WearableSprite(s, wearableType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAttachments(Attachments, disguisedFaceAttachmentElement, WearableType.FaceAttachment);
|
||||||
|
loadAttachments(Attachments, disguisedBeardElement, WearableType.Beard);
|
||||||
|
loadAttachments(Attachments, disguisedMoustacheElement, WearableType.Moustache);
|
||||||
|
loadAttachments(Attachments, disguisedHairElement, WearableType.Hair);
|
||||||
|
|
||||||
|
loadAttachments(Attachments,
|
||||||
|
characterInfo.OmitJobInPortraitClothing
|
||||||
|
? JobPrefab.NoJobElement?.Element("PortraitClothing")
|
||||||
|
: JobPrefab?.ClothingElement,
|
||||||
|
WearableType.JobIndicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
HairColor = hairColor;
|
||||||
|
FacialHairColor = facialHairColor;
|
||||||
|
SkinColor = skinColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public OwnerAppearance StoredOwnerAppearance = default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Matrix transform = Matrix.CreateRotationZ(item.body.Rotation);
|
Matrix transform = Matrix.CreateRotationZ(item.body.DrawRotation);
|
||||||
if (item.body.Dir == -1.0f)
|
if (item.body.Dir == -1.0f)
|
||||||
{
|
{
|
||||||
transformedItemPos.X = -transformedItemPos.X;
|
transformedItemPos.X = -transformedItemPos.X;
|
||||||
@@ -300,7 +300,7 @@ namespace Barotrauma.Items.Components
|
|||||||
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
|
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
|
||||||
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
|
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
|
||||||
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
|
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
|
||||||
transformedItemPos += item.DrawPosition;
|
transformedItemPos += item.body.DrawPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 currentItemPos = transformedItemPos;
|
Vector2 currentItemPos = transformedItemPos;
|
||||||
|
|||||||
@@ -380,9 +380,20 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
if (requiredItem.UseCondition && requiredItem.MinCondition < 1.0f)
|
if (requiredItem.UseCondition && requiredItem.MinCondition < 1.0f)
|
||||||
{
|
{
|
||||||
GUI.DrawRectangle(spriteBatch, new Rectangle(slotRect.X, slotRect.Bottom - 8, slotRect.Width, 8), Color.Black * 0.8f, true);
|
DrawConditionBar(spriteBatch, requiredItem.MinCondition);
|
||||||
|
}
|
||||||
|
else if (requiredItem.MaxCondition < 1.0f)
|
||||||
|
{
|
||||||
|
DrawConditionBar(spriteBatch, requiredItem.MaxCondition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawConditionBar(SpriteBatch sb, float condition)
|
||||||
|
{
|
||||||
|
int spacing = GUI.IntScale(4);
|
||||||
|
int height = GUI.IntScale(10);
|
||||||
|
GUI.DrawRectangle(spriteBatch, new Rectangle(slotRect.X + spacing, slotRect.Bottom - spacing - height, slotRect.Width - spacing * 2, height), Color.Black * 0.8f, true);
|
||||||
GUI.DrawRectangle(spriteBatch,
|
GUI.DrawRectangle(spriteBatch,
|
||||||
new Rectangle(slotRect.X, slotRect.Bottom - 8, (int)(slotRect.Width * requiredItem.MinCondition), 8),
|
new Rectangle(slotRect.X + spacing, slotRect.Bottom - spacing - height, (int)((slotRect.Width - spacing * 2) * condition), height),
|
||||||
GUI.Style.Green * 0.8f, true);
|
GUI.Style.Green * 0.8f, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +406,10 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%";
|
toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%";
|
||||||
}
|
}
|
||||||
|
else if(requiredItem.MaxCondition < 1.0f)
|
||||||
|
{
|
||||||
|
toolTipText += " 0-" + (int)Math.Round(requiredItem.MaxCondition * 100) + "%";
|
||||||
|
}
|
||||||
else if (requiredItem.MaxCondition <= 0.0f)
|
else if (requiredItem.MaxCondition <= 0.0f)
|
||||||
{
|
{
|
||||||
toolTipText = TextManager.GetWithVariable("displayname.emptyitem", "[itemname]", toolTipText);
|
toolTipText = TextManager.GetWithVariable("displayname.emptyitem", "[itemname]", toolTipText);
|
||||||
@@ -649,8 +664,13 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
foreach (GUIComponent child in itemList.Content.Children)
|
foreach (GUIComponent child in itemList.Content.Children)
|
||||||
{
|
{
|
||||||
var itemPrefab = child.UserData as FabricationRecipe;
|
if (!(child.UserData is FabricationRecipe itemPrefab)) { continue; }
|
||||||
if (itemPrefab == null) continue;
|
|
||||||
|
if (itemPrefab != selectedItem &&
|
||||||
|
(child.Rect.Y > itemList.Rect.Bottom || child.Rect.Bottom < itemList.Rect.Y))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients, character);
|
bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients, character);
|
||||||
if (itemPrefab == selectedItem)
|
if (itemPrefab == selectedItem)
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
private bool isConnectedToSteering;
|
private bool isConnectedToSteering;
|
||||||
|
|
||||||
private static string caveLabel;
|
private static string caveLabel, ruinLabel;
|
||||||
|
|
||||||
private bool AllowUsingMineralScanner =>
|
private bool AllowUsingMineralScanner =>
|
||||||
HasMineralScanner && !isConnectedToSteering;
|
HasMineralScanner && !isConnectedToSteering;
|
||||||
@@ -880,7 +880,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
foreach (AITarget aiTarget in AITarget.List)
|
foreach (AITarget aiTarget in AITarget.List)
|
||||||
{
|
{
|
||||||
if (!aiTarget.Enabled) { continue; }
|
if (aiTarget.InDetectable) { continue; }
|
||||||
if (string.IsNullOrEmpty(aiTarget.SonarLabel) || aiTarget.SoundRange <= 0.0f) { continue; }
|
if (string.IsNullOrEmpty(aiTarget.SonarLabel) || aiTarget.SoundRange <= 0.0f) { continue; }
|
||||||
|
|
||||||
if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < aiTarget.SoundRange * aiTarget.SoundRange)
|
if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < aiTarget.SoundRange * aiTarget.SoundRange)
|
||||||
@@ -1234,7 +1234,7 @@ namespace Barotrauma.Items.Components
|
|||||||
foreach (AITarget aiTarget in AITarget.List)
|
foreach (AITarget aiTarget in AITarget.List)
|
||||||
{
|
{
|
||||||
float disruption = aiTarget.Entity is Character c ? c.Params.SonarDisruption : aiTarget.SonarDisruption;
|
float disruption = aiTarget.Entity is Character c ? c.Params.SonarDisruption : aiTarget.SonarDisruption;
|
||||||
if (disruption <= 0.0f || !aiTarget.Enabled) { continue; }
|
if (disruption <= 0.0f || aiTarget.InDetectable) { continue; }
|
||||||
float distSqr = Vector2.DistanceSquared(aiTarget.WorldPosition, pingSource);
|
float distSqr = Vector2.DistanceSquared(aiTarget.WorldPosition, pingSource);
|
||||||
if (distSqr > worldPingRadiusSqr) { continue; }
|
if (distSqr > worldPingRadiusSqr) { continue; }
|
||||||
float disruptionDist = (float)Math.Sqrt(distSqr);
|
float disruptionDist = (float)Math.Sqrt(distSqr);
|
||||||
@@ -1359,28 +1359,6 @@ namespace Barotrauma.Items.Components
|
|||||||
blipType : cell.IsDestructible ? BlipType.Destructible : BlipType.Default);
|
blipType : cell.IsDestructible ? BlipType.Destructible : BlipType.Default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
pingSource, transducerPos,
|
|
||||||
pingRadius, prevPingRadius,
|
|
||||||
100.0f, 1000.0f, range, pingStrength, passive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Item item in Item.ItemList)
|
foreach (Item item in Item.ItemList)
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
private GUITextBlock progressBarOverlayText;
|
private GUITextBlock progressBarOverlayText;
|
||||||
|
|
||||||
|
private GUILayoutGroup extraButtonContainer;
|
||||||
|
|
||||||
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
||||||
//the corresponding particle emitter is active when the condition is within this range
|
//the corresponding particle emitter is active when the condition is within this range
|
||||||
private readonly List<Vector2> particleEmitterConditionRanges = new List<Vector2>();
|
private readonly List<Vector2> particleEmitterConditionRanges = new List<Vector2>();
|
||||||
@@ -145,10 +147,16 @@ namespace Barotrauma.Items.Components
|
|||||||
progressBarHolder.RectTransform.MinSize = RepairButton.RectTransform.MinSize;
|
progressBarHolder.RectTransform.MinSize = RepairButton.RectTransform.MinSize;
|
||||||
RepairButton.RectTransform.MinSize = new Point((int)(RepairButton.TextBlock.TextSize.X * 1.2f), RepairButton.RectTransform.MinSize.Y);
|
RepairButton.RectTransform.MinSize = new Point((int)(RepairButton.TextBlock.TextSize.X * 1.2f), RepairButton.RectTransform.MinSize.Y);
|
||||||
|
|
||||||
|
extraButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform), isHorizontal: true)
|
||||||
|
{
|
||||||
|
IgnoreLayoutGroups = true,
|
||||||
|
Stretch = true,
|
||||||
|
AbsoluteSpacing = GUI.IntScale(5)
|
||||||
|
};
|
||||||
|
|
||||||
sabotageButtonText = TextManager.Get("SabotageButton");
|
sabotageButtonText = TextManager.Get("SabotageButton");
|
||||||
sabotagingText = TextManager.Get("Sabotaging");
|
sabotagingText = TextManager.Get("Sabotaging");
|
||||||
SabotageButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.15f), paddedFrame.RectTransform, Anchor.BottomCenter), sabotageButtonText, style: "GUIButtonSmall")
|
SabotageButton = new GUIButton(new RectTransform(Vector2.One, extraButtonContainer.RectTransform), sabotageButtonText, style: "GUIButtonSmall")
|
||||||
{
|
{
|
||||||
IgnoreLayoutGroups = true,
|
IgnoreLayoutGroups = true,
|
||||||
Visible = false,
|
Visible = false,
|
||||||
@@ -160,9 +168,9 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tinkerButtonText = "Tinker";
|
tinkerButtonText = TextManager.Get("TinkerButton", returnNull: true) ?? "Tinker";
|
||||||
tinkeringText = "Tinkering";
|
tinkeringText = TextManager.Get("Tinkering", returnNull: true) ?? "Tinkering";
|
||||||
TinkerButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.15f), paddedFrame.RectTransform, Anchor.BottomCenter), tinkerButtonText)
|
TinkerButton = new GUIButton(new RectTransform(Vector2.One, extraButtonContainer.RectTransform), tinkerButtonText, style: "GUIButtonSmall")
|
||||||
{
|
{
|
||||||
IgnoreLayoutGroups = true,
|
IgnoreLayoutGroups = true,
|
||||||
Visible = false,
|
Visible = false,
|
||||||
@@ -173,6 +181,8 @@ namespace Barotrauma.Items.Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extraButtonContainer.RectTransform.MinSize = new Point(0, SabotageButton.RectTransform.MinSize.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void UpdateProjSpecific(float deltaTime)
|
partial void UpdateProjSpecific(float deltaTime)
|
||||||
@@ -274,6 +284,10 @@ namespace Barotrauma.Items.Components
|
|||||||
tinkeringText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
|
tinkeringText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
|
||||||
|
|
||||||
System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
|
System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
|
||||||
|
|
||||||
|
extraButtonContainer.Visible = SabotageButton.Visible || TinkerButton.Visible;
|
||||||
|
extraButtonContainer.IgnoreLayoutGroups = !extraButtonContainer.Visible;
|
||||||
|
|
||||||
foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
|
foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
|
||||||
{
|
{
|
||||||
if (!(c.UserData is Skill skill)) continue;
|
if (!(c.UserData is Skill skill)) continue;
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ namespace Barotrauma.Items.Components
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serialize("0.5,0.5)", false)]
|
||||||
|
public Vector2 Origin { get; set; } = new Vector2(0.5f, 0.5f);
|
||||||
|
|
||||||
public Vector2 DrawSize
|
public Vector2 DrawSize
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -57,7 +60,6 @@ namespace Barotrauma.Items.Components
|
|||||||
sourcePos = sourceLimb.body.DrawPosition;
|
sourcePos = sourceLimb.body.DrawPosition;
|
||||||
}
|
}
|
||||||
return sourcePos;
|
return sourcePos;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void InitProjSpecific(XElement element)
|
partial void InitProjSpecific(XElement element)
|
||||||
@@ -87,7 +89,8 @@ namespace Barotrauma.Items.Components
|
|||||||
startPos.Y = -startPos.Y;
|
startPos.Y = -startPos.Y;
|
||||||
if (source is Item sourceItem)
|
if (source is Item sourceItem)
|
||||||
{
|
{
|
||||||
var turret = sourceItem?.GetComponent<Turret>();
|
var turret = sourceItem.GetComponent<Turret>();
|
||||||
|
var weapon = sourceItem.GetComponent<RangedWeapon>();
|
||||||
if (turret != null)
|
if (turret != null)
|
||||||
{
|
{
|
||||||
startPos = new Vector2(sourceItem.WorldRect.X + turret.TransformedBarrelPos.X, -(sourceItem.WorldRect.Y - turret.TransformedBarrelPos.Y));
|
startPos = new Vector2(sourceItem.WorldRect.X + turret.TransformedBarrelPos.X, -(sourceItem.WorldRect.Y - turret.TransformedBarrelPos.Y));
|
||||||
@@ -96,8 +99,21 @@ namespace Barotrauma.Items.Components
|
|||||||
startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * item.Scale * 0.9f;
|
startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * item.Scale * 0.9f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (weapon != null)
|
||||||
|
{
|
||||||
|
Vector2 barrelPos = FarseerPhysics.ConvertUnits.ToDisplayUnits(weapon.TransformedBarrelPos);
|
||||||
|
barrelPos.Y = -barrelPos.Y;
|
||||||
|
startPos += barrelPos * item.Scale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Vector2 endPos = new Vector2(target.DrawPosition.X, -target.DrawPosition.Y);
|
Vector2 endPos = new Vector2(target.DrawPosition.X, target.DrawPosition.Y);
|
||||||
|
Vector2 flippedPos = target.Sprite.size * target.Scale * (Origin - new Vector2(0.5f));
|
||||||
|
if (target.body.Dir < 0.0f)
|
||||||
|
{
|
||||||
|
flippedPos.X = -flippedPos.X;
|
||||||
|
}
|
||||||
|
endPos += Vector2.Transform(flippedPos, Matrix.CreateRotationZ(target.body.Rotation));
|
||||||
|
endPos.Y = -endPos.Y;
|
||||||
|
|
||||||
if (Snapped)
|
if (Snapped)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Barotrauma.Networking;
|
||||||
|
|
||||||
|
namespace Barotrauma.Items.Components
|
||||||
|
{
|
||||||
|
partial class Scanner : ItemComponent, IServerSerializable
|
||||||
|
{
|
||||||
|
partial void UpdateProjSpecific()
|
||||||
|
{
|
||||||
|
if (Holdable != null && Holdable.Attached && (AlwaysDisplayProgressBar || DisplayProgressBar) && !IsScanCompleted)
|
||||||
|
{
|
||||||
|
Character.Controlled?.UpdateHUDProgressBar(this,
|
||||||
|
item.WorldPosition,
|
||||||
|
ScanTimer / ScanDuration,
|
||||||
|
GUI.Style.Red, GUI.Style.Green,
|
||||||
|
textTag: "progressbar.scanning");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||||
|
{
|
||||||
|
bool wasScanCompletedPreviously = IsScanCompleted;
|
||||||
|
scanTimer = msg.ReadSingle();
|
||||||
|
if (!wasScanCompletedPreviously && IsScanCompleted)
|
||||||
|
{
|
||||||
|
OnScanCompleted?.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
using Barotrauma.Extensions;
|
||||||
|
using Barotrauma.Networking;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
namespace Barotrauma.Items.Components
|
||||||
|
{
|
||||||
|
partial class ButtonTerminal : ItemComponent, IClientSerializable, IServerSerializable
|
||||||
|
{
|
||||||
|
private string[] terminalButtonStyles;
|
||||||
|
private GUIFrame containerHolder;
|
||||||
|
private GUIImage containerIndicator;
|
||||||
|
private GUIComponentStyle indicatorStyleRed, indicatorStyleGreen;
|
||||||
|
|
||||||
|
partial void InitProjSpecific(XElement element)
|
||||||
|
{
|
||||||
|
terminalButtonStyles = new string[RequiredSignalCount];
|
||||||
|
int i = 0;
|
||||||
|
foreach (var childElement in element.GetChildElements("TerminalButton"))
|
||||||
|
{
|
||||||
|
string style = childElement.GetAttributeString("style", null);
|
||||||
|
if (style == null) { continue; }
|
||||||
|
terminalButtonStyles[i++] = style;
|
||||||
|
}
|
||||||
|
indicatorStyleRed = GUI.Style.GetComponentStyle("IndicatorLightRed");
|
||||||
|
indicatorStyleGreen = GUI.Style.GetComponentStyle("IndicatorLightGreen");
|
||||||
|
CreateGUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreateGUI()
|
||||||
|
{
|
||||||
|
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.8f), GuiFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||||
|
{
|
||||||
|
Stretch = true,
|
||||||
|
RelativeSpacing = 0.08f
|
||||||
|
};
|
||||||
|
paddedFrame.OnAddedToGUIUpdateList += (component) =>
|
||||||
|
{
|
||||||
|
bool buttonsEnabled = AllowUsingButtons;
|
||||||
|
foreach (var child in component.Children)
|
||||||
|
{
|
||||||
|
if (!(child is GUIButton)) { continue; }
|
||||||
|
if (!(child.UserData is int)) { continue; }
|
||||||
|
child.Enabled = buttonsEnabled;
|
||||||
|
child.Children.ForEach(c => c.Enabled = buttonsEnabled);
|
||||||
|
}
|
||||||
|
bool itemsContained = Container.Inventory.AllItems.Any();
|
||||||
|
if (itemsContained)
|
||||||
|
{
|
||||||
|
var indicatorStyle = buttonsEnabled ? indicatorStyleGreen : indicatorStyleRed;
|
||||||
|
if (containerIndicator.Style != indicatorStyle)
|
||||||
|
{
|
||||||
|
containerIndicator.ApplyStyle(indicatorStyle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
containerIndicator.OverrideState = itemsContained ? GUIComponent.ComponentState.Selected : GUIComponent.ComponentState.None;
|
||||||
|
};
|
||||||
|
|
||||||
|
float x = 1.0f / (1 + RequiredSignalCount);
|
||||||
|
float y = (x * paddedFrame.Rect.Width) / paddedFrame.Rect.Height;
|
||||||
|
Vector2 relativeSize = new Vector2(x, y);
|
||||||
|
|
||||||
|
var containerSection = new GUIFrame(new RectTransform(new Vector2(x, 1.0f), paddedFrame.RectTransform), style: null);
|
||||||
|
var containerSlot = new GUIFrame(new RectTransform(new Vector2(1.0f, y), containerSection.RectTransform, anchor: Anchor.Center), style: null);
|
||||||
|
containerHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.2f), containerSlot.RectTransform, Anchor.BottomCenter), style: null);
|
||||||
|
containerIndicator = new GUIImage(new RectTransform(new Vector2(0.5f, 0.5f * y), containerSection.RectTransform, anchor: Anchor.Center) { RelativeOffset = new Vector2(0.0f, 0.05f + 0.5f * y) },
|
||||||
|
style: "IndicatorLightRed", scaleToFit: true);
|
||||||
|
|
||||||
|
for (int i = 0; i < RequiredSignalCount; i++)
|
||||||
|
{
|
||||||
|
var button = new GUIButton(new RectTransform(relativeSize, paddedFrame.RectTransform), style: null)
|
||||||
|
{
|
||||||
|
UserData = i,
|
||||||
|
OnClicked = (button, userData) =>
|
||||||
|
{
|
||||||
|
if (GameMain.IsSingleplayer)
|
||||||
|
{
|
||||||
|
SendSignal((int)userData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.CreateClientEvent(this, new object[] { userData });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var image = new GUIImage(new RectTransform(Vector2.One, button.RectTransform), terminalButtonStyles[i], scaleToFit: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnResolutionChanged()
|
||||||
|
{
|
||||||
|
base.OnResolutionChanged();
|
||||||
|
OnItemLoadedProjSpecific();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnItemLoadedProjSpecific()
|
||||||
|
{
|
||||||
|
Container.AllowUIOverlap = true;
|
||||||
|
Container.Inventory.RectTransform = containerHolder.RectTransform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||||
|
{
|
||||||
|
Write(msg, extraData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||||
|
{
|
||||||
|
SendSignal(msg.ReadRangedInteger(0, Signals.Length - 1), isServerMessage: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -276,7 +276,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
BrokenItemSprite fadeInBrokenSprite = null;
|
BrokenItemSprite fadeInBrokenSprite = null;
|
||||||
float fadeInBrokenSpriteAlpha = 0.0f;
|
float fadeInBrokenSpriteAlpha = 0.0f;
|
||||||
float displayCondition = FakeBroken ? 0.0f : condition;
|
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
|
||||||
Vector2 drawOffset = Vector2.Zero;
|
Vector2 drawOffset = Vector2.Zero;
|
||||||
if (displayCondition < MaxCondition)
|
if (displayCondition < MaxCondition)
|
||||||
{
|
{
|
||||||
@@ -326,9 +326,14 @@ namespace Barotrauma
|
|||||||
size, color: color,
|
size, color: color,
|
||||||
textureScale: Vector2.One * Scale,
|
textureScale: Vector2.One * Scale,
|
||||||
depth: depth);
|
depth: depth);
|
||||||
fadeInBrokenSprite?.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + fadeInBrokenSprite.Offset.ToVector2() * Scale, size, color: color * fadeInBrokenSpriteAlpha,
|
|
||||||
textureScale: Vector2.One * Scale,
|
if (fadeInBrokenSprite != null)
|
||||||
depth: depth - 0.000001f);
|
{
|
||||||
|
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
|
||||||
|
fadeInBrokenSprite.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + fadeInBrokenSprite.Offset.ToVector2() * Scale, size, color: color * fadeInBrokenSpriteAlpha,
|
||||||
|
textureScale: Vector2.One * Scale,
|
||||||
|
depth: d);
|
||||||
|
}
|
||||||
foreach (var decorativeSprite in Prefab.DecorativeSprites)
|
foreach (var decorativeSprite in Prefab.DecorativeSprites)
|
||||||
{
|
{
|
||||||
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
||||||
@@ -357,7 +362,11 @@ namespace Barotrauma
|
|||||||
if (color.A > 0)
|
if (color.A > 0)
|
||||||
{
|
{
|
||||||
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth);
|
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth);
|
||||||
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f);
|
if (fadeInBrokenSprite != null)
|
||||||
|
{
|
||||||
|
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
|
||||||
|
fadeInBrokenSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Infector != null && (Infector.ParentBallastFlora.HasBrokenThrough || BallastFloraBehavior.AlwaysShowBallastFloraSprite))
|
if (Infector != null && (Infector.ParentBallastFlora.HasBrokenThrough || BallastFloraBehavior.AlwaysShowBallastFloraSprite))
|
||||||
{
|
{
|
||||||
@@ -410,8 +419,11 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
body.Draw(spriteBatch, activeSprite, color, depth, Scale);
|
body.Draw(spriteBatch, activeSprite, color, depth, Scale);
|
||||||
if (fadeInBrokenSprite != null) { body.Draw(spriteBatch, fadeInBrokenSprite.Sprite, color * fadeInBrokenSpriteAlpha, depth - 0.000001f, Scale); }
|
if (fadeInBrokenSprite != null)
|
||||||
|
{
|
||||||
|
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
|
||||||
|
body.Draw(spriteBatch, fadeInBrokenSprite.Sprite, color * fadeInBrokenSpriteAlpha, d, Scale);
|
||||||
|
}
|
||||||
foreach (var decorativeSprite in Prefab.DecorativeSprites)
|
foreach (var decorativeSprite in Prefab.DecorativeSprites)
|
||||||
{
|
{
|
||||||
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
||||||
@@ -464,6 +476,11 @@ namespace Barotrauma
|
|||||||
if (GameMain.DebugDraw)
|
if (GameMain.DebugDraw)
|
||||||
{
|
{
|
||||||
body?.DebugDraw(spriteBatch, Color.White);
|
body?.DebugDraw(spriteBatch, Color.White);
|
||||||
|
if (GetComponent<TriggerComponent>()?.PhysicsBody is PhysicsBody triggerBody)
|
||||||
|
{
|
||||||
|
triggerBody.UpdateDrawPosition();
|
||||||
|
triggerBody.DebugDraw(spriteBatch, Color.White);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editing && IsSelected && PlayerInput.KeyDown(Keys.Space))
|
if (editing && IsSelected && PlayerInput.KeyDown(Keys.Space))
|
||||||
|
|||||||
@@ -7,14 +7,9 @@ namespace Barotrauma.RuinGeneration
|
|||||||
{
|
{
|
||||||
public void DebugDraw(SpriteBatch spriteBatch)
|
public void DebugDraw(SpriteBatch spriteBatch)
|
||||||
{
|
{
|
||||||
foreach (RuinShape shape in allShapes)
|
Rectangle drawRect = Area;
|
||||||
{
|
drawRect.Y = -drawRect.Y - Area.Height;
|
||||||
GUI.DrawString(spriteBatch, new Vector2(shape.Center.X, -shape.Center.Y - 50), shape.DistanceFromEntrance.ToString(), Color.White, Color.Black * 0.5f, font: GUI.LargeFont);
|
GUI.DrawRectangle(spriteBatch, drawRect, Color.Cyan, false, 0, 6);
|
||||||
}
|
|
||||||
foreach (Line line in walls)
|
|
||||||
{
|
|
||||||
GUI.DrawLine(spriteBatch, new Vector2(line.A.X, -line.A.Y), new Vector2(line.B.X, -line.B.Y), GUI.Style.Red, 0.0f, 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -846,16 +846,15 @@ namespace Barotrauma.Lights
|
|||||||
if (chList.Submarine == null)
|
if (chList.Submarine == null)
|
||||||
{
|
{
|
||||||
list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
|
list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
|
||||||
|
|
||||||
}
|
}
|
||||||
//light is outside, convexhull inside a sub
|
//light is outside, convexhull inside a sub
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Rectangle subBorders = chList.Submarine.Borders;
|
Rectangle subBorders = chList.Submarine.Borders;
|
||||||
subBorders.Y -= chList.Submarine.Borders.Height;
|
subBorders.Y -= chList.Submarine.Borders.Height;
|
||||||
if (!MathUtils.CircleIntersectsRectangle(lightPos - chList.Submarine.WorldPosition, range, subBorders)) continue;
|
if (!MathUtils.CircleIntersectsRectangle(lightPos - chList.Submarine.WorldPosition, range, subBorders)) { continue; }
|
||||||
|
|
||||||
lightPos -= (chList.Submarine.WorldPosition - chList.Submarine.HiddenSubPosition);
|
lightPos -= chList.Submarine.WorldPosition - chList.Submarine.HiddenSubPosition;
|
||||||
|
|
||||||
list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
|
list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
|
||||||
}
|
}
|
||||||
@@ -865,14 +864,6 @@ namespace Barotrauma.Lights
|
|||||||
//light is inside, convexhull outside
|
//light is inside, convexhull outside
|
||||||
if (chList.Submarine == null)
|
if (chList.Submarine == null)
|
||||||
{
|
{
|
||||||
lightPos += (ParentSub.WorldPosition - ParentSub.HiddenSubPosition);
|
|
||||||
HashSet<RuinGeneration.Ruin> visibleRuins = new HashSet<RuinGeneration.Ruin>();
|
|
||||||
foreach (RuinGeneration.Ruin ruin in Level.Loaded.Ruins)
|
|
||||||
{
|
|
||||||
if (!MathUtils.CircleIntersectsRectangle(lightPos, range, ruin.Area)) { continue; }
|
|
||||||
visibleRuins.Add(ruin);
|
|
||||||
}
|
|
||||||
list.AddRange(chList.List.FindAll(ch => ch.ParentEntity?.ParentRuin != null && visibleRuins.Contains(ch.ParentEntity.ParentRuin)));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//light and convexhull are both inside the same sub
|
//light and convexhull are both inside the same sub
|
||||||
|
|||||||
@@ -1291,7 +1291,7 @@ namespace Barotrauma.Lights
|
|||||||
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
|
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
|
||||||
drawPos.Y = -drawPos.Y;
|
drawPos.Y = -drawPos.Y;
|
||||||
|
|
||||||
spriteBatch.Draw(currentTexture, drawPos, null, Color.Multiply(CurrentBrightness), -rotation, center, scale, SpriteEffects.None, 1);
|
spriteBatch.Draw(currentTexture, drawPos, null, Color.Multiply(CurrentBrightness), -rotation + MathHelper.ToRadians(LightSourceParams.Rotation), center, scale, SpriteEffects.None, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
//drawing ----------------------------------------------------
|
//drawing ----------------------------------------------------
|
||||||
private static readonly HashSet<Submarine> visibleSubs = new HashSet<Submarine>();
|
private static readonly HashSet<Submarine> visibleSubs = new HashSet<Submarine>();
|
||||||
private static readonly HashSet<Ruin> visibleRuins = new HashSet<Ruin>();
|
|
||||||
public static void CullEntities(Camera cam)
|
public static void CullEntities(Camera cam)
|
||||||
{
|
{
|
||||||
visibleSubs.Clear();
|
visibleSubs.Clear();
|
||||||
@@ -198,24 +197,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visibleRuins.Clear();
|
|
||||||
if (Level.Loaded != null)
|
|
||||||
{
|
|
||||||
foreach (Ruin ruin in Level.Loaded.Ruins)
|
|
||||||
{
|
|
||||||
Rectangle worldBorders = new Rectangle(
|
|
||||||
ruin.Area.X - 500,
|
|
||||||
ruin.Area.Y + ruin.Area.Height + 500,
|
|
||||||
ruin.Area.Width + 1000,
|
|
||||||
ruin.Area.Height + 1000);
|
|
||||||
|
|
||||||
if (RectsOverlap(worldBorders, cam.WorldView))
|
|
||||||
{
|
|
||||||
visibleRuins.Add(ruin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visibleEntities == null)
|
if (visibleEntities == null)
|
||||||
{
|
{
|
||||||
visibleEntities = new List<MapEntity>(MapEntity.mapEntityList.Count);
|
visibleEntities = new List<MapEntity>(MapEntity.mapEntityList.Count);
|
||||||
@@ -232,10 +213,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (!visibleSubs.Contains(entity.Submarine)) { continue; }
|
if (!visibleSubs.Contains(entity.Submarine)) { continue; }
|
||||||
}
|
}
|
||||||
else if (entity.ParentRuin != null)
|
|
||||||
{
|
|
||||||
if (!visibleRuins.Contains(entity.ParentRuin)) { continue; }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.IsVisible(worldView)) { visibleEntities.Add(entity); }
|
if (entity.IsVisible(worldView)) { visibleEntities.Add(entity); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -600,6 +600,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
var prevScissorRect = GameMain.Instance.GraphicsDevice.ScissorRectangle;
|
var prevScissorRect = GameMain.Instance.GraphicsDevice.ScissorRectangle;
|
||||||
GameMain.Instance.GraphicsDevice.ScissorRectangle = scissorRectangle;
|
GameMain.Instance.GraphicsDevice.ScissorRectangle = scissorRectangle;
|
||||||
|
var prevRasterizerState = GameMain.Instance.GraphicsDevice.RasterizerState;
|
||||||
|
GameMain.Instance.GraphicsDevice.RasterizerState = GameMain.ScissorTestEnable;
|
||||||
|
|
||||||
spriteRecorder.Render(camera);
|
spriteRecorder.Render(camera);
|
||||||
|
|
||||||
@@ -643,6 +645,7 @@ namespace Barotrauma
|
|||||||
spriteBatch.End();
|
spriteBatch.End();
|
||||||
|
|
||||||
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||||
|
GameMain.Instance.GraphicsDevice.RasterizerState = prevRasterizerState;
|
||||||
spriteBatch.Begin(SpriteSortMode.Deferred);
|
spriteBatch.Begin(SpriteSortMode.Deferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -171,10 +171,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (MapEntity e in mapEntityList)
|
foreach (MapEntity e in mapEntityList)
|
||||||
{
|
{
|
||||||
if (e.GetType() != typeof(WayPoint)) continue;
|
if (!(e is WayPoint) || e == this || !e.IsHighlighted) { continue; }
|
||||||
if (e == this) continue;
|
|
||||||
|
|
||||||
if (!Submarine.RectContains(e.Rect, position)) continue;
|
|
||||||
|
|
||||||
if (linkedTo.Contains(e))
|
if (linkedTo.Contains(e))
|
||||||
{
|
{
|
||||||
|
|||||||
+16
-1
@@ -287,7 +287,9 @@ namespace Barotrauma
|
|||||||
|
|
||||||
characterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.275f), subLayout.RectTransform));
|
characterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.275f), subLayout.RectTransform));
|
||||||
|
|
||||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), subLayout.RectTransform), job.Name, job.UIColor);
|
var jobTextContainer =
|
||||||
|
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), subLayout.RectTransform), style: null);
|
||||||
|
var jobText = new GUITextBlock(new RectTransform(Vector2.One, jobTextContainer.RectTransform), job.Name, job.UIColor);
|
||||||
|
|
||||||
var characterName = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.1f), subLayout.RectTransform))
|
var characterName = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.1f), subLayout.RectTransform))
|
||||||
{
|
{
|
||||||
@@ -327,8 +329,11 @@ namespace Barotrauma
|
|||||||
characterName.Text = characterInfo.Name;
|
characterName.Text = characterInfo.Name;
|
||||||
characterName.UserData = "random";
|
characterName.UserData = "random";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StealRandomizeButton(menu, jobTextContainer);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
StealRandomizeButton(CharacterMenus[i], jobTextContainer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,6 +386,16 @@ namespace Barotrauma
|
|||||||
maxMissionCountContainer.Children.ForEach(c => c.ToolTip = maxMissionCountSettingHolder.ToolTip);
|
maxMissionCountContainer.Children.ForEach(c => c.ToolTip = maxMissionCountSettingHolder.ToolTip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void StealRandomizeButton(CharacterInfo.AppearanceCustomizationMenu menu, GUIComponent parent)
|
||||||
|
{
|
||||||
|
//This is just stupid
|
||||||
|
var randomizeButton = menu.RandomizeButton;
|
||||||
|
var oldButton = parent.GetChild<GUIButton>();
|
||||||
|
parent.RemoveChild(oldButton);
|
||||||
|
randomizeButton.RectTransform.Parent = parent.RectTransform;
|
||||||
|
randomizeButton.RectTransform.RelativeSize = Vector2.One * 1.3f;
|
||||||
|
}
|
||||||
|
|
||||||
private bool FinishSetup(GUIButton btn, object userdata)
|
private bool FinishSetup(GUIButton btn, object userdata)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(saveNameBox.Text))
|
if (string.IsNullOrWhiteSpace(saveNameBox.Text))
|
||||||
|
|||||||
+29
-30
@@ -483,21 +483,18 @@ namespace Barotrauma.CharacterEditor
|
|||||||
// It's possible that the physics are disabled, because the angle widgets handle input logic in the draw method (which they shouldn't)
|
// It's possible that the physics are disabled, because the angle widgets handle input logic in the draw method (which they shouldn't)
|
||||||
character.AnimController.Collider.PhysEnabled = true;
|
character.AnimController.Collider.PhysEnabled = true;
|
||||||
}
|
}
|
||||||
if (character.IsHumanoid)
|
animTestPoseToggle.Enabled = CurrentAnimation.IsGroundedAnimation;
|
||||||
|
if (animTestPoseToggle.Enabled)
|
||||||
{
|
{
|
||||||
animTestPoseToggle.Enabled = CurrentAnimation.IsGroundedAnimation;
|
if (PlayerInput.KeyHit(Keys.X))
|
||||||
if (animTestPoseToggle.Enabled)
|
|
||||||
{
|
{
|
||||||
if (PlayerInput.KeyHit(Keys.X))
|
SetToggle(animTestPoseToggle, !animTestPoseToggle.Selected);
|
||||||
{
|
|
||||||
SetToggle(animTestPoseToggle, !animTestPoseToggle.Selected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
animTestPoseToggle.Selected = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
animTestPoseToggle.Selected = false;
|
||||||
|
}
|
||||||
if (PlayerInput.KeyHit(InputType.Run))
|
if (PlayerInput.KeyHit(InputType.Run))
|
||||||
{
|
{
|
||||||
// TODO: refactor this horrible hacky index manipulation mess
|
// TODO: refactor this horrible hacky index manipulation mess
|
||||||
@@ -1072,7 +1069,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
{
|
{
|
||||||
if (jointCreationMode == JointCreationMode.Create)
|
if (jointCreationMode == JointCreationMode.Create)
|
||||||
{
|
{
|
||||||
jointEndLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => l != null && l != jointStartLimb && l.ActiveSprite != null);
|
jointEndLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => l != null && l != jointStartLimb && l.ActiveSprite != null && !l.Hidden);
|
||||||
if (jointEndLimb != null && PlayerInput.PrimaryMouseButtonClicked())
|
if (jointEndLimb != null && PlayerInput.PrimaryMouseButtonClicked())
|
||||||
{
|
{
|
||||||
Vector2 anchor1 = anchor1Pos.HasValue ? anchor1Pos.Value / spriteSheetZoom : Vector2.Zero;
|
Vector2 anchor1 = anchor1Pos.HasValue ? anchor1Pos.Value / spriteSheetZoom : Vector2.Zero;
|
||||||
@@ -1085,7 +1082,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
}
|
}
|
||||||
else if (PlayerInput.PrimaryMouseButtonClicked())
|
else if (PlayerInput.PrimaryMouseButtonClicked())
|
||||||
{
|
{
|
||||||
jointStartLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => selectedLimbs.Contains(l));
|
jointStartLimb = GetClosestLimbOnSpritesheet(PlayerInput.MousePosition, l => selectedLimbs.Contains(l) && !l.Hidden);
|
||||||
anchor1Pos = GetLimbSpritesheetRect(jointStartLimb).Center.ToVector2() - PlayerInput.MousePosition;
|
anchor1Pos = GetLimbSpritesheetRect(jointStartLimb).Center.ToVector2() - PlayerInput.MousePosition;
|
||||||
jointCreationMode = JointCreationMode.Create;
|
jointCreationMode = JointCreationMode.Create;
|
||||||
}
|
}
|
||||||
@@ -1094,7 +1091,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
{
|
{
|
||||||
if (jointCreationMode == JointCreationMode.Create)
|
if (jointCreationMode == JointCreationMode.Create)
|
||||||
{
|
{
|
||||||
jointEndLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => l != null && l != jointStartLimb && l.ActiveSprite != null);
|
jointEndLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => l != null && l != jointStartLimb && l.ActiveSprite != null && !l.Hidden);
|
||||||
if (jointEndLimb != null && PlayerInput.PrimaryMouseButtonClicked())
|
if (jointEndLimb != null && PlayerInput.PrimaryMouseButtonClicked())
|
||||||
{
|
{
|
||||||
Vector2 anchor1 = anchor1Pos ?? Vector2.Zero;
|
Vector2 anchor1 = anchor1Pos ?? Vector2.Zero;
|
||||||
@@ -1105,7 +1102,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
}
|
}
|
||||||
else if (PlayerInput.PrimaryMouseButtonClicked())
|
else if (PlayerInput.PrimaryMouseButtonClicked())
|
||||||
{
|
{
|
||||||
jointStartLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => selectedLimbs.Contains(l));
|
jointStartLimb = GetClosestLimbOnRagdoll(PlayerInput.MousePosition, l => selectedLimbs.Contains(l) && !l.Hidden);
|
||||||
anchor1Pos = ConvertUnits.ToDisplayUnits(jointStartLimb.body.FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
|
anchor1Pos = ConvertUnits.ToDisplayUnits(jointStartLimb.body.FarseerBody.GetLocalPoint(ScreenToSim(PlayerInput.MousePosition)));
|
||||||
jointCreationMode = JointCreationMode.Create;
|
jointCreationMode = JointCreationMode.Create;
|
||||||
}
|
}
|
||||||
@@ -1185,8 +1182,15 @@ namespace Barotrauma.CharacterEditor
|
|||||||
|
|
||||||
private void CreateLimb(XElement newElement)
|
private void CreateLimb(XElement newElement)
|
||||||
{
|
{
|
||||||
var lastLimbElement = RagdollParams.MainElement.Elements("limb").Last();
|
var lastElement = RagdollParams.MainElement.GetChildElements("limb").LastOrDefault();
|
||||||
lastLimbElement.AddAfterSelf(newElement);
|
if (lastElement != null)
|
||||||
|
{
|
||||||
|
lastElement.AddAfterSelf(newElement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RagdollParams.MainElement.AddFirst(newElement);
|
||||||
|
}
|
||||||
var newLimbParams = new RagdollParams.LimbParams(newElement, RagdollParams);
|
var newLimbParams = new RagdollParams.LimbParams(newElement, RagdollParams);
|
||||||
RagdollParams.Limbs.Add(newLimbParams);
|
RagdollParams.Limbs.Add(newLimbParams);
|
||||||
character.AnimController.Recreate();
|
character.AnimController.Recreate();
|
||||||
@@ -1217,12 +1221,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
new XAttribute("limb1anchor", $"{a1.X.Format(2)}, {a1.Y.Format(2)}"),
|
new XAttribute("limb1anchor", $"{a1.X.Format(2)}, {a1.Y.Format(2)}"),
|
||||||
new XAttribute("limb2anchor", $"{a2.X.Format(2)}, {a2.Y.Format(2)}")
|
new XAttribute("limb2anchor", $"{a2.X.Format(2)}, {a2.Y.Format(2)}")
|
||||||
);
|
);
|
||||||
var lastJointElement = RagdollParams.MainElement.Elements("joint").LastOrDefault();
|
var lastJointElement = RagdollParams.MainElement.GetChildElements("joint").LastOrDefault() ?? RagdollParams.MainElement.GetChildElements("limb").LastOrDefault();
|
||||||
if (lastJointElement == null)
|
|
||||||
{
|
|
||||||
// If no joints exist, use the last limb element.
|
|
||||||
lastJointElement = RagdollParams.MainElement.Elements("limb").LastOrDefault();
|
|
||||||
}
|
|
||||||
if (lastJointElement == null)
|
if (lastJointElement == null)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError(GetCharacterEditorTranslation("CantAddJointsNoLimbElements"));
|
DebugConsole.ThrowError(GetCharacterEditorTranslation("CantAddJointsNoLimbElements"));
|
||||||
@@ -2196,7 +2195,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
animTestPoseToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("AnimationTestPose"))
|
animTestPoseToggle = new GUITickBox(new RectTransform(toggleSize, layoutGroup.RectTransform), GetCharacterEditorTranslation("AnimationTestPose"))
|
||||||
{
|
{
|
||||||
Selected = character.AnimController.AnimationTestPose,
|
Selected = character.AnimController.AnimationTestPose,
|
||||||
Enabled = character.IsHumanoid,
|
Enabled = true,
|
||||||
OnSelected = box =>
|
OnSelected = box =>
|
||||||
{
|
{
|
||||||
character.AnimController.AnimationTestPose = box.Selected;
|
character.AnimController.AnimationTestPose = box.Selected;
|
||||||
@@ -2760,7 +2759,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!string.IsNullOrEmpty(RagdollParams.Texture) && !File.Exists(RagdollParams.Texture))
|
if (!character.IsHuman && !string.IsNullOrEmpty(RagdollParams.Texture) && !File.Exists(RagdollParams.Texture))
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError($"Invalid texture path: {RagdollParams.Texture}");
|
DebugConsole.ThrowError($"Invalid texture path: {RagdollParams.Texture}");
|
||||||
return false;
|
return false;
|
||||||
@@ -3863,7 +3862,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
{
|
{
|
||||||
// Head angle
|
// Head angle
|
||||||
DrawRadialWidget(spriteBatch, SimToScreen(head.SimPosition), animParams.HeadAngle, GetCharacterEditorTranslation("HeadAngle"), Color.White,
|
DrawRadialWidget(spriteBatch, SimToScreen(head.SimPosition), animParams.HeadAngle, GetCharacterEditorTranslation("HeadAngle"), Color.White,
|
||||||
angle => TryUpdateAnimParam("headangle", angle), circleRadius: 25, rotationOffset: collider.Rotation + MathHelper.Pi, clockWise: dir < 0, wrapAnglePi: true, holdPosition: true);
|
angle => TryUpdateAnimParam("headangle", angle), circleRadius: 25, rotationOffset: -collider.Rotation + head.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi: true, holdPosition: true);
|
||||||
// Head position and leaning
|
// Head position and leaning
|
||||||
Color color = GUI.Style.Red;
|
Color color = GUI.Style.Red;
|
||||||
if (animParams.IsGroundedAnimation)
|
if (animParams.IsGroundedAnimation)
|
||||||
@@ -3972,7 +3971,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
}
|
}
|
||||||
// Torso angle
|
// Torso angle
|
||||||
DrawRadialWidget(spriteBatch, SimToScreen(referencePoint), animParams.TorsoAngle, GetCharacterEditorTranslation("TorsoAngle"), Color.White,
|
DrawRadialWidget(spriteBatch, SimToScreen(referencePoint), animParams.TorsoAngle, GetCharacterEditorTranslation("TorsoAngle"), Color.White,
|
||||||
angle => TryUpdateAnimParam("torsoangle", angle), rotationOffset: collider.Rotation + MathHelper.Pi, clockWise: dir < 0, wrapAnglePi: true, holdPosition: true);
|
angle => TryUpdateAnimParam("torsoangle", angle), rotationOffset: -collider.Rotation + torso.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi: true, holdPosition: true);
|
||||||
Color color = Color.DodgerBlue;
|
Color color = Color.DodgerBlue;
|
||||||
if (animParams.IsGroundedAnimation)
|
if (animParams.IsGroundedAnimation)
|
||||||
{
|
{
|
||||||
@@ -4075,7 +4074,7 @@ namespace Barotrauma.CharacterEditor
|
|||||||
if (tail != null && fishParams != null)
|
if (tail != null && fishParams != null)
|
||||||
{
|
{
|
||||||
DrawRadialWidget(spriteBatch, SimToScreen(tail.SimPosition), fishParams.TailAngle, GetCharacterEditorTranslation("TailAngle"), Color.White,
|
DrawRadialWidget(spriteBatch, SimToScreen(tail.SimPosition), fishParams.TailAngle, GetCharacterEditorTranslation("TailAngle"), Color.White,
|
||||||
angle => TryUpdateAnimParam("tailangle", angle), circleRadius: 25, rotationOffset: collider.Rotation + MathHelper.Pi, clockWise: dir < 0, wrapAnglePi: true, holdPosition: true);
|
angle => TryUpdateAnimParam("tailangle", angle), circleRadius: 25, rotationOffset: -collider.Rotation + tail.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi: true, holdPosition: true);
|
||||||
}
|
}
|
||||||
// Foot angle
|
// Foot angle
|
||||||
if (foot != null)
|
if (foot != null)
|
||||||
@@ -4101,13 +4100,13 @@ namespace Barotrauma.CharacterEditor
|
|||||||
fishParams.FootAnglesInRadians[limb.Params.ID] = MathHelper.ToRadians(angle);
|
fishParams.FootAnglesInRadians[limb.Params.ID] = MathHelper.ToRadians(angle);
|
||||||
TryUpdateAnimParam("footangles", fishParams.FootAngles);
|
TryUpdateAnimParam("footangles", fishParams.FootAngles);
|
||||||
},
|
},
|
||||||
circleRadius: 25, rotationOffset: collider.Rotation, clockWise: dir < 0, wrapAnglePi: true, autoFreeze: true);
|
circleRadius: 25, rotationOffset: -collider.Rotation + limb.Params.GetSpriteOrientation() * dir, clockWise: dir < 0, wrapAnglePi: true, autoFreeze: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (humanParams != null)
|
else if (humanParams != null)
|
||||||
{
|
{
|
||||||
DrawRadialWidget(spriteBatch, SimToScreen(foot.SimPosition), humanParams.FootAngle, GetCharacterEditorTranslation("FootAngle"), Color.White,
|
DrawRadialWidget(spriteBatch, SimToScreen(foot.SimPosition), humanParams.FootAngle, GetCharacterEditorTranslation("FootAngle"), Color.White,
|
||||||
angle => TryUpdateAnimParam("footangle", angle), circleRadius: 25, rotationOffset: collider.Rotation + MathHelper.Pi, clockWise: dir < 0, wrapAnglePi: true);
|
angle => TryUpdateAnimParam("footangle", angle), circleRadius: 25, rotationOffset: -collider.Rotation + foot.Params.GetSpriteOrientation() * dir, clockWise: dir > 0, wrapAnglePi: true);
|
||||||
}
|
}
|
||||||
// Grounded only
|
// Grounded only
|
||||||
if (groundedParams != null)
|
if (groundedParams != null)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private readonly GUITextBox seedBox;
|
private readonly GUITextBox seedBox;
|
||||||
|
|
||||||
private readonly GUITickBox lightingEnabled, cursorLightEnabled;
|
private readonly GUITickBox lightingEnabled, cursorLightEnabled, mirrorLevel;
|
||||||
|
|
||||||
private Sprite editingSprite;
|
private Sprite editingSprite;
|
||||||
|
|
||||||
@@ -74,9 +74,7 @@ namespace Barotrauma
|
|||||||
ruinParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.1f), paddedLeftPanel.RectTransform));
|
ruinParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.1f), paddedLeftPanel.RectTransform));
|
||||||
ruinParamsList.OnSelected += (GUIComponent component, object obj) =>
|
ruinParamsList.OnSelected += (GUIComponent component, object obj) =>
|
||||||
{
|
{
|
||||||
var ruinGenerationParams = obj as RuinGenerationParams;
|
CreateOutpostGenerationParamsEditor(obj as OutpostGenerationParams);
|
||||||
editorContainer.ClearChildren();
|
|
||||||
new SerializableEntityEditor(editorContainer.Content.RectTransform, ruinGenerationParams, false, true, elementHeight: 20);
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -95,101 +93,7 @@ namespace Barotrauma
|
|||||||
outpostParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), paddedLeftPanel.RectTransform));
|
outpostParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), paddedLeftPanel.RectTransform));
|
||||||
outpostParamsList.OnSelected += (GUIComponent component, object obj) =>
|
outpostParamsList.OnSelected += (GUIComponent component, object obj) =>
|
||||||
{
|
{
|
||||||
var outpostGenerationParams = obj as OutpostGenerationParams;
|
CreateOutpostGenerationParamsEditor(obj as OutpostGenerationParams);
|
||||||
editorContainer.ClearChildren();
|
|
||||||
var outpostParamsEditor = new SerializableEntityEditor(editorContainer.Content.RectTransform, outpostGenerationParams, false, true, elementHeight: 20);
|
|
||||||
|
|
||||||
// location type -------------------------
|
|
||||||
|
|
||||||
var locationTypeGroup = new GUILayoutGroup(new RectTransform(new Point(editorContainer.Content.Rect.Width, 20)), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
|
||||||
{
|
|
||||||
Stretch = true
|
|
||||||
};
|
|
||||||
|
|
||||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), locationTypeGroup.RectTransform), TextManager.Get("outpostmoduleallowedlocationtypes"), textAlignment: Alignment.CenterLeft);
|
|
||||||
HashSet<string> availableLocationTypes = new HashSet<string> { "any" };
|
|
||||||
foreach (LocationType locationType in LocationType.List) { availableLocationTypes.Add(locationType.Identifier); }
|
|
||||||
|
|
||||||
var locationTypeDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), locationTypeGroup.RectTransform),
|
|
||||||
text: string.Join(", ", outpostGenerationParams.AllowedLocationTypes.Select(lt => TextManager.Capitalize(lt)) ?? "any".ToEnumerable()), selectMultiple: true);
|
|
||||||
foreach (string locationType in availableLocationTypes)
|
|
||||||
{
|
|
||||||
locationTypeDropDown.AddItem(TextManager.Capitalize(locationType), locationType);
|
|
||||||
if (outpostGenerationParams.AllowedLocationTypes.Contains(locationType))
|
|
||||||
{
|
|
||||||
locationTypeDropDown.SelectItem(locationType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!outpostGenerationParams.AllowedLocationTypes.Any())
|
|
||||||
{
|
|
||||||
locationTypeDropDown.SelectItem("any");
|
|
||||||
}
|
|
||||||
|
|
||||||
locationTypeDropDown.OnSelected += (_, __) =>
|
|
||||||
{
|
|
||||||
outpostGenerationParams.SetAllowedLocationTypes(locationTypeDropDown.SelectedDataMultiple.Cast<string>());
|
|
||||||
locationTypeDropDown.Text = ToolBox.LimitString(locationTypeDropDown.Text, locationTypeDropDown.Font, locationTypeDropDown.Rect.Width);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
locationTypeGroup.RectTransform.MinSize = new Point(locationTypeGroup.Rect.Width, locationTypeGroup.RectTransform.Children.Max(c => c.MinSize.Y));
|
|
||||||
|
|
||||||
outpostParamsEditor.AddCustomContent(locationTypeGroup, 100);
|
|
||||||
|
|
||||||
// module count -------------------------
|
|
||||||
|
|
||||||
var moduleLabel = new GUITextBlock(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(70 * GUI.Scale))), TextManager.Get("submarinetype.outpostmodules"), font: GUI.SubHeadingFont);
|
|
||||||
outpostParamsEditor.AddCustomContent(moduleLabel, 100);
|
|
||||||
|
|
||||||
foreach (KeyValuePair<string, int> moduleCount in outpostGenerationParams.ModuleCounts)
|
|
||||||
{
|
|
||||||
var moduleCountGroup = new GUILayoutGroup(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(25 * GUI.Scale))), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
|
||||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), moduleCountGroup.RectTransform), TextManager.Capitalize(moduleCount.Key), textAlignment: Alignment.CenterLeft);
|
|
||||||
new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), moduleCountGroup.RectTransform), GUINumberInput.NumberType.Int)
|
|
||||||
{
|
|
||||||
MinValueInt = 0,
|
|
||||||
MaxValueInt = 100,
|
|
||||||
IntValue = moduleCount.Value,
|
|
||||||
OnValueChanged = (numInput) =>
|
|
||||||
{
|
|
||||||
outpostGenerationParams.SetModuleCount(moduleCount.Key, numInput.IntValue);
|
|
||||||
if (numInput.IntValue == 0)
|
|
||||||
{
|
|
||||||
outpostParamsList.Select(outpostParamsList.SelectedData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
moduleCountGroup.RectTransform.MinSize = new Point(moduleCountGroup.Rect.Width, moduleCountGroup.RectTransform.Children.Max(c => c.MinSize.Y));
|
|
||||||
outpostParamsEditor.AddCustomContent(moduleCountGroup, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add module count -------------------------
|
|
||||||
|
|
||||||
var addModuleCountGroup = new GUILayoutGroup(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(40 * GUI.Scale))), isHorizontal: true, childAnchor: Anchor.Center);
|
|
||||||
|
|
||||||
HashSet<string> availableFlags = new HashSet<string>();
|
|
||||||
foreach (string flag in OutpostGenerationParams.Params.SelectMany(p => p.ModuleCounts.Select(m => m.Key))) { availableFlags.Add(flag); }
|
|
||||||
foreach (var sub in SubmarineInfo.SavedSubmarines)
|
|
||||||
{
|
|
||||||
if (sub.OutpostModuleInfo == null) { continue; }
|
|
||||||
foreach (string flag in sub.OutpostModuleInfo.ModuleFlags) { availableFlags.Add(flag); }
|
|
||||||
}
|
|
||||||
|
|
||||||
var moduleTypeDropDown = new GUIDropDown(new RectTransform(new Vector2(0.8f, 0.8f), addModuleCountGroup.RectTransform),
|
|
||||||
text: TextManager.Get("leveleditor.addmoduletype"));
|
|
||||||
foreach (string flag in availableFlags)
|
|
||||||
{
|
|
||||||
if (outpostGenerationParams.ModuleCounts.Any(mc => mc.Key.Equals(flag, StringComparison.OrdinalIgnoreCase))) { continue; }
|
|
||||||
moduleTypeDropDown.AddItem(TextManager.Capitalize(flag), flag);
|
|
||||||
}
|
|
||||||
moduleTypeDropDown.OnSelected += (_, userdata) =>
|
|
||||||
{
|
|
||||||
outpostGenerationParams.SetModuleCount(userdata as string, 1);
|
|
||||||
outpostParamsList.Select(outpostParamsList.SelectedData);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
addModuleCountGroup.RectTransform.MinSize = new Point(addModuleCountGroup.Rect.Width, addModuleCountGroup.RectTransform.Children.Max(c => c.MinSize.Y));
|
|
||||||
outpostParamsEditor.AddCustomContent(addModuleCountGroup, 100);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -239,10 +143,12 @@ namespace Barotrauma
|
|||||||
|
|
||||||
editorContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedRightPanel.RectTransform));
|
editorContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedRightPanel.RectTransform));
|
||||||
|
|
||||||
var seedContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), paddedRightPanel.RectTransform), isHorizontal: true);
|
var seedContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.02f), paddedRightPanel.RectTransform), isHorizontal: true);
|
||||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), seedContainer.RectTransform), TextManager.Get("leveleditor.levelseed"));
|
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), seedContainer.RectTransform), TextManager.Get("leveleditor.levelseed"));
|
||||||
seedBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), seedContainer.RectTransform), ToolBox.RandomSeed(8));
|
seedBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), seedContainer.RectTransform), ToolBox.RandomSeed(8));
|
||||||
|
|
||||||
|
mirrorLevel = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.02f), paddedRightPanel.RectTransform), TextManager.Get("mirrorentityx"));
|
||||||
|
|
||||||
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), paddedRightPanel.RectTransform),
|
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), paddedRightPanel.RectTransform),
|
||||||
TextManager.Get("leveleditor.generate"))
|
TextManager.Get("leveleditor.generate"))
|
||||||
{
|
{
|
||||||
@@ -253,7 +159,7 @@ namespace Barotrauma
|
|||||||
GameMain.LightManager.ClearLights();
|
GameMain.LightManager.ClearLights();
|
||||||
LevelData levelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams);
|
LevelData levelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams);
|
||||||
levelData.ForceOutpostGenerationParams = outpostParamsList.SelectedData as OutpostGenerationParams;
|
levelData.ForceOutpostGenerationParams = outpostParamsList.SelectedData as OutpostGenerationParams;
|
||||||
Level.Generate(levelData, mirror: false);
|
Level.Generate(levelData, mirror: mirrorLevel.Selected);
|
||||||
GameMain.LightManager.AddLight(pointerLightSource);
|
GameMain.LightManager.AddLight(pointerLightSource);
|
||||||
if (!wasLevelLoaded || cam.Position.X < 0 || cam.Position.Y < 0 || cam.Position.Y > Level.Loaded.Size.X || cam.Position.Y > Level.Loaded.Size.Y)
|
if (!wasLevelLoaded || cam.Position.X < 0 || cam.Position.Y < 0 || cam.Position.Y > Level.Loaded.Size.X || cam.Position.Y > Level.Loaded.Size.Y)
|
||||||
{
|
{
|
||||||
@@ -408,7 +314,7 @@ namespace Barotrauma
|
|||||||
editorContainer.ClearChildren();
|
editorContainer.ClearChildren();
|
||||||
ruinParamsList.Content.ClearChildren();
|
ruinParamsList.Content.ClearChildren();
|
||||||
|
|
||||||
foreach (RuinGenerationParams genParams in RuinGenerationParams.List)
|
foreach (RuinGenerationParams genParams in RuinGenerationParams.RuinParams)
|
||||||
{
|
{
|
||||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), ruinParamsList.Content.RectTransform) { MinSize = new Point(0, 20) },
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), ruinParamsList.Content.RectTransform) { MinSize = new Point(0, 20) },
|
||||||
genParams.Name)
|
genParams.Name)
|
||||||
@@ -500,6 +406,104 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CreateOutpostGenerationParamsEditor(OutpostGenerationParams outpostGenerationParams)
|
||||||
|
{
|
||||||
|
editorContainer.ClearChildren();
|
||||||
|
var outpostParamsEditor = new SerializableEntityEditor(editorContainer.Content.RectTransform, outpostGenerationParams, false, true, elementHeight: 20);
|
||||||
|
|
||||||
|
// location type -------------------------
|
||||||
|
|
||||||
|
var locationTypeGroup = new GUILayoutGroup(new RectTransform(new Point(editorContainer.Content.Rect.Width, 20)), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||||
|
{
|
||||||
|
Stretch = true
|
||||||
|
};
|
||||||
|
|
||||||
|
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), locationTypeGroup.RectTransform), TextManager.Get("outpostmoduleallowedlocationtypes"), textAlignment: Alignment.CenterLeft);
|
||||||
|
HashSet<string> availableLocationTypes = new HashSet<string> { "any" };
|
||||||
|
foreach (LocationType locationType in LocationType.List) { availableLocationTypes.Add(locationType.Identifier); }
|
||||||
|
|
||||||
|
var locationTypeDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), locationTypeGroup.RectTransform),
|
||||||
|
text: string.Join(", ", outpostGenerationParams.AllowedLocationTypes.Select(lt => TextManager.Capitalize(lt)) ?? "any".ToEnumerable()), selectMultiple: true);
|
||||||
|
foreach (string locationType in availableLocationTypes)
|
||||||
|
{
|
||||||
|
locationTypeDropDown.AddItem(TextManager.Capitalize(locationType), locationType);
|
||||||
|
if (outpostGenerationParams.AllowedLocationTypes.Contains(locationType))
|
||||||
|
{
|
||||||
|
locationTypeDropDown.SelectItem(locationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!outpostGenerationParams.AllowedLocationTypes.Any())
|
||||||
|
{
|
||||||
|
locationTypeDropDown.SelectItem("any");
|
||||||
|
}
|
||||||
|
|
||||||
|
locationTypeDropDown.OnSelected += (_, __) =>
|
||||||
|
{
|
||||||
|
outpostGenerationParams.SetAllowedLocationTypes(locationTypeDropDown.SelectedDataMultiple.Cast<string>());
|
||||||
|
locationTypeDropDown.Text = ToolBox.LimitString(locationTypeDropDown.Text, locationTypeDropDown.Font, locationTypeDropDown.Rect.Width);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
locationTypeGroup.RectTransform.MinSize = new Point(locationTypeGroup.Rect.Width, locationTypeGroup.RectTransform.Children.Max(c => c.MinSize.Y));
|
||||||
|
|
||||||
|
outpostParamsEditor.AddCustomContent(locationTypeGroup, 100);
|
||||||
|
|
||||||
|
// module count -------------------------
|
||||||
|
|
||||||
|
var moduleLabel = new GUITextBlock(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(70 * GUI.Scale))), TextManager.Get("submarinetype.outpostmodules"), font: GUI.SubHeadingFont);
|
||||||
|
outpostParamsEditor.AddCustomContent(moduleLabel, 100);
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, int> moduleCount in outpostGenerationParams.ModuleCounts)
|
||||||
|
{
|
||||||
|
var moduleCountGroup = new GUILayoutGroup(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(25 * GUI.Scale))), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||||
|
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), moduleCountGroup.RectTransform), TextManager.Capitalize(moduleCount.Key), textAlignment: Alignment.CenterLeft);
|
||||||
|
new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), moduleCountGroup.RectTransform), GUINumberInput.NumberType.Int)
|
||||||
|
{
|
||||||
|
MinValueInt = 0,
|
||||||
|
MaxValueInt = 100,
|
||||||
|
IntValue = moduleCount.Value,
|
||||||
|
OnValueChanged = (numInput) =>
|
||||||
|
{
|
||||||
|
outpostGenerationParams.SetModuleCount(moduleCount.Key, numInput.IntValue);
|
||||||
|
if (numInput.IntValue == 0)
|
||||||
|
{
|
||||||
|
outpostParamsList.Select(outpostParamsList.SelectedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
moduleCountGroup.RectTransform.MinSize = new Point(moduleCountGroup.Rect.Width, moduleCountGroup.RectTransform.Children.Max(c => c.MinSize.Y));
|
||||||
|
outpostParamsEditor.AddCustomContent(moduleCountGroup, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add module count -------------------------
|
||||||
|
|
||||||
|
var addModuleCountGroup = new GUILayoutGroup(new RectTransform(new Point(editorContainer.Content.Rect.Width, (int)(40 * GUI.Scale))), isHorizontal: true, childAnchor: Anchor.Center);
|
||||||
|
|
||||||
|
HashSet<string> availableFlags = new HashSet<string>();
|
||||||
|
foreach (string flag in OutpostGenerationParams.Params.SelectMany(p => p.ModuleCounts.Select(m => m.Key))) { availableFlags.Add(flag); }
|
||||||
|
foreach (var sub in SubmarineInfo.SavedSubmarines)
|
||||||
|
{
|
||||||
|
if (sub.OutpostModuleInfo == null) { continue; }
|
||||||
|
foreach (string flag in sub.OutpostModuleInfo.ModuleFlags) { availableFlags.Add(flag); }
|
||||||
|
}
|
||||||
|
|
||||||
|
var moduleTypeDropDown = new GUIDropDown(new RectTransform(new Vector2(0.8f, 0.8f), addModuleCountGroup.RectTransform),
|
||||||
|
text: TextManager.Get("leveleditor.addmoduletype"));
|
||||||
|
foreach (string flag in availableFlags)
|
||||||
|
{
|
||||||
|
if (outpostGenerationParams.ModuleCounts.Any(mc => mc.Key.Equals(flag, StringComparison.OrdinalIgnoreCase))) { continue; }
|
||||||
|
moduleTypeDropDown.AddItem(TextManager.Capitalize(flag), flag);
|
||||||
|
}
|
||||||
|
moduleTypeDropDown.OnSelected += (_, userdata) =>
|
||||||
|
{
|
||||||
|
outpostGenerationParams.SetModuleCount(userdata as string, 1);
|
||||||
|
outpostParamsList.Select(outpostParamsList.SelectedData);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
addModuleCountGroup.RectTransform.MinSize = new Point(addModuleCountGroup.Rect.Width, addModuleCountGroup.RectTransform.Children.Max(c => c.MinSize.Y));
|
||||||
|
outpostParamsEditor.AddCustomContent(addModuleCountGroup, 100);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void CreateLevelObjectEditor(LevelObjectPrefab levelObjectPrefab)
|
private void CreateLevelObjectEditor(LevelObjectPrefab levelObjectPrefab)
|
||||||
{
|
{
|
||||||
editorContainer.ClearChildren();
|
editorContainer.ClearChildren();
|
||||||
|
|||||||
@@ -435,7 +435,7 @@ namespace Barotrauma
|
|||||||
menuTabs[(int)Tab.Settings] = new GUIFrame(new RectTransform(new Vector2(relativeSize.X, 0.8f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing },
|
menuTabs[(int)Tab.Settings] = new GUIFrame(new RectTransform(new Vector2(relativeSize.X, 0.8f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing },
|
||||||
style: null);
|
style: null);
|
||||||
|
|
||||||
menuTabs[(int)Tab.NewGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
|
menuTabs[(int)Tab.NewGame] = new GUIFrame(new RectTransform(relativeSize * new Vector2(1.0f, 1.15f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
|
||||||
menuTabs[(int)Tab.LoadGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
|
menuTabs[(int)Tab.LoadGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeSpacing });
|
||||||
|
|
||||||
CreateCampaignSetupUI();
|
CreateCampaignSetupUI();
|
||||||
|
|||||||
@@ -982,22 +982,24 @@ namespace Barotrauma
|
|||||||
Visible = false,
|
Visible = false,
|
||||||
CanBeFocused = false
|
CanBeFocused = false
|
||||||
};
|
};
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform),
|
else
|
||||||
TextManager.Get("MissionType." + missionType.ToString()))
|
|
||||||
{
|
{
|
||||||
UserData = (int)missionType,
|
missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform),
|
||||||
ToolTip = TextManager.Get("MissionTypeDescription." + missionType.ToString(), returnNull: true),
|
TextManager.Get("MissionType." + missionType.ToString()))
|
||||||
OnSelected = (tickbox) =>
|
|
||||||
{
|
{
|
||||||
int missionTypeOr = tickbox.Selected ? (int)tickbox.UserData : (int)MissionType.None;
|
UserData = (int)missionType,
|
||||||
int missionTypeAnd = (int)MissionType.All & (!tickbox.Selected ? (~(int)tickbox.UserData) : (int)MissionType.All);
|
ToolTip = TextManager.Get("MissionTypeDescription." + missionType.ToString(), returnNull: true),
|
||||||
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, (int)missionTypeOr, (int)missionTypeAnd);
|
OnSelected = (tickbox) =>
|
||||||
return true;
|
{
|
||||||
}
|
int missionTypeOr = tickbox.Selected ? (int)tickbox.UserData : (int)MissionType.None;
|
||||||
};
|
int missionTypeAnd = (int)MissionType.All & (!tickbox.Selected ? (~(int)tickbox.UserData) : (int)MissionType.All);
|
||||||
frame.RectTransform.MinSize = missionTypeTickBoxes[index].RectTransform.MinSize;
|
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, (int)missionTypeOr, (int)missionTypeAnd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
frame.RectTransform.MinSize = missionTypeTickBoxes[index].RectTransform.MinSize;
|
||||||
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
clientDisabledElements.AddRange(missionTypeTickBoxes);
|
clientDisabledElements.AddRange(missionTypeTickBoxes);
|
||||||
@@ -1428,9 +1430,9 @@ namespace Barotrauma
|
|||||||
|
|
||||||
bool isGameRunning = GameMain.GameSession?.IsRunning ?? false;
|
bool isGameRunning = GameMain.GameSession?.IsRunning ?? false;
|
||||||
|
|
||||||
infoContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, isGameRunning ? 0.95f : 0.9f), parent.RectTransform, Anchor.BottomCenter), childAnchor: Anchor.TopCenter)
|
infoContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, isGameRunning ? 0.97f : 0.92f), parent.RectTransform, Anchor.BottomCenter), childAnchor: Anchor.TopCenter)
|
||||||
{
|
{
|
||||||
RelativeSpacing = 0.015f,
|
RelativeSpacing = 0.0f,
|
||||||
Stretch = true,
|
Stretch = true,
|
||||||
UserData = characterInfo
|
UserData = characterInfo
|
||||||
};
|
};
|
||||||
@@ -1486,21 +1488,24 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//spacing
|
||||||
|
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.006f), infoContainer.RectTransform), style: null);
|
||||||
|
|
||||||
if (allowEditing)
|
if (allowEditing)
|
||||||
{
|
{
|
||||||
GUILayoutGroup characterInfoTabs = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.02f), infoContainer.RectTransform), isHorizontal: true)
|
GUILayoutGroup characterInfoTabs = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.016f), infoContainer.RectTransform), isHorizontal: true)
|
||||||
{
|
{
|
||||||
Stretch = true,
|
Stretch = true,
|
||||||
RelativeSpacing = 0.02f
|
RelativeSpacing = 0.02f
|
||||||
};
|
};
|
||||||
|
|
||||||
jobPreferencesButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.33f), characterInfoTabs.RectTransform),
|
jobPreferencesButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1f), characterInfoTabs.RectTransform),
|
||||||
TextManager.Get("JobPreferences"), style: "GUITabButton")
|
TextManager.Get("JobPreferences"), style: "GUITabButton")
|
||||||
{
|
{
|
||||||
Selected = true,
|
Selected = true,
|
||||||
OnClicked = SelectJobPreferencesTab
|
OnClicked = SelectJobPreferencesTab
|
||||||
};
|
};
|
||||||
appearanceButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.33f), characterInfoTabs.RectTransform),
|
appearanceButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1f), characterInfoTabs.RectTransform),
|
||||||
TextManager.Get("CharacterAppearance"), style: "GUITabButton")
|
TextManager.Get("CharacterAppearance"), style: "GUITabButton")
|
||||||
{
|
{
|
||||||
OnClicked = SelectAppearanceTab
|
OnClicked = SelectAppearanceTab
|
||||||
@@ -1515,7 +1520,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
JobPreferenceContainer = new GUIFrame(new RectTransform(Vector2.One, characterInfoFrame.RectTransform),
|
JobPreferenceContainer = new GUIFrame(new RectTransform(Vector2.One, characterInfoFrame.RectTransform),
|
||||||
style: "GUIFrameListBox");
|
style: "GUIFrameListBox");
|
||||||
characterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.4f), JobPreferenceContainer.RectTransform, Anchor.TopCenter));
|
characterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.4f), JobPreferenceContainer.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0f, 0.025f) });
|
||||||
JobList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.6f), JobPreferenceContainer.RectTransform, Anchor.BottomCenter), true)
|
JobList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.6f), JobPreferenceContainer.RectTransform, Anchor.BottomCenter), true)
|
||||||
{
|
{
|
||||||
Enabled = true,
|
Enabled = true,
|
||||||
@@ -3191,7 +3196,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
GUICustomComponent characterIcon = JobPreferenceContainer.GetChild<GUICustomComponent>();
|
GUICustomComponent characterIcon = JobPreferenceContainer.GetChild<GUICustomComponent>();
|
||||||
JobPreferenceContainer.RemoveChild(characterIcon);
|
JobPreferenceContainer.RemoveChild(characterIcon);
|
||||||
GameMain.Client.CharacterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.4f), JobPreferenceContainer.RectTransform, Anchor.TopCenter));
|
GameMain.Client.CharacterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.4f), JobPreferenceContainer.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.025f) });
|
||||||
|
|
||||||
GUIListBox listBox = JobPreferenceContainer.GetChild<GUIListBox>();
|
GUIListBox listBox = JobPreferenceContainer.GetChild<GUIListBox>();
|
||||||
/*foreach (Sprite sprite in jobPreferenceSprites) { sprite.Remove(); }
|
/*foreach (Sprite sprite in jobPreferenceSprites) { sprite.Remove(); }
|
||||||
@@ -3297,10 +3302,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private GUIButton CreateJobVariantButton(Pair<JobPrefab, int> jobPrefab, int variantIndex, int variantCount, GUIComponent slot)
|
private GUIButton CreateJobVariantButton(Pair<JobPrefab, int> jobPrefab, int variantIndex, int variantCount, GUIComponent slot)
|
||||||
{
|
{
|
||||||
float relativeSize = 0.2f;
|
float relativeSize = 0.15f;
|
||||||
|
|
||||||
var btn = new GUIButton(new RectTransform(new Vector2(relativeSize), slot.RectTransform, Anchor.TopCenter, scaleBasis: ScaleBasis.BothHeight)
|
var btn = new GUIButton(new RectTransform(new Vector2(relativeSize), slot.RectTransform, Anchor.TopCenter, scaleBasis: ScaleBasis.BothHeight)
|
||||||
{ RelativeOffset = new Vector2(relativeSize * 1.05f * (variantIndex - (variantCount - 1) / 2.0f), 0.02f) },
|
{ RelativeOffset = new Vector2(relativeSize * 1.3f * (variantIndex - (variantCount - 1) / 2.0f), 0.02f) },
|
||||||
(variantIndex + 1).ToString(), style: "JobVariantButton")
|
(variantIndex + 1).ToString(), style: "JobVariantButton")
|
||||||
{
|
{
|
||||||
Selected = jobPrefab.Second == variantIndex,
|
Selected = jobPrefab.Second == variantIndex,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -271,6 +271,21 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (newValue is string[] a)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < fields.Length; i++)
|
||||||
|
{
|
||||||
|
if (i >= a.Length) { break; }
|
||||||
|
if (fields[i] is GUITextBox textBox)
|
||||||
|
{
|
||||||
|
textBox.Text = a[i];
|
||||||
|
if (flash)
|
||||||
|
{
|
||||||
|
textBox.Flash(GUI.Style.Green);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, bool inGame, bool showName, string style = "", int elementHeight = 24, ScalableFont titleFont = null)
|
public SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, bool inGame, bool showName, string style = "", int elementHeight = 24, ScalableFont titleFont = null)
|
||||||
@@ -423,6 +438,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
propertyField = CreateRectangleField(entity, property, r, displayName, toolTip);
|
propertyField = CreateRectangleField(entity, property, r, displayName, toolTip);
|
||||||
}
|
}
|
||||||
|
else if(value is string[] a)
|
||||||
|
{
|
||||||
|
propertyField = CreateStringArrayField(entity, property, a, displayName, toolTip);
|
||||||
|
}
|
||||||
return propertyField;
|
return propertyField;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1164,6 +1183,75 @@ namespace Barotrauma
|
|||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GUIComponent CreateStringArrayField(ISerializableEntity entity, SerializableProperty property, string[] value, string displayName, string toolTip)
|
||||||
|
{
|
||||||
|
int elementCount = (value.Length + 1);
|
||||||
|
var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, elementCount * elementHeight), layoutGroup.RectTransform, isFixedSize: true), color: Color.Transparent);
|
||||||
|
var label = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f / elementCount), frame.RectTransform), displayName, font: GUI.SmallFont)
|
||||||
|
{
|
||||||
|
ToolTip = toolTip
|
||||||
|
};
|
||||||
|
var editableAttribute = property.GetAttribute<Editable>();
|
||||||
|
var fields = new GUIComponent[value.Length];
|
||||||
|
var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, (float)(elementCount - 1) / elementCount), frame.RectTransform, anchor: Anchor.BottomLeft))
|
||||||
|
{
|
||||||
|
RelativeSpacing = 0.01f
|
||||||
|
};
|
||||||
|
elementCount -= 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < value.Length; i++)
|
||||||
|
{
|
||||||
|
var element = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f / elementCount), inputArea.RectTransform) { MinSize = new Point(50, 0), MaxSize = new Point((int)(0.9f * inputArea.Rect.Width), 50) }, style: null);
|
||||||
|
var elementLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, element.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||||
|
// Set the label to be (i + 1) so it's easier to understand for non-programmers
|
||||||
|
string componentLabel = (i + 1).ToString();
|
||||||
|
new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), elementLayoutGroup.RectTransform) { MaxSize = new Point(25, elementLayoutGroup.Rect.Height) }, componentLabel, font: GUI.SmallFont, textAlignment: Alignment.Center);
|
||||||
|
GUITextBox textBox = new GUITextBox(new RectTransform(new Vector2(0.7f, 1), elementLayoutGroup.RectTransform), text: value[i]) { Font = GUI.SmallFont };
|
||||||
|
int comp = i;
|
||||||
|
textBox.OnEnterPressed += (textBox, text) => OnApply(textBox);
|
||||||
|
textBox.OnDeselected += (textBox, keys) => OnApply(textBox);
|
||||||
|
fields[i] = textBox;
|
||||||
|
|
||||||
|
bool OnApply(GUITextBox textBox)
|
||||||
|
{
|
||||||
|
// Reserve the semicolon for serializing the value
|
||||||
|
bool containsForbiddenCharacters = textBox.Text.Contains(';');
|
||||||
|
string[] newValue = (string[])property.GetValue(entity);
|
||||||
|
if (!containsForbiddenCharacters)
|
||||||
|
{
|
||||||
|
newValue[comp] = textBox.Text;
|
||||||
|
if (SetPropertyValue(property, entity, newValue))
|
||||||
|
{
|
||||||
|
TrySendNetworkUpdate(entity, property);
|
||||||
|
textBox.Flash(color: GUI.Style.Green, flashDuration: 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
textBox.Text = newValue[comp];
|
||||||
|
textBox.Flash(color: GUI.Style.Red, flashDuration: 1f);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh += () =>
|
||||||
|
{
|
||||||
|
if (fields.None(f => ((GUITextBox)f).Selected))
|
||||||
|
{
|
||||||
|
string[] value = (string[])property.GetValue(entity);
|
||||||
|
for (int i = 0; i < fields.Length; i++)
|
||||||
|
{
|
||||||
|
((GUITextBox)fields[i]).Text = value[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
frame.RectTransform.MinSize = new Point(0, frame.RectTransform.Children.Sum(c => c.MinSize.Y));
|
||||||
|
if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); }
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
public void CreateTextPicker(string textTag, ISerializableEntity entity, SerializableProperty property, GUITextBox textBox)
|
public void CreateTextPicker(string textTag, ISerializableEntity entity, SerializableProperty property, GUITextBox textBox)
|
||||||
{
|
{
|
||||||
var msgBox = new GUIMessageBox("", "", new string[] { TextManager.Get("Cancel") }, new Vector2(0.2f, 0.5f), new Point(300, 400));
|
var msgBox = new GUIMessageBox("", "", new string[] { TextManager.Get("Cancel") }, new Vector2(0.2f, 0.5f), new Point(300, 400));
|
||||||
|
|||||||
@@ -829,7 +829,6 @@ namespace Barotrauma
|
|||||||
GameMain.GameSession.EventManager.CurrentIntensity * 100.0f : 0.0f;
|
GameMain.GameSession.EventManager.CurrentIntensity * 100.0f : 0.0f;
|
||||||
|
|
||||||
IEnumerable<BackgroundMusic> suitableMusic = GetSuitableMusicClips(currentMusicType, currentIntensity);
|
IEnumerable<BackgroundMusic> suitableMusic = GetSuitableMusicClips(currentMusicType, currentIntensity);
|
||||||
|
|
||||||
int mainTrackIndex = 0;
|
int mainTrackIndex = 0;
|
||||||
if (suitableMusic.Count() == 0)
|
if (suitableMusic.Count() == 0)
|
||||||
{
|
{
|
||||||
@@ -861,7 +860,6 @@ namespace Barotrauma
|
|||||||
IEnumerable<BackgroundMusic> suitableNoiseLoops = Screen.Selected == GameMain.GameScreen ?
|
IEnumerable<BackgroundMusic> suitableNoiseLoops = Screen.Selected == GameMain.GameScreen ?
|
||||||
GetSuitableMusicClips(Level.Loaded.LevelData?.Biome?.Identifier, currentIntensity) :
|
GetSuitableMusicClips(Level.Loaded.LevelData?.Biome?.Identifier, currentIntensity) :
|
||||||
Enumerable.Empty<BackgroundMusic>();
|
Enumerable.Empty<BackgroundMusic>();
|
||||||
|
|
||||||
if (suitableNoiseLoops.Count() == 0)
|
if (suitableNoiseLoops.Count() == 0)
|
||||||
{
|
{
|
||||||
targetMusic[noiseLoopIndex] = null;
|
targetMusic[noiseLoopIndex] = null;
|
||||||
@@ -877,12 +875,23 @@ namespace Barotrauma
|
|||||||
targetMusic[noiseLoopIndex] = null;
|
targetMusic[noiseLoopIndex] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IEnumerable<BackgroundMusic> suitableTypeAmbiences = GetSuitableMusicClips($"{currentMusicType}ambience", currentIntensity);
|
||||||
|
int typeAmbienceTrackIndex = 2;
|
||||||
|
if (suitableTypeAmbiences.None())
|
||||||
|
{
|
||||||
|
targetMusic[typeAmbienceTrackIndex] = null;
|
||||||
|
}
|
||||||
|
// Switch the type ambience if nothing playing atm or the currently playing clip is not suitable anymore
|
||||||
|
else if (targetMusic[typeAmbienceTrackIndex] == null || currentMusic[typeAmbienceTrackIndex] == null || !currentMusic[typeAmbienceTrackIndex].IsPlaying() || suitableTypeAmbiences.None(m => m.File == currentMusic[typeAmbienceTrackIndex].Filename))
|
||||||
|
{
|
||||||
|
targetMusic[mainTrackIndex] = suitableMusic.GetRandom();
|
||||||
|
}
|
||||||
|
|
||||||
//get the appropriate intensity layers for current situation
|
//get the appropriate intensity layers for current situation
|
||||||
IEnumerable<BackgroundMusic> suitableIntensityMusic = Screen.Selected == GameMain.GameScreen ?
|
IEnumerable<BackgroundMusic> suitableIntensityMusic = Screen.Selected == GameMain.GameScreen ?
|
||||||
GetSuitableMusicClips("intensity", currentIntensity) :
|
GetSuitableMusicClips("intensity", currentIntensity) :
|
||||||
Enumerable.Empty<BackgroundMusic>();
|
Enumerable.Empty<BackgroundMusic>();
|
||||||
|
int intensityTrackStartIndex = 3;
|
||||||
int intensityTrackStartIndex = 2;
|
|
||||||
for (int i = intensityTrackStartIndex; i < MaxMusicChannels; i++)
|
for (int i = intensityTrackStartIndex; i < MaxMusicChannels; i++)
|
||||||
{
|
{
|
||||||
//disable targetmusics that aren't suitable anymore
|
//disable targetmusics that aren't suitable anymore
|
||||||
@@ -891,7 +900,6 @@ namespace Barotrauma
|
|||||||
targetMusic[i] = null;
|
targetMusic[i] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (BackgroundMusic intensityMusic in suitableIntensityMusic)
|
foreach (BackgroundMusic intensityMusic in suitableIntensityMusic)
|
||||||
{
|
{
|
||||||
//already playing, do nothing
|
//already playing, do nothing
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>0.1500.4.0</Version>
|
<Version>0.1500.5.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>0.1500.4.0</Version>
|
<Version>0.1500.5.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma</Product>
|
<Product>Barotrauma</Product>
|
||||||
<Version>0.1500.4.0</Version>
|
<Version>0.1500.5.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>Barotrauma</AssemblyName>
|
<AssemblyName>Barotrauma</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>0.1500.4.0</Version>
|
<Version>0.1500.5.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>0.1500.4.0</Version>
|
<Version>0.1500.5.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos)
|
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos)
|
||||||
{
|
{
|
||||||
|
if (Character == null || Character.Removed) { return; }
|
||||||
if (!prevSentSkill.ContainsKey(skillIdentifier))
|
if (!prevSentSkill.ContainsKey(skillIdentifier))
|
||||||
{
|
{
|
||||||
prevSentSkill[skillIdentifier] = prevLevel;
|
prevSentSkill[skillIdentifier] = prevLevel;
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using Barotrauma.Networking;
|
||||||
|
|
||||||
|
namespace Barotrauma
|
||||||
|
{
|
||||||
|
partial class AlienRuinMission : Mission
|
||||||
|
{
|
||||||
|
public override void ServerWriteInitial(IWriteMessage msg, Client c)
|
||||||
|
{
|
||||||
|
msg.Write((ushort)existingTargets.Count);
|
||||||
|
foreach (var t in existingTargets)
|
||||||
|
{
|
||||||
|
msg.Write(t != null ? t.ID : Entity.NullEntityID);
|
||||||
|
}
|
||||||
|
msg.Write((ushort)spawnedTargets.Count);
|
||||||
|
foreach (var t in spawnedTargets)
|
||||||
|
{
|
||||||
|
t.WriteSpawnData(msg, t.ID, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,5 +17,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract void ServerWriteInitial(IWriteMessage msg, Client c);
|
public abstract void ServerWriteInitial(IWriteMessage msg, Client c);
|
||||||
|
|
||||||
|
public virtual void ServerWrite(IWriteMessage msg)
|
||||||
|
{
|
||||||
|
msg.Write((ushort)State);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using Barotrauma.Networking;
|
||||||
|
|
||||||
|
namespace Barotrauma
|
||||||
|
{
|
||||||
|
partial class ScanMission : Mission
|
||||||
|
{
|
||||||
|
public override void ServerWriteInitial(IWriteMessage msg, Client c)
|
||||||
|
{
|
||||||
|
msg.Write((ushort)startingItems.Count);
|
||||||
|
foreach (var item in startingItems)
|
||||||
|
{
|
||||||
|
item.WriteSpawnData(msg,
|
||||||
|
item.ID,
|
||||||
|
parentInventoryIDs.ContainsKey(item) ? parentInventoryIDs[item] : Entity.NullEntityID,
|
||||||
|
parentItemContainerIndices.ContainsKey(item) ? parentItemContainerIndices[item] : (byte)0);
|
||||||
|
}
|
||||||
|
ServerWriteScanTargetStatus(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ServerWrite(IWriteMessage msg)
|
||||||
|
{
|
||||||
|
base.ServerWrite(msg);
|
||||||
|
ServerWriteScanTargetStatus(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ServerWriteScanTargetStatus(IWriteMessage msg)
|
||||||
|
{
|
||||||
|
msg.Write((byte)scanTargets.Count);
|
||||||
|
foreach (var kvp in scanTargets)
|
||||||
|
{
|
||||||
|
msg.Write(kvp.Key != null ? kvp.Key.ID : Entity.NullEntityID);
|
||||||
|
msg.Write(kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Barotrauma.Networking;
|
||||||
|
|
||||||
|
namespace Barotrauma.Items.Components
|
||||||
|
{
|
||||||
|
partial class Scanner : ItemComponent, IServerSerializable
|
||||||
|
{
|
||||||
|
private float LastSentScanTimer { get; set; }
|
||||||
|
|
||||||
|
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||||
|
{
|
||||||
|
msg.Write(scanTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using Barotrauma.Networking;
|
||||||
|
|
||||||
|
namespace Barotrauma.Items.Components
|
||||||
|
{
|
||||||
|
partial class ButtonTerminal : ItemComponent, IClientSerializable, IServerSerializable
|
||||||
|
{
|
||||||
|
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
|
||||||
|
{
|
||||||
|
int signalIndex = msg.ReadRangedInteger(0, Signals.Length - 1);
|
||||||
|
if (!item.CanClientAccess(c)) { return; }
|
||||||
|
if (!SendSignal(signalIndex)) { return; }
|
||||||
|
GameServer.Log($"{GameServer.CharacterLogName(c.Character)} sent a signal \"{Signals[signalIndex]}\" from {item.Name}", ServerLog.MessageType.ItemInteraction);
|
||||||
|
item.CreateServerEvent(this, new object[] { signalIndex });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||||
|
{
|
||||||
|
Write(msg, extraData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3791,7 +3791,7 @@ namespace Barotrauma.Networking
|
|||||||
return preferredClient;
|
return preferredClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateMissionState(Mission mission, int state)
|
public void UpdateMissionState(Mission mission)
|
||||||
{
|
{
|
||||||
foreach (var client in connectedClients)
|
foreach (var client in connectedClients)
|
||||||
{
|
{
|
||||||
@@ -3799,7 +3799,7 @@ namespace Barotrauma.Networking
|
|||||||
msg.Write((byte)ServerPacketHeader.MISSION);
|
msg.Write((byte)ServerPacketHeader.MISSION);
|
||||||
int missionIndex = GameMain.GameSession.GetMissionIndex(mission);
|
int missionIndex = GameMain.GameSession.GetMissionIndex(mission);
|
||||||
msg.Write((byte)(missionIndex == -1 ? 255: missionIndex));
|
msg.Write((byte)(missionIndex == -1 ? 255: missionIndex));
|
||||||
msg.Write((ushort)state);
|
mission?.ServerWrite(msg);
|
||||||
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
|
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
partial class RespawnManager : Entity, IServerSerializable
|
partial class RespawnManager : Entity, IServerSerializable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// How much skills drop towards the job's default skill levels when respawning midround in the campaign
|
||||||
|
/// </summary>
|
||||||
|
const float SkillReductionOnCampaignMidroundRespawn = 0.5f;
|
||||||
|
|
||||||
private DateTime despawnTime;
|
private DateTime despawnTime;
|
||||||
|
|
||||||
private float shuttleEmptyTimer;
|
private float shuttleEmptyTimer;
|
||||||
@@ -361,9 +366,21 @@ namespace Barotrauma.Networking
|
|||||||
if (!bot && campaign != null)
|
if (!bot && campaign != null)
|
||||||
{
|
{
|
||||||
var matchingData = campaign?.GetClientCharacterData(clients[i]);
|
var matchingData = campaign?.GetClientCharacterData(clients[i]);
|
||||||
if (matchingData != null && !matchingData.HasSpawned)
|
if (matchingData != null)
|
||||||
{
|
{
|
||||||
forceSpawnInMainSub = true;
|
if (!matchingData.HasSpawned)
|
||||||
|
{
|
||||||
|
forceSpawnInMainSub = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (Skill skill in characterInfos[i].Job.Skills)
|
||||||
|
{
|
||||||
|
var skillPrefab = characterInfos[i].Job.Prefab.Skills.Find(s => skill.Prefab == s);
|
||||||
|
if (skillPrefab == null) { continue; }
|
||||||
|
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.X, SkillReductionOnCampaignMidroundRespawn);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<RootNamespace>Barotrauma</RootNamespace>
|
<RootNamespace>Barotrauma</RootNamespace>
|
||||||
<Authors>FakeFish, Undertow Games</Authors>
|
<Authors>FakeFish, Undertow Games</Authors>
|
||||||
<Product>Barotrauma Dedicated Server</Product>
|
<Product>Barotrauma Dedicated Server</Product>
|
||||||
<Version>0.1500.4.0</Version>
|
<Version>0.1500.5.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -80,6 +80,7 @@
|
|||||||
<Character file="Content/Characters/Endworm/Endworm.xml" />
|
<Character file="Content/Characters/Endworm/Endworm.xml" />
|
||||||
<Character file="Content/Characters/Fractalguardian/Fractalguardian.xml" />
|
<Character file="Content/Characters/Fractalguardian/Fractalguardian.xml" />
|
||||||
<Character file="Content/Characters/Fractalguardian2/Fractalguardian2.xml" />
|
<Character file="Content/Characters/Fractalguardian2/Fractalguardian2.xml" />
|
||||||
|
<Character file="Content/Characters/Swarmfeeder/Swarmfeeder.xml" />
|
||||||
<Character file="Content/Characters/Hammerhead/Hammerhead.xml" />
|
<Character file="Content/Characters/Hammerhead/Hammerhead.xml" />
|
||||||
<Character file="Content/Characters/Hammerheadgold/Hammerheadgold.xml" />
|
<Character file="Content/Characters/Hammerheadgold/Hammerheadgold.xml" />
|
||||||
<Character file="Content/Characters/Hammerheadmatriarch/Hammerheadmatriarch.xml" />
|
<Character file="Content/Characters/Hammerheadmatriarch/Hammerheadmatriarch.xml" />
|
||||||
@@ -87,6 +88,8 @@
|
|||||||
<Character file="Content/Characters/Human/Human.xml" />
|
<Character file="Content/Characters/Human/Human.xml" />
|
||||||
<Character file="Content/Characters/Husk/Husk.xml" />
|
<Character file="Content/Characters/Husk/Husk.xml" />
|
||||||
<Character file="Content/Characters/Humanhusk/Humanhusk.xml" />
|
<Character file="Content/Characters/Humanhusk/Humanhusk.xml" />
|
||||||
|
<Character file="Content/Characters/Legacyfractalguardian/Legacyfractalguardian.xml" />
|
||||||
|
<Character file="Content/Characters/Legacyfractalguardian2/Legacyfractalguardian2.xml" />
|
||||||
<Character file="Content/Characters/Legacyhusk/Legacyhusk.xml" />
|
<Character file="Content/Characters/Legacyhusk/Legacyhusk.xml" />
|
||||||
<Character file="Content/Characters/Legacycharybdis/Legacycharybdis.xml" />
|
<Character file="Content/Characters/Legacycharybdis/Legacycharybdis.xml" />
|
||||||
<Character file="Content/Characters/Legacycrawler/Legacycrawler.xml" />
|
<Character file="Content/Characters/Legacycrawler/Legacycrawler.xml" />
|
||||||
@@ -118,7 +121,6 @@
|
|||||||
<Character file="Content/Characters/Variants/Mudraptor_hatchling/Mudraptor_hatchling.xml" />
|
<Character file="Content/Characters/Variants/Mudraptor_hatchling/Mudraptor_hatchling.xml" />
|
||||||
<Character file="Content/Characters/Variants/Mudraptor_passive/Mudraptor_passive.xml" />
|
<Character file="Content/Characters/Variants/Mudraptor_passive/Mudraptor_passive.xml" />
|
||||||
<Character file="Content/Characters/Variants/Tigerthresher_hatchling/Tigerthresher_hatchling.xml" />
|
<Character file="Content/Characters/Variants/Tigerthresher_hatchling/Tigerthresher_hatchling.xml" />
|
||||||
<Character file="Content/Characters/TintTest/TintTest.xml" />
|
|
||||||
<MapCreature file="Content/Items/Gardening/ballastflora.xml" />
|
<MapCreature file="Content/Items/Gardening/ballastflora.xml" />
|
||||||
<Wreck file="Content/Map/Wrecks/Dugong_Wrecked.sub" />
|
<Wreck file="Content/Map/Wrecks/Dugong_Wrecked.sub" />
|
||||||
<Wreck file="Content/Map/Wrecks/Kastrull_Wrecked.sub" />
|
<Wreck file="Content/Map/Wrecks/Kastrull_Wrecked.sub" />
|
||||||
@@ -170,6 +172,32 @@
|
|||||||
<Structure file="Content/Map/Thalamus/thalamusstructures.xml" />
|
<Structure file="Content/Map/Thalamus/thalamusstructures.xml" />
|
||||||
<UpgradeModules file="Content/Upgrades/UpgradeModules.xml" />
|
<UpgradeModules file="Content/Upgrades/UpgradeModules.xml" />
|
||||||
<RuinConfig file="Content/Map/RuinConfig.xml" />
|
<RuinConfig file="Content/Map/RuinConfig.xml" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Chambers4Way.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Chasm.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_DeadEndL.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_DeadEndR.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_HallwayHorizontal.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_HallwayVertical.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_HallwaySphere.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_MaintenancePassages.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_MaintenancePassages2.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_SecurityModule.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_VaultModule1.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_VaultModule2.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Chambers1.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Chambers2.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_MaintenanceTunnels1.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_MaintenanceTunnels2R.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_MaintenanceTunnels2L.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Entrance1.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_GeneticStorage1L.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_GeneticStorage1R.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Chasm2.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Chasm3.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Entrance2.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Storage1R.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Storage1L.sub" />
|
||||||
|
<OutpostModule file="Content/Map/RuinModules/Alien_Offices1.sub" />
|
||||||
<WreckAIConfig file="Content/Map/Thalamus/ThalamusAI.xml" />
|
<WreckAIConfig file="Content/Map/Thalamus/ThalamusAI.xml" />
|
||||||
<BackgroundCreaturePrefabs file="Content/BackgroundCreatures/BackgroundCreaturePrefabs.xml" />
|
<BackgroundCreaturePrefabs file="Content/BackgroundCreatures/BackgroundCreaturePrefabs.xml" />
|
||||||
<LevelObjectPrefabs file="Content/Map/Biomes/Common/CommonLevelObjects.xml" />
|
<LevelObjectPrefabs file="Content/Map/Biomes/Common/CommonLevelObjects.xml" />
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ namespace Barotrauma
|
|||||||
(!requireNonDirty || !pathSteering.IsPathDirty);
|
(!requireNonDirty || !pathSteering.IsPathDirty);
|
||||||
|
|
||||||
protected readonly float colliderWidth;
|
protected readonly float colliderWidth;
|
||||||
|
protected readonly float minGapSize;
|
||||||
|
protected readonly float minHullSize;
|
||||||
protected readonly float colliderLength;
|
protected readonly float colliderLength;
|
||||||
protected readonly float avoidLookAheadDistance;
|
protected readonly float avoidLookAheadDistance;
|
||||||
|
|
||||||
@@ -116,6 +118,8 @@ namespace Barotrauma
|
|||||||
colliderWidth = size.X;
|
colliderWidth = size.X;
|
||||||
colliderLength = size.Y;
|
colliderLength = size.Y;
|
||||||
avoidLookAheadDistance = Math.Max(Math.Max(colliderWidth, colliderLength) * 3, 1.5f);
|
avoidLookAheadDistance = Math.Max(Math.Max(colliderWidth, colliderLength) * 3, 1.5f);
|
||||||
|
minGapSize = ConvertUnits.ToDisplayUnits(Math.Min(colliderWidth, colliderLength));
|
||||||
|
minHullSize = ConvertUnits.ToDisplayUnits(Math.Max(colliderLength, colliderWidth) * 1.1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnAttacked(Character attacker, AttackResult attackResult) { }
|
public virtual void OnAttacked(Character attacker, AttackResult attackResult) { }
|
||||||
@@ -390,8 +394,7 @@ namespace Barotrauma
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (gap.Open < 1) { continue; }
|
if (gap.Open < 1) { continue; }
|
||||||
bool canGetThrough = ConvertUnits.ToDisplayUnits(colliderWidth) < gap.Size;
|
if (gap.Size < minGapSize) { continue; }
|
||||||
if (!canGetThrough) { continue; }
|
|
||||||
}
|
}
|
||||||
if (gap.FlowTargetHull == Character.CurrentHull)
|
if (gap.FlowTargetHull == Character.CurrentHull)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -92,7 +92,16 @@ namespace Barotrauma
|
|||||||
public string SonarLabel;
|
public string SonarLabel;
|
||||||
public string SonarIconIdentifier;
|
public string SonarIconIdentifier;
|
||||||
|
|
||||||
public bool Enabled => SoundRange > 0 || SightRange > 0;
|
private bool inDetectable;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should be reset to false each frame and kept indetectable by e.g. a status effect.
|
||||||
|
/// </summary>
|
||||||
|
public bool InDetectable
|
||||||
|
{
|
||||||
|
get => inDetectable || (SoundRange <= 0 && SightRange <= 0);
|
||||||
|
set => inDetectable = value;
|
||||||
|
}
|
||||||
|
|
||||||
public float MinSoundRange, MinSightRange;
|
public float MinSoundRange, MinSightRange;
|
||||||
public float MaxSoundRange = 100000, MaxSightRange = 100000;
|
public float MaxSoundRange = 100000, MaxSightRange = 100000;
|
||||||
@@ -181,14 +190,15 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void Update(float deltaTime)
|
public void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
if (Enabled && !Static && FadeOutTime > 0)
|
InDetectable = false;
|
||||||
|
if (!Static && FadeOutTime > 0)
|
||||||
{
|
{
|
||||||
// The aitarget goes silent/invisible if the components don't keep it active
|
// The aitarget goes silent/invisible if the components don't keep it active
|
||||||
if (!StaticSight)
|
if (!StaticSight && SightRange > 0)
|
||||||
{
|
{
|
||||||
DecreaseSightRange(deltaTime);
|
DecreaseSightRange(deltaTime);
|
||||||
}
|
}
|
||||||
if (!StaticSound)
|
if (!StaticSound && SoundRange > 0)
|
||||||
{
|
{
|
||||||
DecreaseSoundRange(deltaTime);
|
DecreaseSoundRange(deltaTime);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1036,16 +1036,19 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
if (previousAttackResults.ContainsKey(attacker))
|
if (previousAttackResults.ContainsKey(attacker))
|
||||||
{
|
{
|
||||||
foreach (Affliction newAffliction in attackResult.Afflictions)
|
if (attackResult.Afflictions != null)
|
||||||
{
|
{
|
||||||
var matchingAffliction = previousAttackResults[attacker].Afflictions.Find(a => a.Prefab == newAffliction.Prefab && a.Source == newAffliction.Source);
|
foreach (Affliction newAffliction in attackResult.Afflictions)
|
||||||
if (matchingAffliction == null)
|
|
||||||
{
|
{
|
||||||
previousAttackResults[attacker].Afflictions.Add(newAffliction);
|
var matchingAffliction = previousAttackResults[attacker].Afflictions.Find(a => a.Prefab == newAffliction.Prefab && a.Source == newAffliction.Source);
|
||||||
}
|
if (matchingAffliction == null)
|
||||||
else
|
{
|
||||||
{
|
previousAttackResults[attacker].Afflictions.Add(newAffliction);
|
||||||
matchingAffliction.Strength += newAffliction.Strength;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
matchingAffliction.Strength += newAffliction.Strength;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
previousAttackResults[attacker] = new AttackResult(previousAttackResults[attacker].Afflictions, previousAttackResults[attacker].HitLimb);
|
previousAttackResults[attacker] = new AttackResult(previousAttackResults[attacker].Afflictions, previousAttackResults[attacker].HitLimb);
|
||||||
@@ -1062,9 +1065,12 @@ namespace Barotrauma
|
|||||||
float realDamage = attackResult.Damage;
|
float realDamage = attackResult.Damage;
|
||||||
// including poisons etc
|
// including poisons etc
|
||||||
float totalDamage = realDamage;
|
float totalDamage = realDamage;
|
||||||
foreach (Affliction affliction in attackResult.Afflictions)
|
if (attackResult.Afflictions != null)
|
||||||
{
|
{
|
||||||
totalDamage -= affliction.Prefab.KarmaChangeOnApplied * affliction.Strength;
|
foreach (Affliction affliction in attackResult.Afflictions)
|
||||||
|
{
|
||||||
|
totalDamage -= affliction.Prefab.KarmaChangeOnApplied * affliction.Strength;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (totalDamage <= 0.01f) { return; }
|
if (totalDamage <= 0.01f) { return; }
|
||||||
if (Character.IsBot)
|
if (Character.IsBot)
|
||||||
@@ -1255,7 +1261,7 @@ namespace Barotrauma
|
|||||||
// Already targeting the attacker -> treat as a more serious threat.
|
// Already targeting the attacker -> treat as a more serious threat.
|
||||||
cumulativeDamage *= 2;
|
cumulativeDamage *= 2;
|
||||||
}
|
}
|
||||||
if (attackResult.Afflictions.Any(a => a is AfflictionHusk))
|
if (attackResult.Afflictions != null && attackResult.Afflictions.Any(a => a is AfflictionHusk))
|
||||||
{
|
{
|
||||||
cumulativeDamage = 100;
|
cumulativeDamage = 100;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Barotrauma.Extensions;
|
|
||||||
using FarseerPhysics;
|
using FarseerPhysics;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
@@ -15,6 +14,11 @@ namespace Barotrauma
|
|||||||
private bool canOpenDoors;
|
private bool canOpenDoors;
|
||||||
public bool CanBreakDoors { get; set; }
|
public bool CanBreakDoors { get; set; }
|
||||||
|
|
||||||
|
private bool ShouldBreakDoor(Door door) =>
|
||||||
|
CanBreakDoors &&
|
||||||
|
!door.Item.Indestructible && !door.Item.InvulnerableToDamage &&
|
||||||
|
(door.Item.Submarine == null || door.Item.Submarine.TeamID != character.TeamID);
|
||||||
|
|
||||||
private Character character;
|
private Character character;
|
||||||
|
|
||||||
private Vector2 currentTarget;
|
private Vector2 currentTarget;
|
||||||
@@ -23,7 +27,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private float buttonPressCooldown;
|
private float buttonPressCooldown;
|
||||||
|
|
||||||
const float ButtonPressInterval = 0.5f;
|
const float ButtonPressInterval = 0.25f;
|
||||||
|
|
||||||
public SteeringPath CurrentPath
|
public SteeringPath CurrentPath
|
||||||
{
|
{
|
||||||
@@ -111,9 +115,9 @@ namespace Barotrauma
|
|||||||
IsPathDirty = true;
|
IsPathDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SteeringSeek(Vector2 target, float weight, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisiblity = true)
|
public void SteeringSeek(Vector2 target, float weight, float minGapWidth = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisiblity = true)
|
||||||
{
|
{
|
||||||
steering += CalculateSteeringSeek(target, weight, startNodeFilter, endNodeFilter, nodeFilter, checkVisiblity);
|
steering += CalculateSteeringSeek(target, weight, minGapWidth, startNodeFilter, endNodeFilter, nodeFilter, checkVisiblity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -158,7 +162,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2 CalculateSteeringSeek(Vector2 target, float weight, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
|
private Vector2 CalculateSteeringSeek(Vector2 target, float weight, float minGapSize = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
|
||||||
{
|
{
|
||||||
bool needsNewPath = currentPath == null || currentPath.Unreachable;
|
bool needsNewPath = currentPath == null || currentPath.Unreachable;
|
||||||
if (!needsNewPath && character.Submarine != null && character.Params.PathFinderPriority > 0.5f)
|
if (!needsNewPath && character.Submarine != null && character.Params.PathFinderPriority > 0.5f)
|
||||||
@@ -200,7 +204,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
pathFinder.InsideSubmarine = character.Submarine != null;
|
pathFinder.InsideSubmarine = character.Submarine != null;
|
||||||
pathFinder.ApplyPenaltyToOutsideNodes = character.PressureProtection <= 0;
|
pathFinder.ApplyPenaltyToOutsideNodes = character.PressureProtection <= 0;
|
||||||
var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility);
|
var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", minGapSize, startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility);
|
||||||
bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.X) <= 0;
|
bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.X) <= 0;
|
||||||
if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable)
|
if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable)
|
||||||
{
|
{
|
||||||
@@ -241,6 +245,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
if (useNewPath)
|
if (useNewPath)
|
||||||
{
|
{
|
||||||
|
if (currentPath != null)
|
||||||
|
{
|
||||||
|
CheckDoorsInPath();
|
||||||
|
}
|
||||||
currentPath = newPath;
|
currentPath = newPath;
|
||||||
}
|
}
|
||||||
float priority = MathHelper.Lerp(3, 1, character.Params.PathFinderPriority);
|
float priority = MathHelper.Lerp(3, 1, character.Params.PathFinderPriority);
|
||||||
@@ -306,10 +314,12 @@ namespace Barotrauma
|
|||||||
pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition;
|
pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition;
|
||||||
}
|
}
|
||||||
return currentTarget - pos2;
|
return currentTarget - pos2;
|
||||||
}
|
}
|
||||||
if (canOpenDoors && !character.LockHands && buttonPressCooldown <= 0.0f)
|
bool doorsChecked = false;
|
||||||
|
if (!character.LockHands && buttonPressCooldown <= 0.0f)
|
||||||
{
|
{
|
||||||
CheckDoorsInPath();
|
CheckDoorsInPath();
|
||||||
|
doorsChecked = true;
|
||||||
}
|
}
|
||||||
Vector2 pos = host.SimPosition;
|
Vector2 pos = host.SimPosition;
|
||||||
if (character != null && CurrentPath.CurrentNode?.Submarine != null)
|
if (character != null && CurrentPath.CurrentNode?.Submarine != null)
|
||||||
@@ -394,7 +404,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
if (isAboveFloor || nextLadderSameAsCurrent)
|
if (isAboveFloor || nextLadderSameAsCurrent)
|
||||||
{
|
{
|
||||||
currentPath.SkipToNextNode();
|
NextNode(!doorsChecked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (nextLadder != null)
|
else if (nextLadder != null)
|
||||||
@@ -404,7 +414,7 @@ namespace Barotrauma
|
|||||||
//e.g. no point in going down to reach the starting point of a path when we could go directly to the one above
|
//e.g. no point in going down to reach the starting point of a path when we could go directly to the one above
|
||||||
if (Math.Sign(currentPath.CurrentNode.WorldPosition.Y - character.WorldPosition.Y) != Math.Sign(currentPath.NextNode.WorldPosition.Y - character.WorldPosition.Y))
|
if (Math.Sign(currentPath.CurrentNode.WorldPosition.Y - character.WorldPosition.Y) != Math.Sign(currentPath.NextNode.WorldPosition.Y - character.WorldPosition.Y))
|
||||||
{
|
{
|
||||||
currentPath.SkipToNextNode();
|
NextNode(!doorsChecked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return diff;
|
return diff;
|
||||||
@@ -426,7 +436,7 @@ namespace Barotrauma
|
|||||||
float distance = horizontalDistance + verticalDistance;
|
float distance = horizontalDistance + verticalDistance;
|
||||||
if (ConvertUnits.ToSimUnits(distance) < targetDistance)
|
if (ConvertUnits.ToSimUnits(distance) < targetDistance)
|
||||||
{
|
{
|
||||||
currentPath.SkipToNextNode();
|
NextNode(!doorsChecked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -451,7 +461,7 @@ namespace Barotrauma
|
|||||||
float targetDistance = Math.Max(colliderSize.X / 2 * margin, minWidth / 2);
|
float targetDistance = Math.Max(colliderSize.X / 2 * margin, minWidth / 2);
|
||||||
if (horizontalDistance < targetDistance && isAboveFeet && isNotTooHigh && (door == null || door.CanBeTraversed))
|
if (horizontalDistance < targetDistance && isAboveFeet && isNotTooHigh && (door == null || door.CanBeTraversed))
|
||||||
{
|
{
|
||||||
currentPath.SkipToNextNode();
|
NextNode(!doorsChecked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentPath.CurrentNode == null)
|
if (currentPath.CurrentNode == null)
|
||||||
@@ -461,28 +471,51 @@ namespace Barotrauma
|
|||||||
return currentPath.CurrentNode.SimPosition - pos;
|
return currentPath.CurrentNode.SimPosition - pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void NextNode(bool checkDoors)
|
||||||
|
{
|
||||||
|
if (checkDoors)
|
||||||
|
{
|
||||||
|
CheckDoorsInPath();
|
||||||
|
}
|
||||||
|
currentPath.SkipToNextNode();
|
||||||
|
}
|
||||||
|
|
||||||
private bool CanAccessDoor(Door door, Func<Controller, bool> buttonFilter = null)
|
private bool CanAccessDoor(Door door, Func<Controller, bool> buttonFilter = null)
|
||||||
{
|
{
|
||||||
if (door.IsOpen || door.IsBroken) { return true; }
|
if (door.IsBroken) { return true; }
|
||||||
if (!door.Item.IsInteractable(character)) { return false; }
|
if (!door.IsOpen)
|
||||||
if (!CanBreakDoors)
|
|
||||||
{
|
{
|
||||||
if (door.IsStuck || door.IsJammed) { return false; }
|
if (!door.Item.IsInteractable(character)) { return false; }
|
||||||
if (!canOpenDoors || character.LockHands) { return false; }
|
if (!ShouldBreakDoor(door))
|
||||||
|
{
|
||||||
|
if (door.IsStuck || door.IsJammed) { return false; }
|
||||||
|
if (!canOpenDoors || character.LockHands) { return false; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (door.HasIntegratedButtons)
|
if (door.HasIntegratedButtons)
|
||||||
{
|
{
|
||||||
return door.HasAccess(character) || CanBreakDoors;
|
return door.IsOpen || door.HasAccess(character) || ShouldBreakDoor(door);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return door.Item.GetConnectedComponents<Controller>(true).Any(b => b.HasAccess(character) && (buttonFilter == null || buttonFilter(b))) || CanBreakDoors;
|
// We'll want this to run each time, because the delegate is used to find a valid button component.
|
||||||
|
bool canAccessButtons = door.Item.GetConnectedComponents<Controller>(true).Any(b => b.HasAccess(character) && (buttonFilter == null || buttonFilter(b)));
|
||||||
|
return canAccessButtons || door.IsOpen || ShouldBreakDoor(door);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Vector2 GetColliderSize() => ConvertUnits.ToDisplayUnits(character.AnimController.Collider.GetSize());
|
||||||
|
|
||||||
|
private float GetColliderLength()
|
||||||
|
{
|
||||||
|
Vector2 colliderSize = character.AnimController.Collider.GetSize();
|
||||||
|
return ConvertUnits.ToDisplayUnits(Math.Max(colliderSize.X, colliderSize.Y));
|
||||||
|
}
|
||||||
|
|
||||||
private void CheckDoorsInPath()
|
private void CheckDoorsInPath()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 2; i++)
|
if (!canOpenDoors) { return; }
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
WayPoint currentWaypoint = null;
|
WayPoint currentWaypoint = null;
|
||||||
WayPoint nextWaypoint = null;
|
WayPoint nextWaypoint = null;
|
||||||
@@ -493,17 +526,21 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
door = currentPath.Nodes.First().ConnectedDoor;
|
door = currentPath.Nodes.First().ConnectedDoor;
|
||||||
shouldBeOpen = door != null;
|
shouldBeOpen = door != null;
|
||||||
|
if (i > 0) { break; }
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (i == 0)
|
bool closeDoors = character.IsBot && character.IsInFriendlySub || character.Params.AI != null && character.Params.AI.KeepDoorsClosed;
|
||||||
|
if (i == 0 || !closeDoors)
|
||||||
{
|
{
|
||||||
currentWaypoint = currentPath.CurrentNode;
|
currentWaypoint = currentPath.CurrentNode;
|
||||||
nextWaypoint = currentPath.NextNode;
|
nextWaypoint = currentPath.NextNode;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
currentWaypoint = currentPath.PrevNode;
|
int previousIndex = currentPath.CurrentIndex - i;
|
||||||
|
if (previousIndex < 0) { break; }
|
||||||
|
currentWaypoint = currentPath.Nodes[previousIndex];
|
||||||
nextWaypoint = currentPath.CurrentNode;
|
nextWaypoint = currentPath.CurrentNode;
|
||||||
}
|
}
|
||||||
if (currentWaypoint?.ConnectedDoor == null) { continue; }
|
if (currentWaypoint?.ConnectedDoor == null) { continue; }
|
||||||
@@ -520,16 +557,18 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
float colliderLength = GetColliderLength();
|
||||||
door = currentWaypoint.ConnectedDoor;
|
door = currentWaypoint.ConnectedDoor;
|
||||||
if (door.LinkedGap.IsHorizontal)
|
if (door.LinkedGap.IsHorizontal)
|
||||||
{
|
{
|
||||||
int dir = Math.Sign(nextWaypoint.WorldPosition.X - door.Item.WorldPosition.X);
|
int dir = Math.Sign(nextWaypoint.WorldPosition.X - door.Item.WorldPosition.X);
|
||||||
shouldBeOpen = (door.Item.WorldPosition.X - character.WorldPosition.X) * dir > -50.0f;
|
float size = character.AnimController.InWater ? colliderLength : GetColliderSize().X;
|
||||||
|
shouldBeOpen = (door.Item.WorldPosition.X - character.WorldPosition.X) * dir > -size;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int dir = Math.Sign(nextWaypoint.WorldPosition.Y - door.Item.WorldPosition.Y);
|
int dir = Math.Sign(nextWaypoint.WorldPosition.Y - door.Item.WorldPosition.Y);
|
||||||
shouldBeOpen = (door.Item.WorldPosition.Y - character.WorldPosition.Y) * dir > -80.0f;
|
shouldBeOpen = (door.Item.WorldPosition.Y - character.WorldPosition.Y) * dir > -colliderLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,7 +612,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else if (closestButton != null)
|
else if (closestButton != null)
|
||||||
{
|
{
|
||||||
if (Vector2.DistanceSquared(closestButton.Item.WorldPosition, character.WorldPosition) < MathUtils.Pow(closestButton.Item.InteractDistance * 2, 2))
|
if (Vector2.DistanceSquared(closestButton.Item.WorldPosition, character.WorldPosition) < MathUtils.Pow(closestButton.Item.InteractDistance + GetColliderLength(), 2))
|
||||||
{
|
{
|
||||||
closestButton.Item.TryInteract(character, false, true);
|
closestButton.Item.TryInteract(character, false, true);
|
||||||
buttonPressCooldown = ButtonPressInterval;
|
buttonPressCooldown = ButtonPressInterval;
|
||||||
|
|||||||
@@ -19,16 +19,19 @@ namespace Barotrauma
|
|||||||
private Body targetBody;
|
private Body targetBody;
|
||||||
private Vector2 attachSurfaceNormal;
|
private Vector2 attachSurfaceNormal;
|
||||||
private Submarine targetSubmarine;
|
private Submarine targetSubmarine;
|
||||||
|
private Character targetCharacter;
|
||||||
private readonly Character character;
|
private readonly Character character;
|
||||||
|
|
||||||
public bool AttachToSub { get; private set; }
|
public bool AttachToSub { get; private set; }
|
||||||
public bool AttachToWalls { get; private set; }
|
public bool AttachToWalls { get; private set; }
|
||||||
|
public bool AttachToCharacters { get; private set; }
|
||||||
|
|
||||||
private readonly float minDeattachSpeed, maxDeattachSpeed;
|
private readonly float minDeattachSpeed, maxDeattachSpeed, maxAttachDuration;
|
||||||
private readonly float damageOnDetach, detachStun;
|
private readonly float damageOnDetach, detachStun;
|
||||||
private float deattachTimer;
|
private readonly bool weld;
|
||||||
|
private float deattachCheckTimer;
|
||||||
|
|
||||||
private Vector2 wallAttachPos;
|
private Vector2 _attachPos;
|
||||||
|
|
||||||
private float attachCooldown;
|
private float attachCooldown;
|
||||||
|
|
||||||
@@ -38,9 +41,9 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private float jointDir;
|
private float jointDir;
|
||||||
|
|
||||||
public List<WeldJoint> AttachJoints { get; } = new List<WeldJoint>();
|
public List<Joint> AttachJoints { get; } = new List<Joint>();
|
||||||
|
|
||||||
public Vector2? WallAttachPos
|
public Vector2? AttachPos
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
private set;
|
private set;
|
||||||
@@ -48,18 +51,21 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public bool IsAttached => AttachJoints.Count > 0;
|
public bool IsAttached => AttachJoints.Count > 0;
|
||||||
|
|
||||||
public bool IsAttachedToSub => IsAttached && targetSubmarine != null;
|
public bool IsAttachedToSub => IsAttached && targetSubmarine != null && targetCharacter == null;
|
||||||
|
|
||||||
public LatchOntoAI(XElement element, EnemyAIController enemyAI)
|
public LatchOntoAI(XElement element, EnemyAIController enemyAI)
|
||||||
{
|
{
|
||||||
AttachToWalls = element.GetAttributeBool("attachtowalls", false);
|
AttachToWalls = element.GetAttributeBool("attachtowalls", false);
|
||||||
AttachToSub = element.GetAttributeBool("attachtosub", false);
|
AttachToSub = element.GetAttributeBool("attachtosub", false);
|
||||||
|
AttachToCharacters = element.GetAttributeBool("attachtocharacters", false);
|
||||||
minDeattachSpeed = element.GetAttributeFloat("mindeattachspeed", 5.0f);
|
minDeattachSpeed = element.GetAttributeFloat("mindeattachspeed", 5.0f);
|
||||||
maxDeattachSpeed = Math.Max(minDeattachSpeed, element.GetAttributeFloat("maxdeattachspeed", 8.0f));
|
maxDeattachSpeed = Math.Max(minDeattachSpeed, element.GetAttributeFloat("maxdeattachspeed", 8.0f));
|
||||||
|
maxAttachDuration = element.GetAttributeFloat("maxattachduration", -1.0f);
|
||||||
damageOnDetach = element.GetAttributeFloat("damageondetach", 0.0f);
|
damageOnDetach = element.GetAttributeFloat("damageondetach", 0.0f);
|
||||||
detachStun = element.GetAttributeFloat("detachstun", 0.0f);
|
detachStun = element.GetAttributeFloat("detachstun", 0.0f);
|
||||||
localAttachPos = ConvertUnits.ToSimUnits(element.GetAttributeVector2("localattachpos", Vector2.Zero));
|
localAttachPos = ConvertUnits.ToSimUnits(element.GetAttributeVector2("localattachpos", Vector2.Zero));
|
||||||
attachLimbRotation = MathHelper.ToRadians(element.GetAttributeFloat("attachlimbrotation", 0.0f));
|
attachLimbRotation = MathHelper.ToRadians(element.GetAttributeFloat("attachlimbrotation", 0.0f));
|
||||||
|
weld = element.GetAttributeBool("weld", true);
|
||||||
|
|
||||||
string limbString = element.GetAttributeString("attachlimb", null);
|
string limbString = element.GetAttributeString("attachlimb", null);
|
||||||
attachLimb = enemyAI.Character.AnimController.Limbs.FirstOrDefault(l => string.Equals(l.Name, limbString, StringComparison.OrdinalIgnoreCase));
|
attachLimb = enemyAI.Character.AnimController.Limbs.FirstOrDefault(l => string.Equals(l.Name, limbString, StringComparison.OrdinalIgnoreCase));
|
||||||
@@ -81,30 +87,54 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void SetAttachTarget(Structure wall, Vector2 attachPos, Vector2 attachSurfaceNormal)
|
public void SetAttachTarget(Structure wall, Vector2 attachPos, Vector2 attachSurfaceNormal)
|
||||||
{
|
{
|
||||||
|
if (!AttachToSub) { return; }
|
||||||
if (wall == null) { return; }
|
if (wall == null) { return; }
|
||||||
var sub = wall.Submarine;
|
var sub = wall.Submarine;
|
||||||
if (sub == null) { return; }
|
if (sub == null) { return; }
|
||||||
|
Reset();
|
||||||
targetWall = wall;
|
targetWall = wall;
|
||||||
targetSubmarine = sub;
|
targetSubmarine = sub;
|
||||||
targetBody = targetSubmarine.PhysicsBody.FarseerBody;
|
targetBody = targetSubmarine.PhysicsBody.FarseerBody;
|
||||||
this.attachSurfaceNormal = attachSurfaceNormal;
|
this.attachSurfaceNormal = attachSurfaceNormal;
|
||||||
wallAttachPos = attachPos;
|
_attachPos = attachPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAttachTarget(Character target)
|
||||||
|
{
|
||||||
|
if (!AttachToCharacters) { return; }
|
||||||
|
Reset();
|
||||||
|
targetCharacter = target;
|
||||||
|
targetSubmarine = target.Submarine;
|
||||||
|
targetBody = target.AnimController.Collider.FarseerBody;
|
||||||
|
attachSurfaceNormal = Vector2.Normalize(character.WorldPosition - target.WorldPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(EnemyAIController enemyAI, float deltaTime)
|
public void Update(EnemyAIController enemyAI, float deltaTime)
|
||||||
{
|
{
|
||||||
if (character.Submarine != null)
|
if (character.Submarine != null)
|
||||||
{
|
{
|
||||||
DeattachFromBody(reset: true);
|
if (targetCharacter != null && targetCharacter.Submarine != targetSubmarine ||
|
||||||
return;
|
character.Submarine != null && targetSubmarine != null && targetCharacter == null)
|
||||||
|
{
|
||||||
|
DeattachFromBody(reset: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (AttachJoints.Count > 0)
|
if (IsAttached)
|
||||||
{
|
{
|
||||||
if (Math.Sign(attachLimb.Dir) != Math.Sign(jointDir))
|
if (Math.Sign(attachLimb.Dir) != Math.Sign(jointDir))
|
||||||
{
|
{
|
||||||
AttachJoints[0].LocalAnchorA =
|
var attachJoint = AttachJoints[0];
|
||||||
new Vector2(-AttachJoints[0].LocalAnchorA.X, AttachJoints[0].LocalAnchorA.Y);
|
if (attachJoint is WeldJoint weldJoint)
|
||||||
AttachJoints[0].ReferenceAngle = -AttachJoints[0].ReferenceAngle;
|
{
|
||||||
|
weldJoint.LocalAnchorA = new Vector2(-weldJoint.LocalAnchorA.X, weldJoint.LocalAnchorA.Y);
|
||||||
|
weldJoint.ReferenceAngle = -weldJoint.ReferenceAngle;
|
||||||
|
}
|
||||||
|
else if (attachJoint is RevoluteJoint revoluteJoint)
|
||||||
|
{
|
||||||
|
revoluteJoint.LocalAnchorA = new Vector2(-revoluteJoint.LocalAnchorA.X, revoluteJoint.LocalAnchorA.Y);
|
||||||
|
revoluteJoint.ReferenceAngle = -revoluteJoint.ReferenceAngle;
|
||||||
|
}
|
||||||
jointDir = attachLimb.Dir;
|
jointDir = attachLimb.Dir;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < AttachJoints.Count; i++)
|
for (int i = 0; i < AttachJoints.Count; i++)
|
||||||
@@ -113,31 +143,51 @@ namespace Barotrauma
|
|||||||
if (Vector2.DistanceSquared(AttachJoints[i].WorldAnchorB, AttachJoints[i].BodyA.Position) > 10.0f * 10.0f)
|
if (Vector2.DistanceSquared(AttachJoints[i].WorldAnchorB, AttachJoints[i].BodyA.Position) > 10.0f * 10.0f)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
DebugConsole.ThrowError("Limb body of the character \"" + character.Name + "\" is very far from the attach joint anchor -> deattach");
|
DebugConsole.Log("Limb body of the character \"" + character.Name + "\" is very far from the attach joint anchor -> deattach");
|
||||||
#endif
|
#endif
|
||||||
DeattachFromBody(reset: true);
|
DeattachFromBody(reset: true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (targetCharacter != null)
|
||||||
|
{
|
||||||
|
if (enemyAI.AttackingLimb?.attack == null)
|
||||||
|
{
|
||||||
|
DeattachFromBody(reset: true, cooldown: 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float range = enemyAI.AttackingLimb.attack.DamageRange * 2f;
|
||||||
|
if (Vector2.DistanceSquared(targetCharacter.WorldPosition, enemyAI.AttackingLimb.WorldPosition) > range * range)
|
||||||
|
{
|
||||||
|
DeattachFromBody(reset: true, cooldown: 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attachCooldown > 0)
|
if (attachCooldown > 0)
|
||||||
{
|
{
|
||||||
attachCooldown -= deltaTime;
|
attachCooldown -= deltaTime;
|
||||||
}
|
}
|
||||||
if (deattachTimer > 0)
|
if (deattachCheckTimer > 0)
|
||||||
{
|
{
|
||||||
deattachTimer -= deltaTime;
|
deattachCheckTimer -= deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 transformedAttachPos = wallAttachPos;
|
if (targetCharacter != null)
|
||||||
|
{
|
||||||
|
// Own sim pos -> target where we are
|
||||||
|
_attachPos = character.SimPosition;
|
||||||
|
}
|
||||||
|
Vector2 transformedAttachPos = _attachPos;
|
||||||
if (character.Submarine == null && targetSubmarine != null)
|
if (character.Submarine == null && targetSubmarine != null)
|
||||||
{
|
{
|
||||||
transformedAttachPos += ConvertUnits.ToSimUnits(targetSubmarine.Position);
|
transformedAttachPos += ConvertUnits.ToSimUnits(targetSubmarine.Position);
|
||||||
}
|
}
|
||||||
if (transformedAttachPos != Vector2.Zero)
|
if (transformedAttachPos != Vector2.Zero)
|
||||||
{
|
{
|
||||||
WallAttachPos = transformedAttachPos;
|
AttachPos = transformedAttachPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (enemyAI.State)
|
switch (enemyAI.State)
|
||||||
@@ -151,7 +201,7 @@ namespace Barotrauma
|
|||||||
//check if there are any walls nearby the character could attach to
|
//check if there are any walls nearby the character could attach to
|
||||||
if (raycastTimer < 0.0f)
|
if (raycastTimer < 0.0f)
|
||||||
{
|
{
|
||||||
wallAttachPos = Vector2.Zero;
|
_attachPos = Vector2.Zero;
|
||||||
|
|
||||||
var cells = Level.Loaded.GetCells(character.WorldPosition, 1);
|
var cells = Level.Loaded.GetCells(character.WorldPosition, 1);
|
||||||
if (cells.Count > 0)
|
if (cells.Count > 0)
|
||||||
@@ -169,7 +219,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
attachSurfaceNormal = edge.GetNormal(cell);
|
attachSurfaceNormal = edge.GetNormal(cell);
|
||||||
targetBody = cell.Body;
|
targetBody = cell.Body;
|
||||||
wallAttachPos = potentialAttachPos;
|
_attachPos = potentialAttachPos;
|
||||||
closestDist = distSqr;
|
closestDist = distSqr;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -183,21 +233,20 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wallAttachPos = Vector2.Zero;
|
_attachPos = Vector2.Zero;
|
||||||
}
|
}
|
||||||
|
if (_attachPos == Vector2.Zero || targetBody == null)
|
||||||
if (wallAttachPos == Vector2.Zero || targetBody == null)
|
|
||||||
{
|
{
|
||||||
DeattachFromBody(reset: false);
|
DeattachFromBody(reset: false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
float squaredDistance = Vector2.DistanceSquared(character.SimPosition, wallAttachPos);
|
float squaredDistance = Vector2.DistanceSquared(character.SimPosition, _attachPos);
|
||||||
float targetDistance = Math.Max(Math.Max(character.AnimController.Collider.radius, character.AnimController.Collider.width), character.AnimController.Collider.height) * 1.2f;
|
float targetDistance = Math.Max(Math.Max(character.AnimController.Collider.radius, character.AnimController.Collider.width), character.AnimController.Collider.height) * 1.2f;
|
||||||
if (squaredDistance < targetDistance * targetDistance)
|
if (squaredDistance < targetDistance * targetDistance)
|
||||||
{
|
{
|
||||||
//close enough to a wall -> attach
|
//close enough to a wall -> attach
|
||||||
AttachToBody(wallAttachPos);
|
AttachToBody(_attachPos);
|
||||||
enemyAI.SteeringManager.Reset();
|
enemyAI.SteeringManager.Reset();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -205,25 +254,22 @@ namespace Barotrauma
|
|||||||
//move closer to the wall
|
//move closer to the wall
|
||||||
DeattachFromBody(reset: false);
|
DeattachFromBody(reset: false);
|
||||||
enemyAI.SteeringManager.SteeringAvoid(deltaTime, 1.0f, 0.1f);
|
enemyAI.SteeringManager.SteeringAvoid(deltaTime, 1.0f, 0.1f);
|
||||||
enemyAI.SteeringManager.SteeringSeek(wallAttachPos);
|
enemyAI.SteeringManager.SteeringSeek(_attachPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AIState.Attack:
|
case AIState.Attack:
|
||||||
case AIState.Aggressive:
|
case AIState.Aggressive:
|
||||||
if (enemyAI.AttackingLimb != null)
|
if (enemyAI.IsSteeringThroughGap) { break; }
|
||||||
|
if (_attachPos == Vector2.Zero) { break; }
|
||||||
|
if (!AttachToSub && !AttachToCharacters) { break; }
|
||||||
|
if (enemyAI.AttackingLimb == null) { break; }
|
||||||
|
if (targetBody == null) { break; }
|
||||||
|
if (IsAttached && AttachJoints[0].BodyB == targetBody) { break; }
|
||||||
|
Vector2 referencePos = targetCharacter != null ? targetCharacter.WorldPosition : ConvertUnits.ToDisplayUnits(transformedAttachPos);
|
||||||
|
if (Vector2.DistanceSquared(referencePos, enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.DamageRange * enemyAI.AttackingLimb.attack.DamageRange)
|
||||||
{
|
{
|
||||||
if (AttachToSub && !enemyAI.IsSteeringThroughGap && wallAttachPos != Vector2.Zero && targetBody != null)
|
AttachToBody(transformedAttachPos);
|
||||||
{
|
|
||||||
// is not attached or is attached to something else
|
|
||||||
if (!IsAttached || IsAttached && AttachJoints[0].BodyB != targetBody)
|
|
||||||
{
|
|
||||||
if (Vector2.DistanceSquared(ConvertUnits.ToDisplayUnits(transformedAttachPos), enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.DamageRange * enemyAI.AttackingLimb.attack.DamageRange)
|
|
||||||
{
|
|
||||||
AttachToBody(transformedAttachPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -231,43 +277,50 @@ namespace Barotrauma
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsAttached && targetBody != null && targetWall != null && targetSubmarine != null && deattachTimer <= 0.0f)
|
if (IsAttached && targetBody != null && deattachCheckTimer <= 0.0f)
|
||||||
{
|
{
|
||||||
bool deattach = false;
|
bool deattach = false;
|
||||||
// Deattach if the wall is broken enough where we are attached to
|
if (maxAttachDuration > 0)
|
||||||
int targetSection = targetWall.FindSectionIndex(attachLimb.WorldPosition, world: true, clamp: true);
|
|
||||||
if (enemyAI.CanPassThroughHole(targetWall, targetSection))
|
|
||||||
{
|
{
|
||||||
deattach = true;
|
deattach = true;
|
||||||
attachCooldown = 2;
|
|
||||||
}
|
}
|
||||||
if (!deattach)
|
if (!deattach && targetWall != null && targetSubmarine != null)
|
||||||
{
|
{
|
||||||
// Deattach if the velocity is high
|
// Deattach if the wall is broken enough where we are attached to
|
||||||
float velocity = targetSubmarine.Velocity == Vector2.Zero ? 0.0f : targetSubmarine.Velocity.Length();
|
int targetSection = targetWall.FindSectionIndex(attachLimb.WorldPosition, world: true, clamp: true);
|
||||||
deattach = velocity > maxDeattachSpeed;
|
if (enemyAI.CanPassThroughHole(targetWall, targetSection))
|
||||||
|
{
|
||||||
|
deattach = true;
|
||||||
|
attachCooldown = 2;
|
||||||
|
}
|
||||||
if (!deattach)
|
if (!deattach)
|
||||||
{
|
{
|
||||||
if (velocity > minDeattachSpeed)
|
// Deattach if the velocity is high
|
||||||
|
float velocity = targetSubmarine.Velocity == Vector2.Zero ? 0.0f : targetSubmarine.Velocity.Length();
|
||||||
|
deattach = velocity > maxDeattachSpeed;
|
||||||
|
if (!deattach)
|
||||||
{
|
{
|
||||||
float velocityFactor = (maxDeattachSpeed - minDeattachSpeed <= 0.0f) ?
|
if (velocity > minDeattachSpeed)
|
||||||
Math.Sign(Math.Abs(velocity) - minDeattachSpeed) :
|
|
||||||
(Math.Abs(velocity) - minDeattachSpeed) / (maxDeattachSpeed - minDeattachSpeed);
|
|
||||||
|
|
||||||
if (Rand.Range(0.0f, 1.0f) < velocityFactor)
|
|
||||||
{
|
{
|
||||||
deattach = true;
|
float velocityFactor = (maxDeattachSpeed - minDeattachSpeed <= 0.0f) ?
|
||||||
character.AddDamage(character.WorldPosition, new List<Affliction>() { AfflictionPrefab.InternalDamage.Instantiate(damageOnDetach) }, detachStun, true);
|
Math.Sign(Math.Abs(velocity) - minDeattachSpeed) :
|
||||||
attachCooldown = detachStun * 2;
|
(Math.Abs(velocity) - minDeattachSpeed) / (maxDeattachSpeed - minDeattachSpeed);
|
||||||
|
|
||||||
|
if (Rand.Range(0.0f, 1.0f) < velocityFactor)
|
||||||
|
{
|
||||||
|
deattach = true;
|
||||||
|
character.AddDamage(character.WorldPosition, new List<Affliction>() { AfflictionPrefab.InternalDamage.Instantiate(damageOnDetach) }, detachStun, true);
|
||||||
|
attachCooldown = detachStun * 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
deattachCheckTimer = 5.0f;
|
||||||
}
|
}
|
||||||
if (deattach)
|
if (deattach)
|
||||||
{
|
{
|
||||||
DeattachFromBody(reset: true);
|
DeattachFromBody(reset: true);
|
||||||
}
|
}
|
||||||
deattachTimer = 5.0f;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,16 +368,30 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
collider.SetTransform(attachPos + attachSurfaceNormal * colliderFront.Length(), MathUtils.VectorToAngle(-attachSurfaceNormal) - MathHelper.PiOver2);
|
collider.SetTransform(attachPos + attachSurfaceNormal * colliderFront.Length(), MathUtils.VectorToAngle(-attachSurfaceNormal) - MathHelper.PiOver2);
|
||||||
|
|
||||||
var colliderJoint = new WeldJoint(collider.FarseerBody, targetBody, colliderFront, targetBody.GetLocalPoint(attachPos), false)
|
Joint colliderJoint = weld ?
|
||||||
{
|
new WeldJoint(collider.FarseerBody, targetBody, colliderFront, targetBody.GetLocalPoint(attachPos), false)
|
||||||
FrequencyHz = 10.0f,
|
{
|
||||||
DampingRatio = 0.5f,
|
FrequencyHz = 10.0f,
|
||||||
KinematicBodyB = true,
|
DampingRatio = 0.5f,
|
||||||
CollideConnected = false,
|
KinematicBodyB = true,
|
||||||
//Length = 0.1f
|
CollideConnected = false,
|
||||||
};
|
} :
|
||||||
|
new RevoluteJoint(collider.FarseerBody, targetBody, colliderFront, targetBody.GetLocalPoint(attachPos), false)
|
||||||
|
{
|
||||||
|
MotorEnabled = true,
|
||||||
|
MaxMotorTorque = 0.25f
|
||||||
|
} as Joint;
|
||||||
|
|
||||||
GameMain.World.Add(colliderJoint);
|
GameMain.World.Add(colliderJoint);
|
||||||
AttachJoints.Add(colliderJoint);
|
AttachJoints.Add(colliderJoint);
|
||||||
|
if (targetCharacter != null)
|
||||||
|
{
|
||||||
|
targetCharacter.Latchers.Add(this);
|
||||||
|
}
|
||||||
|
if (maxAttachDuration > 0)
|
||||||
|
{
|
||||||
|
deattachCheckTimer = maxAttachDuration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeattachFromBody(bool reset, float cooldown = 0)
|
public void DeattachFromBody(bool reset, float cooldown = 0)
|
||||||
@@ -342,14 +409,23 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
if (targetCharacter != null)
|
||||||
|
{
|
||||||
|
targetCharacter.Latchers.Remove(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Reset()
|
private void Reset()
|
||||||
{
|
{
|
||||||
|
if (targetCharacter != null)
|
||||||
|
{
|
||||||
|
targetCharacter.Latchers.Remove(this);
|
||||||
|
}
|
||||||
|
targetCharacter = null;
|
||||||
targetWall = null;
|
targetWall = null;
|
||||||
targetSubmarine = null;
|
targetSubmarine = null;
|
||||||
targetBody = null;
|
targetBody = null;
|
||||||
WallAttachPos = null;
|
AttachPos = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCharacterDeath(Character character, CauseOfDeath causeOfDeath)
|
private void OnCharacterDeath(Character character, CauseOfDeath causeOfDeath)
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ namespace Barotrauma
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (HumanAIController.debugai && objectiveManager.IsOrder(this) && !objectiveManager.IsCurrentOrder<AIObjectiveGoTo>() && !objectiveManager.IsCurrentOrder<AIObjectiveReturn>())
|
if (HumanAIController.debugai && objectiveManager.IsOrder(this) && !objectiveManager.IsCurrentOrder<AIObjectiveGoTo>() && !objectiveManager.IsCurrentOrder<AIObjectiveReturn>())
|
||||||
{
|
{
|
||||||
|
// TODO: dismiss
|
||||||
throw new Exception("Order abandoned!");
|
throw new Exception("Order abandoned!");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+1
-1
@@ -111,7 +111,7 @@ namespace Barotrauma
|
|||||||
public CombatMode Mode { get; private set; }
|
public CombatMode Mode { get; private set; }
|
||||||
|
|
||||||
private bool IsOffensiveOrArrest => initialMode == CombatMode.Offensive || initialMode == CombatMode.Arrest;
|
private bool IsOffensiveOrArrest => initialMode == CombatMode.Offensive || initialMode == CombatMode.Arrest;
|
||||||
private bool TargetEliminated => IsEnemyDisabled || Enemy.IsUnconscious;
|
private bool TargetEliminated => IsEnemyDisabled || (Enemy.IsUnconscious && Enemy.Params.Health.ConstantHealthRegeneration <= 0.0f);
|
||||||
private bool IsEnemyDisabled => Enemy == null || Enemy.Removed || Enemy.IsDead;
|
private bool IsEnemyDisabled => Enemy == null || Enemy.Removed || Enemy.IsDead;
|
||||||
|
|
||||||
private float AimSpeed => HumanAIController.AimSpeed;
|
private float AimSpeed => HumanAIController.AimSpeed;
|
||||||
|
|||||||
+2
-1
@@ -56,7 +56,8 @@ namespace Barotrauma
|
|||||||
public static bool IsValidTarget(Character target, Character character)
|
public static bool IsValidTarget(Character target, Character character)
|
||||||
{
|
{
|
||||||
if (target == null || target.Removed) { return false; }
|
if (target == null || target.Removed) { return false; }
|
||||||
if (target.IsDead || target.IsUnconscious) { return false; }
|
if (target.IsDead) { return false; }
|
||||||
|
if (target.IsUnconscious && target.Params.Health.ConstantHealthRegeneration <= 0.0f) { return false; }
|
||||||
if (target == character) { return false; }
|
if (target == character) { return false; }
|
||||||
if (target.Submarine == null) { return false; }
|
if (target.Submarine == null) { return false; }
|
||||||
if (character.Submarine == null) { return false; }
|
if (character.Submarine == null) { return false; }
|
||||||
|
|||||||
+2
@@ -307,6 +307,8 @@ namespace Barotrauma
|
|||||||
foreach (Hull hull in Hull.hullList.OrderByDescending(h => EstimateHullSuitability(h)))
|
foreach (Hull hull in Hull.hullList.OrderByDescending(h => EstimateHullSuitability(h)))
|
||||||
{
|
{
|
||||||
if (hull.Submarine == null) { continue; }
|
if (hull.Submarine == null) { continue; }
|
||||||
|
// Ruins are mazes filled with water. There's no safe hulls and we don't want to use the resources on it.
|
||||||
|
if (hull.Submarine.Info.IsRuin) { continue; }
|
||||||
if (!allowChangingTheSubmarine && hull.Submarine != character.Submarine) { continue; }
|
if (!allowChangingTheSubmarine && hull.Submarine != character.Submarine) { continue; }
|
||||||
if (hull.Rect.Height < ConvertUnits.ToDisplayUnits(character.AnimController.ColliderHeightFromFloor) * 2) { continue; }
|
if (hull.Rect.Height < ConvertUnits.ToDisplayUnits(character.AnimController.ColliderHeightFromFloor) * 2) { continue; }
|
||||||
if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; }
|
if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; }
|
||||||
|
|||||||
+5
-5
@@ -461,11 +461,11 @@ namespace Barotrauma
|
|||||||
nodeFilter = n => n.Waypoint.Tunnel != null;
|
nodeFilter = n => n.Waypoint.Tunnel != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
PathSteering.SteeringSeek(targetPos, 1,
|
PathSteering.SteeringSeek(targetPos, weight: 1,
|
||||||
startNodeFilter: n => (n.Waypoint.CurrentHull == null) == (character.CurrentHull == null),
|
startNodeFilter: n => (n.Waypoint.CurrentHull == null) == (character.CurrentHull == null),
|
||||||
endNodeFilter,
|
endNodeFilter: endNodeFilter,
|
||||||
nodeFilter,
|
nodeFilter: nodeFilter,
|
||||||
CheckVisibility);
|
checkVisiblity: CheckVisibility);
|
||||||
|
|
||||||
if (!isInside && (PathSteering.CurrentPath == null || PathSteering.IsPathDirty || PathSteering.CurrentPath.Unreachable))
|
if (!isInside && (PathSteering.CurrentPath == null || PathSteering.IsPathDirty || PathSteering.CurrentPath.Unreachable))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private readonly List<PathNode> sortedNodes = new List<PathNode>();
|
private readonly List<PathNode> sortedNodes = new List<PathNode>();
|
||||||
|
|
||||||
public SteeringPath FindPath(Vector2 start, Vector2 end, Submarine hostSub = null, string errorMsgStr = null, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
|
public SteeringPath FindPath(Vector2 start, Vector2 end, Submarine hostSub = null, string errorMsgStr = null, float minGapSize = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
|
||||||
{
|
{
|
||||||
foreach (PathNode node in nodes)
|
foreach (PathNode node in nodes)
|
||||||
{
|
{
|
||||||
@@ -233,6 +233,10 @@ namespace Barotrauma
|
|||||||
// Always check the visibility for the start node
|
// Always check the visibility for the start node
|
||||||
if (!IsWaypointVisible(node, start)) { continue; }
|
if (!IsWaypointVisible(node, start)) { continue; }
|
||||||
if (node.IsBlocked()) { continue; }
|
if (node.IsBlocked()) { continue; }
|
||||||
|
if (node.Waypoint.ConnectedGap != null)
|
||||||
|
{
|
||||||
|
if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { continue; }
|
||||||
|
}
|
||||||
startNode = node;
|
startNode = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,6 +286,10 @@ namespace Barotrauma
|
|||||||
// Only check the visibility for the end node when allowed (fix leaks)
|
// Only check the visibility for the end node when allowed (fix leaks)
|
||||||
if (!IsWaypointVisible(node, end, checkVisibility: checkVisibility)) { continue; }
|
if (!IsWaypointVisible(node, end, checkVisibility: checkVisibility)) { continue; }
|
||||||
if (node.IsBlocked()) { continue; }
|
if (node.IsBlocked()) { continue; }
|
||||||
|
if (node.Waypoint.ConnectedGap != null)
|
||||||
|
{
|
||||||
|
if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { continue; }
|
||||||
|
}
|
||||||
endNode = node;
|
endNode = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,40 +302,12 @@ namespace Barotrauma
|
|||||||
return new SteeringPath(true);
|
return new SteeringPath(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = FindPath(startNode, endNode, nodeFilter, errorMsgStr);
|
var path = FindPath(startNode, endNode, nodeFilter, errorMsgStr, minGapSize);
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SteeringPath FindPath(WayPoint start, WayPoint end)
|
private SteeringPath FindPath(PathNode start, PathNode end, Func<PathNode, bool> filter = null, string errorMsgStr = "", float minGapSize = 0)
|
||||||
{
|
|
||||||
PathNode startNode = null, endNode = null;
|
|
||||||
foreach (PathNode node in nodes)
|
|
||||||
{
|
|
||||||
if (node.Waypoint == start)
|
|
||||||
{
|
|
||||||
startNode = node;
|
|
||||||
if (endNode != null) { break; }
|
|
||||||
}
|
|
||||||
if (node.Waypoint == end)
|
|
||||||
{
|
|
||||||
endNode = node;
|
|
||||||
if (startNode != null) { break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startNode == null || endNode == null)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
DebugConsole.NewMessage("Pathfinding error, couldn't find matching pathnodes to waypoints.", Color.DarkRed);
|
|
||||||
#endif
|
|
||||||
return new SteeringPath(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FindPath(startNode, endNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SteeringPath FindPath(PathNode start, PathNode end, Func<PathNode, bool> filter = null, string errorMsgStr = "")
|
|
||||||
{
|
{
|
||||||
if (start == end)
|
if (start == end)
|
||||||
{
|
{
|
||||||
@@ -356,7 +336,10 @@ namespace Barotrauma
|
|||||||
if (isCharacter && node.Waypoint.isObstructed) { continue; }
|
if (isCharacter && node.Waypoint.isObstructed) { continue; }
|
||||||
if (filter != null && !filter(node)) { continue; }
|
if (filter != null && !filter(node)) { continue; }
|
||||||
if (node.IsBlocked()) { continue; }
|
if (node.IsBlocked()) { continue; }
|
||||||
|
if (node.Waypoint.ConnectedGap != null)
|
||||||
|
{
|
||||||
|
if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { continue; }
|
||||||
|
}
|
||||||
dist = node.F;
|
dist = node.F;
|
||||||
currNode = node;
|
currNode = node;
|
||||||
}
|
}
|
||||||
@@ -460,6 +443,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CanFitThroughGap(Gap gap, float minWidth) => gap.IsHorizontal ? gap.RectHeight > minWidth : gap.RectWidth > minWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,31 @@
|
|||||||
using FarseerPhysics;
|
using Barotrauma.Items.Components;
|
||||||
|
using FarseerPhysics;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Barotrauma.Extensions;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
abstract class AnimController : Ragdoll
|
abstract class AnimController : Ragdoll
|
||||||
{
|
{
|
||||||
|
public Vector2 RightHandIKPos { get; protected set; }
|
||||||
|
public Vector2 LeftHandIKPos { get; protected set; }
|
||||||
|
|
||||||
|
protected LimbJoint rightShoulder, leftShoulder;
|
||||||
|
protected float upperArmLength, forearmLength;
|
||||||
|
protected float useItemTimer;
|
||||||
|
protected bool aiming;
|
||||||
|
protected bool wasAiming;
|
||||||
|
protected bool aimingMelee;
|
||||||
|
protected bool wasAimingMelee;
|
||||||
|
|
||||||
|
public bool IsAiming => wasAiming;
|
||||||
|
public bool IsAimingMelee => wasAimingMelee;
|
||||||
|
|
||||||
|
public float ArmLength => upperArmLength + forearmLength;
|
||||||
|
|
||||||
public abstract GroundedMovementParams WalkParams { get; set; }
|
public abstract GroundedMovementParams WalkParams { get; set; }
|
||||||
public abstract GroundedMovementParams RunParams { get; set; }
|
public abstract GroundedMovementParams RunParams { get; set; }
|
||||||
public abstract SwimParams SwimSlowParams { get; set; }
|
public abstract SwimParams SwimSlowParams { get; set; }
|
||||||
@@ -60,14 +79,14 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return IsMovingFast? SwimFastParams : SwimSlowParams;
|
return IsMovingFast ? SwimFastParams : SwimSlowParams;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanWalk => RagdollParams.CanWalk;
|
public bool CanWalk => RagdollParams.CanWalk;
|
||||||
public bool IsMovingBackwards => !InWater && Math.Sign(targetMovement.X) == -Math.Sign(Dir);
|
public bool IsMovingBackwards => !InWater && Math.Sign(targetMovement.X) == -Math.Sign(Dir);
|
||||||
|
|
||||||
// TODO: define death anim duration in XML
|
// TODO: define death anim duration in XML
|
||||||
protected float deathAnimTimer, deathAnimDuration = 5.0f;
|
protected float deathAnimTimer, deathAnimDuration = 5.0f;
|
||||||
|
|
||||||
@@ -155,13 +174,9 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public AnimController(Character character, string seed, RagdollParams ragdollParams = null) : base(character, seed, ragdollParams) { }
|
public AnimController(Character character, string seed, RagdollParams ragdollParams = null) : base(character, seed, ragdollParams) { }
|
||||||
|
|
||||||
public virtual void UpdateAnim(float deltaTime) { }
|
public abstract void UpdateAnim(float deltaTime);
|
||||||
|
|
||||||
public virtual void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle = 0.0f, bool aimingMelee = false) { }
|
public abstract void DragCharacter(Character target, float deltaTime);
|
||||||
|
|
||||||
public virtual void DragCharacter(Character target, float deltaTime) { }
|
|
||||||
|
|
||||||
public virtual void UpdateUseItem(bool allowMovement, Vector2 handWorldPos) { }
|
|
||||||
|
|
||||||
public virtual float GetSpeed(AnimationType type)
|
public virtual float GetSpeed(AnimationType type)
|
||||||
{
|
{
|
||||||
@@ -253,5 +268,437 @@ namespace Barotrauma
|
|||||||
throw new NotImplementedException(type.ToString());
|
throw new NotImplementedException(type.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
|
||||||
|
{
|
||||||
|
useItemTimer = 0.5f;
|
||||||
|
Anim = Animation.UsingConstruction;
|
||||||
|
|
||||||
|
if (!allowMovement)
|
||||||
|
{
|
||||||
|
TargetMovement = Vector2.Zero;
|
||||||
|
TargetDir = handWorldPos.X > character.WorldPosition.X ? Direction.Right : Direction.Left;
|
||||||
|
float sqrDist = Vector2.DistanceSquared(character.WorldPosition, handWorldPos);
|
||||||
|
if (sqrDist > MathUtils.Pow(ConvertUnits.ToDisplayUnits(upperArmLength + forearmLength), 2))
|
||||||
|
{
|
||||||
|
TargetMovement = Vector2.Normalize(handWorldPos - character.WorldPosition) * GetCurrentSpeed(false) * Math.Max(character.SpeedMultiplier, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!character.Enabled) { return; }
|
||||||
|
|
||||||
|
Vector2 handSimPos = ConvertUnits.ToSimUnits(handWorldPos);
|
||||||
|
if (character.Submarine != null)
|
||||||
|
{
|
||||||
|
handSimPos -= character.Submarine.SimPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
var leftHand = GetLimb(LimbType.LeftHand);
|
||||||
|
if (leftHand != null)
|
||||||
|
{
|
||||||
|
leftHand.Disabled = true;
|
||||||
|
leftHand.PullJointEnabled = true;
|
||||||
|
leftHand.PullJointWorldAnchorB = handSimPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rightHand = GetLimb(LimbType.RightHand);
|
||||||
|
if (rightHand != null)
|
||||||
|
{
|
||||||
|
rightHand.Disabled = true;
|
||||||
|
rightHand.PullJointEnabled = true;
|
||||||
|
rightHand.PullJointWorldAnchorB = handSimPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Grab(Vector2 rightHandPos, Vector2 leftHandPos)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
Limb pullLimb = (i == 0) ? GetLimb(LimbType.LeftHand) : GetLimb(LimbType.RightHand);
|
||||||
|
|
||||||
|
pullLimb.Disabled = true;
|
||||||
|
|
||||||
|
pullLimb.PullJointEnabled = true;
|
||||||
|
pullLimb.PullJointWorldAnchorB = (i == 0) ? rightHandPos : leftHandPos;
|
||||||
|
pullLimb.PullJointMaxForce = 500.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Direction previousDirection;
|
||||||
|
private readonly Vector2[] transformedHandlePos = new Vector2[2];
|
||||||
|
//TODO: refactor this method, it's way too convoluted
|
||||||
|
public void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle = 0.0f, bool aimMelee = false)
|
||||||
|
{
|
||||||
|
aimingMelee = aimMelee;
|
||||||
|
if (character.Stun > 0.0f || character.IsIncapacitated)
|
||||||
|
{
|
||||||
|
aim = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//calculate the handle positions
|
||||||
|
Matrix itemTransfrom = Matrix.CreateRotationZ(item.body.Rotation);
|
||||||
|
float horizontalOffset = ConvertUnits.ToSimUnits((item.Sprite.size.X / 2 - item.Sprite.Origin.X) * item.Scale);
|
||||||
|
|
||||||
|
//handlePos[0] = ConvertUnits.ToSimUnits(new Vector2(-45,25) * 0.5f);
|
||||||
|
//handlePos[1] = ConvertUnits.ToSimUnits(new Vector2(-65,30) * 0.5f);
|
||||||
|
|
||||||
|
transformedHandlePos[0] = Vector2.Transform(new Vector2(handlePos[0].X + horizontalOffset, handlePos[0].Y), itemTransfrom);
|
||||||
|
transformedHandlePos[1] = Vector2.Transform(new Vector2(handlePos[1].X + horizontalOffset, handlePos[1].Y), itemTransfrom);
|
||||||
|
|
||||||
|
Limb torso = GetLimb(LimbType.Torso) ?? MainLimb;
|
||||||
|
Limb leftHand = GetLimb(LimbType.LeftHand);
|
||||||
|
Limb rightHand = GetLimb(LimbType.RightHand);
|
||||||
|
|
||||||
|
Vector2 itemPos = aim ? aimPos : holdPos;
|
||||||
|
|
||||||
|
var controller = character.SelectedConstruction?.GetComponent<Controller>();
|
||||||
|
bool usingController = controller != null && !controller.AllowAiming;
|
||||||
|
bool isClimbing = character.IsClimbing && Math.Abs(character.AnimController.TargetMovement.Y) > 0.01f;
|
||||||
|
float itemAngle;
|
||||||
|
Holdable holdable = item.GetComponent<Holdable>();
|
||||||
|
float torsoRotation = torso.Rotation;
|
||||||
|
bool equippedInRightHand = character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == item && rightHand != null && !rightHand.IsSevered;
|
||||||
|
bool equippedInLefthand = character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand) == item && leftHand != null && !leftHand.IsSevered;
|
||||||
|
if (aim && !isClimbing && !usingController && character.Stun <= 0.0f && itemPos != Vector2.Zero && !character.IsIncapacitated)
|
||||||
|
{
|
||||||
|
Vector2 mousePos = ConvertUnits.ToSimUnits(character.SmoothedCursorPosition);
|
||||||
|
Vector2 diff = holdable.Aimable ? (mousePos - AimSourceSimPos) * Dir : Vector2.UnitX;
|
||||||
|
holdAngle = MathUtils.VectorToAngle(new Vector2(diff.X, diff.Y * Dir)) - torsoRotation * Dir;
|
||||||
|
holdAngle += GetAimWobble(rightHand, leftHand, item);
|
||||||
|
itemAngle = torsoRotation + holdAngle * Dir;
|
||||||
|
if (holdable.ControlPose)
|
||||||
|
{
|
||||||
|
var head = GetLimb(LimbType.Head);
|
||||||
|
if (head != null)
|
||||||
|
{
|
||||||
|
head.body.SmoothRotate(itemAngle, force: 30 * head.Mass);
|
||||||
|
}
|
||||||
|
if (TargetMovement == Vector2.Zero && inWater)
|
||||||
|
{
|
||||||
|
torso.body.AngularVelocity -= torso.body.AngularVelocity * 0.1f;
|
||||||
|
torso.body.ApplyForce(torso.body.LinearVelocity * -0.5f);
|
||||||
|
}
|
||||||
|
aiming = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (holdable.UseHandRotationForHoldAngle)
|
||||||
|
{
|
||||||
|
if (equippedInRightHand)
|
||||||
|
{
|
||||||
|
itemAngle = rightHand.Rotation + holdAngle * Dir;
|
||||||
|
}
|
||||||
|
else if (equippedInLefthand)
|
||||||
|
{
|
||||||
|
itemAngle = leftHand.Rotation + holdAngle * Dir;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
itemAngle = torsoRotation + holdAngle * Dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
itemAngle = torsoRotation + holdAngle * Dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightShoulder == null) { return; }
|
||||||
|
Vector2 transformedHoldPos = rightShoulder.WorldAnchorA;
|
||||||
|
if (itemPos == Vector2.Zero || isClimbing || usingController)
|
||||||
|
{
|
||||||
|
if (equippedInRightHand)
|
||||||
|
{
|
||||||
|
transformedHoldPos = rightHand.PullJointWorldAnchorA - transformedHandlePos[0];
|
||||||
|
itemAngle = rightHand.Rotation + (holdAngle - rightHand.Params.GetSpriteOrientation() + MathHelper.PiOver2) * Dir;
|
||||||
|
}
|
||||||
|
else if (equippedInLefthand)
|
||||||
|
{
|
||||||
|
transformedHoldPos = leftHand.PullJointWorldAnchorA - transformedHandlePos[1];
|
||||||
|
itemAngle = leftHand.Rotation + (holdAngle - leftHand.Params.GetSpriteOrientation() + MathHelper.PiOver2) * Dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (equippedInRightHand)
|
||||||
|
{
|
||||||
|
transformedHoldPos = rightShoulder.WorldAnchorA;
|
||||||
|
rightHand.Disabled = true;
|
||||||
|
}
|
||||||
|
if (equippedInLefthand)
|
||||||
|
{
|
||||||
|
if (leftShoulder == null) { return; }
|
||||||
|
transformedHoldPos = leftShoulder.WorldAnchorA;
|
||||||
|
leftHand.Disabled = true;
|
||||||
|
}
|
||||||
|
itemPos.X *= Dir;
|
||||||
|
transformedHoldPos += Vector2.Transform(itemPos, Matrix.CreateRotationZ(itemAngle));
|
||||||
|
}
|
||||||
|
|
||||||
|
item.body.ResetDynamics();
|
||||||
|
|
||||||
|
Vector2 currItemPos = equippedInRightHand ?
|
||||||
|
rightHand.PullJointWorldAnchorA - transformedHandlePos[0] :
|
||||||
|
leftHand.PullJointWorldAnchorA - transformedHandlePos[1];
|
||||||
|
|
||||||
|
if (!MathUtils.IsValid(currItemPos))
|
||||||
|
{
|
||||||
|
string errorMsg = "Attempted to move the item \"" + item + "\" to an invalid position in HumanidAnimController.HoldItem: " +
|
||||||
|
currItemPos + ", rightHandPos: " + rightHand.PullJointWorldAnchorA + ", leftHandPos: " + leftHand.PullJointWorldAnchorA +
|
||||||
|
", handlePos[0]: " + handlePos[0] + ", handlePos[1]: " + handlePos[1] +
|
||||||
|
", transformedHandlePos[0]: " + transformedHandlePos[0] + ", transformedHandlePos[1]:" + transformedHandlePos[1] +
|
||||||
|
", item pos: " + item.SimPosition + ", itemAngle: " + itemAngle +
|
||||||
|
", collider pos: " + character.SimPosition;
|
||||||
|
DebugConsole.Log(errorMsg);
|
||||||
|
GameAnalyticsManager.AddErrorEventOnce(
|
||||||
|
"HumanoidAnimController.HoldItem:InvalidPos:" + character.Name + item.Name,
|
||||||
|
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||||
|
errorMsg);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holdable.Pusher != null)
|
||||||
|
{
|
||||||
|
if (character.Stun > 0.0f || character.IsIncapacitated)
|
||||||
|
{
|
||||||
|
holdable.Pusher.Enabled = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!holdable.Pusher.Enabled)
|
||||||
|
{
|
||||||
|
holdable.Pusher.Enabled = true;
|
||||||
|
holdable.Pusher.ResetDynamics();
|
||||||
|
holdable.Pusher.SetTransform(currItemPos, itemAngle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
holdable.Pusher.TargetPosition = currItemPos;
|
||||||
|
holdable.Pusher.TargetRotation = holdAngle * Dir;
|
||||||
|
|
||||||
|
holdable.Pusher.MoveToTargetPosition(true);
|
||||||
|
|
||||||
|
currItemPos = holdable.Pusher.SimPosition;
|
||||||
|
itemAngle = holdable.Pusher.Rotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float targetAngle = MathUtils.WrapAngleTwoPi(itemAngle + itemAngleRelativeToHoldAngle * Dir);
|
||||||
|
float currentRotation = MathUtils.WrapAngleTwoPi(item.body.Rotation);
|
||||||
|
float itemRotation = MathHelper.SmoothStep(currentRotation, targetAngle, deltaTime * 25);
|
||||||
|
if (previousDirection != dir || Math.Abs(targetAngle - currentRotation) > MathHelper.Pi)
|
||||||
|
{
|
||||||
|
itemRotation = targetAngle;
|
||||||
|
}
|
||||||
|
item.SetTransform(currItemPos, itemRotation, setPrevTransform: false);
|
||||||
|
previousDirection = dir;
|
||||||
|
|
||||||
|
if (!isClimbing && !character.IsIncapacitated && itemPos != Vector2.Zero && (aim || !holdable.UseHandRotationForHoldAngle))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
if (!character.Inventory.IsInLimbSlot(item, i == 0 ? InvSlotType.RightHand : InvSlotType.LeftHand)) { continue; }
|
||||||
|
#if DEBUG
|
||||||
|
if (handlePos[i].LengthSquared() > ArmLength)
|
||||||
|
{
|
||||||
|
DebugConsole.AddWarning($"Aim position for the item {item.Name} may be incorrect (further than the length of the character's arm)");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
HandIK(i == 0 ? rightHand : leftHand, transformedHoldPos + transformedHandlePos[i], CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetAimWobble(Limb rightHand, Limb leftHand, Item heldItem)
|
||||||
|
{
|
||||||
|
float wobbleStrength = 0.0f;
|
||||||
|
if (character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == heldItem)
|
||||||
|
{
|
||||||
|
wobbleStrength += Character.CharacterHealth.GetLimbDamage(rightHand, afflictionType: "damage");
|
||||||
|
}
|
||||||
|
if (character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand) == heldItem)
|
||||||
|
{
|
||||||
|
wobbleStrength += Character.CharacterHealth.GetLimbDamage(leftHand, afflictionType: "damage");
|
||||||
|
}
|
||||||
|
if (wobbleStrength <= 0.1f) { return 0.0f; }
|
||||||
|
wobbleStrength = (float)Math.Min(wobbleStrength, 1.0f);
|
||||||
|
|
||||||
|
float lowFreqNoise = PerlinNoise.GetPerlin((float)Timing.TotalTime / 320.0f, (float)Timing.TotalTime / 240.0f) - 0.5f;
|
||||||
|
float highFreqNoise = PerlinNoise.GetPerlin((float)Timing.TotalTime / 40.0f, (float)Timing.TotalTime / 50.0f) - 0.5f;
|
||||||
|
|
||||||
|
return (lowFreqNoise * 1.0f + highFreqNoise * 0.1f) * wobbleStrength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandIK(Limb hand, Vector2 pos, float armTorque = 1.0f, float handTorque = 1.0f)
|
||||||
|
{
|
||||||
|
Vector2 shoulderPos;
|
||||||
|
|
||||||
|
Limb arm, forearm;
|
||||||
|
if (hand.type == LimbType.LeftHand)
|
||||||
|
{
|
||||||
|
if (leftShoulder == null) { return; }
|
||||||
|
shoulderPos = leftShoulder.WorldAnchorA;
|
||||||
|
arm = GetLimb(LimbType.LeftArm);
|
||||||
|
forearm = GetLimb(LimbType.LeftForearm);
|
||||||
|
LeftHandIKPos = pos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (rightShoulder == null) { return; }
|
||||||
|
shoulderPos = rightShoulder.WorldAnchorA;
|
||||||
|
arm = GetLimb(LimbType.RightArm);
|
||||||
|
forearm = GetLimb(LimbType.RightForearm);
|
||||||
|
RightHandIKPos = pos;
|
||||||
|
}
|
||||||
|
if (arm == null) { return; }
|
||||||
|
|
||||||
|
//distance from shoulder to holdpos
|
||||||
|
float c = Vector2.Distance(pos, shoulderPos);
|
||||||
|
c = MathHelper.Clamp(c, Math.Abs(upperArmLength - forearmLength), forearmLength + upperArmLength - 0.01f);
|
||||||
|
|
||||||
|
float armAngle = MathUtils.VectorToAngle(pos - shoulderPos) + arm.Params.GetSpriteOrientation() - MathHelper.PiOver2;
|
||||||
|
float upperArmAngle = MathUtils.SolveTriangleSSS(forearmLength, upperArmLength, c) * Dir;
|
||||||
|
float lowerArmAngle = MathUtils.SolveTriangleSSS(upperArmLength, forearmLength, c) * Dir;
|
||||||
|
|
||||||
|
//make sure the arm angle "has the same number of revolutions" as the arm
|
||||||
|
while (arm.Rotation - armAngle > MathHelper.Pi)
|
||||||
|
{
|
||||||
|
armAngle += MathHelper.TwoPi;
|
||||||
|
}
|
||||||
|
while (arm.Rotation - armAngle < -MathHelper.Pi)
|
||||||
|
{
|
||||||
|
armAngle -= MathHelper.TwoPi;
|
||||||
|
}
|
||||||
|
|
||||||
|
arm?.body.SmoothRotate(armAngle - upperArmAngle, 100.0f * armTorque * arm.Mass, wrapAngle: false);
|
||||||
|
float forearmAngle = armAngle + lowerArmAngle;
|
||||||
|
forearm?.body.SmoothRotate(forearmAngle, 100.0f * handTorque * forearm.Mass, wrapAngle: false);
|
||||||
|
float handAngle = forearm != null ? forearmAngle : armAngle;
|
||||||
|
hand?.body.SmoothRotate(handAngle, 10.0f * handTorque * hand.Mass, wrapAngle: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyPose(Vector2 leftHandPos, Vector2 rightHandPos, Vector2 leftFootPos, Vector2 rightFootPos, float footMoveForce = 10)
|
||||||
|
{
|
||||||
|
var leftHand = GetLimb(LimbType.LeftHand);
|
||||||
|
var rightHand = GetLimb(LimbType.RightHand);
|
||||||
|
var waist = GetLimb(LimbType.Waist) ?? GetLimb(LimbType.Torso);
|
||||||
|
if (waist == null) { return; }
|
||||||
|
Vector2 midPos = waist.SimPosition;
|
||||||
|
if (leftHand != null)
|
||||||
|
{
|
||||||
|
leftHand.Disabled = true;
|
||||||
|
leftHandPos.X *= Dir;
|
||||||
|
leftHandPos += midPos;
|
||||||
|
HandIK(leftHand, leftHandPos);
|
||||||
|
}
|
||||||
|
if (rightHand != null)
|
||||||
|
{
|
||||||
|
rightHand.Disabled = true;
|
||||||
|
rightHandPos.X *= Dir;
|
||||||
|
rightHandPos += midPos;
|
||||||
|
HandIK(rightHand, rightHandPos);
|
||||||
|
}
|
||||||
|
var leftFoot = GetLimb(LimbType.LeftFoot);
|
||||||
|
if (leftFoot != null)
|
||||||
|
{
|
||||||
|
leftFoot.Disabled = true;
|
||||||
|
leftFootPos = new Vector2(waist.SimPosition.X + leftFootPos.X * Dir, GetColliderBottom().Y + leftFootPos.Y);
|
||||||
|
MoveLimb(leftFoot, leftFootPos, Math.Abs(leftFoot.SimPosition.X - leftFootPos.X) * footMoveForce * leftFoot.Mass, true);
|
||||||
|
}
|
||||||
|
var rightFoot = GetLimb(LimbType.RightFoot);
|
||||||
|
if (rightFoot != null)
|
||||||
|
{
|
||||||
|
rightFoot.Disabled = true;
|
||||||
|
rightFootPos = new Vector2(waist.SimPosition.X + rightFootPos.X * Dir, GetColliderBottom().Y + rightFootPos.Y);
|
||||||
|
MoveLimb(rightFoot, rightFootPos, Math.Abs(rightFoot.SimPosition.X - rightFootPos.X) * footMoveForce * rightFoot.Mass, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyTestPose()
|
||||||
|
{
|
||||||
|
var waist = GetLimb(LimbType.Waist) ?? GetLimb(LimbType.Torso);
|
||||||
|
if (waist != null)
|
||||||
|
{
|
||||||
|
ApplyPose(
|
||||||
|
new Vector2(-0.75f, -0.2f),
|
||||||
|
new Vector2(0.75f, -0.2f),
|
||||||
|
new Vector2(-WalkParams.StepSize.X * 0.5f, -0.1f * RagdollParams.JointScale),
|
||||||
|
new Vector2(WalkParams.StepSize.X * 0.5f, -0.1f * RagdollParams.JointScale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void CalculateArmLengths()
|
||||||
|
{
|
||||||
|
//calculate arm and forearm length (atm this assumes that both arms are the same size)
|
||||||
|
Limb rightForearm = GetLimb(LimbType.RightForearm);
|
||||||
|
Limb rightHand = GetLimb(LimbType.RightHand);
|
||||||
|
if (rightHand == null) { return; }
|
||||||
|
|
||||||
|
rightShoulder = GetJointBetweenLimbs(LimbType.Torso, LimbType.RightArm) ?? GetJointBetweenLimbs(LimbType.Head, LimbType.RightArm) ?? GetJoint(LimbType.RightArm, new LimbType[] { LimbType.RightHand, LimbType.RightForearm });
|
||||||
|
leftShoulder = GetJointBetweenLimbs(LimbType.Torso, LimbType.LeftArm) ?? GetJointBetweenLimbs(LimbType.Head, LimbType.LeftArm) ?? GetJoint(LimbType.LeftArm, new LimbType[] { LimbType.LeftHand, LimbType.LeftForearm });
|
||||||
|
|
||||||
|
Vector2 localAnchorShoulder = Vector2.Zero;
|
||||||
|
Vector2 localAnchorElbow = Vector2.Zero;
|
||||||
|
if (rightShoulder != null)
|
||||||
|
{
|
||||||
|
localAnchorShoulder = rightShoulder.LimbA.type == LimbType.RightArm ? rightShoulder.LocalAnchorA : rightShoulder.LocalAnchorB;
|
||||||
|
}
|
||||||
|
LimbJoint rightElbow = rightForearm == null ?
|
||||||
|
GetJointBetweenLimbs(LimbType.RightArm, LimbType.RightHand) :
|
||||||
|
GetJointBetweenLimbs(LimbType.RightArm, LimbType.RightForearm);
|
||||||
|
if (rightElbow != null)
|
||||||
|
{
|
||||||
|
localAnchorElbow = rightElbow.LimbA.type == LimbType.RightArm ? rightElbow.LocalAnchorA : rightElbow.LocalAnchorB;
|
||||||
|
}
|
||||||
|
upperArmLength = Vector2.Distance(localAnchorShoulder, localAnchorElbow);
|
||||||
|
if (rightElbow != null)
|
||||||
|
{
|
||||||
|
if (rightForearm == null)
|
||||||
|
{
|
||||||
|
forearmLength = Vector2.Distance(
|
||||||
|
rightHand.PullJointLocalAnchorA,
|
||||||
|
rightElbow.LimbA.type == LimbType.RightHand ? rightElbow.LocalAnchorA : rightElbow.LocalAnchorB);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LimbJoint rightWrist = GetJointBetweenLimbs(LimbType.RightForearm, LimbType.RightHand);
|
||||||
|
if (rightWrist != null)
|
||||||
|
{
|
||||||
|
forearmLength = Vector2.Distance(
|
||||||
|
rightElbow.LimbA.type == LimbType.RightForearm ? rightElbow.LocalAnchorA : rightElbow.LocalAnchorB,
|
||||||
|
rightWrist.LimbA.type == LimbType.RightForearm ? rightWrist.LocalAnchorA : rightWrist.LocalAnchorB);
|
||||||
|
|
||||||
|
forearmLength += Vector2.Distance(
|
||||||
|
rightHand.PullJointLocalAnchorA,
|
||||||
|
rightElbow.LimbA.type == LimbType.RightHand ? rightElbow.LocalAnchorA : rightElbow.LocalAnchorB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected LimbJoint GetJointBetweenLimbs(LimbType limbTypeA, LimbType limbTypeB)
|
||||||
|
{
|
||||||
|
return LimbJoints.FirstOrDefault(lj =>
|
||||||
|
(lj.LimbA.type == limbTypeA && lj.LimbB.type == limbTypeB) ||
|
||||||
|
(lj.LimbB.type == limbTypeA && lj.LimbA.type == limbTypeB));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected LimbJoint GetJoint(LimbType matchingType, IEnumerable<LimbType> ignoredTypes)
|
||||||
|
{
|
||||||
|
return LimbJoints.FirstOrDefault(lj =>
|
||||||
|
lj.LimbA.type == matchingType && ignoredTypes.None(t => lj.LimbB.type == t) ||
|
||||||
|
lj.LimbB.type == matchingType && ignoredTypes.None(t => lj.LimbB.type == t));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Recreate(RagdollParams ragdollParams = null)
|
||||||
|
{
|
||||||
|
base.Recreate(ragdollParams);
|
||||||
|
if (Character.Params.CanInteract)
|
||||||
|
{
|
||||||
|
CalculateArmLengths();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-5
@@ -139,7 +139,7 @@ namespace Barotrauma
|
|||||||
if (MainLimb == null) { return; }
|
if (MainLimb == null) { return; }
|
||||||
var mainLimb = MainLimb;
|
var mainLimb = MainLimb;
|
||||||
|
|
||||||
levitatingCollider = true;
|
levitatingCollider = !IsHanging;
|
||||||
|
|
||||||
if (!character.CanMove)
|
if (!character.CanMove)
|
||||||
{
|
{
|
||||||
@@ -192,6 +192,11 @@ namespace Barotrauma
|
|||||||
strongestImpact = 0.0f;
|
strongestImpact = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aiming)
|
||||||
|
{
|
||||||
|
TargetMovement = TargetMovement.ClampLength(2);
|
||||||
|
}
|
||||||
|
|
||||||
if (inWater && !forceStanding)
|
if (inWater && !forceStanding)
|
||||||
{
|
{
|
||||||
Collider.FarseerBody.FixedRotation = false;
|
Collider.FarseerBody.FixedRotation = false;
|
||||||
@@ -202,7 +207,7 @@ namespace Barotrauma
|
|||||||
if (CurrentGroundedParams != null)
|
if (CurrentGroundedParams != null)
|
||||||
{
|
{
|
||||||
//rotate collider back upright
|
//rotate collider back upright
|
||||||
float standAngle = dir == Direction.Right ? CurrentGroundedParams.ColliderStandAngleInRadians : -CurrentGroundedParams.ColliderStandAngleInRadians;
|
float standAngle = CurrentGroundedParams.ColliderStandAngleInRadians * Dir;
|
||||||
if (Math.Abs(MathUtils.GetShortestAngle(Collider.Rotation, standAngle)) > 0.001f)
|
if (Math.Abs(MathUtils.GetShortestAngle(Collider.Rotation, standAngle)) > 0.001f)
|
||||||
{
|
{
|
||||||
Collider.AngularVelocity = MathUtils.GetShortestAngle(Collider.Rotation, standAngle) * 60.0f;
|
Collider.AngularVelocity = MathUtils.GetShortestAngle(Collider.Rotation, standAngle) * 60.0f;
|
||||||
@@ -215,17 +220,19 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
UpdateWalkAnim(deltaTime);
|
UpdateWalkAnim(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (character.SelectedCharacter != null)
|
if (character.SelectedCharacter != null)
|
||||||
{
|
{
|
||||||
DragCharacter(character.SelectedCharacter, deltaTime);
|
DragCharacter(character.SelectedCharacter, deltaTime);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (character.AnimController.AnimationTestPose)
|
||||||
|
{
|
||||||
|
ApplyTestPose();
|
||||||
|
}
|
||||||
//don't flip when simply physics is enabled
|
//don't flip when simply physics is enabled
|
||||||
if (SimplePhysicsEnabled) { return; }
|
if (SimplePhysicsEnabled) { return; }
|
||||||
|
|
||||||
if (!character.IsRemotelyControlled && (character.AIController == null || character.AIController.CanFlip))
|
if (!character.IsRemotelyControlled && (character.AIController == null || character.AIController.CanFlip) && !aiming)
|
||||||
{
|
{
|
||||||
if (!inWater || (CurrentSwimParams != null && CurrentSwimParams.Mirror))
|
if (!inWater || (CurrentSwimParams != null && CurrentSwimParams.Mirror))
|
||||||
{
|
{
|
||||||
@@ -290,6 +297,10 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
flipTimer = 0.0f;
|
flipTimer = 0.0f;
|
||||||
}
|
}
|
||||||
|
wasAiming = aiming;
|
||||||
|
aiming = false;
|
||||||
|
wasAimingMelee = aimingMelee;
|
||||||
|
aimingMelee = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanDrag(Character target)
|
private bool CanDrag(Character target)
|
||||||
@@ -449,6 +460,16 @@ namespace Barotrauma
|
|||||||
//limbs are disabled when simple physics is enabled, no need to move them
|
//limbs are disabled when simple physics is enabled, no need to move them
|
||||||
if (SimplePhysicsEnabled) { return; }
|
if (SimplePhysicsEnabled) { return; }
|
||||||
mainLimb.PullJointEnabled = true;
|
mainLimb.PullJointEnabled = true;
|
||||||
|
|
||||||
|
if (aiming && movement.Length() <= 0.1f)
|
||||||
|
{
|
||||||
|
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
|
||||||
|
Vector2 diff = (mousePos - (GetLimb(LimbType.Torso) ?? MainLimb).SimPosition) * Dir;
|
||||||
|
TargetMovement = new Vector2(0.0f, -0.1f);
|
||||||
|
float newRotation = MathUtils.VectorToAngle(diff);
|
||||||
|
Collider.SmoothRotate(newRotation, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isMoving)
|
if (!isMoving)
|
||||||
{
|
{
|
||||||
WalkPos = MathHelper.SmoothStep(WalkPos, MathHelper.PiOver2, deltaTime * 5);
|
WalkPos = MathHelper.SmoothStep(WalkPos, MathHelper.PiOver2, deltaTime * 5);
|
||||||
|
|||||||
+37
-404
@@ -146,34 +146,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public bool Crouching;
|
public bool Crouching;
|
||||||
|
|
||||||
private float upperArmLength = 0.0f, forearmLength = 0.0f;
|
|
||||||
|
|
||||||
public float ArmLength => upperArmLength + forearmLength;
|
|
||||||
|
|
||||||
public Vector2 RightHandIKPos
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
public Vector2 LeftHandIKPos
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LimbJoint rightShoulder, leftShoulder;
|
|
||||||
|
|
||||||
private float upperLegLength = 0.0f, lowerLegLength = 0.0f;
|
private float upperLegLength = 0.0f, lowerLegLength = 0.0f;
|
||||||
|
|
||||||
private bool aiming;
|
|
||||||
private bool wasAiming;
|
|
||||||
|
|
||||||
private bool aimingMelee;
|
|
||||||
private bool wasAimingMelee;
|
|
||||||
|
|
||||||
public bool IsAiming => wasAiming;
|
|
||||||
public bool IsAimingMelee => wasAimingMelee;
|
|
||||||
|
|
||||||
private readonly float movementLerp;
|
private readonly float movementLerp;
|
||||||
|
|
||||||
private float cprAnimTimer;
|
private float cprAnimTimer;
|
||||||
@@ -184,7 +158,6 @@ namespace Barotrauma
|
|||||||
//prevents rapid switches between swimming/walking if the water level is fluctuating around the minimum swimming depth
|
//prevents rapid switches between swimming/walking if the water level is fluctuating around the minimum swimming depth
|
||||||
private float swimmingStateLockTimer;
|
private float swimmingStateLockTimer;
|
||||||
|
|
||||||
private float useItemTimer;
|
|
||||||
public float HeadLeanAmount => CurrentGroundedParams.HeadLeanAmount;
|
public float HeadLeanAmount => CurrentGroundedParams.HeadLeanAmount;
|
||||||
public float TorsoLeanAmount => CurrentGroundedParams.TorsoLeanAmount;
|
public float TorsoLeanAmount => CurrentGroundedParams.TorsoLeanAmount;
|
||||||
public Vector2 FootMoveOffset => CurrentGroundedParams.FootMoveOffset * RagdollParams.JointScale;
|
public Vector2 FootMoveOffset => CurrentGroundedParams.FootMoveOffset * RagdollParams.JointScale;
|
||||||
@@ -219,61 +192,12 @@ namespace Barotrauma
|
|||||||
movementLerp = RagdollParams.MainElement.GetAttributeFloat("movementlerp", 0.4f);
|
movementLerp = RagdollParams.MainElement.GetAttributeFloat("movementlerp", 0.4f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Recreate(RagdollParams ragdollParams)
|
public override void Recreate(RagdollParams ragdollParams = null)
|
||||||
{
|
{
|
||||||
base.Recreate(ragdollParams);
|
base.Recreate(ragdollParams);
|
||||||
CalculateArmLengths();
|
|
||||||
CalculateLegLengths();
|
CalculateLegLengths();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CalculateArmLengths()
|
|
||||||
{
|
|
||||||
//calculate arm and forearm length (atm this assumes that both arms are the same size)
|
|
||||||
Limb rightForearm = GetLimb(LimbType.RightForearm);
|
|
||||||
Limb rightHand = GetLimb(LimbType.RightHand);
|
|
||||||
if (rightHand == null) { return; }
|
|
||||||
|
|
||||||
rightShoulder = GetJointBetweenLimbs(LimbType.Torso, LimbType.RightArm);
|
|
||||||
leftShoulder = GetJointBetweenLimbs(LimbType.Torso, LimbType.LeftArm);
|
|
||||||
Vector2 localAnchorShoulder = Vector2.Zero;
|
|
||||||
Vector2 localAnchorElbow = Vector2.Zero;
|
|
||||||
if (rightShoulder != null)
|
|
||||||
{
|
|
||||||
localAnchorShoulder = rightShoulder.LimbA.type == LimbType.RightArm ? rightShoulder.LocalAnchorA : rightShoulder.LocalAnchorB;
|
|
||||||
}
|
|
||||||
LimbJoint rightElbow = rightForearm == null ?
|
|
||||||
GetJointBetweenLimbs(LimbType.RightArm, LimbType.RightHand) :
|
|
||||||
GetJointBetweenLimbs(LimbType.RightArm, LimbType.RightForearm);
|
|
||||||
if (rightElbow != null)
|
|
||||||
{
|
|
||||||
localAnchorElbow = rightElbow.LimbA.type == LimbType.RightArm ? rightElbow.LocalAnchorA : rightElbow.LocalAnchorB;
|
|
||||||
}
|
|
||||||
upperArmLength = Vector2.Distance(localAnchorShoulder, localAnchorElbow);
|
|
||||||
if (rightElbow != null)
|
|
||||||
{
|
|
||||||
if (rightForearm == null)
|
|
||||||
{
|
|
||||||
forearmLength = Vector2.Distance(
|
|
||||||
rightHand.PullJointLocalAnchorA,
|
|
||||||
rightElbow.LimbA.type == LimbType.RightHand ? rightElbow.LocalAnchorA : rightElbow.LocalAnchorB);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LimbJoint rightWrist = GetJointBetweenLimbs(LimbType.RightForearm, LimbType.RightHand);
|
|
||||||
if (rightWrist != null)
|
|
||||||
{
|
|
||||||
forearmLength = Vector2.Distance(
|
|
||||||
rightElbow.LimbA.type == LimbType.RightForearm ? rightElbow.LocalAnchorA : rightElbow.LocalAnchorB,
|
|
||||||
rightWrist.LimbA.type == LimbType.RightForearm ? rightWrist.LocalAnchorA : rightWrist.LocalAnchorB);
|
|
||||||
|
|
||||||
forearmLength += Vector2.Distance(
|
|
||||||
rightHand.PullJointLocalAnchorA,
|
|
||||||
rightElbow.LimbA.type == LimbType.RightHand ? rightElbow.LocalAnchorA : rightElbow.LocalAnchorB);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateLegLengths()
|
private void CalculateLegLengths()
|
||||||
{
|
{
|
||||||
//calculate upper and lower leg length (atm this assumes that both legs are the same size)
|
//calculate upper and lower leg length (atm this assumes that both legs are the same size)
|
||||||
@@ -304,21 +228,16 @@ namespace Barotrauma
|
|||||||
ankleJoint.LimbA.type == footType ? ankleJoint.LocalAnchorA : ankleJoint.LocalAnchorB,
|
ankleJoint.LimbA.type == footType ? ankleJoint.LocalAnchorA : ankleJoint.LocalAnchorB,
|
||||||
GetLimb(footType).PullJointLocalAnchorA);
|
GetLimb(footType).PullJointLocalAnchorA);
|
||||||
}
|
}
|
||||||
private LimbJoint GetJointBetweenLimbs(LimbType limbTypeA, LimbType limbTypeB)
|
|
||||||
{
|
|
||||||
return LimbJoints.FirstOrDefault(lj =>
|
|
||||||
(lj.LimbA.type == limbTypeA && lj.LimbB.type == limbTypeB) ||
|
|
||||||
(lj.LimbB.type == limbTypeA && lj.LimbA.type == limbTypeB));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateAnim(float deltaTime)
|
public override void UpdateAnim(float deltaTime)
|
||||||
{
|
{
|
||||||
if (Frozen) return;
|
if (Frozen) return;
|
||||||
if (MainLimb == null) { return; }
|
if (MainLimb == null) { return; }
|
||||||
|
|
||||||
levitatingCollider = true;
|
levitatingCollider = !IsHanging;
|
||||||
ColliderIndex = Crouching && !swimming ? 1 : 0;
|
ColliderIndex = Crouching && !swimming ? 1 : 0;
|
||||||
if (character.SelectedConstruction?.GetComponent<Controller>()?.ControlCharacterPose ?? false ||
|
if (character.SelectedConstruction?.GetComponent<Controller>()?.ControlCharacterPose ?? false ||
|
||||||
|
character.SelectedConstruction?.GetComponent<Ladder>() != null ||
|
||||||
(ForceSelectAnimationType != AnimationType.Crouch && ForceSelectAnimationType != AnimationType.NotDefined))
|
(ForceSelectAnimationType != AnimationType.Crouch && ForceSelectAnimationType != AnimationType.NotDefined))
|
||||||
{
|
{
|
||||||
Crouching = false;
|
Crouching = false;
|
||||||
@@ -422,41 +341,25 @@ namespace Barotrauma
|
|||||||
midPos += Vector2.Transform(new Vector2(-0.3f * Dir, -0.2f), torsoTransform);
|
midPos += Vector2.Transform(new Vector2(-0.3f * Dir, -0.2f), torsoTransform);
|
||||||
|
|
||||||
if (rightHand.PullJointEnabled) midPos = (midPos + rightHand.PullJointWorldAnchorB) / 2.0f;
|
if (rightHand.PullJointEnabled) midPos = (midPos + rightHand.PullJointWorldAnchorB) / 2.0f;
|
||||||
HandIK(rightHand, midPos, CurrentHumanAnimParams.ArmIKStrength, CurrentHumanAnimParams.HandIKStrength);
|
HandIK(rightHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
|
||||||
HandIK(leftHand, midPos, CurrentHumanAnimParams.ArmIKStrength, CurrentHumanAnimParams.HandIKStrength);
|
HandIK(leftHand, midPos, CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
|
||||||
}
|
}
|
||||||
else if (character.AnimController.AnimationTestPose)
|
else if (character.AnimController.AnimationTestPose)
|
||||||
{
|
{
|
||||||
var leftHand = GetLimb(LimbType.LeftHand);
|
ApplyTestPose();
|
||||||
var rightHand = GetLimb(LimbType.RightHand);
|
|
||||||
var waist = GetLimb(LimbType.Waist) ?? GetLimb(LimbType.Torso);
|
|
||||||
rightHand.Disabled = true;
|
|
||||||
leftHand.Disabled = true;
|
|
||||||
Vector2 midPos = waist.SimPosition;
|
|
||||||
HandIK(rightHand, midPos + new Vector2(-1, -0.2f) * Dir);
|
|
||||||
HandIK(leftHand, midPos + new Vector2(1, -0.2f) * Dir);
|
|
||||||
|
|
||||||
var leftFoot = GetLimb(LimbType.LeftFoot);
|
|
||||||
var rightFoot = GetLimb(LimbType.RightFoot);
|
|
||||||
rightFoot.Disabled = true;
|
|
||||||
leftFoot.Disabled = true;
|
|
||||||
// The code here is a bit obscure, but it's pretty much copy-pasted from the block that is used for crouching.
|
|
||||||
for (int i = -1; i < 2; i += 2)
|
|
||||||
{
|
|
||||||
Vector2 footPos = GetColliderBottom();
|
|
||||||
footPos = new Vector2(waist.SimPosition.X + Math.Sign(WalkParams.StepSize.X * i) * Dir * 0.3f, footPos.Y - 0.1f * RagdollParams.JointScale);
|
|
||||||
var foot = i == -1 ? rightFoot : leftFoot;
|
|
||||||
MoveLimb(foot, footPos, Math.Abs(foot.SimPosition.X - footPos.X) * 100.0f, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Anim != Animation.UsingConstruction) ResetPullJoints();
|
if (Anim != Animation.UsingConstruction)
|
||||||
|
{
|
||||||
|
ResetPullJoints();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SimplePhysicsEnabled)
|
if (SimplePhysicsEnabled)
|
||||||
{
|
{
|
||||||
UpdateStandingSimple();
|
UpdateStandingSimple();
|
||||||
|
IsHanging = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,12 +423,11 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
limb.Disabled = false;
|
limb.Disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
wasAiming = aiming;
|
wasAiming = aiming;
|
||||||
aiming = false;
|
aiming = false;
|
||||||
wasAimingMelee = aimingMelee;
|
wasAimingMelee = aimingMelee;
|
||||||
aimingMelee = false;
|
aimingMelee = false;
|
||||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) return;
|
IsHanging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateStanding()
|
void UpdateStanding()
|
||||||
@@ -844,16 +746,18 @@ namespace Barotrauma
|
|||||||
var arm = GetLimb(armType);
|
var arm = GetLimb(armType);
|
||||||
if (arm != null && Math.Abs(arm.body.AngularVelocity) < 10.0f)
|
if (arm != null && Math.Abs(arm.body.AngularVelocity) < 10.0f)
|
||||||
{
|
{
|
||||||
arm.body.SmoothRotate(MathHelper.Clamp(-arm.body.AngularVelocity, -0.1f, 0.1f), arm.Mass * 10.0f);
|
arm.body.SmoothRotate(MathHelper.Clamp(-arm.body.AngularVelocity, -0.5f, 0.5f), arm.Mass * 50.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the elbow to a neutral rotation
|
//get the elbow to a neutral rotation
|
||||||
if (Math.Abs(hand.body.AngularVelocity) < 10.0f)
|
if (Math.Abs(hand.body.AngularVelocity) < 10.0f)
|
||||||
{
|
{
|
||||||
LimbJoint elbow = GetJointBetweenLimbs(armType, hand.type) ?? GetJointBetweenLimbs(armType, foreArmType);
|
var forearm = GetLimb(foreArmType) ?? hand;
|
||||||
|
LimbJoint elbow = GetJointBetweenLimbs(armType, foreArmType) ?? GetJointBetweenLimbs(armType, hand.type);
|
||||||
if (elbow != null)
|
if (elbow != null)
|
||||||
{
|
{
|
||||||
hand.body.ApplyTorque(MathHelper.Clamp(-elbow.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * hand.Mass * 10.0f);
|
float diff = elbow.JointAngle - (Dir > 0 ? elbow.LowerLimit : elbow.UpperLimit);
|
||||||
|
forearm.body.ApplyTorque(MathHelper.Clamp(-diff, -MathHelper.PiOver2, MathHelper.PiOver2) * forearm.Mass * 100.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1099,6 +1003,7 @@ namespace Barotrauma
|
|||||||
rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X);
|
rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X);
|
||||||
rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
|
rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
|
||||||
float speedMultiplier = character.SpeedMultiplier * (1 - Character.GetRightHandPenalty());
|
float speedMultiplier = character.SpeedMultiplier * (1 - Character.GetRightHandPenalty());
|
||||||
|
// Limb hand, Vector2 pos, float force = 1.0f
|
||||||
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
|
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1390,6 +1295,8 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
target.Oxygen += deltaTime * 0.5f; //Stabilize them
|
target.Oxygen += deltaTime * 0.5f; //Stabilize them
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool powerfulCPR = character.HasAbilityFlag(AbilityFlags.PowerfulCPR);
|
||||||
|
|
||||||
int skill = (int)character.GetSkillLevel("medical");
|
int skill = (int)character.GetSkillLevel("medical");
|
||||||
//pump for 15 seconds (cprAnimTimer 0-15), then do mouth-to-mouth for 2 seconds (cprAnimTimer 15-17)
|
//pump for 15 seconds (cprAnimTimer 0-15), then do mouth-to-mouth for 2 seconds (cprAnimTimer 15-17)
|
||||||
@@ -1406,13 +1313,19 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (target.Oxygen < -10.0f)
|
if (target.Oxygen < -10.0f)
|
||||||
{
|
{
|
||||||
//stabilize the oxygen level but don't allow it to go positive and revive the character yet
|
if (powerfulCPR)
|
||||||
float stabilizationAmount = skill * CPRSettings.StabilizationPerSkill;
|
{
|
||||||
stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.StabilizationMin, CPRSettings.StabilizationMax);
|
//prevent the patient from suffocating no matter how fast their oxygen level is dropping
|
||||||
character.Oxygen -= (1.0f / stabilizationAmount) * deltaTime; //Worse skill = more oxygen required
|
target.Oxygen = Math.Max(target.Oxygen, -10.0f);
|
||||||
if (character.Oxygen > 0.0f) target.Oxygen += stabilizationAmount * deltaTime; //we didn't suffocate yet did we
|
}
|
||||||
|
else
|
||||||
//DebugConsole.NewMessage("CPR Us: " + character.Oxygen + " Them: " + target.Oxygen + " How good we are: restore " + cpr + " use " + (30.0f - cpr), Color.Aqua);
|
{
|
||||||
|
//stabilize the oxygen level but don't allow it to go positive and revive the character yet
|
||||||
|
float stabilizationAmount = skill * CPRSettings.StabilizationPerSkill;
|
||||||
|
stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.StabilizationMin, CPRSettings.StabilizationMax);
|
||||||
|
character.Oxygen -= 1.0f / stabilizationAmount * deltaTime; //Worse skill = more oxygen required
|
||||||
|
if (character.Oxygen > 0.0f) { target.Oxygen += stabilizationAmount * deltaTime; } //we didn't suffocate yet did we
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1447,6 +1360,8 @@ namespace Barotrauma
|
|||||||
reviveChance = (float)Math.Pow(reviveChance, CPRSettings.ReviveChanceExponent);
|
reviveChance = (float)Math.Pow(reviveChance, CPRSettings.ReviveChanceExponent);
|
||||||
reviveChance = MathHelper.Clamp(reviveChance, CPRSettings.ReviveChanceMin, CPRSettings.ReviveChanceMax);
|
reviveChance = MathHelper.Clamp(reviveChance, CPRSettings.ReviveChanceMin, CPRSettings.ReviveChanceMax);
|
||||||
|
|
||||||
|
if (powerfulCPR) { reviveChance *= 2.0f; }
|
||||||
|
|
||||||
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.Server) <= reviveChance)
|
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.Server) <= reviveChance)
|
||||||
{
|
{
|
||||||
//increase oxygen and clamp it above zero
|
//increase oxygen and clamp it above zero
|
||||||
@@ -1706,248 +1621,6 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Grab(Vector2 rightHandPos, Vector2 leftHandPos)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
Limb pullLimb = (i == 0) ? GetLimb(LimbType.LeftHand) : GetLimb(LimbType.RightHand);
|
|
||||||
|
|
||||||
pullLimb.Disabled = true;
|
|
||||||
|
|
||||||
pullLimb.PullJointEnabled = true;
|
|
||||||
pullLimb.PullJointWorldAnchorB = (i == 0) ? rightHandPos : leftHandPos;
|
|
||||||
pullLimb.PullJointMaxForce = 500.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: refactor this method, it's way too convoluted
|
|
||||||
public override void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle = 0.0f, bool aimingMelee = false)
|
|
||||||
{
|
|
||||||
if (character.Stun > 0.0f || character.IsIncapacitated)
|
|
||||||
{
|
|
||||||
aim = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//calculate the handle positions
|
|
||||||
Matrix itemTransfrom = Matrix.CreateRotationZ(item.body.Rotation);
|
|
||||||
// TODO: don't create new arrays, reuse
|
|
||||||
Vector2[] transformedHandlePos = new Vector2[2];
|
|
||||||
transformedHandlePos[0] = Vector2.Transform(handlePos[0], itemTransfrom);
|
|
||||||
transformedHandlePos[1] = Vector2.Transform(handlePos[1], itemTransfrom);
|
|
||||||
|
|
||||||
Limb head = GetLimb(LimbType.Head);
|
|
||||||
Limb torso = GetLimb(LimbType.Torso);
|
|
||||||
Limb leftHand = GetLimb(LimbType.LeftHand);
|
|
||||||
Limb rightHand = GetLimb(LimbType.RightHand);
|
|
||||||
|
|
||||||
// TODO: Remove this. Provide the position in params.
|
|
||||||
Vector2 itemPos = aim ? aimPos : holdPos;
|
|
||||||
|
|
||||||
var controller = character.SelectedConstruction?.GetComponent<Controller>();
|
|
||||||
bool usingController = controller != null && !controller.AllowAiming;
|
|
||||||
bool isClimbing = character.IsClimbing && Math.Abs(character.AnimController.TargetMovement.Y) > 0.01f;
|
|
||||||
|
|
||||||
float itemAngle;
|
|
||||||
|
|
||||||
Holdable holdable = item.GetComponent<Holdable>();
|
|
||||||
|
|
||||||
this.aimingMelee = aimingMelee;
|
|
||||||
|
|
||||||
if (!isClimbing && !usingController && character.Stun <= 0.0f && aim && itemPos != Vector2.Zero && !character.IsIncapacitated)
|
|
||||||
{
|
|
||||||
Vector2 mousePos = ConvertUnits.ToSimUnits(character.SmoothedCursorPosition);
|
|
||||||
|
|
||||||
Vector2 diff = holdable.Aimable ? (mousePos - AimSourceSimPos) * Dir : Vector2.UnitX;
|
|
||||||
|
|
||||||
holdAngle = MathUtils.VectorToAngle(new Vector2(diff.X, diff.Y * Dir)) - torso.body.Rotation * Dir;
|
|
||||||
holdAngle += GetAimWobble(rightHand, leftHand, item);
|
|
||||||
|
|
||||||
itemAngle = torso.body.Rotation + holdAngle * Dir;
|
|
||||||
|
|
||||||
if (holdable.ControlPose)
|
|
||||||
{
|
|
||||||
head?.body.SmoothRotate(itemAngle);
|
|
||||||
|
|
||||||
if (TargetMovement == Vector2.Zero && inWater)
|
|
||||||
{
|
|
||||||
torso.body.AngularVelocity -= torso.body.AngularVelocity * 0.1f;
|
|
||||||
torso.body.ApplyForce(torso.body.LinearVelocity * -0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
aiming = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itemAngle = torso.body.Rotation + holdAngle * Dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 transformedHoldPos = rightShoulder.WorldAnchorA;
|
|
||||||
if (itemPos == Vector2.Zero || isClimbing || usingController)
|
|
||||||
{
|
|
||||||
if (character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == item)
|
|
||||||
{
|
|
||||||
if (rightHand == null || rightHand.IsSevered) { return; }
|
|
||||||
transformedHoldPos = rightHand.PullJointWorldAnchorA - transformedHandlePos[0];
|
|
||||||
itemAngle = (rightHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
|
|
||||||
}
|
|
||||||
else if (character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand) == item)
|
|
||||||
{
|
|
||||||
if (leftHand == null || leftHand.IsSevered) { return; }
|
|
||||||
transformedHoldPos = leftHand.PullJointWorldAnchorA - transformedHandlePos[1];
|
|
||||||
itemAngle = (leftHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == item)
|
|
||||||
{
|
|
||||||
if (rightHand == null || rightHand.IsSevered) { return; }
|
|
||||||
transformedHoldPos = rightShoulder.WorldAnchorA;
|
|
||||||
rightHand.Disabled = true;
|
|
||||||
}
|
|
||||||
if (character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand) == item)
|
|
||||||
{
|
|
||||||
if (leftHand == null || leftHand.IsSevered) { return; }
|
|
||||||
transformedHoldPos = leftShoulder.WorldAnchorA;
|
|
||||||
leftHand.Disabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemPos.X *= Dir;
|
|
||||||
transformedHoldPos += Vector2.Transform(itemPos, Matrix.CreateRotationZ(itemAngle));
|
|
||||||
}
|
|
||||||
|
|
||||||
item.body.ResetDynamics();
|
|
||||||
|
|
||||||
Vector2 currItemPos = (character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == item) ?
|
|
||||||
rightHand.PullJointWorldAnchorA - transformedHandlePos[0] :
|
|
||||||
leftHand.PullJointWorldAnchorA - transformedHandlePos[1];
|
|
||||||
|
|
||||||
if (!MathUtils.IsValid(currItemPos))
|
|
||||||
{
|
|
||||||
string errorMsg = "Attempted to move the item \"" + item + "\" to an invalid position in HumanidAnimController.HoldItem: " +
|
|
||||||
currItemPos + ", rightHandPos: " + rightHand.PullJointWorldAnchorA + ", leftHandPos: " + leftHand.PullJointWorldAnchorA +
|
|
||||||
", handlePos[0]: " + handlePos[0] + ", handlePos[1]: " + handlePos[1] +
|
|
||||||
", transformedHandlePos[0]: " + transformedHandlePos[0] + ", transformedHandlePos[1]:" + transformedHandlePos[1] +
|
|
||||||
", item pos: " + item.SimPosition + ", itemAngle: " + itemAngle +
|
|
||||||
", collider pos: " + character.SimPosition;
|
|
||||||
DebugConsole.Log(errorMsg);
|
|
||||||
GameAnalyticsManager.AddErrorEventOnce(
|
|
||||||
"HumanoidAnimController.HoldItem:InvalidPos:" + character.Name + item.Name,
|
|
||||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
|
||||||
errorMsg);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (holdable.Pusher != null)
|
|
||||||
{
|
|
||||||
if (character.Stun > 0.0f || character.IsIncapacitated)
|
|
||||||
{
|
|
||||||
holdable.Pusher.Enabled = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!holdable.Pusher.Enabled)
|
|
||||||
{
|
|
||||||
holdable.Pusher.Enabled = true;
|
|
||||||
holdable.Pusher.ResetDynamics();
|
|
||||||
holdable.Pusher.SetTransform(currItemPos, itemAngle);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
holdable.Pusher.TargetPosition = currItemPos;
|
|
||||||
holdable.Pusher.TargetRotation = holdAngle * Dir;
|
|
||||||
|
|
||||||
holdable.Pusher.MoveToTargetPosition(true);
|
|
||||||
|
|
||||||
currItemPos = holdable.Pusher.SimPosition;
|
|
||||||
itemAngle = holdable.Pusher.Rotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item.SetTransform(currItemPos, itemAngle + itemAngleRelativeToHoldAngle * Dir, setPrevTransform: false);
|
|
||||||
|
|
||||||
if (!isClimbing && !character.IsIncapacitated && itemPos != Vector2.Zero)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
if (!character.Inventory.IsInLimbSlot(item, i == 0 ? InvSlotType.RightHand : InvSlotType.LeftHand)) { continue; }
|
|
||||||
HandIK(i == 0 ? rightHand : leftHand, transformedHoldPos + transformedHandlePos[i], CurrentHumanAnimParams.ArmIKStrength, CurrentHumanAnimParams.HandIKStrength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetAimWobble(Limb rightHand, Limb leftHand, Item heldItem)
|
|
||||||
{
|
|
||||||
float wobbleStrength = 0.0f;
|
|
||||||
if (character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == heldItem)
|
|
||||||
{
|
|
||||||
wobbleStrength += Character.CharacterHealth.GetLimbDamage(rightHand, afflictionType: "damage");
|
|
||||||
}
|
|
||||||
if (character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand) == heldItem)
|
|
||||||
{
|
|
||||||
wobbleStrength += Character.CharacterHealth.GetLimbDamage(leftHand, afflictionType: "damage");
|
|
||||||
}
|
|
||||||
if (wobbleStrength <= 0.1f) { return 0.0f; }
|
|
||||||
wobbleStrength = (float)Math.Min(wobbleStrength, 1.0f);
|
|
||||||
|
|
||||||
float lowFreqNoise = PerlinNoise.GetPerlin((float)Timing.TotalTime / 320.0f, (float)Timing.TotalTime / 240.0f) - 0.5f;
|
|
||||||
float highFreqNoise = PerlinNoise.GetPerlin((float)Timing.TotalTime / 40.0f, (float)Timing.TotalTime / 50.0f) - 0.5f;
|
|
||||||
|
|
||||||
return (lowFreqNoise * 1.0f + highFreqNoise * 0.1f) * wobbleStrength;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandIK(Limb hand, Vector2 pos, float armTorque = 1.0f, float handTorque = 1.0f)
|
|
||||||
{
|
|
||||||
Vector2 shoulderPos;
|
|
||||||
|
|
||||||
Limb arm, forearm;
|
|
||||||
if (hand.type == LimbType.LeftHand)
|
|
||||||
{
|
|
||||||
if (leftShoulder == null) { return; }
|
|
||||||
shoulderPos = leftShoulder.WorldAnchorA;
|
|
||||||
arm = GetLimb(LimbType.LeftArm);
|
|
||||||
forearm = GetLimb(LimbType.LeftForearm);
|
|
||||||
LeftHandIKPos = pos;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (rightShoulder == null) { return; }
|
|
||||||
shoulderPos = rightShoulder.WorldAnchorA;
|
|
||||||
arm = GetLimb(LimbType.RightArm);
|
|
||||||
forearm = GetLimb(LimbType.RightForearm);
|
|
||||||
RightHandIKPos = pos;
|
|
||||||
}
|
|
||||||
if (arm == null) { return; }
|
|
||||||
|
|
||||||
//distance from shoulder to holdpos
|
|
||||||
float c = Vector2.Distance(pos, shoulderPos);
|
|
||||||
c = MathHelper.Clamp(c, Math.Abs(upperArmLength - forearmLength), forearmLength + upperArmLength - 0.01f);
|
|
||||||
|
|
||||||
float armAngle = MathUtils.VectorToAngle(pos - shoulderPos) + MathHelper.PiOver2;
|
|
||||||
|
|
||||||
float upperArmAngle = MathUtils.SolveTriangleSSS(forearmLength, upperArmLength, c) * Dir;
|
|
||||||
float lowerArmAngle = MathUtils.SolveTriangleSSS(upperArmLength, forearmLength, c) * Dir;
|
|
||||||
|
|
||||||
//make sure the arm angle "has the same number of revolutions" as the arm
|
|
||||||
while (arm.Rotation - armAngle > MathHelper.Pi)
|
|
||||||
{
|
|
||||||
armAngle += MathHelper.TwoPi;
|
|
||||||
}
|
|
||||||
while (arm.Rotation - armAngle < -MathHelper.Pi)
|
|
||||||
{
|
|
||||||
armAngle -= MathHelper.TwoPi;
|
|
||||||
}
|
|
||||||
|
|
||||||
arm?.body.SmoothRotate(armAngle - upperArmAngle, 100.0f * armTorque * arm.Mass, wrapAngle: false);
|
|
||||||
float forearmAngle = armAngle + lowerArmAngle;
|
|
||||||
forearm?.body.SmoothRotate(forearmAngle, 100.0f * handTorque * forearm.Mass, wrapAngle: false);
|
|
||||||
float handAngle = forearm != null ? armAngle : forearmAngle;
|
|
||||||
hand?.body.SmoothRotate(handAngle, 100.0f * handTorque * hand.Mass, wrapAngle: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FootIK(Limb foot, Vector2 pos, float legTorque, float footTorque, float footAngle)
|
private void FootIK(Limb foot, Vector2 pos, float legTorque, float footTorque, float footAngle)
|
||||||
{
|
{
|
||||||
if (!MathUtils.IsValid(pos))
|
if (!MathUtils.IsValid(pos))
|
||||||
@@ -2014,47 +1687,6 @@ namespace Barotrauma
|
|||||||
foot.body.SmoothRotate((legAngle - (lowerLegAngle + footAngle) * Dir), foot.Mass * footTorque, wrapAngle: false);
|
foot.body.SmoothRotate((legAngle - (lowerLegAngle + footAngle) * Dir), foot.Mass * footTorque, wrapAngle: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
|
|
||||||
{
|
|
||||||
useItemTimer = 0.5f;
|
|
||||||
Anim = Animation.UsingConstruction;
|
|
||||||
|
|
||||||
if (!allowMovement)
|
|
||||||
{
|
|
||||||
TargetMovement = Vector2.Zero;
|
|
||||||
TargetDir = handWorldPos.X > character.WorldPosition.X ? Direction.Right : Direction.Left;
|
|
||||||
float sqrDist = Vector2.DistanceSquared(character.WorldPosition, handWorldPos);
|
|
||||||
if (sqrDist > MathUtils.Pow(ConvertUnits.ToDisplayUnits(upperArmLength + forearmLength), 2))
|
|
||||||
{
|
|
||||||
TargetMovement = Vector2.Normalize(handWorldPos - character.WorldPosition) * GetCurrentSpeed(false) * Math.Max(character.SpeedMultiplier, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!character.Enabled) { return; }
|
|
||||||
|
|
||||||
Vector2 handSimPos = ConvertUnits.ToSimUnits(handWorldPos);
|
|
||||||
if (character.Submarine != null)
|
|
||||||
{
|
|
||||||
handSimPos -= character.Submarine.SimPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
var leftHand = GetLimb(LimbType.LeftHand);
|
|
||||||
if (leftHand != null)
|
|
||||||
{
|
|
||||||
leftHand.Disabled = true;
|
|
||||||
leftHand.PullJointEnabled = true;
|
|
||||||
leftHand.PullJointWorldAnchorB = handSimPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rightHand = GetLimb(LimbType.RightHand);
|
|
||||||
if (rightHand != null)
|
|
||||||
{
|
|
||||||
rightHand.Disabled = true;
|
|
||||||
rightHand.PullJointEnabled = true;
|
|
||||||
rightHand.PullJointWorldAnchorB = handSimPos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flip()
|
public override void Flip()
|
||||||
{
|
{
|
||||||
base.Flip();
|
base.Flip();
|
||||||
@@ -2073,7 +1705,8 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
heldItem.FlipX(relativeToSub: false);
|
heldItem.FlipX(relativeToSub: false);
|
||||||
}
|
}
|
||||||
heldItem.FlipX(relativeToSub: false);
|
// TODO: was this added by a mistake?
|
||||||
|
//heldItem.FlipX(relativeToSub: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Limb limb in Limbs)
|
foreach (Limb limb in Limbs)
|
||||||
|
|||||||
@@ -125,7 +125,8 @@ namespace Barotrauma
|
|||||||
protected float surfaceY;
|
protected float surfaceY;
|
||||||
|
|
||||||
protected bool inWater, headInWater;
|
protected bool inWater, headInWater;
|
||||||
public bool onGround;
|
protected bool onGround;
|
||||||
|
public bool OnGround => onGround;
|
||||||
private Vector2 lastFloorCheckPos;
|
private Vector2 lastFloorCheckPos;
|
||||||
private bool lastFloorCheckIgnoreStairs, lastFloorCheckIgnorePlatforms;
|
private bool lastFloorCheckIgnoreStairs, lastFloorCheckIgnorePlatforms;
|
||||||
|
|
||||||
@@ -887,7 +888,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
|
|
||||||
/// <param name="pullFromCenter">if false, force is applied to the position of pullJoint</param>
|
/// <param name="pullFromCenter">if false, force is applied to the position of pullJoint</param>
|
||||||
protected void MoveLimb(Limb limb, Vector2 pos, float amount, bool pullFromCenter = false)
|
public void MoveLimb(Limb limb, Vector2 pos, float amount, bool pullFromCenter = false)
|
||||||
{
|
{
|
||||||
limb.MoveToPos(pos, amount, pullFromCenter);
|
limb.MoveToPos(pos, amount, pullFromCenter);
|
||||||
}
|
}
|
||||||
@@ -974,8 +975,7 @@ namespace Barotrauma
|
|||||||
Vector2 newSubPos = newHull.Submarine == null ? Vector2.Zero : newHull.Submarine.Position;
|
Vector2 newSubPos = newHull.Submarine == null ? Vector2.Zero : newHull.Submarine.Position;
|
||||||
Vector2 prevSubPos = currentHull.Submarine == null ? Vector2.Zero : currentHull.Submarine.Position;
|
Vector2 prevSubPos = currentHull.Submarine == null ? Vector2.Zero : currentHull.Submarine.Position;
|
||||||
|
|
||||||
Teleport(ConvertUnits.ToSimUnits(prevSubPos - newSubPos),
|
Teleport(ConvertUnits.ToSimUnits(prevSubPos - newSubPos), Vector2.Zero);
|
||||||
Vector2.Zero);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1099,6 +1099,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool forceStanding;
|
public bool forceStanding;
|
||||||
|
public bool forceNotStanding;
|
||||||
|
|
||||||
public void Update(float deltaTime, Camera cam)
|
public void Update(float deltaTime, Camera cam)
|
||||||
{
|
{
|
||||||
@@ -1270,6 +1271,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
UpdateProjSpecific(deltaTime, cam);
|
UpdateProjSpecific(deltaTime, cam);
|
||||||
|
forceNotStanding = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckBodyInRest(float deltaTime)
|
private void CheckBodyInRest(float deltaTime)
|
||||||
@@ -1569,7 +1571,7 @@ namespace Barotrauma
|
|||||||
return closestFraction;
|
return closestFraction;
|
||||||
}, rayStart, rayEnd, Physics.CollisionStairs | Physics.CollisionPlatform | Physics.CollisionWall | Physics.CollisionLevel);
|
}, rayStart, rayEnd, Physics.CollisionStairs | Physics.CollisionPlatform | Physics.CollisionWall | Physics.CollisionLevel);
|
||||||
|
|
||||||
if (standOnFloorFixture != null)
|
if (standOnFloorFixture != null && !IsHanging)
|
||||||
{
|
{
|
||||||
standOnFloorY = rayStart.Y + (rayEnd.Y - rayStart.Y) * standOnFloorFraction;
|
standOnFloorY = rayStart.Y + (rayEnd.Y - rayStart.Y) * standOnFloorFraction;
|
||||||
if (rayStart.Y - standOnFloorY < Collider.height * 0.5f + Collider.radius + ColliderHeightFromFloor * 1.2f)
|
if (rayStart.Y - standOnFloorY < Collider.height * 0.5f + Collider.radius + ColliderHeightFromFloor * 1.2f)
|
||||||
@@ -1606,6 +1608,13 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
if (MainLimb == null) { return; }
|
if (MainLimb == null) { return; }
|
||||||
|
|
||||||
|
if (Character.AIController is EnemyAIController enemyAI && enemyAI.LatchOntoAI != null && enemyAI.LatchOntoAI.IsAttached)
|
||||||
|
{
|
||||||
|
enemyAI.LatchOntoAI.DeattachFromBody(reset: true);
|
||||||
|
}
|
||||||
|
Character.Latchers.ForEachMod(l => l.DeattachFromBody(reset: true));
|
||||||
|
Character.Latchers.Clear();
|
||||||
|
|
||||||
Vector2 limbMoveAmount = forceMainLimbToCollider ? simPosition - MainLimb.SimPosition : simPosition - Collider.SimPosition;
|
Vector2 limbMoveAmount = forceMainLimbToCollider ? simPosition - MainLimb.SimPosition : simPosition - Collider.SimPosition;
|
||||||
if (lerp)
|
if (lerp)
|
||||||
{
|
{
|
||||||
@@ -1629,6 +1638,16 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsHanging { get; protected set; }
|
||||||
|
|
||||||
|
public void Hang()
|
||||||
|
{
|
||||||
|
ResetPullJoints();
|
||||||
|
onGround = false;
|
||||||
|
levitatingCollider = false;
|
||||||
|
IsHanging = true;
|
||||||
|
}
|
||||||
|
|
||||||
protected void TrySetLimbPosition(Limb limb, Vector2 original, Vector2 simPosition, bool lerp = false, bool ignorePlatforms = true)
|
protected void TrySetLimbPosition(Limb limb, Vector2 original, Vector2 simPosition, bool lerp = false, bool ignorePlatforms = true)
|
||||||
{
|
{
|
||||||
Vector2 movePos = simPosition;
|
Vector2 movePos = simPosition;
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly HashSet<LatchOntoAI> Latchers = new HashSet<LatchOntoAI>();
|
||||||
|
|
||||||
protected readonly Dictionary<string, ActiveTeamChange> activeTeamChanges = new Dictionary<string, ActiveTeamChange>();
|
protected readonly Dictionary<string, ActiveTeamChange> activeTeamChanges = new Dictionary<string, ActiveTeamChange>();
|
||||||
protected ActiveTeamChange currentTeamChange;
|
protected ActiveTeamChange currentTeamChange;
|
||||||
const string OriginalTeamIdentifier = "original";
|
const string OriginalTeamIdentifier = "original";
|
||||||
@@ -264,6 +266,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public readonly CharacterParams Params;
|
public readonly CharacterParams Params;
|
||||||
public string SpeciesName => Params.SpeciesName;
|
public string SpeciesName => Params.SpeciesName;
|
||||||
|
public string Group => Params.Group;
|
||||||
public bool IsHumanoid => Params.Humanoid;
|
public bool IsHumanoid => Params.Humanoid;
|
||||||
public bool IsHusk => Params.Husk;
|
public bool IsHusk => Params.Husk;
|
||||||
|
|
||||||
@@ -473,8 +476,7 @@ namespace Barotrauma
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public bool CanInteract => AllowInput && Params.CanInteract && !LockHands;
|
||||||
public bool CanInteract => AllowInput && IsHumanoid && !LockHands;
|
|
||||||
|
|
||||||
// Eating is not implemented for humanoids. If we implement that at some point, we could remove this restriction.
|
// Eating is not implemented for humanoids. If we implement that at some point, we could remove this restriction.
|
||||||
public bool CanEat => !IsHumanoid && Params.CanEat && AllowInput && AnimController.GetLimb(LimbType.Head) != null;
|
public bool CanEat => !IsHumanoid && Params.CanEat && AllowInput && AnimController.GetLimb(LimbType.Head) != null;
|
||||||
@@ -592,6 +594,7 @@ namespace Barotrauma
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (IsUnconscious) { return true; }
|
if (IsUnconscious) { return true; }
|
||||||
|
if (IsDead) { return true; }
|
||||||
return CharacterHealth.Afflictions.Any(a => a.Prefab.AfflictionType == "paralysis" && a.Strength >= a.Prefab.MaxStrength);
|
return CharacterHealth.Afflictions.Any(a => a.Prefab.AfflictionType == "paralysis" && a.Strength >= a.Prefab.MaxStrength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,7 +629,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public float Stun
|
public float Stun
|
||||||
{
|
{
|
||||||
get { return IsRagdolled ? 1.0f : CharacterHealth.Stun; }
|
get { return IsRagdolled && !AnimController.IsHanging ? 1.0f : CharacterHealth.Stun; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||||
@@ -1172,6 +1175,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
LoadHeadAttachments();
|
LoadHeadAttachments();
|
||||||
}
|
}
|
||||||
|
ApplyStatusEffects(ActionType.OnSpawn, 1.0f);
|
||||||
}
|
}
|
||||||
partial void InitProjSpecific(XElement mainElement);
|
partial void InitProjSpecific(XElement mainElement);
|
||||||
|
|
||||||
@@ -1669,7 +1673,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!aiControlled &&
|
if (!aiControlled &&
|
||||||
AnimController.onGround &&
|
AnimController.OnGround &&
|
||||||
!AnimController.InWater &&
|
!AnimController.InWater &&
|
||||||
AnimController.Anim != AnimController.Animation.UsingConstruction &&
|
AnimController.Anim != AnimController.Animation.UsingConstruction &&
|
||||||
AnimController.Anim != AnimController.Animation.CPR &&
|
AnimController.Anim != AnimController.Animation.CPR &&
|
||||||
@@ -2697,6 +2701,11 @@ namespace Barotrauma
|
|||||||
ApplyStatusEffects(AnimController.InWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
ApplyStatusEffects(AnimController.InWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
||||||
ApplyStatusEffects(ActionType.OnActive, deltaTime);
|
ApplyStatusEffects(ActionType.OnActive, deltaTime);
|
||||||
|
|
||||||
|
if (aiTarget != null)
|
||||||
|
{
|
||||||
|
aiTarget.InDetectable = false;
|
||||||
|
}
|
||||||
|
|
||||||
UpdateControlled(deltaTime, cam);
|
UpdateControlled(deltaTime, cam);
|
||||||
|
|
||||||
//Health effects
|
//Health effects
|
||||||
@@ -2720,7 +2729,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
//Do ragdoll shenanigans before Stun because it's still technically a stun, innit? Less network updates for us!
|
//Do ragdoll shenanigans before Stun because it's still technically a stun, innit? Less network updates for us!
|
||||||
bool allowRagdoll = GameMain.NetworkMember?.ServerSettings?.AllowRagdollButton ?? true;
|
bool allowRagdoll = GameMain.NetworkMember?.ServerSettings?.AllowRagdollButton ?? true;
|
||||||
bool tooFastToUnragdoll = AnimController.Collider.LinearVelocity.LengthSquared() > 2.5f * 2.5f;
|
bool tooFastToUnragdoll = AnimController.Collider.LinearVelocity.LengthSquared() > 8.0f * 8.0f;
|
||||||
bool wasRagdolled = false;
|
bool wasRagdolled = false;
|
||||||
bool selfRagdolled = false;
|
bool selfRagdolled = false;
|
||||||
|
|
||||||
@@ -2838,7 +2847,7 @@ namespace Barotrauma
|
|||||||
// If the damage is very low, let's not forget so quickly, or we can't cumulate the damage from repair tools (high frequency, low damage)
|
// If the damage is very low, let's not forget so quickly, or we can't cumulate the damage from repair tools (high frequency, low damage)
|
||||||
reduction *= 0.5f;
|
reduction *= 0.5f;
|
||||||
}
|
}
|
||||||
enemy.Damage = Math.Max(0.0f, enemy.Damage-reduction);
|
enemy.Damage = Math.Max(0.0f, enemy.Damage - reduction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3514,7 +3523,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (Removed) { return new AttackResult(); }
|
if (Removed) { return new AttackResult(); }
|
||||||
|
|
||||||
if (attacker != null && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
|
if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
|
||||||
{
|
{
|
||||||
if (attacker.TeamID == TeamID) { return new AttackResult(); }
|
if (attacker.TeamID == TeamID) { return new AttackResult(); }
|
||||||
}
|
}
|
||||||
@@ -3676,6 +3685,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && !isNetworkMessage) { return; }
|
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && !isNetworkMessage) { return; }
|
||||||
if (Screen.Selected != GameMain.GameScreen) { return; }
|
if (Screen.Selected != GameMain.GameScreen) { return; }
|
||||||
|
if (newStun > 0 && Params.Health.StunImmunity) { return; }
|
||||||
if ((newStun <= Stun && !allowStunDecrease) || !MathUtils.IsValid(newStun)) { return; }
|
if ((newStun <= Stun && !allowStunDecrease) || !MathUtils.IsValid(newStun)) { return; }
|
||||||
if (Math.Sign(newStun) != Math.Sign(Stun))
|
if (Math.Sign(newStun) != Math.Sign(Stun))
|
||||||
{
|
{
|
||||||
@@ -3876,9 +3886,12 @@ namespace Barotrauma
|
|||||||
AnimController.movement = Vector2.Zero;
|
AnimController.movement = Vector2.Zero;
|
||||||
AnimController.TargetMovement = Vector2.Zero;
|
AnimController.TargetMovement = Vector2.Zero;
|
||||||
|
|
||||||
foreach (Item heldItem in HeldItems.ToList())
|
if (!LockHands)
|
||||||
{
|
{
|
||||||
heldItem.Drop(this);
|
foreach (Item heldItem in HeldItems.ToList())
|
||||||
|
{
|
||||||
|
heldItem.Drop(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectedConstruction = null;
|
SelectedConstruction = null;
|
||||||
@@ -4435,6 +4448,11 @@ namespace Barotrauma
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Dictionary<StatTypes, float> statValues = new Dictionary<StatTypes, float>();
|
private readonly Dictionary<StatTypes, float> statValues = new Dictionary<StatTypes, float>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary with temporary values, updated when the character equips/unequips wearables. Used to reduce unnecessary inventory checking.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Dictionary<StatTypes, float> wearableStatValues = new Dictionary<StatTypes, float>();
|
||||||
|
|
||||||
public float GetStatValue(StatTypes statType)
|
public float GetStatValue(StatTypes statType)
|
||||||
{
|
{
|
||||||
if (!IsHuman) { return 0f; }
|
if (!IsHuman) { return 0f; }
|
||||||
@@ -4453,23 +4471,37 @@ namespace Barotrauma
|
|||||||
// could be optimized by instead updating the Character.cs statvalues dictionary whenever the CharacterInfo.cs values change
|
// could be optimized by instead updating the Character.cs statvalues dictionary whenever the CharacterInfo.cs values change
|
||||||
statValue += Info.GetSavedStatValue(statType);
|
statValue += Info.GetSavedStatValue(statType);
|
||||||
}
|
}
|
||||||
|
if (wearableStatValues.TryGetValue(statType, out float wearableValue))
|
||||||
//replace by updating the character wearable stat values when equipping or unequipping wearables
|
|
||||||
for (int i = 0; i < Inventory.Capacity; i++)
|
|
||||||
{
|
{
|
||||||
if (Inventory.SlotTypes[i] != InvSlotType.Any && Inventory.SlotTypes[i] != InvSlotType.LeftHand && Inventory.SlotTypes[i] != InvSlotType.RightHand
|
statValue += wearableValue;
|
||||||
&& Inventory.GetItemAt(i)?.GetComponent<Wearable>() is Wearable wearable)
|
|
||||||
{
|
|
||||||
if (wearable.WearableStatValues.TryGetValue(statType, out float wearableValue))
|
|
||||||
{
|
|
||||||
statValue += wearableValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return statValue;
|
return statValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnWearablesChanged()
|
||||||
|
{
|
||||||
|
wearableStatValues.Clear();
|
||||||
|
for (int i = 0; i < Inventory.Capacity; i++)
|
||||||
|
{
|
||||||
|
if (Inventory.SlotTypes[i] != InvSlotType.Any && Inventory.SlotTypes[i] != InvSlotType.LeftHand && Inventory.SlotTypes[i] != InvSlotType.RightHand
|
||||||
|
&& Inventory.GetItemAt(i)?.GetComponent<Wearable>() is Wearable wearable)
|
||||||
|
{
|
||||||
|
foreach (var statValuePair in wearable.WearableStatValues)
|
||||||
|
{
|
||||||
|
if (wearableStatValues.ContainsKey(statValuePair.Key))
|
||||||
|
{
|
||||||
|
wearableStatValues[statValuePair.Key] += statValuePair.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wearableStatValues.Add(statValuePair.Key, statValuePair.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ChangeStat(StatTypes statType, float value)
|
public void ChangeStat(StatTypes statType, float value)
|
||||||
{
|
{
|
||||||
if (statValues.ContainsKey(statType))
|
if (statValues.ContainsKey(statType))
|
||||||
@@ -4516,7 +4548,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public bool HasAbilityFlag(AbilityFlags abilityFlag)
|
public bool HasAbilityFlag(AbilityFlags abilityFlag)
|
||||||
{
|
{
|
||||||
return abilityFlags.Contains(abilityFlag);
|
return abilityFlags.Contains(abilityFlag) || CharacterHealth.HasFlag(abilityFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Dictionary<string, float> abilityResistances = new Dictionary<string, float>();
|
private readonly Dictionary<string, float> abilityResistances = new Dictionary<string, float>();
|
||||||
|
|||||||
@@ -218,30 +218,30 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public int AdditionalTalentPoints { get; set; }
|
public int AdditionalTalentPoints { get; set; }
|
||||||
|
|
||||||
private Sprite headSprite;
|
private Sprite _headSprite;
|
||||||
public Sprite HeadSprite
|
public Sprite HeadSprite
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (headSprite == null)
|
if (_headSprite == null)
|
||||||
{
|
{
|
||||||
LoadHeadSprite();
|
LoadHeadSprite();
|
||||||
}
|
}
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
if (headSprite != null)
|
if (_headSprite != null)
|
||||||
{
|
{
|
||||||
CalculateHeadPosition(headSprite);
|
CalculateHeadPosition(_headSprite);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return headSprite;
|
return _headSprite;
|
||||||
}
|
}
|
||||||
private set
|
private set
|
||||||
{
|
{
|
||||||
if (headSprite != null)
|
if (_headSprite != null)
|
||||||
{
|
{
|
||||||
headSprite.Remove();
|
_headSprite.Remove();
|
||||||
}
|
}
|
||||||
headSprite = value;
|
_headSprite = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +287,7 @@ namespace Barotrauma
|
|||||||
Character.CharacterHealth.ApplyAffliction(Character.AnimController.GetLimb(LimbType.Head), AfflictionPrefab.List.FirstOrDefault(a => a.Identifier.Equals("disguised", StringComparison.OrdinalIgnoreCase)).Instantiate(100f));
|
Character.CharacterHealth.ApplyAffliction(Character.AnimController.GetLimb(LimbType.Head), AfflictionPrefab.List.FirstOrDefault(a => a.Identifier.Equals("disguised", StringComparison.OrdinalIgnoreCase)).Instantiate(100f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idCard ??= Character.Inventory?.GetItemInLimbSlot(InvSlotType.Card)?.GetComponent<IdCard>();
|
||||||
if (idCard != null)
|
if (idCard != null)
|
||||||
{
|
{
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
@@ -294,19 +295,6 @@ namespace Barotrauma
|
|||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Character.Inventory != null)
|
|
||||||
{
|
|
||||||
idCard = Character.Inventory.GetItemInLimbSlot(InvSlotType.Card)?.GetComponent<IdCard>();
|
|
||||||
if (idCard != null)
|
|
||||||
{
|
|
||||||
#if CLIENT
|
|
||||||
GetDisguisedSprites(idCard);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
@@ -1085,7 +1073,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<XElement> AddEmpty(IEnumerable<XElement> elements, WearableType type, float commonness = 1)
|
public static List<XElement> AddEmpty(IEnumerable<XElement> elements, WearableType type, float commonness = 1)
|
||||||
{
|
{
|
||||||
// Let's add an empty element so that there's a chance that we don't get any actual element -> allows bald and beardless guys, for example.
|
// Let's add an empty element so that there's a chance that we don't get any actual element -> allows bald and beardless guys, for example.
|
||||||
var emptyElement = new XElement("EmptyWearable", type.ToString(), new XAttribute("commonness", commonness));
|
var emptyElement = new XElement("EmptyWearable", type.ToString(), new XAttribute("commonness", commonness));
|
||||||
@@ -1094,9 +1082,9 @@ namespace Barotrauma
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private XElement GetRandomElement(IEnumerable<XElement> elements)
|
public XElement GetRandomElement(IEnumerable<XElement> elements)
|
||||||
{
|
{
|
||||||
var filtered = elements.Where(e => IsWearableAllowed(e));
|
var filtered = elements.Where(IsWearableAllowed);
|
||||||
if (filtered.Count() == 0) { return null; }
|
if (filtered.Count() == 0) { return null; }
|
||||||
var element = ToolBox.SelectWeightedRandom(filtered.ToList(), GetWeights(filtered).ToList(), Rand.RandSync.Unsynced);
|
var element = ToolBox.SelectWeightedRandom(filtered.ToList(), GetWeights(filtered).ToList(), Rand.RandSync.Unsynced);
|
||||||
return element == null || element.Name == "Empty" ? null : element;
|
return element == null || element.Name == "Empty" ? null : element;
|
||||||
@@ -1121,7 +1109,7 @@ namespace Barotrauma
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsValidIndex(int index, List<XElement> list) => index >= 0 && index < list.Count;
|
public static bool IsValidIndex(int index, List<XElement> list) => index >= 0 && index < list.Count;
|
||||||
|
|
||||||
private static IEnumerable<float> GetWeights(IEnumerable<XElement> elements) => elements.Select(h => h.GetAttributeFloat("commonness", 1f));
|
private static IEnumerable<float> GetWeights(IEnumerable<XElement> elements) => elements.Select(h => h.GetAttributeFloat("commonness", 1f));
|
||||||
|
|
||||||
@@ -1219,8 +1207,8 @@ namespace Barotrauma
|
|||||||
OnExperienceChanged(prevAmount, ExperiencePoints, Character.Position + Vector2.UnitY * 150.0f);
|
OnExperienceChanged(prevAmount, ExperiencePoints, Character.Position + Vector2.UnitY * 150.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int BaseExperienceRequired = 150;
|
const int BaseExperienceRequired = 50;
|
||||||
const int AddedExperienceRequiredPerLevel = 350;
|
const int AddedExperienceRequiredPerLevel = 450;
|
||||||
|
|
||||||
public int GetTotalTalentPoints()
|
public int GetTotalTalentPoints()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -191,6 +191,30 @@ namespace Barotrauma
|
|||||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Color GetFaceTint()
|
||||||
|
{
|
||||||
|
if (Strength < Prefab.ActivationThreshold) { return Color.TransparentBlack; }
|
||||||
|
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||||
|
if (currentEffect == null) { return Color.TransparentBlack; }
|
||||||
|
|
||||||
|
return Color.Lerp(
|
||||||
|
currentEffect.MinFaceTint,
|
||||||
|
currentEffect.MaxFaceTint,
|
||||||
|
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color GetBodyTint()
|
||||||
|
{
|
||||||
|
if (Strength < Prefab.ActivationThreshold) { return Color.TransparentBlack; }
|
||||||
|
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||||
|
if (currentEffect == null) { return Color.TransparentBlack; }
|
||||||
|
|
||||||
|
return Color.Lerp(
|
||||||
|
currentEffect.MinBodyTint,
|
||||||
|
currentEffect.MaxBodyTint,
|
||||||
|
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||||
|
}
|
||||||
|
|
||||||
public float GetScreenBlurStrength()
|
public float GetScreenBlurStrength()
|
||||||
{
|
{
|
||||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||||
@@ -277,6 +301,13 @@ namespace Barotrauma
|
|||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasFlag(AbilityFlags flagType)
|
||||||
|
{
|
||||||
|
if (!(GetViableEffect() is AfflictionPrefab.Effect currentEffect)) { return false; }
|
||||||
|
|
||||||
|
return currentEffect.AfflictionAbilityFlags.Contains(flagType);
|
||||||
|
}
|
||||||
|
|
||||||
private AfflictionPrefab.Effect GetViableEffect()
|
private AfflictionPrefab.Effect GetViableEffect()
|
||||||
{
|
{
|
||||||
if (Strength < Prefab.ActivationThreshold) { return null; }
|
if (Strength < Prefab.ActivationThreshold) { return null; }
|
||||||
|
|||||||
+6
@@ -3,6 +3,7 @@ using System.Linq;
|
|||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using System;
|
using System;
|
||||||
using Barotrauma.Extensions;
|
using Barotrauma.Extensions;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -216,6 +217,11 @@ namespace Barotrauma
|
|||||||
XElement parentElement = new XElement("CharacterInfo");
|
XElement parentElement = new XElement("CharacterInfo");
|
||||||
XElement infoElement = character.Info?.Save(parentElement);
|
XElement infoElement = character.Info?.Save(parentElement);
|
||||||
CharacterInfo huskCharacterInfo = infoElement == null ? null : new CharacterInfo(infoElement);
|
CharacterInfo huskCharacterInfo = infoElement == null ? null : new CharacterInfo(infoElement);
|
||||||
|
|
||||||
|
var bodyTint = GetBodyTint();
|
||||||
|
huskCharacterInfo.SkinColor =
|
||||||
|
Color.Lerp(huskCharacterInfo.SkinColor, bodyTint.Opaque(), bodyTint.A / 255.0f);
|
||||||
|
|
||||||
var husk = Character.Create(huskedSpeciesName, character.WorldPosition, ToolBox.RandomSeed(8), huskCharacterInfo, isRemotePlayer: false, hasAi: true);
|
var husk = Character.Create(huskedSpeciesName, character.WorldPosition, ToolBox.RandomSeed(8), huskCharacterInfo, isRemotePlayer: false, hasAi: true);
|
||||||
if (husk.Info != null)
|
if (husk.Info != null)
|
||||||
{
|
{
|
||||||
|
|||||||
+20
-4
@@ -1,11 +1,10 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Barotrauma.Abilities;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using Barotrauma.Abilities;
|
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -223,7 +222,20 @@ namespace Barotrauma
|
|||||||
[Serialize("", false)]
|
[Serialize("", false)]
|
||||||
public string DialogFlag { get; private set; }
|
public string DialogFlag { get; private set; }
|
||||||
|
|
||||||
|
[Serialize("0,0,0,0", false)]
|
||||||
|
public Color MinFaceTint { get; private set; }
|
||||||
|
|
||||||
|
[Serialize("0,0,0,0", false)]
|
||||||
|
public Color MaxFaceTint { get; private set; }
|
||||||
|
|
||||||
|
[Serialize("0,0,0,0", false)]
|
||||||
|
public Color MinBodyTint { get; private set; }
|
||||||
|
|
||||||
|
[Serialize("0,0,0,0", false)]
|
||||||
|
public Color MaxBodyTint { get; private set; }
|
||||||
|
|
||||||
public readonly Dictionary<StatTypes, (float minValue, float maxValue)> AfflictionStatValues = new Dictionary<StatTypes, (float minValue, float maxValue)>();
|
public readonly Dictionary<StatTypes, (float minValue, float maxValue)> AfflictionStatValues = new Dictionary<StatTypes, (float minValue, float maxValue)>();
|
||||||
|
public readonly HashSet<AbilityFlags> AfflictionAbilityFlags = new HashSet<AbilityFlags>();
|
||||||
|
|
||||||
//statuseffects applied on the character when the affliction is active
|
//statuseffects applied on the character when the affliction is active
|
||||||
public readonly List<StatusEffect> StatusEffects = new List<StatusEffect>();
|
public readonly List<StatusEffect> StatusEffects = new List<StatusEffect>();
|
||||||
@@ -250,6 +262,10 @@ namespace Barotrauma
|
|||||||
|
|
||||||
AfflictionStatValues.TryAdd(statType, (minValue, maxValue));
|
AfflictionStatValues.TryAdd(statType, (minValue, maxValue));
|
||||||
break;
|
break;
|
||||||
|
case "abilityflag":
|
||||||
|
var flagType = CharacterAbilityGroup.ParseFlagType(subElement.GetAttributeString("flagtype", ""), parentDebugName);
|
||||||
|
AfflictionAbilityFlags.Add(flagType);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public bool IsUnconscious
|
public bool IsUnconscious
|
||||||
{
|
{
|
||||||
get { return Vitality <= 0.0f || Character.IsDead; }
|
get { return (Vitality <= 0.0f || Character.IsDead) && !Character.HasAbilityFlag(AbilityFlags.AlwaysStayConscious); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public float PressureKillDelay { get; private set; } = 5.0f;
|
public float PressureKillDelay { get; private set; } = 5.0f;
|
||||||
@@ -169,6 +169,20 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Color DefaultFaceTint = Color.TransparentBlack;
|
||||||
|
|
||||||
|
public Color FaceTint
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color BodyTint
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
public float OxygenAmount
|
public float OxygenAmount
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -409,7 +423,7 @@ namespace Barotrauma
|
|||||||
return strength;
|
return strength;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyAffliction(Limb targetLimb, Affliction affliction)
|
public void ApplyAffliction(Limb targetLimb, Affliction affliction, bool allowStacking = true)
|
||||||
{
|
{
|
||||||
if (!affliction.Prefab.IsBuff && Unkillable || Character.GodMode) { return; }
|
if (!affliction.Prefab.IsBuff && Unkillable || Character.GodMode) { return; }
|
||||||
if (affliction.Prefab.LimbSpecific)
|
if (affliction.Prefab.LimbSpecific)
|
||||||
@@ -419,17 +433,17 @@ namespace Barotrauma
|
|||||||
//if a limb-specific affliction is applied to no specific limb, apply to all limbs
|
//if a limb-specific affliction is applied to no specific limb, apply to all limbs
|
||||||
foreach (LimbHealth limbHealth in limbHealths)
|
foreach (LimbHealth limbHealth in limbHealths)
|
||||||
{
|
{
|
||||||
AddLimbAffliction(limbHealth, affliction);
|
AddLimbAffliction(limbHealth, affliction, allowStacking: allowStacking);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddLimbAffliction(targetLimb, affliction);
|
AddLimbAffliction(targetLimb, affliction, allowStacking: allowStacking);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddAffliction(affliction);
|
AddAffliction(affliction, allowStacking: allowStacking);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,6 +467,15 @@ namespace Barotrauma
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasFlag(AbilityFlags flagType)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < afflictions.Count; i++)
|
||||||
|
{
|
||||||
|
if (afflictions[i].HasFlag(flagType)) { return true; }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly List<Affliction> matchingAfflictions = new List<Affliction>();
|
private readonly List<Affliction> matchingAfflictions = new List<Affliction>();
|
||||||
public void ReduceAffliction(Limb targetLimb, string affliction, float amount, ActionType? treatmentAction = null)
|
public void ReduceAffliction(Limb targetLimb, string affliction, float amount, ActionType? treatmentAction = null)
|
||||||
{
|
{
|
||||||
@@ -671,6 +694,7 @@ namespace Barotrauma
|
|||||||
private void AddAffliction(Affliction newAffliction, bool allowStacking = true)
|
private void AddAffliction(Affliction newAffliction, bool allowStacking = true)
|
||||||
{
|
{
|
||||||
if (!DoesBleed && newAffliction is AfflictionBleeding) { return; }
|
if (!DoesBleed && newAffliction is AfflictionBleeding) { return; }
|
||||||
|
if (Character.Params.Health.StunImmunity && newAffliction.Prefab.AfflictionType == "stun") { return; }
|
||||||
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) { return; }
|
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) { return; }
|
||||||
if (newAffliction.Prefab is AfflictionPrefabHusk huskPrefab)
|
if (newAffliction.Prefab is AfflictionPrefabHusk huskPrefab)
|
||||||
{
|
{
|
||||||
@@ -725,6 +749,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
StunTimer = Stun > 0 ? StunTimer + deltaTime : 0;
|
StunTimer = Stun > 0 ? StunTimer + deltaTime : 0;
|
||||||
|
|
||||||
|
FaceTint = DefaultFaceTint;
|
||||||
|
|
||||||
for (int i = 0; i < limbHealths.Count; i++)
|
for (int i = 0; i < limbHealths.Count; i++)
|
||||||
{
|
{
|
||||||
for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--)
|
for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--)
|
||||||
@@ -749,10 +775,14 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
UpdateBleedingProjSpecific(bleeding, targetLimb, deltaTime);
|
UpdateBleedingProjSpecific(bleeding, targetLimb, deltaTime);
|
||||||
}
|
}
|
||||||
|
Color faceTint = affliction.GetFaceTint();
|
||||||
|
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||||
|
Color bodyTint = affliction.GetBodyTint();
|
||||||
|
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
|
||||||
Character.StackSpeedMultiplier(affliction.GetSpeedMultiplier());
|
Character.StackSpeedMultiplier(affliction.GetSpeedMultiplier());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = afflictions.Count - 1; i >= 0; i--)
|
for (int i = afflictions.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var affliction = afflictions[i];
|
var affliction = afflictions[i];
|
||||||
@@ -768,6 +798,10 @@ namespace Barotrauma
|
|||||||
var affliction = afflictions[i];
|
var affliction = afflictions[i];
|
||||||
affliction.Update(this, null, deltaTime);
|
affliction.Update(this, null, deltaTime);
|
||||||
affliction.DamagePerSecondTimer += deltaTime;
|
affliction.DamagePerSecondTimer += deltaTime;
|
||||||
|
Color faceTint = affliction.GetFaceTint();
|
||||||
|
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||||
|
Color bodyTint = affliction.GetBodyTint();
|
||||||
|
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
|
||||||
Character.StackSpeedMultiplier(affliction.GetSpeedMultiplier());
|
Character.StackSpeedMultiplier(affliction.GetSpeedMultiplier());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -543,6 +543,8 @@ namespace Barotrauma
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (character.IsHumanoid) { return false; }
|
if (character.IsHumanoid) { return false; }
|
||||||
|
// TODO: We might need this or solve the cases where a limb is severed while holding on to an item
|
||||||
|
//if (character.Params.CanInteract) { return false; }
|
||||||
if (this == character.AnimController.MainLimb) { return false; }
|
if (this == character.AnimController.MainLimb) { return false; }
|
||||||
if (character.AnimController.CanWalk)
|
if (character.AnimController.CanWalk)
|
||||||
{
|
{
|
||||||
|
|||||||
+6
@@ -129,6 +129,12 @@ namespace Barotrauma
|
|||||||
[Serialize(AnimationType.NotDefined, true), Editable]
|
[Serialize(AnimationType.NotDefined, true), Editable]
|
||||||
public virtual AnimationType AnimationType { get; protected set; }
|
public virtual AnimationType AnimationType { get; protected set; }
|
||||||
|
|
||||||
|
[Serialize(1f, true, description: "How much force is used to rotate the arms to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||||
|
public float ArmIKStrength { get; set; }
|
||||||
|
|
||||||
|
[Serialize(1f, true, description: "How much force is used to rotate the hands to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||||
|
public float HandIKStrength { get; set; }
|
||||||
|
|
||||||
public static string GetDefaultFileName(string speciesName, AnimationType animType) => $"{speciesName.CapitaliseFirstInvariant()}{animType}";
|
public static string GetDefaultFileName(string speciesName, AnimationType animType) => $"{speciesName.CapitaliseFirstInvariant()}{animType}";
|
||||||
public static string GetDefaultFile(string speciesName, AnimationType animType) => Path.Combine(GetFolder(speciesName), $"{GetDefaultFileName(speciesName, animType)}.xml");
|
public static string GetDefaultFile(string speciesName, AnimationType animType) => Path.Combine(GetFolder(speciesName), $"{GetDefaultFileName(speciesName, animType)}.xml");
|
||||||
|
|
||||||
|
|||||||
-16
@@ -94,12 +94,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
[Serialize(1f, true, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
[Serialize(1f, true, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||||
public float HandMoveStrength { get; set; }
|
public float HandMoveStrength { get; set; }
|
||||||
|
|
||||||
[Serialize(1f, true, description: "How much force is used to rotate the arms to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
|
||||||
public float ArmIKStrength { get; set; }
|
|
||||||
|
|
||||||
[Serialize(1f, true, description: "How much force is used to rotate the hands to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
|
||||||
public float HandIKStrength { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class HumanGroundedParams : GroundedMovementParams, IHumanAnimation
|
abstract class HumanGroundedParams : GroundedMovementParams, IHumanAnimation
|
||||||
@@ -153,12 +147,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
[Serialize(1f, true, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
[Serialize(1f, true, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||||
public float HandMoveStrength { get; set; }
|
public float HandMoveStrength { get; set; }
|
||||||
|
|
||||||
[Serialize(1f, true, description: "How much force is used to rotate the arms to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
|
||||||
public float ArmIKStrength { get; set; }
|
|
||||||
|
|
||||||
[Serialize(1f, true, description: "How much force is used to rotate the hands to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
|
||||||
public float HandIKStrength { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IHumanAnimation
|
public interface IHumanAnimation
|
||||||
@@ -169,9 +157,5 @@ namespace Barotrauma
|
|||||||
float ArmMoveStrength { get; set; }
|
float ArmMoveStrength { get; set; }
|
||||||
|
|
||||||
float HandMoveStrength { get; set; }
|
float HandMoveStrength { get; set; }
|
||||||
|
|
||||||
float ArmIKStrength { get; set; }
|
|
||||||
|
|
||||||
float HandIKStrength { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ namespace Barotrauma
|
|||||||
[Serialize(false, true), Editable(ReadOnly = true)]
|
[Serialize(false, true), Editable(ReadOnly = true)]
|
||||||
public bool HasInfo { get; private set; }
|
public bool HasInfo { get; private set; }
|
||||||
|
|
||||||
|
[Serialize(false, true, description: "Can the creature interact with items?"), Editable]
|
||||||
|
public bool CanInteract { get; private set; }
|
||||||
|
|
||||||
[Serialize(false, true), Editable]
|
[Serialize(false, true), Editable]
|
||||||
public bool Husk { get; private set; }
|
public bool Husk { get; private set; }
|
||||||
|
|
||||||
@@ -454,6 +457,9 @@ namespace Barotrauma
|
|||||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||||
public float HealthRegenerationWhenEating { get; private set; }
|
public float HealthRegenerationWhenEating { get; private set; }
|
||||||
|
|
||||||
|
[Serialize(false, true), Editable]
|
||||||
|
public bool StunImmunity { get; set; }
|
||||||
|
|
||||||
// TODO: limbhealths, sprite?
|
// TODO: limbhealths, sprite?
|
||||||
|
|
||||||
public HealthParams(XElement element, CharacterParams character) : base(element, character) { }
|
public HealthParams(XElement element, CharacterParams character) : base(element, character) { }
|
||||||
@@ -553,15 +559,24 @@ namespace Barotrauma
|
|||||||
[Serialize(false, true, description: "If enabled, the character chooses randomly from the available attacks. The priority is used as a weight for weighted random."), Editable]
|
[Serialize(false, true, description: "If enabled, the character chooses randomly from the available attacks. The priority is used as a weight for weighted random."), Editable]
|
||||||
public bool RandomAttack { get; private set; }
|
public bool RandomAttack { get; private set; }
|
||||||
|
|
||||||
[Serialize(false, true, description:"Does the creature know how to open doors (still requires a proper ID card). Only applies on humanoids. Humans can always open doors (They don't use this AI definition)."), Editable]
|
[Serialize(false, true, description:"Does the creature know how to open doors (still requires a proper ID card). Humans can always open doors (They don't use this AI definition)."), Editable]
|
||||||
public bool CanOpenDoors { get; private set; }
|
public bool CanOpenDoors { get; private set; }
|
||||||
|
|
||||||
|
[Serialize(false, true, description: "Does the creature close the doors behind it. Humans don't use this AI definition."), Editable]
|
||||||
|
public bool KeepDoorsClosed { get; private set; }
|
||||||
|
|
||||||
[Serialize(true, true, "Is the creature allowed to navigate from and into the depths of the abyss? When enabled, the creatures will try to avoid the depths."), Editable]
|
[Serialize(true, true, "Is the creature allowed to navigate from and into the depths of the abyss? When enabled, the creatures will try to avoid the depths."), Editable]
|
||||||
public bool AvoidAbyss { get; set; }
|
public bool AvoidAbyss { get; set; }
|
||||||
|
|
||||||
[Serialize(false, true, "Does the creature try to keep in the abyss? Has effect only when AvoidAbyss is false."), Editable]
|
[Serialize(false, true, "Does the creature try to keep in the abyss? Has effect only when AvoidAbyss is false."), Editable]
|
||||||
public bool StayInAbyss { get; set; }
|
public bool StayInAbyss { get; set; }
|
||||||
|
|
||||||
|
[Serialize(false, true, "Does the creature patrol the flooded hulls while idling inside a friendly submarine?"), Editable]
|
||||||
|
public bool PatrolFlooded { get; set; }
|
||||||
|
|
||||||
|
[Serialize(false, true, "Does the creature patrol the dry hulls while idling inside a friendly submarine?"), Editable]
|
||||||
|
public bool PatrolDry { get; set; }
|
||||||
|
|
||||||
[Serialize(0f, true, description: ""), Editable]
|
[Serialize(0f, true, description: ""), Editable]
|
||||||
public float StartAggression { get; private set; }
|
public float StartAggression { get; private set; }
|
||||||
|
|
||||||
@@ -682,8 +697,14 @@ namespace Barotrauma
|
|||||||
[Serialize(false, true), Editable]
|
[Serialize(false, true), Editable]
|
||||||
public bool IgnoreIncapacitated { get; set; }
|
public bool IgnoreIncapacitated { get; set; }
|
||||||
|
|
||||||
[Serialize(0f, true, description: "How much damage the protected target should take from an attacker before the creature starts defending it."), Editable]
|
[Serialize(0f, true, description: "A generic threshold. For example, how much damage the protected target should take from an attacker before the creature starts defending it."), Editable]
|
||||||
public float DamageThreshold { get; private set; }
|
public float Threshold { get; private set; }
|
||||||
|
|
||||||
|
[Serialize(-1f, true, description: "A generic min threshold. Not used if set to negative."), Editable]
|
||||||
|
public float ThresholdMin { get; private set; }
|
||||||
|
|
||||||
|
[Serialize(-1f, true, description: "A generic max threshold. Not used if set to negative."), Editable]
|
||||||
|
public float ThresholdMax { get; private set; }
|
||||||
|
|
||||||
[Serialize(AttackPattern.Straight, true), Editable]
|
[Serialize(AttackPattern.Straight, true), Editable]
|
||||||
public AttackPattern AttackPattern { get; set; }
|
public AttackPattern AttackPattern { get; set; }
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace Barotrauma
|
|||||||
[Serialize("1.0,1.0,1.0,1.0", true), Editable()]
|
[Serialize("1.0,1.0,1.0,1.0", true), Editable()]
|
||||||
public Color Color { get; set; }
|
public Color Color { get; set; }
|
||||||
|
|
||||||
[Serialize(0.0f, true, description: "The orientation of the sprites as drawn on the sprite sheet. Can be overridden by setting a value for Limb's 'Sprite Orientation'. Used mainly for animations and widgets."), Editable(-360, 360)]
|
[Serialize(0.0f, true, description: "The orientation of the sprites as drawn on the sprite sheet. Can be overridden by setting a value for Limb's 'Sprite Orientation'."), Editable(-360, 360)]
|
||||||
public float SpritesheetOrientation { get; set; }
|
public float SpritesheetOrientation { get; set; }
|
||||||
|
|
||||||
public bool IsSpritesheetOrientationHorizontal
|
public bool IsSpritesheetOrientationHorizontal
|
||||||
@@ -572,7 +572,9 @@ namespace Barotrauma
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The orientation of the sprite as drawn on the sprite sheet (in radians).
|
/// The orientation of the sprite as drawn on the sprite sheet (in radians).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float GetSpriteOrientation() => MathHelper.ToRadians(float.IsNaN(SpriteOrientation) ? Ragdoll.SpritesheetOrientation : SpriteOrientation);
|
public float GetSpriteOrientation() => MathHelper.ToRadians(GetSpriteOrientationInDegrees());
|
||||||
|
|
||||||
|
public float GetSpriteOrientationInDegrees() => float.IsNaN(SpriteOrientation) ? Ragdoll.SpritesheetOrientation : SpriteOrientation;
|
||||||
|
|
||||||
[Serialize("", true), Editable]
|
[Serialize("", true), Editable]
|
||||||
public string Notes { get; set; }
|
public string Notes { get; set; }
|
||||||
@@ -592,7 +594,7 @@ namespace Barotrauma
|
|||||||
[Serialize(false, true, description: "Disable drawing for this limb."), Editable()]
|
[Serialize(false, true, description: "Disable drawing for this limb."), Editable()]
|
||||||
public bool Hide { get; set; }
|
public bool Hide { get; set; }
|
||||||
|
|
||||||
[Serialize(float.NaN, true, description: "The orientation of the sprite as drawn on the sprite sheet. Overrides the value defined in the Ragdoll settings. Used mainly for animations and widgets."), Editable(-360, 360, ValueStep = 90, DecimalCount = 0)]
|
[Serialize(float.NaN, true, description: "The orientation of the sprite as drawn on the sprite sheet. Overrides the value defined in the Ragdoll settings."), Editable(-360, 360, ValueStep = 90, DecimalCount = 0)]
|
||||||
public float SpriteOrientation { get; set; }
|
public float SpriteOrientation { get; set; }
|
||||||
|
|
||||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 500)]
|
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 500)]
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ namespace Barotrauma.Abilities
|
|||||||
|
|
||||||
if (afflictions.Any())
|
if (afflictions.Any())
|
||||||
{
|
{
|
||||||
if (!afflictions.Any(a => attackResult.Afflictions.Select(c => c.Identifier).Contains(a))) { return false; }
|
if (attackResult.Afflictions == null || !afflictions.Any(a => attackResult.Afflictions.Select(c => c.Identifier).Contains(a))) { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+2
-2
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
class AbilityConditionScavenger : AbilityConditionData
|
class AbilityConditionItemOutsideSubmarine : AbilityConditionData
|
||||||
{
|
{
|
||||||
|
|
||||||
public AbilityConditionScavenger(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement) { }
|
public AbilityConditionItemOutsideSubmarine(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement) { }
|
||||||
|
|
||||||
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
|
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
|
||||||
{
|
{
|
||||||
+2
-2
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
class AbilityConditionScrounger : AbilityConditionData
|
class AbilityConditionItemWreck : AbilityConditionData
|
||||||
{
|
{
|
||||||
|
|
||||||
public AbilityConditionScrounger(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement) { }
|
public AbilityConditionItemWreck(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement) { }
|
||||||
|
|
||||||
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
|
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
|
||||||
{
|
{
|
||||||
+9
-3
@@ -5,19 +5,25 @@ namespace Barotrauma.Abilities
|
|||||||
{
|
{
|
||||||
class AbilityConditionHasPermanentStat : AbilityConditionDataless
|
class AbilityConditionHasPermanentStat : AbilityConditionDataless
|
||||||
{
|
{
|
||||||
|
private readonly string statIdentifier;
|
||||||
private readonly StatTypes statType;
|
private readonly StatTypes statType;
|
||||||
private readonly float min;
|
private readonly float min;
|
||||||
|
|
||||||
public AbilityConditionHasPermanentStat(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
public AbilityConditionHasPermanentStat(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||||
{
|
{
|
||||||
statType = CharacterAbilityGroup.ParseStatType(conditionElement.GetAttributeString("stattype", ""), characterTalent.DebugIdentifier);
|
statIdentifier = conditionElement.GetAttributeString("statidentifier", string.Empty);
|
||||||
|
if (string.IsNullOrEmpty(statIdentifier))
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"No stat identifier defined for {this} in talent {characterTalent.DebugIdentifier}!");
|
||||||
|
}
|
||||||
|
string statTypeName = conditionElement.GetAttributeString("stattype", string.Empty);
|
||||||
|
statType = string.IsNullOrEmpty(statTypeName) ? StatTypes.None : CharacterAbilityGroup.ParseStatType(statTypeName, characterTalent.DebugIdentifier);
|
||||||
min = conditionElement.GetAttributeFloat("min", 0f);
|
min = conditionElement.GetAttributeFloat("min", 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool MatchesConditionSpecific()
|
protected override bool MatchesConditionSpecific()
|
||||||
{
|
{
|
||||||
// should consider decoupling this from stat values entirely
|
return character.Info.GetSavedStatValue(statType, statIdentifier) >= min;
|
||||||
return character.Info.GetSavedStatValue(statType) >= min;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
using Barotrauma.Items.Components;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
namespace Barotrauma.Abilities
|
||||||
|
{
|
||||||
|
class AbilityConditionHasSkill : AbilityConditionDataless
|
||||||
|
{
|
||||||
|
private readonly string skillIdentifier;
|
||||||
|
private readonly float minValue;
|
||||||
|
|
||||||
|
public AbilityConditionHasSkill(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||||
|
{
|
||||||
|
skillIdentifier = conditionElement.GetAttributeString("skillidentifier", string.Empty);
|
||||||
|
minValue = conditionElement.GetAttributeFloat("minvalue", 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool MatchesConditionSpecific()
|
||||||
|
{
|
||||||
|
return character.GetSkillLevel(skillIdentifier) >= minValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Barotrauma.Abilities
|
namespace Barotrauma.Abilities
|
||||||
{
|
{
|
||||||
class AbilityObject
|
abstract class AbilityObject
|
||||||
{
|
{
|
||||||
// kept as blank for now, as we are using a composition and only using this object to enforce parameter types
|
// kept as blank for now, as we are using a composition and only using this object to enforce parameter types
|
||||||
}
|
}
|
||||||
@@ -160,6 +160,22 @@ namespace Barotrauma.Abilities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AbilityApplyTreatment : AbilityObject, IAbilityCharacter, IAbilityItem
|
||||||
|
{
|
||||||
|
public Character Character { get; set; }
|
||||||
|
|
||||||
|
public Character User { get; set; }
|
||||||
|
|
||||||
|
public Item Item { get; set; }
|
||||||
|
|
||||||
|
public AbilityApplyTreatment(Character user, Character target, Item item)
|
||||||
|
{
|
||||||
|
Character = target;
|
||||||
|
User = user;
|
||||||
|
Item = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AbilityAttackResult : AbilityObject, IAbilityAttackResult
|
class AbilityAttackResult : AbilityObject, IAbilityAttackResult
|
||||||
{
|
{
|
||||||
public AttackResult AttackResult { get; set; }
|
public AttackResult AttackResult { get; set; }
|
||||||
|
|||||||
+1
-10
@@ -62,7 +62,7 @@ namespace Barotrauma.Abilities
|
|||||||
|
|
||||||
protected virtual void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
protected virtual void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError($"Ability {this} does not have an implementation for VerifyState! This ability does not work in interval ability groups.");
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}: Ability {this} does not have an implementation for VerifyState! This ability does not work in interval ability groups.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyAbilityEffect(AbilityObject abilityObject)
|
public void ApplyAbilityEffect(AbilityObject abilityObject)
|
||||||
@@ -128,14 +128,5 @@ namespace Barotrauma.Abilities
|
|||||||
DebugConsole.AddWarning("Instantiated " + characterAbility + " for talent " + characterAbilityGroup.CharacterTalent.DebugIdentifier);
|
DebugConsole.AddWarning("Instantiated " + characterAbility + " for talent " + characterAbilityGroup.CharacterTalent.DebugIdentifier);
|
||||||
return characterAbility;
|
return characterAbility;
|
||||||
}
|
}
|
||||||
public static AbilityFlags ParseFlagType(string flagTypeString, string debugIdentifier)
|
|
||||||
{
|
|
||||||
AbilityFlags flagType = AbilityFlags.None;
|
|
||||||
if (!Enum.TryParse(flagTypeString, true, out flagType))
|
|
||||||
{
|
|
||||||
DebugConsole.ThrowError("Invalid flag type type \"" + flagTypeString + "\" in CharacterTalent (" + debugIdentifier + ")");
|
|
||||||
}
|
|
||||||
return flagType;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
namespace Barotrauma.Abilities
|
||||||
|
{
|
||||||
|
class CharacterAbilityGiveAffliction : CharacterAbility
|
||||||
|
{
|
||||||
|
private readonly string afflictionId;
|
||||||
|
private readonly float strength;
|
||||||
|
private readonly string multiplyStrengthBySkill;
|
||||||
|
private readonly bool setValue;
|
||||||
|
|
||||||
|
public CharacterAbilityGiveAffliction(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
|
{
|
||||||
|
afflictionId = abilityElement.GetAttributeString("afflictionid", abilityElement.GetAttributeString("affliction", string.Empty));
|
||||||
|
strength = abilityElement.GetAttributeFloat("strength", 0f);
|
||||||
|
multiplyStrengthBySkill = abilityElement.GetAttributeString("multiplystrengthbyskill", string.Empty);
|
||||||
|
setValue = abilityElement.GetAttributeBool("setvalue", false);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(afflictionId))
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError("Error in CharacterAbilityGiveAffliction - affliction identifier not set.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||||
|
{
|
||||||
|
if (abilityObject is IAbilityCharacter character)
|
||||||
|
{
|
||||||
|
var afflictionPrefab = AfflictionPrefab.Prefabs.Find(a => a.Identifier.Equals(afflictionId, System.StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (afflictionPrefab == null)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in CharacterAbilityGiveAffliction - could not find an affliction with the identifier \"{afflictionId}\".");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float strength = this.strength;
|
||||||
|
if (!string.IsNullOrEmpty(multiplyStrengthBySkill))
|
||||||
|
{
|
||||||
|
strength *= Character.GetSkillLevel(multiplyStrengthBySkill);
|
||||||
|
}
|
||||||
|
character.Character.CharacterHealth.ApplyAffliction(null, afflictionPrefab.Instantiate(strength), allowStacking: !setValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -4,12 +4,12 @@ namespace Barotrauma.Abilities
|
|||||||
{
|
{
|
||||||
class CharacterAbilityGiveFlag : CharacterAbility
|
class CharacterAbilityGiveFlag : CharacterAbility
|
||||||
{
|
{
|
||||||
private AbilityFlags abilityFlag;
|
private readonly AbilityFlags abilityFlag;
|
||||||
|
|
||||||
// this and resistance giving should probably be moved directly to charactertalent attributes, as they don't need to interact with either ability group types
|
// this and resistance giving should probably be moved directly to charactertalent attributes, as they don't need to interact with either ability group types
|
||||||
public CharacterAbilityGiveFlag(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityGiveFlag(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
abilityFlag = ParseFlagType(abilityElement.GetAttributeString("flagtype", ""), CharacterTalent.DebugIdentifier);
|
abilityFlag = CharacterAbilityGroup.ParseFlagType(abilityElement.GetAttributeString("flagtype", ""), CharacterTalent.DebugIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void InitializeAbility(bool addingFirstTime)
|
public override void InitializeAbility(bool addingFirstTime)
|
||||||
|
|||||||
+4
-4
@@ -7,20 +7,20 @@ namespace Barotrauma.Abilities
|
|||||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||||
|
|
||||||
private readonly int amount;
|
private readonly int amount;
|
||||||
private readonly StatTypes scalingStatType;
|
private readonly string scalingStatIdentifier;
|
||||||
|
|
||||||
public CharacterAbilityGiveMoney(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityGiveMoney(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
amount = abilityElement.GetAttributeInt("amount", 0);
|
amount = abilityElement.GetAttributeInt("amount", 0);
|
||||||
scalingStatType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("scalingstattype", "None"), CharacterTalent.DebugIdentifier);
|
scalingStatIdentifier = abilityElement.GetAttributeString("scalingstatidentifier", string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyEffectSpecific(Character targetCharacter)
|
private void ApplyEffectSpecific(Character targetCharacter)
|
||||||
{
|
{
|
||||||
float multiplier = 1f;
|
float multiplier = 1f;
|
||||||
if (scalingStatType != StatTypes.None)
|
if (!string.IsNullOrEmpty(scalingStatIdentifier))
|
||||||
{
|
{
|
||||||
multiplier = 0 + Character.Info.GetSavedStatValue(scalingStatType);
|
multiplier = 0 + Character.Info.GetSavedStatValue(StatTypes.None, scalingStatIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
targetCharacter.GiveMoney((int)(multiplier * amount));
|
targetCharacter.GiveMoney((int)(multiplier * amount));
|
||||||
|
|||||||
+4
-5
@@ -11,7 +11,6 @@ namespace Barotrauma.Abilities
|
|||||||
private readonly float maxValue;
|
private readonly float maxValue;
|
||||||
private readonly bool targetAllies;
|
private readonly bool targetAllies;
|
||||||
private readonly bool removeOnDeath;
|
private readonly bool removeOnDeath;
|
||||||
private readonly bool removeAfterRound;
|
|
||||||
private readonly bool giveOnAddingFirstTime;
|
private readonly bool giveOnAddingFirstTime;
|
||||||
private readonly bool setValue;
|
private readonly bool setValue;
|
||||||
|
|
||||||
@@ -22,12 +21,12 @@ namespace Barotrauma.Abilities
|
|||||||
public CharacterAbilityGivePermanentStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityGivePermanentStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
statIdentifier = abilityElement.GetAttributeString("statidentifier", "").ToLowerInvariant();
|
statIdentifier = abilityElement.GetAttributeString("statidentifier", "").ToLowerInvariant();
|
||||||
statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier);
|
string statTypeName = abilityElement.GetAttributeString("stattype", string.Empty);
|
||||||
|
statType = string.IsNullOrEmpty(statTypeName) ? StatTypes.None : CharacterAbilityGroup.ParseStatType(statTypeName, CharacterTalent.DebugIdentifier);
|
||||||
value = abilityElement.GetAttributeFloat("value", 0f);
|
value = abilityElement.GetAttributeFloat("value", 0f);
|
||||||
maxValue = abilityElement.GetAttributeFloat("maxvalue", float.MaxValue);
|
maxValue = abilityElement.GetAttributeFloat("maxvalue", float.MaxValue);
|
||||||
targetAllies = abilityElement.GetAttributeBool("targetallies", false);
|
targetAllies = abilityElement.GetAttributeBool("targetallies", false);
|
||||||
removeOnDeath = abilityElement.GetAttributeBool("removeondeath", true);
|
removeOnDeath = abilityElement.GetAttributeBool("removeondeath", true);
|
||||||
removeAfterRound = abilityElement.GetAttributeBool("removeafterround", false);
|
|
||||||
giveOnAddingFirstTime = abilityElement.GetAttributeBool("giveonaddingfirsttime", characterAbilityGroup.AbilityEffectType == AbilityEffectType.None);
|
giveOnAddingFirstTime = abilityElement.GetAttributeBool("giveonaddingfirsttime", characterAbilityGroup.AbilityEffectType == AbilityEffectType.None);
|
||||||
setValue = abilityElement.GetAttributeBool("setvalue", false);
|
setValue = abilityElement.GetAttributeBool("setvalue", false);
|
||||||
}
|
}
|
||||||
@@ -54,11 +53,11 @@ namespace Barotrauma.Abilities
|
|||||||
{
|
{
|
||||||
if (targetAllies)
|
if (targetAllies)
|
||||||
{
|
{
|
||||||
Character.GetFriendlyCrew(Character).ForEach(c => c?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath, removeAfterRound, maxValue, setValue));
|
Character.GetFriendlyCrew(Character).ForEach(c => c?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath, maxValue: maxValue, setValue: setValue));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Character?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath, removeAfterRound, maxValue, setValue);
|
Character?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath, maxValue: maxValue, setValue: setValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -5,13 +5,13 @@ namespace Barotrauma.Abilities
|
|||||||
{
|
{
|
||||||
class CharacterAbilityModifyFlag : CharacterAbility
|
class CharacterAbilityModifyFlag : CharacterAbility
|
||||||
{
|
{
|
||||||
private AbilityFlags abilityFlag;
|
private readonly AbilityFlags abilityFlag;
|
||||||
|
|
||||||
private bool lastState;
|
private bool lastState;
|
||||||
|
|
||||||
public CharacterAbilityModifyFlag(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
public CharacterAbilityModifyFlag(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||||
{
|
{
|
||||||
abilityFlag = ParseFlagType(abilityElement.GetAttributeString("flagtype", ""), CharacterTalent.DebugIdentifier);
|
abilityFlag = CharacterAbilityGroup.ParseFlagType(abilityElement.GetAttributeString("flagtype", ""), CharacterTalent.DebugIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||||
|
|||||||
+10
@@ -208,5 +208,15 @@ namespace Barotrauma.Abilities
|
|||||||
|
|
||||||
return afflictions;
|
return afflictions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AbilityFlags ParseFlagType(string flagTypeString, string debugIdentifier)
|
||||||
|
{
|
||||||
|
AbilityFlags flagType = AbilityFlags.None;
|
||||||
|
if (!Enum.TryParse(flagTypeString, true, out flagType))
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError("Invalid flag type type \"" + flagTypeString + "\" in CharacterTalent (" + debugIdentifier + ")");
|
||||||
|
}
|
||||||
|
return flagType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
ConfigElement = element;
|
ConfigElement = element;
|
||||||
|
|
||||||
string jobIdentifier = element.GetAttributeString("jobidentifier", "");
|
string jobIdentifier = element.GetAttributeString("jobidentifier", "").ToLowerInvariant();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(jobIdentifier))
|
if (string.IsNullOrEmpty(jobIdentifier))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -863,6 +863,60 @@ namespace Barotrauma
|
|||||||
};
|
};
|
||||||
}, isCheat: true));
|
}, isCheat: true));
|
||||||
|
|
||||||
|
commands.Add(new Command("unlocktalents", "unlocktalents [all/[jobname]]: give the controlled characters all the talents of the specified class", (string[] args) =>
|
||||||
|
{
|
||||||
|
if (Character.Controlled == null) { return; }
|
||||||
|
if (args.Length == 0 || args[0].Equals("all", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
foreach (var talentTree in TalentTree.JobTalentTrees)
|
||||||
|
{
|
||||||
|
foreach (var subTree in talentTree.Value.TalentSubTrees)
|
||||||
|
{
|
||||||
|
foreach (var option in subTree.TalentOptionStages)
|
||||||
|
{
|
||||||
|
foreach (var talent in option.Talents)
|
||||||
|
{
|
||||||
|
Character.Controlled.GiveTalent(talent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var job = JobPrefab.Prefabs.Find(jp => jp.Name != null && jp.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (job == null)
|
||||||
|
{
|
||||||
|
ThrowError($"Failed to find the job \"{args[0]}\".");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!TalentTree.JobTalentTrees.TryGetValue(job.Identifier, out TalentTree talentTree))
|
||||||
|
{
|
||||||
|
ThrowError($"No talents configured for the job \"{args[0]}\".");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach (var subTree in talentTree.TalentSubTrees)
|
||||||
|
{
|
||||||
|
foreach (var option in subTree.TalentOptionStages)
|
||||||
|
{
|
||||||
|
foreach (var talent in option.Talents)
|
||||||
|
{
|
||||||
|
Character.Controlled.GiveTalent(talent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
List<string> availableArgs = new List<string>() { "All" };
|
||||||
|
availableArgs.AddRange(JobPrefab.Prefabs.Select(j => j.Name));
|
||||||
|
return new string[][]
|
||||||
|
{
|
||||||
|
availableArgs.ToArray()
|
||||||
|
};
|
||||||
|
}, isCheat: true));
|
||||||
|
|
||||||
commands.Add(new Command("giveexperience", "giveexperience [amount] [character]: Give experience to character.", (string[] args) =>
|
commands.Add(new Command("giveexperience", "giveexperience [amount] [character]: Give experience to character.", (string[] args) =>
|
||||||
{
|
{
|
||||||
if (args.Length < 1)
|
if (args.Length < 1)
|
||||||
@@ -892,7 +946,7 @@ namespace Barotrauma
|
|||||||
}, isCheat: true, getValidArgs: () =>
|
}, isCheat: true, getValidArgs: () =>
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new string[] { "100" },
|
new string[] { "100" },
|
||||||
Character.CharacterList.Select(c => c.Name).Distinct().ToArray(),
|
Character.CharacterList.Select(c => c.Name).Distinct().ToArray(),
|
||||||
};
|
};
|
||||||
@@ -2041,6 +2095,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
spawnLocation = args[^2];
|
spawnLocation = args[^2];
|
||||||
if (!int.TryParse(args[^1], NumberStyles.Any, CultureInfo.InvariantCulture, out amount)) { amount = 1; }
|
if (!int.TryParse(args[^1], NumberStyles.Any, CultureInfo.InvariantCulture, out amount)) { amount = 1; }
|
||||||
|
amount = Math.Min(amount, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (spawnLocation)
|
switch (spawnLocation)
|
||||||
|
|||||||
@@ -22,9 +22,10 @@
|
|||||||
OnSevered = 17,
|
OnSevered = 17,
|
||||||
OnProduceSpawned = 18,
|
OnProduceSpawned = 18,
|
||||||
OnOpen = 19, OnClose = 20,
|
OnOpen = 19, OnClose = 20,
|
||||||
OnDeath = OnBroken,
|
OnSpawn = 21,
|
||||||
OnSuccess,
|
OnSuccess = 22,
|
||||||
OnAbility,
|
OnAbility = 23,
|
||||||
|
OnDeath = OnBroken
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AbilityEffectType
|
public enum AbilityEffectType
|
||||||
@@ -62,7 +63,10 @@
|
|||||||
OnItemDeconstructedInventory,
|
OnItemDeconstructedInventory,
|
||||||
OnStopTinkering,
|
OnStopTinkering,
|
||||||
OnItemPicked,
|
OnItemPicked,
|
||||||
|
OnGeneticMaterialCombinedOrRefined,
|
||||||
|
OnCrewGeneticMaterialCombinedOrRefined,
|
||||||
AfterSubmarineAttacked,
|
AfterSubmarineAttacked,
|
||||||
|
OnApplyTreatment,
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum StatTypes
|
public enum StatTypes
|
||||||
@@ -100,7 +104,8 @@
|
|||||||
RepairToolStructureRepairMultiplier,
|
RepairToolStructureRepairMultiplier,
|
||||||
RepairToolStructureDamageMultiplier,
|
RepairToolStructureDamageMultiplier,
|
||||||
RepairToolDeattachTimeMultiplier,
|
RepairToolDeattachTimeMultiplier,
|
||||||
MaxRepairConditionMultiplier,
|
MaxRepairConditionMultiplierMechanical,
|
||||||
|
MaxRepairConditionMultiplierElectrical,
|
||||||
IncreaseFabricationQuality,
|
IncreaseFabricationQuality,
|
||||||
GeneticMaterialRefineBonus,
|
GeneticMaterialRefineBonus,
|
||||||
GeneticMaterialTaintedProbabilityReductionOnCombine,
|
GeneticMaterialTaintedProbabilityReductionOnCombine,
|
||||||
@@ -114,11 +119,9 @@
|
|||||||
MissionMoneyGainMultiplier,
|
MissionMoneyGainMultiplier,
|
||||||
ExperienceGainMultiplier,
|
ExperienceGainMultiplier,
|
||||||
MissionExperienceGainMultiplier,
|
MissionExperienceGainMultiplier,
|
||||||
// these should be deprecated and moved to their own implementation, no sense making them share space with stat values
|
ExtraSpecialSalesCount,
|
||||||
Coauthor,
|
ApplyTreatmentsOnSelfFraction,
|
||||||
WarriorPoetMissionRuns,
|
MaxAttachableCount,
|
||||||
WarriorPoetEnemiesKilled,
|
|
||||||
QuickfixRepairCount,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AbilityFlags
|
public enum AbilityFlags
|
||||||
@@ -134,6 +137,8 @@
|
|||||||
GainSkillPastMaximum,
|
GainSkillPastMaximum,
|
||||||
RetainExperienceForNewCharacter,
|
RetainExperienceForNewCharacter,
|
||||||
AllowSecondOrderedTarget,
|
AllowSecondOrderedTarget,
|
||||||
|
PowerfulCPR,
|
||||||
|
AlwaysStayConscious,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,11 +226,11 @@ namespace Barotrauma
|
|||||||
List<Item> potentialItems = SpawnLocation switch
|
List<Item> potentialItems = SpawnLocation switch
|
||||||
{
|
{
|
||||||
SpawnLocationType.MainSub => Item.ItemList.FindAll(it => it.Submarine == Submarine.MainSub),
|
SpawnLocationType.MainSub => Item.ItemList.FindAll(it => it.Submarine == Submarine.MainSub),
|
||||||
SpawnLocationType.MainPath => Item.ItemList.FindAll(it => it.Submarine == null && it.ParentRuin == null),
|
SpawnLocationType.MainPath => Item.ItemList.FindAll(it => it.Submarine == null),
|
||||||
SpawnLocationType.Outpost => Item.ItemList.FindAll(it => it.Submarine != null && it.Submarine.Info.IsOutpost),
|
SpawnLocationType.Outpost => Item.ItemList.FindAll(it => it.Submarine?.Info != null && it.Submarine.Info.IsOutpost),
|
||||||
SpawnLocationType.Wreck => Item.ItemList.FindAll(it => it.Submarine != null && it.Submarine.Info.IsWreck),
|
SpawnLocationType.Wreck => Item.ItemList.FindAll(it => it.Submarine?.Info != null && it.Submarine.Info.IsWreck),
|
||||||
SpawnLocationType.Ruin => Item.ItemList.FindAll(it => it.ParentRuin != null),
|
SpawnLocationType.Ruin => Item.ItemList.FindAll(it => it.Submarine?.Info != null && it.Submarine.Info.IsRuin),
|
||||||
SpawnLocationType.BeaconStation => Item.ItemList.FindAll(it => it.Submarine != null && it.Submarine.Info.IsBeacon),
|
SpawnLocationType.BeaconStation => Item.ItemList.FindAll(it => it.Submarine?.Info != null && it.Submarine.Info.IsBeacon),
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -252,11 +252,11 @@ namespace Barotrauma
|
|||||||
List<WayPoint> potentialSpawnPoints = spawnLocation switch
|
List<WayPoint> potentialSpawnPoints = spawnLocation switch
|
||||||
{
|
{
|
||||||
SpawnLocationType.MainSub => WayPoint.WayPointList.FindAll(wp => wp.Submarine == Submarine.MainSub && wp.CurrentHull != null),
|
SpawnLocationType.MainSub => WayPoint.WayPointList.FindAll(wp => wp.Submarine == Submarine.MainSub && wp.CurrentHull != null),
|
||||||
SpawnLocationType.MainPath => WayPoint.WayPointList.FindAll(wp => wp.Submarine == null && wp.ParentRuin == null),
|
SpawnLocationType.MainPath => WayPoint.WayPointList.FindAll(wp => wp.Submarine == null),
|
||||||
SpawnLocationType.Outpost => WayPoint.WayPointList.FindAll(wp => wp.Submarine != null && wp.CurrentHull != null && wp.Submarine.Info.IsOutpost),
|
SpawnLocationType.Outpost => WayPoint.WayPointList.FindAll(wp => wp.Submarine?.Info != null && wp.CurrentHull != null && wp.Submarine.Info.IsOutpost),
|
||||||
SpawnLocationType.Wreck => WayPoint.WayPointList.FindAll(wp => wp.Submarine != null && wp.Submarine.Info.IsWreck),
|
SpawnLocationType.Wreck => WayPoint.WayPointList.FindAll(wp => wp.Submarine?.Info != null && wp.Submarine.Info.IsWreck),
|
||||||
SpawnLocationType.Ruin => WayPoint.WayPointList.FindAll(wp => wp.ParentRuin != null),
|
SpawnLocationType.Ruin => WayPoint.WayPointList.FindAll(wp => wp.Submarine?.Info != null && wp.Submarine.Info.IsRuin),
|
||||||
SpawnLocationType.BeaconStation => WayPoint.WayPointList.FindAll(wp => wp.Submarine != null && wp.Submarine.Info.IsBeacon),
|
SpawnLocationType.BeaconStation => WayPoint.WayPointList.FindAll(wp => wp.Submarine?.Info != null && wp.Submarine.Info.IsBeacon),
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -981,6 +981,7 @@ namespace Barotrauma
|
|||||||
return false;
|
return false;
|
||||||
case SubmarineType.Wreck:
|
case SubmarineType.Wreck:
|
||||||
case SubmarineType.BeaconStation:
|
case SubmarineType.BeaconStation:
|
||||||
|
case SubmarineType.Ruin:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
using Barotrauma.Extensions;
|
||||||
|
using Barotrauma.RuinGeneration;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Barotrauma
|
||||||
|
{
|
||||||
|
partial class AlienRuinMission : Mission
|
||||||
|
{
|
||||||
|
private readonly string[] targetItemIdentifiers;
|
||||||
|
private readonly string[] targetEnemyIdentifiers;
|
||||||
|
private readonly int minEnemyCount;
|
||||||
|
private readonly HashSet<Entity> existingTargets = new HashSet<Entity>();
|
||||||
|
private readonly HashSet<Character> spawnedTargets = new HashSet<Character>();
|
||||||
|
private readonly HashSet<Entity> allTargets = new HashSet<Entity>();
|
||||||
|
|
||||||
|
private Ruin TargetRuin { get; set; }
|
||||||
|
|
||||||
|
public override IEnumerable<Vector2> SonarPositions
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (State == 0)
|
||||||
|
{
|
||||||
|
return allTargets.Where(t => (t is Item i && !IsItemDestroyed(i)) || (t is Character c && !IsEnemyDefeated(c))).Select(t => t.WorldPosition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<Vector2>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlienRuinMission(MissionPrefab prefab, Location[] locations, Submarine sub) : base(prefab, locations, sub)
|
||||||
|
{
|
||||||
|
targetItemIdentifiers = prefab.ConfigElement.GetAttributeStringArray("targetitems", new string[0], convertToLowerInvariant: true);
|
||||||
|
targetEnemyIdentifiers = prefab.ConfigElement.GetAttributeStringArray("targetenemies", new string[0], convertToLowerInvariant: true);
|
||||||
|
minEnemyCount = prefab.ConfigElement.GetAttributeInt("minenemycount", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void StartMissionSpecific(Level level)
|
||||||
|
{
|
||||||
|
existingTargets.Clear();
|
||||||
|
spawnedTargets.Clear();
|
||||||
|
allTargets.Clear();
|
||||||
|
if (IsClient) { return; }
|
||||||
|
TargetRuin = Level.Loaded?.Ruins?.GetRandom(randSync: Rand.RandSync.Server);
|
||||||
|
if (TargetRuin == null)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Failed to initialize an Alien Ruin mission (\"{Prefab.Identifier}\"): level contains no alien ruins");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (targetItemIdentifiers.Length < 1 && targetEnemyIdentifiers.Length < 1)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Failed to initialize an Alien Ruin mission (\"{Prefab.Identifier}\"): no target identifiers set in the mission definition");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach (var item in Item.ItemList)
|
||||||
|
{
|
||||||
|
if (!targetItemIdentifiers.Contains(item.Prefab.Identifier)) { continue; }
|
||||||
|
if (item.Submarine != TargetRuin.Submarine) { continue; }
|
||||||
|
existingTargets.Add(item);
|
||||||
|
allTargets.Add(item);
|
||||||
|
}
|
||||||
|
int existingEnemyCount = 0;
|
||||||
|
foreach (var character in Character.CharacterList)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(character.SpeciesName)) { continue; }
|
||||||
|
if (!targetEnemyIdentifiers.Contains(character.SpeciesName.ToLowerInvariant())) { continue; }
|
||||||
|
if (character.Submarine != TargetRuin.Submarine) { continue; }
|
||||||
|
existingTargets.Add(character);
|
||||||
|
allTargets.Add(character);
|
||||||
|
existingEnemyCount++;
|
||||||
|
}
|
||||||
|
if (existingEnemyCount < minEnemyCount)
|
||||||
|
{
|
||||||
|
var enemyPrefabs = new HashSet<CharacterPrefab>();
|
||||||
|
foreach (string identifier in targetEnemyIdentifiers)
|
||||||
|
{
|
||||||
|
var prefab = CharacterPrefab.FindBySpeciesName(identifier);
|
||||||
|
if (prefab != null)
|
||||||
|
{
|
||||||
|
enemyPrefabs.Add(prefab);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): could not find a character prefab with the species \"{identifier}\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enemyPrefabs.None())
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): no enemy species defined that could be used to spawn more ({minEnemyCount - existingEnemyCount}) enemies");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < (minEnemyCount - existingEnemyCount); i++)
|
||||||
|
{
|
||||||
|
var prefab = enemyPrefabs.GetRandom();
|
||||||
|
var spawnPos = TargetRuin.Submarine.GetWaypoints(false).GetRandom(w => w.CurrentHull != null)?.WorldPosition;
|
||||||
|
if (!spawnPos.HasValue)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in an Alien Ruin mission (\"{Prefab.Identifier}\"): no valid spawn positions could be found for the additional ({minEnemyCount - existingEnemyCount}) enemies to be spawned");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var newEnemy = Character.Create(prefab.Identifier, spawnPos.Value, ToolBox.RandomSeed(8), createNetworkEvent: false);
|
||||||
|
spawnedTargets.Add(newEnemy);
|
||||||
|
allTargets.Add(newEnemy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
DebugConsole.NewMessage("********** CLEAR RUIN MISSION INFO **********");
|
||||||
|
DebugConsole.NewMessage($"Existing item targets: {existingTargets.Count - existingEnemyCount}");
|
||||||
|
DebugConsole.NewMessage($"Existing enemy targets: {existingEnemyCount}");
|
||||||
|
DebugConsole.NewMessage($"Spawned enemy targets: {spawnedTargets.Count}");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateMissionSpecific(float deltaTime)
|
||||||
|
{
|
||||||
|
if (IsClient) { return; }
|
||||||
|
switch (State)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if (!AllTargetsEliminated()) { return; }
|
||||||
|
State = 1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (!Submarine.MainSub.AtEndExit && !Submarine.MainSub.AtStartExit) { return; }
|
||||||
|
State = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AllTargetsEliminated()
|
||||||
|
{
|
||||||
|
foreach (var target in allTargets)
|
||||||
|
{
|
||||||
|
if (target is Item targetItem)
|
||||||
|
{
|
||||||
|
if (!IsItemDestroyed(targetItem))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (target is Character targetEnemy)
|
||||||
|
{
|
||||||
|
if (!IsEnemyDefeated(targetEnemy))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in Alien Ruin mission (\"{Prefab.Identifier}\"): unexpected target of type {target?.GetType()?.ToString()}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsItemDestroyed(Item item) => item == null || item.Removed || item.Condition <= 0.0f;
|
||||||
|
|
||||||
|
private bool IsEnemyDefeated(Character enemy) => enemy == null ||enemy.Removed || enemy.IsDead;
|
||||||
|
|
||||||
|
public override void End()
|
||||||
|
{
|
||||||
|
if (AllTargetsEliminated())
|
||||||
|
{
|
||||||
|
GiveReward();
|
||||||
|
completed = true;
|
||||||
|
}
|
||||||
|
failed = !completed && state > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user