Release 1.9.7.0 - Summer Update 2025
This commit is contained in:
@@ -4396,11 +4396,11 @@ namespace Barotrauma
|
||||
else if (canAttackDoors && HasValidPath())
|
||||
{
|
||||
var door = PathSteering.CurrentPath.CurrentNode?.ConnectedDoor ?? PathSteering.CurrentPath.NextNode?.ConnectedDoor;
|
||||
if (door is { CanBeTraversed: false } && !door.HasAccess(Character))
|
||||
if (door is { CanBeTraversed: false } && !door.HasAccess(Character) && door.Item.AiTarget is { } doorAiTarget)
|
||||
{
|
||||
if (SelectedAiTarget != door.Item.AiTarget || State != AIState.Attack)
|
||||
if (SelectedAiTarget != doorAiTarget || State != AIState.Attack)
|
||||
{
|
||||
SelectTarget(door.Item.AiTarget, CurrentTargetMemory.Priority);
|
||||
SelectTarget(doorAiTarget, CurrentTargetMemory.Priority);
|
||||
State = AIState.Attack;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -787,6 +787,13 @@ namespace Barotrauma
|
||||
foreach (Item item in Character.HeldItems)
|
||||
{
|
||||
if (item == null || !item.IsInteractable(Character)) { continue; }
|
||||
if (!item.UnequipAutomatically) { continue; }
|
||||
//NPC set to operate the item they're holding, don't put it away
|
||||
if (ObjectiveManager.CurrentObjective is AIObjectiveOperateItem operateItem &&
|
||||
(operateItem.OperateTarget == item || operateItem.Component?.Item == item))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (Character.TryPutItemInAnySlot(item)) { continue; }
|
||||
if (Character.TryPutItemInBag(item)) { continue; }
|
||||
if (item.HasTag(Tags.Weapon))
|
||||
|
||||
+1
@@ -69,6 +69,7 @@ namespace Barotrauma
|
||||
}
|
||||
var getItemObjective = new AIObjectiveGetItem(character, gearTag, objectiveManager, equip: true)
|
||||
{
|
||||
IsFindDivingGearSubObjective = true,
|
||||
AllowStealing = HumanAIController.NeedsDivingGear(character.CurrentHull, out _),
|
||||
AllowToFindDivingGear = false,
|
||||
AllowDangerousPressure = true,
|
||||
|
||||
+8
@@ -49,6 +49,13 @@ namespace Barotrauma
|
||||
public const float DefaultReach = 100;
|
||||
public const float MaxReach = 150;
|
||||
|
||||
/// <summary>
|
||||
/// Is the goal of this objective to get diving gear (i.e. has it been created by <see cref="AIObjectiveFindDivingGear"/>)?
|
||||
/// If so, the objective won't attempt to create another objective if the path requires diving gear
|
||||
/// (wouldn't make sense to start looking for diving gear so the bot can get to a room they're trying to get diving gear from!)
|
||||
/// </summary>
|
||||
public bool IsFindDivingGearSubObjective;
|
||||
|
||||
public bool AllowToFindDivingGear { get; set; } = true;
|
||||
public bool MustBeSpecificItem { get; set; }
|
||||
|
||||
@@ -378,6 +385,7 @@ namespace Barotrauma
|
||||
{
|
||||
return new AIObjectiveGoTo(moveToTarget, character, objectiveManager, repeat: false, getDivingGearIfNeeded: AllowToFindDivingGear, closeEnough: DefaultReach)
|
||||
{
|
||||
IsFindDivingGearSubObjective = IsFindDivingGearSubObjective,
|
||||
// If the root container changes, the item is no longer where it was (taken by someone -> need to find another item)
|
||||
AbortCondition = obj => targetItem == null || (targetItem.GetRootInventoryOwner() is Entity owner && owner != moveToTarget && owner != character),
|
||||
SpeakIfFails = false,
|
||||
|
||||
+78
-67
@@ -14,6 +14,13 @@ namespace Barotrauma
|
||||
|
||||
public override bool KeepDivingGearOn => GetTargetHull() == null;
|
||||
|
||||
/// <summary>
|
||||
/// Is the goal of this objective to get diving gear (i.e. has it been created by <see cref="AIObjectiveFindDivingGear"/>)?
|
||||
/// If so, the objective won't attempt to create another objective if the path requires diving gear
|
||||
/// (wouldn't make sense to start looking for diving gear so the bot can get to a room they're trying to get diving gear from!)
|
||||
/// </summary>
|
||||
public bool IsFindDivingGearSubObjective;
|
||||
|
||||
private AIObjectiveFindDivingGear findDivingGear;
|
||||
private readonly bool repeat;
|
||||
//how long until the path to the target is declared unreachable
|
||||
@@ -379,85 +386,89 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
if (Abandon) { return; }
|
||||
bool needsDivingSuit = (!isInside || hasOutdoorNodes) && !character.IsImmuneToPressure;
|
||||
bool tryToGetDivingGear = needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit);
|
||||
bool tryToGetDivingSuit = needsDivingSuit;
|
||||
Character followTarget = Target as Character;
|
||||
if (Mimic && !character.IsImmuneToPressure)
|
||||
|
||||
if (!IsFindDivingGearSubObjective)
|
||||
{
|
||||
if (HumanAIController.HasDivingSuit(followTarget))
|
||||
bool needsDivingSuit = (!isInside || hasOutdoorNodes) && !character.IsImmuneToPressure;
|
||||
bool tryToGetDivingGear = needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit);
|
||||
bool tryToGetDivingSuit = needsDivingSuit;
|
||||
Character followTarget = Target as Character;
|
||||
if (Mimic && !character.IsImmuneToPressure)
|
||||
{
|
||||
tryToGetDivingGear = true;
|
||||
tryToGetDivingSuit = true;
|
||||
if (HumanAIController.HasDivingSuit(followTarget))
|
||||
{
|
||||
tryToGetDivingGear = true;
|
||||
tryToGetDivingSuit = true;
|
||||
}
|
||||
else if (HumanAIController.HasDivingMask(followTarget) && character.CharacterHealth.OxygenLowResistance < 1)
|
||||
{
|
||||
tryToGetDivingGear = true;
|
||||
}
|
||||
}
|
||||
else if (HumanAIController.HasDivingMask(followTarget) && character.CharacterHealth.OxygenLowResistance < 1)
|
||||
bool needsEquipment = false;
|
||||
float minOxygen = AIObjectiveFindDivingGear.GetMinOxygen(character);
|
||||
if (tryToGetDivingSuit)
|
||||
{
|
||||
tryToGetDivingGear = true;
|
||||
needsEquipment = !HumanAIController.HasDivingSuit(character, minOxygen, requireSuitablePressureProtection: !objectiveManager.FailedToFindDivingGearForDepth);
|
||||
}
|
||||
}
|
||||
bool needsEquipment = false;
|
||||
float minOxygen = AIObjectiveFindDivingGear.GetMinOxygen(character);
|
||||
if (tryToGetDivingSuit)
|
||||
{
|
||||
needsEquipment = !HumanAIController.HasDivingSuit(character, minOxygen, requireSuitablePressureProtection: !objectiveManager.FailedToFindDivingGearForDepth);
|
||||
}
|
||||
else if (tryToGetDivingGear)
|
||||
{
|
||||
needsEquipment = !HumanAIController.HasDivingGear(character, minOxygen);
|
||||
}
|
||||
if (!getDivingGearIfNeeded)
|
||||
{
|
||||
if (needsEquipment)
|
||||
else if (tryToGetDivingGear)
|
||||
{
|
||||
// Don't try to reach the target without proper equipment.
|
||||
Abandon = true;
|
||||
return;
|
||||
needsEquipment = !HumanAIController.HasDivingGear(character, minOxygen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (character.LockHands)
|
||||
if (!getDivingGearIfNeeded)
|
||||
{
|
||||
cantFindDivingGear = true;
|
||||
if (needsEquipment)
|
||||
{
|
||||
// Don't try to reach the target without proper equipment.
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cantFindDivingGear && needsDivingSuit)
|
||||
else
|
||||
{
|
||||
// Don't try to reach the target without a suit because it's lethal.
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (needsEquipment && !cantFindDivingGear)
|
||||
{
|
||||
SteeringManager.Reset();
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: tryToGetDivingSuit, objectiveManager),
|
||||
onAbandon: () =>
|
||||
{
|
||||
cantFindDivingGear = true;
|
||||
if (needsDivingSuit)
|
||||
if (character.LockHands)
|
||||
{
|
||||
cantFindDivingGear = true;
|
||||
}
|
||||
if (cantFindDivingGear && needsDivingSuit)
|
||||
{
|
||||
// Don't try to reach the target without a suit because it's lethal.
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (needsEquipment && !cantFindDivingGear)
|
||||
{
|
||||
SteeringManager.Reset();
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: tryToGetDivingSuit, objectiveManager),
|
||||
onAbandon: () =>
|
||||
{
|
||||
// Shouldn't try to reach the target without a suit, because it's lethal.
|
||||
Abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try again without requiring the diving suit (or mask)
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: !tryToGetDivingSuit, objectiveManager),
|
||||
onAbandon: () =>
|
||||
{
|
||||
Abandon = character.CurrentHull != null && (objectiveManager.CurrentOrder != this || Target.Submarine == null);
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
},
|
||||
onCompleted: () =>
|
||||
{
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
});
|
||||
}
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref findDivingGear));
|
||||
return;
|
||||
cantFindDivingGear = true;
|
||||
if (needsDivingSuit)
|
||||
{
|
||||
// Shouldn't try to reach the target without a suit, because it's lethal.
|
||||
Abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try again without requiring the diving suit (or mask)
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: !tryToGetDivingSuit, objectiveManager),
|
||||
onAbandon: () =>
|
||||
{
|
||||
Abandon = character.CurrentHull != null && (objectiveManager.CurrentOrder != this || Target.Submarine == null);
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
},
|
||||
onCompleted: () =>
|
||||
{
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
});
|
||||
}
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref findDivingGear));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (IsDoneFollowing())
|
||||
{
|
||||
OnCompleted();
|
||||
|
||||
@@ -347,7 +347,10 @@ namespace Barotrauma
|
||||
useItemTimer = 0.05f;
|
||||
StartUsingItem();
|
||||
|
||||
if (!allowMovement)
|
||||
//make the character move towards the item they're using...
|
||||
if (!allowMovement &&
|
||||
//...except if they've selected an item that controls the character's direction (e.g. a periscope)
|
||||
character.SelectedSecondaryItem?.GetComponent<Controller>() is not { Direction: Direction.Left or Direction.Right })
|
||||
{
|
||||
TargetMovement = Vector2.Zero;
|
||||
TargetDir = handWorldPos.X > character.WorldPosition.X ? Direction.Right : Direction.Left;
|
||||
|
||||
@@ -4,7 +4,11 @@ namespace Barotrauma
|
||||
{
|
||||
enum CauseOfDeathType
|
||||
{
|
||||
Unknown, Pressure, Suffocation, Drowning, Affliction, Disconnected
|
||||
Unknown, Pressure, Suffocation, Drowning, Affliction, Disconnected,
|
||||
/// <summary>
|
||||
/// Special cause of death type returned by <see cref="Character.CauseOfDeathType"/> when the character is not dead.
|
||||
/// </summary>
|
||||
None
|
||||
}
|
||||
|
||||
class CauseOfDeath
|
||||
|
||||
@@ -589,6 +589,9 @@ namespace Barotrauma
|
||||
|
||||
public Identifier VariantOf => Prefab.VariantOf;
|
||||
|
||||
/// <summary>
|
||||
/// Non-localized name of the character (for characters with info, their name, for monsters, their species). E.g. "Mudraptor_veteran", "John Smith".
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
@@ -597,6 +600,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Localized display name of the character (e.g. "Mudraptor Veteran", "John Smith") - this should generally be used in any the player sees.
|
||||
/// </summary>
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
@@ -1207,6 +1213,11 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be used by mods to check the cause of death of the character using conditionals (e.g. if some <see cref="OnDeath"/> effects should or should not be triggered by certain causes of death).
|
||||
/// </summary>
|
||||
public CauseOfDeathType CauseOfDeathType => CauseOfDeath?.Type ?? CauseOfDeathType.None;
|
||||
|
||||
//can other characters select (= grab) this character
|
||||
public bool CanBeSelected
|
||||
{
|
||||
@@ -2406,7 +2417,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (!CanInteractWith(item)) { continue; }
|
||||
|
||||
if (SelectedItem?.OwnInventory != null && SelectedItem.OwnInventory.CanBePut(item))
|
||||
if (SelectedItem?.OwnInventory != null && !SelectedItem.OwnInventory.Locked && SelectedItem.OwnInventory.CanBePut(item))
|
||||
{
|
||||
SelectedItem.OwnInventory.TryPutItem(item, this);
|
||||
}
|
||||
@@ -5703,6 +5714,7 @@ namespace Barotrauma
|
||||
|
||||
public bool HasRecipeForItem(Identifier recipeIdentifier)
|
||||
{
|
||||
if (GameMain.GameSession != null && GameMain.GameSession.UnlockedRecipes.Contains(recipeIdentifier)) { return true; }
|
||||
return characterTalents.Any(t => t.UnlockedRecipes.Contains(recipeIdentifier));
|
||||
}
|
||||
|
||||
|
||||
@@ -408,6 +408,11 @@ namespace Barotrauma
|
||||
|
||||
public HashSet<Identifier> UnlockedTalents { get; private set; } = new HashSet<Identifier>();
|
||||
|
||||
/// <summary>
|
||||
/// Which of the character's extra talents (talents unlocked from outside their own talent tree) are reset when the talents are resetted using e.g. Mindwipe?
|
||||
/// </summary>
|
||||
public HashSet<Identifier> ResettableExtraTalents { get; private set; } = new HashSet<Identifier>();
|
||||
|
||||
private int talentResetCount;
|
||||
|
||||
/// <summary>
|
||||
@@ -792,14 +797,6 @@ namespace Barotrauma
|
||||
Salary = CalculateSalary();
|
||||
}
|
||||
OriginalName = !string.IsNullOrEmpty(originalName) ? originalName : Name;
|
||||
|
||||
TalentRefundPoints = CharacterConfigElement.GetAttributeInt("refundpoints", 0);
|
||||
|
||||
int loadedLastRewardDistribution = CharacterConfigElement.GetAttributeInt("lastrewarddistribution", -1);
|
||||
if (loadedLastRewardDistribution >= 0)
|
||||
{
|
||||
LastRewardDistribution = Option.Some(loadedLastRewardDistribution);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPersonalityTrait()
|
||||
@@ -1016,10 +1013,22 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
UnlockedTalents.Add(talentIdentifier);
|
||||
if (talentElement.GetAttributeBool("resettable", defaultValue: false))
|
||||
{
|
||||
ResettableExtraTalents.Add(talentIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TalentRefundPoints = infoElement.GetAttributeInt("refundpoints", 0);
|
||||
|
||||
int loadedLastRewardDistribution = infoElement.GetAttributeInt("lastrewarddistribution", -1);
|
||||
if (loadedLastRewardDistribution >= 0)
|
||||
{
|
||||
LastRewardDistribution = Option.Some(loadedLastRewardDistribution);
|
||||
}
|
||||
|
||||
LoadHeadAttachments();
|
||||
}
|
||||
|
||||
@@ -1535,7 +1544,13 @@ namespace Barotrauma
|
||||
if (TalentRefundPoints <= 0) { return; }
|
||||
|
||||
//e.g. talents from endocrine booster or extra talents some special NPC has
|
||||
//stored in a list so we can re-unlock them on the character
|
||||
var talentsFromOutsideTree = GetUnlockedTalentsOutsideTree().ToList();
|
||||
//remove resettable talents, so they DON'T get re-unlocked
|
||||
foreach (var resettableExtraTalent in ResettableExtraTalents)
|
||||
{
|
||||
talentsFromOutsideTree.Remove(resettableExtraTalent);
|
||||
}
|
||||
|
||||
UnlockedTalents.Clear();
|
||||
SavedStatValues.Clear();
|
||||
@@ -1570,6 +1585,9 @@ namespace Barotrauma
|
||||
public void Rename(string newName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(newName)) { return; }
|
||||
|
||||
newName = Networking.Client.SanitizeName(newName);
|
||||
|
||||
// Replace the name tag of any existing id cards or duffel bags
|
||||
foreach (var item in Item.ItemList)
|
||||
{
|
||||
@@ -1673,7 +1691,10 @@ namespace Barotrauma
|
||||
|
||||
foreach (Identifier talentIdentifier in UnlockedTalents)
|
||||
{
|
||||
talentElement.Add(new XElement("Talent", new XAttribute("identifier", talentIdentifier)));
|
||||
talentElement.Add(
|
||||
new XElement("Talent",
|
||||
new XAttribute("identifier", talentIdentifier),
|
||||
new XAttribute("resettable", ResettableExtraTalents.Contains(talentIdentifier))));
|
||||
}
|
||||
|
||||
charElement.Add(savedStatElement);
|
||||
|
||||
+14
@@ -882,6 +882,16 @@ namespace Barotrauma
|
||||
/// Its opacity is controlled by the active effect's MinAfflictionOverlayAlphaMultiplier and MaxAfflictionOverlayAlphaMultiplier
|
||||
/// </summary>
|
||||
public readonly Sprite AfflictionOverlay;
|
||||
|
||||
/// <summary>
|
||||
/// The speed of the affliction overlay animation.
|
||||
/// Only applicable with AfflictionOverlayAnimated, and the overlay has to be a spritesheet so there's something to animate.
|
||||
/// </summary>
|
||||
public float AfflictionOverlayAnimSpeed
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ImmutableDictionary<Identifier, float> TreatmentSuitabilities
|
||||
{
|
||||
@@ -1005,6 +1015,10 @@ namespace Barotrauma
|
||||
case "afflictionoverlay":
|
||||
AfflictionOverlay = new Sprite(subElement);
|
||||
break;
|
||||
case "afflictionoverlayanimated":
|
||||
AfflictionOverlay = new SpriteSheet(subElement);
|
||||
AfflictionOverlayAnimSpeed = subElement.GetAttributeFloat("animspeed", 1.0f);
|
||||
break;
|
||||
case "statvalue":
|
||||
DebugConsole.ThrowError($"Error in affliction \"{Identifier}\" - stat values should be configured inside the affliction's effects.",
|
||||
contentPackage: element.ContentPackage);
|
||||
|
||||
@@ -302,7 +302,7 @@ namespace Barotrauma
|
||||
new List<InvSlotType>(item.GetComponent<Wearable>()?.AllowedSlots ?? item.GetComponent<Pickable>().AllowedSlots) :
|
||||
new List<InvSlotType>(item.AllowedSlots);
|
||||
allowedSlots.Remove(InvSlotType.Any);
|
||||
|
||||
item.UnequipAutomatically = false;
|
||||
character.Inventory.TryPutItem(item, null, allowedSlots);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -191,6 +191,7 @@ namespace Barotrauma
|
||||
new List<InvSlotType>(item.GetComponent<Wearable>()?.AllowedSlots ?? item.GetComponent<Pickable>().AllowedSlots) :
|
||||
new List<InvSlotType>(item.AllowedSlots);
|
||||
allowedSlots.Remove(InvSlotType.Any);
|
||||
item.UnequipAutomatically = false;
|
||||
character.Inventory.TryPutItem(item, null, allowedSlots);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -308,6 +308,9 @@ namespace Barotrauma
|
||||
public Vector2 DebugTargetPos;
|
||||
public Vector2 DebugRefPos;
|
||||
|
||||
/// <summary>
|
||||
/// Is the limb the waist, a part of a leg or a tail?
|
||||
/// </summary>
|
||||
public bool IsLowerBody
|
||||
{
|
||||
get
|
||||
@@ -330,22 +333,33 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the limb a leg or a part of a leg (upper or lower leg or foot)
|
||||
/// </summary>
|
||||
public bool IsLeg
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (type)
|
||||
return type switch
|
||||
{
|
||||
case LimbType.LeftFoot:
|
||||
case LimbType.LeftLeg:
|
||||
case LimbType.LeftThigh:
|
||||
case LimbType.RightFoot:
|
||||
case LimbType.RightLeg:
|
||||
case LimbType.RightThigh:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
LimbType.LeftFoot or LimbType.LeftLeg or LimbType.LeftThigh or LimbType.RightFoot or LimbType.RightLeg or LimbType.RightThigh => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the limb an arm or a part of an arm (upper or lower arm or hand)
|
||||
/// </summary>
|
||||
public bool IsArm
|
||||
{
|
||||
get
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
LimbType.LeftArm or LimbType.LeftForearm or LimbType.LeftHand or LimbType.RightArm or LimbType.RightForearm or LimbType.RightHand => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,9 @@ namespace Barotrauma
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "The multiplier of the minimum distance required between this character and the player/submarine before the music starts playing. The default distance is twice the length of the submarine, or a minimum of 50 meters."), Editable]
|
||||
public float MusicRangeMultiplier { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Should the entire crew get an achievement (assuming there is one) if someone from the crew kills the character?")]
|
||||
public bool UnlockKillAchievementForWholeCrew { get; set; }
|
||||
|
||||
public readonly CharacterFile File;
|
||||
public bool IsPet => AI?.IsPet ?? false;
|
||||
|
||||
|
||||
+3
-2
@@ -69,9 +69,9 @@ namespace Barotrauma.Abilities
|
||||
switch (targetType)
|
||||
{
|
||||
case TargetType.Enemy:
|
||||
return !HumanAIController.IsFriendly(character, targetCharacter, onlySameTeam: false);
|
||||
return !HumanAIController.IsFriendly(character, targetCharacter);
|
||||
case TargetType.Ally:
|
||||
return HumanAIController.IsFriendly(character, targetCharacter, onlySameTeam: true);
|
||||
return HumanAIController.IsFriendly(character, targetCharacter);
|
||||
case TargetType.NotSelf:
|
||||
return targetCharacter != character;
|
||||
case TargetType.Alive:
|
||||
@@ -84,5 +84,6 @@ namespace Barotrauma.Abilities
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
if (!nearbyCharactersAppliesToAllies)
|
||||
{
|
||||
targets.RemoveAll(c => c is Character otherCharacter && HumanAIController.IsFriendly(otherCharacter, Character, onlySameTeam: true));
|
||||
targets.RemoveAll(c => c is Character otherCharacter && HumanAIController.IsFriendly(otherCharacter, Character));
|
||||
}
|
||||
if (!nearbyCharactersAppliesToEnemies)
|
||||
{
|
||||
|
||||
+5
-4
@@ -3,7 +3,7 @@
|
||||
class CharacterAbilityPsychoClown : CharacterAbility
|
||||
{
|
||||
private readonly StatTypes statType;
|
||||
private readonly float maxValue;
|
||||
private readonly float minValue, maxValue;
|
||||
private readonly string afflictionIdentifier;
|
||||
private float lastValue = 0f;
|
||||
public override bool AllowClientSimulation => true;
|
||||
@@ -11,8 +11,9 @@
|
||||
public CharacterAbilityPsychoClown(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier);
|
||||
maxValue = abilityElement.GetAttributeFloat("maxvalue", 0f);
|
||||
afflictionIdentifier = abilityElement.GetAttributeString("afflictionidentifier", "");
|
||||
maxValue = abilityElement.GetAttributeFloat(nameof(maxValue), 0f);
|
||||
minValue = abilityElement.GetAttributeFloat(nameof(minValue), 0f);
|
||||
afflictionIdentifier = abilityElement.GetAttributeString(nameof(afflictionIdentifier), "");
|
||||
}
|
||||
|
||||
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
@@ -31,7 +32,7 @@
|
||||
afflictionStrength = affliction.Strength / affliction.Prefab.MaxStrength;
|
||||
}
|
||||
|
||||
lastValue = afflictionStrength * maxValue;
|
||||
lastValue = minValue + afflictionStrength * (maxValue - minValue);
|
||||
Character.ChangeStat(statType, lastValue);
|
||||
}
|
||||
else
|
||||
|
||||
+1
@@ -67,6 +67,7 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (Character.HasTalent(identifier)) { continue; }
|
||||
Character.GiveTalent(identifier);
|
||||
Character.Info.ResettableExtraTalents.Add(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user