Unstable 0.15.13.0

This commit is contained in:
Markus Isberg
2021-10-29 21:57:18 +09:00
parent 1718dbc1c1
commit a43b540d4b
67 changed files with 467 additions and 222 deletions
@@ -104,6 +104,7 @@ namespace Barotrauma
!pathSteering.CurrentPath.Unreachable &&
(!requireNonDirty || !pathSteering.IsPathDirty);
public bool IsCurrentPathNullOrUnreachable => IsCurrentPathUnreachable || steeringManager is IndoorsSteeringManager pathSteering && pathSteering.CurrentPath == null;
public bool IsCurrentPathUnreachable => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Unreachable;
public bool IsCurrentPathFinished => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Finished;
@@ -431,7 +432,7 @@ namespace Barotrauma
Vector2 diff = EscapeTarget.WorldPosition - Character.WorldPosition;
float sqrDist = diff.LengthSquared();
bool isClose = sqrDist < MathUtils.Pow2(100);
if (Character.CurrentHull == null || isClose && !isClosedDoor || pathSteering == null || IsCurrentPathUnreachable || IsCurrentPathFinished)
if (Character.CurrentHull == null || isClose && !isClosedDoor || pathSteering == null || IsCurrentPathNullOrUnreachable || IsCurrentPathFinished)
{
// Very close to the target, outside, or at the end of the path -> try to steer through the gap
SteeringManager.Reset();
@@ -3659,15 +3659,15 @@ namespace Barotrauma
{
if (SelectedAiTarget == null) { return; }
Vector2 escapeDir = Vector2.Normalize(WorldPosition - SelectedAiTarget.WorldPosition);
if (!MathUtils.IsValid(escapeDir))
{
escapeDir = Vector2.UnitY;
}
if (Character.CurrentHull != null && !Character.AnimController.InWater)
{
// Inside
escapeDir = new Vector2(Math.Sign(escapeDir.X), 0);
}
if (!MathUtils.IsValid(escapeDir))
{
escapeDir = Vector2.UnitY;
}
SteeringManager.Reset();
SteeringManager.SteeringManual(deltaTime, escapeDir);
}
@@ -250,7 +250,15 @@ namespace Barotrauma
{
rayEnd += SelectedAiTarget.Entity.Submarine.SimPosition;
}
UseIndoorSteeringOutside = Submarine.PickBody(SimPosition, rayEnd, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null;
IEnumerable<FarseerPhysics.Dynamics.Body> ignoredBodies = null;
if (SelectedAiTarget.Entity is ISpatialEntity spatialTarget)
{
Submarine targetSub = spatialTarget.Submarine;
if (targetSub != null)
{
ignoredBodies = targetSub.PhysicsBody.FarseerBody.ToEnumerable();
}
}
}
}
else
@@ -340,14 +348,21 @@ namespace Barotrauma
IsInsideCave = Character.CurrentHull == null && Level.Loaded?.Caves.FirstOrDefault(c => c.Area.Contains(Character.WorldPosition)) is Level.Cave;
}
if (UseIndoorSteeringOutside || IsInsideCave || Character.CurrentHull?.Submarine != null || hasValidPath && IsCloseEnoughToTarget(maxSteeringBuffer) || IsCloseEnoughToTarget(steeringBuffer))
if (UseIndoorSteeringOutside || IsInsideCave || Character.CurrentHull?.Submarine != null || hasValidPath || IsCloseEnoughToTarget(steeringBuffer))
{
if (steeringManager != insideSteering)
{
insideSteering.Reset();
steeringManager = insideSteering;
}
steeringBuffer += steeringBufferIncreaseSpeed * deltaTime;
if (IsCloseEnoughToTarget(maxSteeringBuffer))
{
steeringBuffer += steeringBufferIncreaseSpeed * deltaTime;
}
else
{
steeringBuffer = minSteeringBuffer;
}
}
else
{
@@ -309,13 +309,15 @@ namespace Barotrauma
}
if (currentPath.Finished)
{
Vector2 pos2 = host.SimPosition;
if (character != null && character.Submarine == null &&
CurrentPath.Nodes.Count > 0 && CurrentPath.Nodes.Last().Submarine != null)
var lastNode = currentPath.Nodes.LastOrDefault();
if (lastNode == null)
{
pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition;
return Vector2.Zero;
}
else
{
return ConvertUnits.ToSimUnits(lastNode.WorldPosition - host.WorldPosition);
}
return currentTarget - pos2;
}
bool doorsChecked = false;
if (!character.LockHands && buttonPressCooldown <= 0.0f)
@@ -323,29 +325,7 @@ namespace Barotrauma
CheckDoorsInPath();
doorsChecked = true;
}
Vector2 pos = host.SimPosition;
if (character != null && CurrentPath.CurrentNode != null)
{
var nodeSub = CurrentPath.CurrentNode.Submarine;
if (nodeSub != null)
{
if (character.Submarine == null)
{
// Going inside
pos -= ConvertUnits.ToSimUnits(nodeSub.Position);
}
else if (character.Submarine != nodeSub)
{
// Different subs
pos -= ConvertUnits.ToSimUnits(nodeSub.Position - character.Submarine.Position);
}
}
else if (character.Submarine != null)
{
// Going outside
pos += ConvertUnits.ToSimUnits(character.Submarine.Position);
}
}
Vector2 pos = host.WorldPosition;
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
// Only humanoids can climb ladders
bool canClimb = character.AnimController is HumanoidAnimController && !character.LockHands;
@@ -384,7 +364,7 @@ namespace Barotrauma
}
if (character.IsClimbing && useLadders)
{
Vector2 diff = currentPath.CurrentNode.SimPosition - pos;
Vector2 diff = currentPath.CurrentNode.WorldPosition - pos;
bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent;
if (nextLadderSameAsCurrent)
{
@@ -397,7 +377,7 @@ namespace Barotrauma
float heightFromFloor = character.AnimController.GetColliderBottom().Y - character.AnimController.FloorY;
if (heightFromFloor <= 0.0f)
{
diff.Y = Math.Max(diff.Y, 1.0f);
diff.Y = Math.Max(diff.Y, 100);
}
// We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative.
bool isAboveFloor = heightFromFloor > -0.1f;
@@ -430,7 +410,7 @@ namespace Barotrauma
NextNode(!doorsChecked);
}
}
return diff;
return ConvertUnits.ToSimUnits(diff);
}
else if (character.AnimController.InWater)
{
@@ -481,7 +461,7 @@ namespace Barotrauma
{
return Vector2.Zero;
}
return currentPath.CurrentNode.SimPosition - pos;
return ConvertUnits.ToSimUnits(currentPath.CurrentNode.WorldPosition - pos);
}
private void NextNode(bool checkDoors)
@@ -45,6 +45,10 @@ namespace Barotrauma
/// </summary>
public bool AllowStealing { get; set; }
public bool TakeWholeStack { get; set; }
/// <summary>
/// Are variants of the specified item allowed
/// </summary>
public bool AllowVariants { get; set; }
public bool Equip { get; set; }
public bool Wear { get; set; }
@@ -265,7 +269,7 @@ namespace Barotrauma
}
float priority = Math.Clamp(objectiveManager.GetCurrentPriority(), 10, 100);
bool checkPath = priority >= AIObjectiveManager.LowestOrderPriority && (objectiveManager.IsCurrentOrder<AIObjectiveFixLeaks>() || objectiveManager.CurrentOrder is AIObjectiveGoTo gotoOrder && gotoOrder.followControlledCharacter);
bool checkPath = priority >= AIObjectiveManager.LowestOrderPriority && (objectiveManager.IsCurrentOrder<AIObjectiveFixLeaks>() || objectiveManager.CurrentOrder is AIObjectiveGoTo gotoOrder && gotoOrder.isFollowOrderObjective);
bool hasCalledPathFinder = false;
int itemsPerFrame = (int)priority;
for (int i = 0; i < itemsPerFrame && currSearchIndex < Item.ItemList.Count - 1; i++)
@@ -408,7 +412,7 @@ namespace Barotrauma
if (ignoredItems.Contains(item)) { return false; };
if (item.Condition < TargetCondition) { return false; }
if (ItemFilter != null && !ItemFilter(item)) { return false; }
return identifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id));
return identifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id) || (AllowVariants && item.Prefab.VariantOf?.Identifier == id));
}
public override void Reset()
@@ -24,7 +24,7 @@ namespace Barotrauma
public Func<float> priorityGetter;
public bool followControlledCharacter;
public bool isFollowOrderObjective;
public bool mimic;
public bool SpeakIfFails { get; set; } = true;
public bool UsePathingOutside { get; set; } = true;
@@ -165,17 +165,10 @@ namespace Barotrauma
protected override void Act(float deltaTime)
{
if (followControlledCharacter)
if (Target == null)
{
if (Character.Controlled != null && HumanAIController.IsFriendly(Character.Controlled))
{
Target = Character.Controlled;
}
if (Target == null)
{
Abandon = true;
return;
}
Abandon = true;
return;
}
if (Target == character || character.SelectedBy != null && HumanAIController.IsFriendly(character.SelectedBy))
{
@@ -205,7 +198,7 @@ namespace Barotrauma
}
}
Hull targetHull = GetTargetHull();
if (!followControlledCharacter)
if (!isFollowOrderObjective)
{
// Abandon if going through unsafe paths. Note ignores unsafe nodes when following an order or when the objective is set to ignore unsafe hulls.
bool containsUnsafeNodes = character.IsDismissed && !HumanAIController.ObjectiveManager.CurrentObjective.IgnoreUnsafeHulls
@@ -223,7 +216,7 @@ namespace Barotrauma
{
Abandon = true;
}
else if (HumanAIController.IsCurrentPathUnreachable)
else if (HumanAIController.IsCurrentPathNullOrUnreachable)
{
waitUntilPathUnreachable -= deltaTime;
SteeringManager.Reset();
@@ -317,8 +310,8 @@ namespace Barotrauma
Character targetCharacter = Target as Character;
if (character.AnimController.InWater)
{
if (character.CurrentHull == null ||
followControlledCharacter &&
if (character.CurrentHull == null ||
isFollowOrderObjective &&
targetCharacter != null && (targetCharacter.CurrentHull == null) != (character.CurrentHull == null) &&
Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition) < maxGapDistance * maxGapDistance)
{
@@ -361,7 +354,7 @@ namespace Barotrauma
}
if (TargetGap != null)
{
if (TargetGap.FlowTargetHull != null && HumanAIController.SteerThroughGap(TargetGap, followControlledCharacter ? Target.WorldPosition : TargetGap.FlowTargetHull.WorldPosition, deltaTime))
if (TargetGap.FlowTargetHull != null && HumanAIController.SteerThroughGap(TargetGap, isFollowOrderObjective ? Target.WorldPosition : TargetGap.FlowTargetHull.WorldPosition, deltaTime))
{
SteeringManager.SteeringAvoid(deltaTime, avoidLookAheadDistance, weight: 1);
return;
@@ -595,7 +588,7 @@ namespace Barotrauma
{
if (gap.Open < 1) { continue; }
if (gap.Submarine == null) { continue; }
if (!followControlledCharacter)
if (!isFollowOrderObjective)
{
if (gap.FlowTargetHull == null) { continue; }
if (gap.Submarine != Target.Submarine) { continue; }
@@ -452,7 +452,7 @@ namespace Barotrauma
extraDistanceWhileSwimming = 100,
AllowGoingOutside = true,
IgnoreIfTargetDead = true,
followControlledCharacter = true,
isFollowOrderObjective = true,
mimic = true,
DialogueIdentifier = "dialogcannotreachplace"
};
@@ -107,7 +107,10 @@ namespace Barotrauma
{
foreach (RelatedItem requiredItem in kvp.Value)
{
var getItemObjective = new AIObjectiveGetItem(character, requiredItem.Identifiers, objectiveManager, true);
var getItemObjective = new AIObjectiveGetItem(character, requiredItem.Identifiers, objectiveManager, true)
{
AllowVariants = requiredItem.AllowVariants
};
if (objectiveManager.IsCurrentOrder<AIObjectiveRepairItems>())
{
if (character.IsOnPlayerTeam)
@@ -408,7 +408,7 @@ namespace Barotrauma
}
bool isCompleted =
AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter) ||
targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Strength <= a.Prefab.TreatmentThreshold);
targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Prefab.IsBuff || a.Strength <= a.Prefab.TreatmentThreshold);
if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam)
{
@@ -83,7 +83,7 @@ namespace Barotrauma
if (character.AIController is HumanAIController humanAI)
{
if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target) ||
target.CharacterHealth.GetAllAfflictions().All(a => a.Strength <= a.Prefab.TreatmentThreshold))
target.CharacterHealth.GetAllAfflictions().All(a => a.Prefab.IsBuff || a.Strength <= a.Prefab.TreatmentThreshold))
{
return false;
}
@@ -1367,7 +1367,7 @@ namespace Barotrauma
info.Job.GiveJobItems(this, spawnPoint);
}
public void GiveIdCardTags(WayPoint spawnPoint)
public void GiveIdCardTags(WayPoint spawnPoint, bool createNetworkEvent = false)
{
if (info?.Job == null || spawnPoint == null) { return; }
@@ -1378,6 +1378,10 @@ namespace Barotrauma
{
item.AddTag(s);
}
if (createNetworkEvent && (GameMain.NetworkMember?.IsServer ?? false))
{
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ChangeProperty, item.SerializableProperties["tags"] });
}
}
}
@@ -1756,6 +1760,7 @@ namespace Barotrauma
}
else if (IsPlayer)
{
float dist = -1;
Vector2 attackPos = SimPosition + ConvertUnits.ToSimUnits(cursorPosition - Position);
List<Body> ignoredBodies = AnimController.Limbs.Select(l => l.body.FarseerBody).ToList();
ignoredBodies.Add(AnimController.Collider.FarseerBody);
@@ -1787,13 +1792,13 @@ namespace Barotrauma
}
else
{
if (body.UserData is IDamageable)
if (body.UserData is IDamageable damageable)
{
attackTarget = (IDamageable)body.UserData;
attackTarget = damageable;
}
else if (body.UserData is Limb)
else if (body.UserData is Limb limb)
{
attackTarget = ((Limb)body.UserData).character;
attackTarget = limb.character;
}
}
}
@@ -1822,7 +1827,20 @@ namespace Barotrauma
var attackLimb = sortedLimbs.FirstOrDefault();
if (attackLimb != null)
{
attackLimb.UpdateAttack(deltaTime, attackPos, attackTarget, out AttackResult attackResult);
if (attackTarget is Character targetCharacter)
{
dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(Submarine.LastPickedPosition, attackLimb.SimPosition));
foreach (Limb limb in targetCharacter.AnimController.Limbs)
{
if (limb.IsSevered || limb.Removed) { continue; }
float tempDist = ConvertUnits.ToDisplayUnits(Vector2.Distance(limb.SimPosition, attackLimb.SimPosition));
if (tempDist < dist)
{
dist = tempDist;
}
}
}
attackLimb.UpdateAttack(deltaTime, attackPos, attackTarget, out AttackResult attackResult, dist);
if (!attackLimb.attack.IsRunning)
{
attackCoolDown = 1.0f;
@@ -3438,7 +3456,7 @@ namespace Barotrauma
var attackResult = targetLimb == null ?
AddDamage(worldPosition, attackAfflictions, attack.Stun, playSound, attackImpulse, out limbHit, attacker, attack.DamageMultiplier * attackData.DamageMultiplier) :
DamageLimb(worldPosition, targetLimb, attackAfflictions, attack.Stun, playSound, attackImpulse, attacker, attack.DamageMultiplier * attackData.DamageMultiplier, penetration: penetration + attackData.AddedPenetration);
DamageLimb(worldPosition, targetLimb, attackAfflictions, attack.Stun, playSound, attackImpulse, attacker, attack.DamageMultiplier * attackData.DamageMultiplier, penetration: penetration + attackData.AddedPenetration, shouldImplode: attackData.ShouldImplode);
if (attacker != null)
{
@@ -3558,12 +3576,12 @@ namespace Barotrauma
public void RecordKill(Character target)
{
var abilityCharacter = new AbilityCharacter(target);
var abilityCharacterKill = new AbilityCharacterKill(target, this);
foreach (Character attackerCrewmember in GetFriendlyCrew(this))
{
attackerCrewmember.CheckTalents(AbilityEffectType.OnCrewKillCharacter, abilityCharacter);
attackerCrewmember.CheckTalents(AbilityEffectType.OnCrewKillCharacter, abilityCharacterKill);
}
CheckTalents(AbilityEffectType.OnKillCharacter, abilityCharacter);
CheckTalents(AbilityEffectType.OnKillCharacter, abilityCharacterKill);
if (!IsOnPlayerTeam) { return; }
if (GameMain.Config.KilledCreatures.Any(name => name.Equals(target.SpeciesName, StringComparison.OrdinalIgnoreCase))) { return; }
@@ -3579,7 +3597,7 @@ namespace Barotrauma
GameMain.Config.RecentlyEncounteredCreatures.Add(other.SpeciesName);
}
public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null, float damageMultiplier = 1, bool allowStacking = true, float penetration = 0f)
public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null, float damageMultiplier = 1, bool allowStacking = true, float penetration = 0f, bool shouldImplode = false)
{
if (Removed) { return new AttackResult(); }
@@ -3638,6 +3656,12 @@ namespace Barotrauma
float prevVitality = CharacterHealth.Vitality;
AttackResult attackResult = hitLimb.AddDamage(simPos, afflictions, playSound, damageMultiplier: damageMultiplier, penetration: penetration, attacker: attacker);
CharacterHealth.ApplyDamage(hitLimb, attackResult, allowStacking);
if (shouldImplode)
{
// Only used by assistant's True Potential talent. Has to run here in order to properly give kill credit when it activates.
Implode();
}
if (attacker != this)
{
OnAttacked?.Invoke(attacker, attackResult);
@@ -3786,7 +3810,7 @@ namespace Barotrauma
}
}
public void Implode(bool isNetworkMessage = false)
private void Implode(bool isNetworkMessage = false)
{
if (CharacterHealth.Unkillable || GodMode || IsDead) { return; }
@@ -4626,4 +4650,16 @@ namespace Barotrauma
AggressiveBehavior = aggressiveBehavior;
}
}
class AbilityCharacterKill : AbilityObject, IAbilityCharacter
{
public AbilityCharacterKill(Character character, Character killer)
{
Character = character;
Killer = killer;
}
public Character Character { get; set; }
public Character Killer { get; set; }
}
}
@@ -291,7 +291,7 @@ namespace Barotrauma
husk.SetStun(5);
yield return new WaitForSeconds(5, false);
#if CLIENT
husk.PlaySound(CharacterSound.SoundType.Idle);
husk?.PlaySound(CharacterSound.SoundType.Idle);
#endif
yield return CoroutineStatus.Success;
}
@@ -844,6 +844,8 @@ namespace Barotrauma
FaceTint = DefaultFaceTint;
BodyTint = Color.TransparentBlack;
if (!(Character?.Params?.Health.ApplyAfflictionColors ?? false)) { return; }
for (int i = 0; i < limbHealths.Count; i++)
{
for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--)
@@ -466,6 +466,9 @@ namespace Barotrauma
[Serialize(false, true), Editable]
public bool StunImmunity { get; set; }
[Serialize(false, true, description: "Can afflictions affect the face/body tint of the character."), Editable]
public bool ApplyAfflictionColors { get; private set; }
// TODO: limbhealths, sprite?
public HealthParams(XElement element, CharacterParams character) : base(element, character) { }
@@ -14,8 +14,9 @@ namespace Barotrauma.Abilities
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
{
if ((abilityObject as IAbilityCharacter)?.Character is Character character)
if (abilityObject is IAbilityCharacter abilityCharacter)
{
if (!(abilityCharacter.Character is Character character)) { return false; }
if (!IsViableTarget(targetTypes, character)) { return false; }
return true;
@@ -0,0 +1,23 @@
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class AbilityConditionGeneHarvester : AbilityConditionData
{
public AbilityConditionGeneHarvester(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement) { }
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
{
if (abilityObject is AbilityCharacterKill abilityCharacterKill)
{
return abilityCharacterKill.Killer.Submarine == null || abilityCharacterKill.Killer.TeamID != abilityCharacterKill.Killer.Submarine.TeamID;
}
else
{
LogAbilityConditionError(abilityObject, typeof(AbilityCharacterKill));
return false;
}
}
}
}
@@ -136,6 +136,7 @@ namespace Barotrauma.Abilities
public float DamageMultiplier { get; set; } = 1f;
public float AddedPenetration { get; set; } = 0f;
public List<Affliction> Afflictions { get; set; }
public bool ShouldImplode { get; set; } = false;
public Attack SourceAttack { get; }
public Character Character { get; set; }
public Character Attacker { get; set; }
@@ -37,12 +37,7 @@ namespace Barotrauma.Abilities
attackData.DamageMultiplier += addedDamageMultiplier;
attackData.AddedPenetration += addedPenetration;
if (implode)
{
// might have issues, as the method used to be private and only used for pressure death
attackData.Character?.Implode();
}
attackData.ShouldImplode = implode;
}
else
{
@@ -19,11 +19,10 @@ namespace Barotrauma.Abilities
moneyPerMission = abilityElement.GetAttributeInt("moneypermission", 0);
}
protected override void ApplyEffect()
protected override void ApplyEffect(AbilityObject abilityObject)
{
if (Character?.Info is CharacterInfo info)
{
Character.GiveMoney(moneyPerMission * info.MissionsCompletedSinceDeath);
}
}