(99feac023) Unstable 0.9.704.0
This commit is contained in:
@@ -142,16 +142,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public AITarget(Entity e, XElement element) : this(e)
|
||||
public void Reset()
|
||||
{
|
||||
SightRange = element.GetAttributeFloat("sightrange", 0.0f);
|
||||
SoundRange = element.GetAttributeFloat("soundrange", 0.0f);
|
||||
MinSightRange = element.GetAttributeFloat("minsightrange", 0f);
|
||||
MinSoundRange = element.GetAttributeFloat("minsoundrange", 0f);
|
||||
MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
|
||||
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
|
||||
FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime);
|
||||
Static = element.GetAttributeBool("static", Static);
|
||||
if (Static)
|
||||
{
|
||||
SightRange = MaxSightRange;
|
||||
@@ -163,7 +155,18 @@ namespace Barotrauma
|
||||
SightRange = MinSightRange;
|
||||
SoundRange = MinSoundRange;
|
||||
}
|
||||
}
|
||||
|
||||
public AITarget(Entity e, XElement element) : this(e)
|
||||
{
|
||||
SightRange = element.GetAttributeFloat("sightrange", 0.0f);
|
||||
SoundRange = element.GetAttributeFloat("soundrange", 0.0f);
|
||||
MinSightRange = element.GetAttributeFloat("minsightrange", 0f);
|
||||
MinSoundRange = element.GetAttributeFloat("minsoundrange", 0f);
|
||||
MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
|
||||
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
|
||||
FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime);
|
||||
Static = element.GetAttributeBool("static", Static);
|
||||
SonarDisruption = element.GetAttributeFloat("sonardisruption", 0.0f);
|
||||
SonarLabel = element.GetAttributeString("sonarlabel", "");
|
||||
SonarIconIdentifier = element.GetAttributeString("sonaricon", "");
|
||||
@@ -172,6 +175,7 @@ namespace Barotrauma
|
||||
{
|
||||
Type = t;
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
|
||||
public AITarget(Entity e)
|
||||
|
||||
@@ -270,6 +270,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (isStateChanged)
|
||||
{
|
||||
if (State == AIState.Idle)
|
||||
{
|
||||
stateResetTimer -= deltaTime;
|
||||
if (stateResetTimer <= 0)
|
||||
{
|
||||
ResetOriginalState();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetIgnoreTimer > 0)
|
||||
{
|
||||
targetIgnoreTimer -= deltaTime;
|
||||
@@ -662,7 +673,7 @@ namespace Barotrauma
|
||||
attackSimPos = Character.GetRelativeSimPosition(SelectedAiTarget.Entity);
|
||||
}
|
||||
|
||||
if (CanEnterSubmarine)
|
||||
if (Character.AnimController.CanEnterSubmarine)
|
||||
{
|
||||
//targeting a wall section that can be passed through -> steer manually through the hole
|
||||
if (wallTarget != null && wallTarget.SectionIndex > -1 && CanPassThroughHole(wallTarget.Structure, wallTarget.SectionIndex))
|
||||
@@ -674,21 +685,20 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Don't think we need this
|
||||
//else if (wallTarget == null && SelectedAiTarget.Entity is Structure wall)
|
||||
//{
|
||||
// for (int i = 0; i < wall.Sections.Length; i++)
|
||||
// {
|
||||
// WallSection section = wall.Sections[i];
|
||||
// if (CanPassThroughHole(wall, i) && section?.gap != null)
|
||||
// {
|
||||
// if (SteerThroughGap(wall, section, section.gap.WorldPosition, deltaTime))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
else if (SelectedAiTarget.Entity is Structure wall)
|
||||
{
|
||||
for (int i = 0; i < wall.Sections.Length; i++)
|
||||
{
|
||||
WallSection section = wall.Sections[i];
|
||||
if (CanPassThroughHole(wall, i) && section?.gap != null)
|
||||
{
|
||||
if (SteerThroughGap(wall, section, section.gap.WorldPosition, deltaTime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (SelectedAiTarget.Entity is Item i)
|
||||
{
|
||||
var door = i.GetComponent<Door>();
|
||||
@@ -1024,7 +1034,7 @@ namespace Barotrauma
|
||||
var door = pathSteering.CurrentPath.CurrentNode?.ConnectedDoor ?? pathSteering.CurrentPath.NextNode?.ConnectedDoor;
|
||||
if (door != null && !door.IsOpen)
|
||||
{
|
||||
if (SelectedAiTarget != door.Item.AiTarget)
|
||||
if (door.Item.AiTarget != null && SelectedAiTarget != door.Item.AiTarget)
|
||||
{
|
||||
SelectTarget(door.Item.AiTarget, selectedTargetMemory.Priority);
|
||||
return;
|
||||
@@ -1076,15 +1086,15 @@ namespace Barotrauma
|
||||
LatchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
Hull targetHull = section.gap?.FlowTargetHull;
|
||||
SelectedAiTarget = targetHull != null ? targetHull.AiTarget : wall.AiTarget;
|
||||
float distance = Vector2.DistanceSquared(Character.WorldPosition, targetWorldPos);
|
||||
float maxDistance = Math.Min(wall.Rect.Width, wall.Rect.Height);
|
||||
if (distance * distance > maxDistance * maxDistance)
|
||||
if (Vector2.DistanceSquared(Character.WorldPosition, targetWorldPos) > maxDistance * maxDistance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (targetHull != null)
|
||||
{
|
||||
// If already inside, target the hull, else target the wall.
|
||||
SelectedAiTarget = Character.CurrentHull != null ? targetHull.AiTarget : wall.AiTarget;
|
||||
if (wall.IsHorizontal)
|
||||
{
|
||||
targetWorldPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2;
|
||||
@@ -1094,6 +1104,7 @@ namespace Barotrauma
|
||||
targetWorldPos.X = targetHull.WorldRect.Center.X;
|
||||
}
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1102,9 +1113,16 @@ namespace Barotrauma
|
||||
private bool CanAttack(Entity target)
|
||||
{
|
||||
if (target == null) { return false; }
|
||||
if (target is Character ch)
|
||||
if (target is Character c)
|
||||
{
|
||||
if (Character.CurrentHull == null && ch.CurrentHull != null || Character.CurrentHull != null && ch.CurrentHull == null)
|
||||
if (Character.CurrentHull == null && c.CurrentHull != null || Character.CurrentHull != null && c.CurrentHull == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (target is Item i && i.GetComponent<Door>() == null)
|
||||
{
|
||||
if (Character.CurrentHull == null && i.CurrentHull != null || Character.CurrentHull != null && i.CurrentHull == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1175,7 +1193,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (wall.SectionBodyDisabled(i))
|
||||
{
|
||||
if (CanEnterSubmarine && CanPassThroughHole(wall, i))
|
||||
if (Character.AnimController.CanEnterSubmarine && CanPassThroughHole(wall, i))
|
||||
{
|
||||
sectionIndex = i;
|
||||
break;
|
||||
@@ -1204,12 +1222,12 @@ namespace Barotrauma
|
||||
sectionPos.X += (wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2 * attachTargetNormal.X;
|
||||
}
|
||||
LatchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal);
|
||||
if (CanEnterSubmarine || !wall.SectionBodyDisabled(sectionIndex) && !IsWallDisabled(wall))
|
||||
if (Character.AnimController.CanEnterSubmarine || !wall.SectionBodyDisabled(sectionIndex) && !IsWallDisabled(wall))
|
||||
{
|
||||
wallTarget = new WallTarget(sectionPos, wall, sectionIndex);
|
||||
}
|
||||
}
|
||||
if (!CanEnterSubmarine && wallTarget == null)
|
||||
if (!Character.AnimController.CanEnterSubmarine && wallTarget == null)
|
||||
{
|
||||
if (closestBody.UserData is Structure w && w.Submarine != null || closestBody.UserData is Item i && i.Submarine != null)
|
||||
{
|
||||
@@ -1251,12 +1269,18 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (attackResult.Damage > 0.0f && Character.Params.AI.AttackWhenProvoked)
|
||||
if (attackResult.Damage > 0.0f)
|
||||
{
|
||||
if (attacker.Submarine == Character.Submarine && canAttackCharacters ||
|
||||
attacker.Submarine != null && canAttackSub)
|
||||
if (Character.Params.AI.AttackWhenProvoked)
|
||||
{
|
||||
ChangeTargetState(attacker, AIState.Attack, 100);
|
||||
if (attacker.Submarine == Character.Submarine && canAttackCharacters || attacker.Submarine != null && canAttackSub)
|
||||
{
|
||||
ChangeTargetState(attacker, AIState.Attack, 100);
|
||||
}
|
||||
}
|
||||
else if (!AIParams.HasTag(attacker.SpeciesName))
|
||||
{
|
||||
ChangeTargetState(attacker, AIState.Flee, 100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1306,7 +1330,8 @@ namespace Barotrauma
|
||||
SelectTarget(aiTarget, GetTargetMemory(SelectedAiTarget).Priority);
|
||||
}
|
||||
}
|
||||
if (SelectedAiTarget.Entity is IDamageable damageTarget)
|
||||
IDamageable damageTarget = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity as IDamageable;
|
||||
if (damageTarget != null)
|
||||
{
|
||||
if (attackingLimb.UpdateAttack(deltaTime, attackSimPos, damageTarget, out AttackResult attackResult, distance, targetLimb))
|
||||
{
|
||||
@@ -1480,9 +1505,9 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't attack targets that are not in the same submarine
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
if (targetCharacter.IsDead)
|
||||
{
|
||||
@@ -1578,7 +1603,7 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
valueModifier = 1;
|
||||
if (!CanEnterSubmarine && IsWallDisabled(s))
|
||||
if (!Character.AnimController.CanEnterSubmarine && IsWallDisabled(s))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1587,7 +1612,7 @@ namespace Barotrauma
|
||||
var section = s.Sections[i];
|
||||
if (section.gap == null) { continue; }
|
||||
bool leadsInside = !section.gap.IsRoomToRoom && section.gap.FlowTargetHull != null;
|
||||
if (CanEnterSubmarine)
|
||||
if (Character.AnimController.CanEnterSubmarine)
|
||||
{
|
||||
if (CanPassThroughHole(s, i))
|
||||
{
|
||||
@@ -1766,7 +1791,7 @@ namespace Barotrauma
|
||||
removals.ForEach(r => targetMemories.Remove(r));
|
||||
}
|
||||
|
||||
private const float targetIgnoreTime = 5;
|
||||
private readonly float targetIgnoreTime = 5;
|
||||
private float targetIgnoreTimer;
|
||||
private readonly HashSet<AITarget> ignoredTargets = new HashSet<AITarget>();
|
||||
public void IgnoreTarget(AITarget target)
|
||||
@@ -1775,25 +1800,16 @@ namespace Barotrauma
|
||||
ignoredTargets.Add(target);
|
||||
targetIgnoreTimer = targetIgnoreTime * Rand.Range(0.75f, 1.25f);
|
||||
}
|
||||
|
||||
protected override void OnTargetChanged(AITarget previousTarget, AITarget newTarget)
|
||||
{
|
||||
if (previousTarget == null || newTarget == null) { return; }
|
||||
var previousCharacter = previousTarget.Entity as Character;
|
||||
var newCharacter = newTarget.Entity as Character;
|
||||
if (previousCharacter == null && newCharacter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (previousCharacter != null && newCharacter != null && previousCharacter.SpeciesName == newCharacter.SpeciesName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
modifiedParams.Keys.ForEachMod(tag => TryResetOriginalState(tag));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region State switching
|
||||
/// <summary>
|
||||
/// How long do we hold on to the current state after losing a target before we reset back to the original state.
|
||||
/// In other words, how long do we have to idle before the original state is restored.
|
||||
/// </summary>
|
||||
private readonly float stateResetCooldown = 10;
|
||||
private float stateResetTimer;
|
||||
private bool isStateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Resets the target's state to the original value defined in the xml.
|
||||
@@ -1806,6 +1822,7 @@ namespace Barotrauma
|
||||
modifiedParams.Remove(tag);
|
||||
if (tempParams.ContainsKey(tag))
|
||||
{
|
||||
tempParams.Values.ForEach(t => AIParams.RemoveTarget(t));
|
||||
tempParams.Remove(tag);
|
||||
}
|
||||
targetParams.Reset();
|
||||
@@ -1829,23 +1846,39 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
private void ChangeTargetState(Character target, AIState state, float? priority = null)
|
||||
{
|
||||
isStateChanged = true;
|
||||
SetStateResetTimer();
|
||||
ChangeParams(target.SpeciesName);
|
||||
// If the target is shooting from the submarine, we might not perceive it because it doesn't move.
|
||||
// --> Target the submarine too.
|
||||
if (target.Submarine != null && state == AIState.Attack && canAttackSub)
|
||||
// Target also items, because if we are blind and the target doesn't move, we can only perceive the target when it uses items
|
||||
if (state == AIState.Attack || state == AIState.Flee)
|
||||
{
|
||||
ChangeParams("room");
|
||||
ChangeParams("wall");
|
||||
ChangeParams("door");
|
||||
ChangeParams("weapon");
|
||||
ChangeParams("tool");
|
||||
}
|
||||
if (state == AIState.Attack)
|
||||
{
|
||||
// If the target is shooting from the submarine, we might not perceive it because it doesn't move.
|
||||
// --> Target the submarine too.
|
||||
if (target.Submarine != null && canAttackSub)
|
||||
{
|
||||
ChangeParams("room");
|
||||
ChangeParams("wall");
|
||||
ChangeParams("door");
|
||||
}
|
||||
ChangeParams("provocative", onlyExisting: true);
|
||||
ChangeParams("light", onlyExisting: true);
|
||||
}
|
||||
|
||||
void ChangeParams(string tag)
|
||||
void ChangeParams(string tag, bool onlyExisting = false)
|
||||
{
|
||||
if (!AIParams.TryGetTarget(tag, out CharacterParams.TargetParams targetParams))
|
||||
{
|
||||
if (AIParams.TryAddNewTarget(tag, state, priority ?? 100, out targetParams))
|
||||
if (!onlyExisting && !tempParams.ContainsKey(tag))
|
||||
{
|
||||
tempParams.Add(tag, targetParams);
|
||||
if (AIParams.TryAddNewTarget(tag, state, priority ?? 100, out targetParams))
|
||||
{
|
||||
tempParams.Add(tag, targetParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetParams != null)
|
||||
@@ -1862,6 +1895,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetOriginalState()
|
||||
{
|
||||
isStateChanged = false;
|
||||
modifiedParams.Keys.ForEachMod(tag => TryResetOriginalState(tag));
|
||||
}
|
||||
#endregion
|
||||
|
||||
protected override void OnStateChanged(AIState from, AIState to)
|
||||
@@ -1873,8 +1912,14 @@ namespace Barotrauma
|
||||
escapeMargin = 0;
|
||||
allGapsSearched = false;
|
||||
unreachableGaps.Clear();
|
||||
if (isStateChanged && to == AIState.Idle && from != to)
|
||||
{
|
||||
SetStateResetTimer();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetStateResetTimer() => stateResetTimer = stateResetCooldown * Rand.Range(0.75f, 1.25f);
|
||||
|
||||
private float GetPerceivingRange(AITarget target) => Math.Max(target.SightRange * Sight, target.SoundRange * Hearing);
|
||||
|
||||
private bool CanPerceive(AITarget target, float dist = -1, float distSquared = -1)
|
||||
|
||||
@@ -711,6 +711,7 @@ namespace Barotrauma
|
||||
|
||||
public void SetOrder(Order order, string option, Character orderGiver, bool speak = true)
|
||||
{
|
||||
SetOrderProjSpecific(order, option);
|
||||
CurrentOrderOption = option;
|
||||
CurrentOrder = order;
|
||||
objectiveManager.SetOrder(order, option, orderGiver);
|
||||
@@ -749,10 +750,9 @@ namespace Barotrauma
|
||||
Character.Speak(TextManager.Get("DialogAffirmative"), null, 1.0f);
|
||||
}
|
||||
}
|
||||
SetOrderProjSpecific(order);
|
||||
}
|
||||
|
||||
partial void SetOrderProjSpecific(Order order);
|
||||
partial void SetOrderProjSpecific(Order order, string option);
|
||||
|
||||
public override void SelectTarget(AITarget target)
|
||||
{
|
||||
@@ -970,8 +970,8 @@ namespace Barotrauma
|
||||
{
|
||||
bool isValidTarget(Character e) => IsActive(e) && !IsFriendly(character, e);
|
||||
int enemyCount = visibleHulls == null ?
|
||||
Character.CharacterList.Count(e => e.CurrentHull == hull && isValidTarget(e)) :
|
||||
Character.CharacterList.Count(e => visibleHulls.Contains(e.CurrentHull) && isValidTarget(e));
|
||||
Character.CharacterList.Count(e => isValidTarget(e) && e.CurrentHull == hull) :
|
||||
Character.CharacterList.Count(e => isValidTarget(e) && visibleHulls.Contains(e.CurrentHull));
|
||||
// The hull safety decreases 90% per enemy up to 100% (TODO: test smaller percentages)
|
||||
enemyFactor = MathHelper.Lerp(1, 0, MathHelper.Clamp(enemyCount * 0.9f, 0, 1));
|
||||
}
|
||||
@@ -988,7 +988,7 @@ namespace Barotrauma
|
||||
return sameSpecies && !differentTeam;
|
||||
}
|
||||
|
||||
public static bool IsActive(Character other) => !other.Removed && !other.IsDead && !other.IsUnconscious;
|
||||
public static bool IsActive(Character other) => other != null && !other.Removed && !other.IsDead && !other.IsUnconscious;
|
||||
|
||||
public static bool IsTrueForAllCrewMembers(Character character, Func<HumanAIController, bool> predicate)
|
||||
{
|
||||
|
||||
+7
-2
@@ -145,9 +145,14 @@ namespace Barotrauma
|
||||
},
|
||||
onAbandon: () =>
|
||||
{
|
||||
// Don't ignore any hulls if outside, because apparently it happens that we can't find a path, in which case we just want to try again.
|
||||
// If we ignore the hull, it might be the only airlock in the target sub, which ignores the whole sub.
|
||||
if (currentHull != null && goToObjective != null)
|
||||
{
|
||||
HumanAIController.UnreachableHulls.Add(goToObjective.Target as Hull);
|
||||
if (goToObjective.Target is Hull hull)
|
||||
{
|
||||
HumanAIController.UnreachableHulls.Add(hull);
|
||||
}
|
||||
}
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
});
|
||||
@@ -172,7 +177,7 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (Character enemy in Character.CharacterList)
|
||||
{
|
||||
if (HumanAIController.IsFriendly(enemy) || !HumanAIController.IsActive(enemy)) { continue; }
|
||||
if (!HumanAIController.IsActive(enemy) || HumanAIController.IsFriendly(enemy)) { continue; }
|
||||
if (HumanAIController.VisibleHulls.Contains(enemy.CurrentHull))
|
||||
{
|
||||
Vector2 dir = character.Position - enemy.Position;
|
||||
|
||||
@@ -265,6 +265,14 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SteeringManager == PathSteering && PathSteering.CurrentPath?.CurrentNode?.Ladders != null)
|
||||
{
|
||||
//don't consider the character to be close enough to the target while climbing ladders,
|
||||
//UNLESS the last node in the path has been reached
|
||||
//otherwise characters can let go of the ladders too soon once they're close enough to the target
|
||||
if (PathSteering.CurrentPath.NextNode != null) { return false; }
|
||||
}
|
||||
|
||||
bool closeEnough = Vector2.DistanceSquared(Target.WorldPosition, character.WorldPosition) < CloseEnough * CloseEnough;
|
||||
if (closeEnough)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Barotrauma
|
||||
public static Sprite ShortcutNode { get; private set; }
|
||||
public static Sprite ExpandNode { get; private set; }
|
||||
public static Sprite NodeContainer { get; private set; }
|
||||
public static Sprite HotkeyContainer { get; private set; }
|
||||
public static Sprite CommandBackground { get; private set; }
|
||||
public static List<Order> PrefabList { get; private set; }
|
||||
public static Order GetPrefab(string identifier)
|
||||
@@ -163,6 +164,9 @@ namespace Barotrauma
|
||||
case "nodecontainer":
|
||||
NodeContainer = new Sprite(spriteElement, lazyLoad: true);
|
||||
break;
|
||||
case "hotkeycontainer":
|
||||
HotkeyContainer = new Sprite(spriteElement, lazyLoad: true);
|
||||
break;
|
||||
case "commandbackground":
|
||||
CommandBackground = new Sprite(spriteElement, lazyLoad: true);
|
||||
break;
|
||||
|
||||
@@ -312,22 +312,15 @@ namespace Barotrauma
|
||||
selectedCharacter.selectedBy = null;
|
||||
selectedCharacter = value;
|
||||
if (selectedCharacter != null)
|
||||
{
|
||||
selectedCharacter.selectedBy = this;
|
||||
|
||||
#if CLIENT
|
||||
if (GameMain.GameSession == null) return;
|
||||
// Quick & dirty hiding of the chat whenever a character with an accessible inventory is selected to prevent overlaps
|
||||
if (GameMain.GameSession.CrewManager.IsSinglePlayer)
|
||||
{
|
||||
if (GameMain.GameSession.CrewManager.ChatBox == null) return;
|
||||
GameMain.GameSession.CrewManager.ChatBox.SetVisibility(!(IsHumanoid && value != null && value.Inventory != null && value.CanInventoryBeAccessed));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GameMain.Client?.ChatBox == null) return;
|
||||
GameMain.Client.ChatBox.SetVisibility(!(IsHumanoid && value != null && value.Inventory != null && value.CanInventoryBeAccessed));
|
||||
}
|
||||
if (Inventory != null)
|
||||
{
|
||||
Inventory.ToggleInventory(true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2315,14 +2308,21 @@ namespace Barotrauma
|
||||
aiTarget.SoundRange = MathHelper.Clamp(range, 0, 10000);
|
||||
}
|
||||
|
||||
public bool CanHearCharacter(Character speaker)
|
||||
{
|
||||
if (speaker == null || speaker.SpeechImpediment > 100.0f) { return false; }
|
||||
ChatMessageType messageType = ChatMessage.CanUseRadio(speaker) && ChatMessage.CanUseRadio(this) ?
|
||||
ChatMessageType.Radio :
|
||||
ChatMessageType.Default;
|
||||
return !string.IsNullOrEmpty(ChatMessage.ApplyDistanceEffect("message", messageType, speaker, this));
|
||||
}
|
||||
|
||||
public void SetOrder(Order order, string orderOption, Character orderGiver, bool speak = true)
|
||||
{
|
||||
if (orderGiver != null)
|
||||
{
|
||||
//set the character order only if the character is close enough to hear the message
|
||||
ChatMessageType messageType = ChatMessage.CanUseRadio(orderGiver) && ChatMessage.CanUseRadio(this) ?
|
||||
ChatMessageType.Radio : ChatMessageType.Default;
|
||||
if (string.IsNullOrEmpty(ChatMessage.ApplyDistanceEffect("message", messageType, orderGiver, this))) return;
|
||||
if (!CanHearCharacter(orderGiver)) { return; }
|
||||
}
|
||||
|
||||
HumanAIController humanAI = AIController as HumanAIController;
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Barotrauma
|
||||
class LimbHealth
|
||||
{
|
||||
public Sprite IndicatorSprite;
|
||||
public Sprite HighlightSprite;
|
||||
|
||||
public Rectangle HighlightArea;
|
||||
|
||||
@@ -44,6 +45,9 @@ namespace Barotrauma
|
||||
IndicatorSprite = new Sprite(subElement);
|
||||
HighlightArea = subElement.GetAttributeRect("highlightarea", new Rectangle(0, 0, (int)IndicatorSprite.size.X, (int)IndicatorSprite.size.Y));
|
||||
break;
|
||||
case "highlightsprite":
|
||||
HighlightSprite = new Sprite(subElement);
|
||||
break;
|
||||
case "vitalitymultiplier":
|
||||
if (subElement.Attribute("name") != null)
|
||||
{
|
||||
|
||||
@@ -61,7 +61,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public readonly Dictionary<int, XElement> ItemSets = new Dictionary<int, XElement>();
|
||||
public readonly Dictionary<int, List<string>> ItemNames = new Dictionary<int, List<string>>();
|
||||
public readonly Dictionary<int, List<string>> ItemIdentifiers = new Dictionary<int, List<string>>();
|
||||
public readonly Dictionary<int, Dictionary<string, bool>> ShowItemPreview = new Dictionary<int, Dictionary<string, bool>>();
|
||||
public readonly List<SkillPrefab> Skills = new List<SkillPrefab>();
|
||||
public readonly List<AutonomousObjective> AutomaticOrders = new List<AutonomousObjective>();
|
||||
public readonly List<string> AppropriateOrders = new List<string>();
|
||||
@@ -184,9 +185,10 @@ namespace Barotrauma
|
||||
{
|
||||
case "itemset":
|
||||
ItemSets.Add(variant, subElement);
|
||||
var itemNames = new List<string>();
|
||||
loadItemNames(subElement, itemNames);
|
||||
ItemNames.Add(variant++, itemNames);
|
||||
ItemIdentifiers[variant] = new List<string>();
|
||||
ShowItemPreview[variant] = new Dictionary<string, bool>();
|
||||
loadItemIdentifiers(subElement, variant);
|
||||
variant++;
|
||||
break;
|
||||
case "skills":
|
||||
foreach (XElement skillElement in subElement.Elements())
|
||||
@@ -201,20 +203,19 @@ namespace Barotrauma
|
||||
case "appropriateorders":
|
||||
subElement.Elements().ForEach(order => AppropriateOrders.Add(order.GetAttributeString("identifier", "").ToLowerInvariant()));
|
||||
break;
|
||||
case "icon":
|
||||
case "jobicon":
|
||||
Icon = new Sprite(subElement.FirstElement());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void loadItemNames(XElement parentElement, List<string> itemNames)
|
||||
void loadItemIdentifiers(XElement parentElement, int variant)
|
||||
{
|
||||
foreach (XElement itemElement in parentElement.GetChildElements("Item"))
|
||||
{
|
||||
if (itemElement.Element("name") != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in job config \"" + Name + "\" - use identifiers instead of names to configure the items.");
|
||||
itemNames.Add(itemElement.GetAttributeString("name", ""));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -222,22 +223,13 @@ namespace Barotrauma
|
||||
if (string.IsNullOrWhiteSpace(itemIdentifier))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in job config \"" + Name + "\" - item with no identifier.");
|
||||
itemNames.Add("");
|
||||
}
|
||||
else
|
||||
{
|
||||
var prefab = MapEntityPrefab.Find(null, itemIdentifier) as ItemPrefab;
|
||||
if (prefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in job config \"" + Name + "\" - item prefab \"" + itemIdentifier + "\" not found.");
|
||||
itemNames.Add("");
|
||||
}
|
||||
else
|
||||
{
|
||||
itemNames.Add(prefab.Name);
|
||||
}
|
||||
ItemIdentifiers[variant].Add(itemIdentifier);
|
||||
ShowItemPreview[variant][itemIdentifier] = itemElement.GetAttributeBool("showpreview", true);
|
||||
}
|
||||
loadItemNames(itemElement, itemNames);
|
||||
loadItemIdentifiers(itemElement, variant);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -536,7 +536,6 @@ namespace Barotrauma
|
||||
|
||||
|
||||
private readonly List<Body> contactBodies = new List<Body>();
|
||||
private List<Body> ignoredBodies;
|
||||
/// <summary>
|
||||
/// Returns true if the attack successfully hit something. If the distance is not given, it will be calculated.
|
||||
/// </summary>
|
||||
@@ -556,20 +555,11 @@ namespace Barotrauma
|
||||
case HitDetection.Distance:
|
||||
if (dist < attack.DamageRange)
|
||||
{
|
||||
if (ignoredBodies == null)
|
||||
{
|
||||
ignoredBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody).ToList();
|
||||
ignoredBodies.Add(character.AnimController.Collider.FarseerBody);
|
||||
}
|
||||
|
||||
structureBody = Submarine.PickBody(
|
||||
SimPosition, attackSimPos,
|
||||
ignoredBodies, Physics.CollisionWall);
|
||||
|
||||
if (damageTarget is Item)
|
||||
structureBody = Submarine.PickBody(SimPosition, attackSimPos, collisionCategory: Physics.CollisionWall | Physics.CollisionLevel, allowInsideFixture: true);
|
||||
if (damageTarget is Item i && i.GetComponent<Items.Components.Door>() != null)
|
||||
{
|
||||
// If the attack is aimed to an item and hits an item, it's successful.
|
||||
// Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example.
|
||||
// Ignore blocking checks on doors, because it causes cases where a Mudraptor cannot hit the hatch, for example.
|
||||
wasHit = true;
|
||||
}
|
||||
else if (damageTarget is Structure wall && structureBody != null &&
|
||||
|
||||
@@ -464,7 +464,7 @@ namespace Barotrauma
|
||||
private bool TryAddTarget(XElement targetElement, out TargetParams target)
|
||||
{
|
||||
string tag = targetElement.GetAttributeString("tag", null);
|
||||
if (!CheckTag(tag))
|
||||
if (!HasTag(tag))
|
||||
{
|
||||
target = null;
|
||||
DebugConsole.ThrowError($"Multiple targets with the same tag ('{tag}') defined! Only the first will be used!");
|
||||
@@ -487,15 +487,18 @@ namespace Barotrauma
|
||||
if (TryAddTarget(element, out targetParams))
|
||||
{
|
||||
Element.Add(element);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return targetParams != null;
|
||||
}
|
||||
|
||||
private bool CheckTag(string tag)
|
||||
public bool HasTag(string tag)
|
||||
{
|
||||
if (tag == null) { return false; }
|
||||
tag = tag.ToLowerInvariant();
|
||||
return targets.None(t => t.Tag == tag);
|
||||
return targets.None(t => t.Tag.Equals(tag, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public bool RemoveTarget(TargetParams target) => RemoveSubParam(target, targets);
|
||||
|
||||
Reference in New Issue
Block a user