(7788ec72a) Test issuing orders automatically.
This commit is contained in:
@@ -14,11 +14,6 @@ namespace Barotrauma
|
||||
{
|
||||
public static bool DisableEnemyAI;
|
||||
|
||||
/// <summary>
|
||||
/// Enable the character to attack the outposts and the characters inside them. Disabled by default.
|
||||
/// </summary>
|
||||
public bool TargetOutposts;
|
||||
|
||||
class WallTarget
|
||||
{
|
||||
public Vector2 Position;
|
||||
@@ -122,7 +117,6 @@ namespace Barotrauma
|
||||
private readonly float memoryFadeTime = 0.5f;
|
||||
|
||||
public LatchOntoAI LatchOntoAI { get; private set; }
|
||||
public SwarmBehavior SwarmBehavior { get; private set; }
|
||||
|
||||
public bool AttackHumans
|
||||
{
|
||||
@@ -216,10 +210,6 @@ namespace Barotrauma
|
||||
case "latchonto":
|
||||
LatchOntoAI = new LatchOntoAI(subElement, this);
|
||||
break;
|
||||
case "swarm":
|
||||
case "swarmbehavior":
|
||||
SwarmBehavior = new SwarmBehavior(subElement, this);
|
||||
break;
|
||||
case "targetpriority":
|
||||
targetingPriorities.Add(subElement.GetAttributeString("tag", "").ToLowerInvariant(), new TargetingPriority(subElement));
|
||||
break;
|
||||
@@ -320,9 +310,8 @@ namespace Barotrauma
|
||||
{
|
||||
State = AIState.Idle;
|
||||
}
|
||||
else if (Character.Health < fleeHealthThreshold && SwarmBehavior == null)
|
||||
else if (Character.Health < fleeHealthThreshold)
|
||||
{
|
||||
// Don't flee from damage if in a swarm.
|
||||
State = AIState.Escape;
|
||||
}
|
||||
else if (targetingPriority != null)
|
||||
@@ -331,6 +320,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
LatchOntoAI?.Update(this, deltaTime);
|
||||
|
||||
if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed))
|
||||
{
|
||||
State = AIState.Idle;
|
||||
@@ -369,14 +360,11 @@ namespace Barotrauma
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
LatchOntoAI?.Update(this, deltaTime);
|
||||
IsSteeringThroughGap = false;
|
||||
if (SwarmBehavior != null)
|
||||
{
|
||||
SwarmBehavior.IsActive = State == AIState.Idle && Character.CurrentHull == null;
|
||||
SwarmBehavior.Refresh();
|
||||
SwarmBehavior.UpdateSteering(deltaTime);
|
||||
}
|
||||
// Just some debug code that makes the characters to follow the mouse cursor
|
||||
//run = true;
|
||||
//Vector2 mousePos = ConvertUnits.ToSimUnits(Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition));
|
||||
//steeringManager.SteeringSeek(mousePos, Character.AnimController.GetCurrentSpeed(run));
|
||||
|
||||
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run));
|
||||
}
|
||||
|
||||
@@ -499,7 +487,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsLatchedOnSub)
|
||||
if (!IsProperlyLatchedOnSub)
|
||||
{
|
||||
UpdateWallTarget();
|
||||
}
|
||||
@@ -558,7 +546,7 @@ namespace Barotrauma
|
||||
{
|
||||
WallSection section = wallTarget.Structure.GetSection(wallTarget.SectionIndex);
|
||||
Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true);
|
||||
if (section?.gap != null && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime))
|
||||
if (section?.gap != null && section.gap.IsRoomToRoom && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -799,21 +787,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSteeringThroughGap { get; private set; }
|
||||
private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime)
|
||||
{
|
||||
IsSteeringThroughGap = true;
|
||||
SelectedAiTarget = wall.AiTarget;
|
||||
wallTarget = null;
|
||||
LatchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
Hull targetHull = section.gap?.FlowTargetHull;
|
||||
float distance = Vector2.Distance(Character.WorldPosition, targetWorldPos);
|
||||
float maxDistance = Math.Min(wall.Rect.Width, wall.Rect.Height);
|
||||
if (distance > maxDistance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (targetHull != null)
|
||||
{
|
||||
if (wall.IsHorizontal)
|
||||
@@ -824,7 +800,16 @@ namespace Barotrauma
|
||||
{
|
||||
targetWorldPos.X = targetHull.WorldRect.Center.X;
|
||||
}
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
|
||||
LatchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
if (steeringManager is IndoorsSteeringManager)
|
||||
{
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetWorldPos));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1056,43 +1041,19 @@ namespace Barotrauma
|
||||
#region Targeting
|
||||
private bool IsLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
//goes through all the AItargets, evaluates how preferable it is to attack the target,
|
||||
//whether the Character can see/hear the target and chooses the most preferable target within
|
||||
//sight/hearing range
|
||||
public AITarget UpdateTargets(Character character, out TargetingPriority priority)
|
||||
{
|
||||
if ((SelectedAiTarget != null || wallTarget != null) && IsLatchedOnSub)
|
||||
if (IsProperlyLatchedOnSub)
|
||||
{
|
||||
var wall = SelectedAiTarget.Entity as Structure;
|
||||
if (wall == null)
|
||||
{
|
||||
wall = wallTarget?.Structure;
|
||||
}
|
||||
// The target is not a wall or it's not the same as we are attached to -> release
|
||||
bool releaseTarget = wall == null || !wall.Bodies.Contains(LatchOntoAI.AttachJoints[0].BodyB);
|
||||
if (!releaseTarget)
|
||||
{
|
||||
for (int i = 0; i < wall.Sections.Length; i++)
|
||||
{
|
||||
if (CanPassThroughHole(wall, i))
|
||||
{
|
||||
releaseTarget = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (releaseTarget)
|
||||
{
|
||||
SelectedAiTarget = null;
|
||||
wallTarget = null;
|
||||
LatchOntoAI.DeattachFromBody();
|
||||
}
|
||||
else if (SelectedAiTarget?.Entity == wallTarget?.Structure)
|
||||
{
|
||||
// If attached to a valid target, just keep the target.
|
||||
// Priority not used in this case.
|
||||
priority = null;
|
||||
return SelectedAiTarget;
|
||||
}
|
||||
// If attached to a valid target, just keep the target.
|
||||
// Priority not used in this case.
|
||||
priority = null;
|
||||
return SelectedAiTarget;
|
||||
}
|
||||
AITarget newTarget = null;
|
||||
priority = null;
|
||||
@@ -1107,10 +1068,9 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
if (target.Type == AITarget.TargetType.HumanOnly) { continue; }
|
||||
if (!TargetOutposts)
|
||||
{
|
||||
if (target.Entity.Submarine != null && target.Entity.Submarine.IsOutpost) { continue; }
|
||||
}
|
||||
// Don't attack outposts.
|
||||
if (target.Entity.Submarine != null && target.Entity.Submarine.IsOutpost) { continue; }
|
||||
|
||||
Character targetCharacter = target.Entity as Character;
|
||||
//ignore the aitarget if it is the Character itself
|
||||
if (targetCharacter == character) continue;
|
||||
@@ -1175,7 +1135,7 @@ namespace Barotrauma
|
||||
else if (target.Entity != null)
|
||||
{
|
||||
//skip the target if it's a room and the character is already inside a sub
|
||||
if (character.CurrentHull != null && target.Entity is Hull) { continue; }
|
||||
if (character.CurrentHull != null && target.Entity is Hull) continue;
|
||||
|
||||
Door door = null;
|
||||
if (target.Entity is Item item)
|
||||
@@ -1204,37 +1164,23 @@ namespace Barotrauma
|
||||
// Ignore structures that doesn't have a body (not walls)
|
||||
continue;
|
||||
}
|
||||
if (s.IsPlatform)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (character.CurrentHull != null)
|
||||
{
|
||||
// Ignore walls when inside.
|
||||
continue;
|
||||
}
|
||||
valueModifier = 1;
|
||||
float wallMaxHealth = 400; // Anything more than this is ignored -> 200 = 1
|
||||
// Prefer weaker targets.
|
||||
valueModifier *= MathHelper.Lerp(1.5f, 0.5f, MathUtils.InverseLerp(0, 1, s.Health / wallMaxHealth));
|
||||
// Ignore walls when inside.
|
||||
valueModifier = character.CurrentHull == null ? 1 : 0;
|
||||
if (aggressiveBoarding)
|
||||
{
|
||||
var hulls = s.Submarine.GetHulls(false);
|
||||
for (int i = 0; i < s.Sections.Length; i++)
|
||||
{
|
||||
var section = s.Sections[i];
|
||||
if (section.gap != null)
|
||||
if (CanPassThroughHole(s, i))
|
||||
{
|
||||
if (CanPassThroughHole(s, i))
|
||||
{
|
||||
bool leadsInside = !section.gap.IsRoomToRoom && section.gap.FlowTargetHull != null && hulls.Any(h => h.Rect.Intersects(section.rect));
|
||||
valueModifier *= leadsInside ? 5 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// up to 100% priority increase for every gap in the wall
|
||||
valueModifier *= 1 + section.gap.Open;
|
||||
}
|
||||
// Ignore walls that can be passed through
|
||||
valueModifier = 0;
|
||||
break;
|
||||
}
|
||||
else if (section.gap != null)
|
||||
{
|
||||
// up to 100% priority increase for every gap in the wall
|
||||
valueModifier *= 1 + section.gap.Open;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -54,6 +55,28 @@ namespace Barotrauma
|
||||
objectiveManager = new AIObjectiveManager(c);
|
||||
objectiveManager.AddObjective(new AIObjectiveFindSafety(c));
|
||||
objectiveManager.AddObjective(new AIObjectiveIdle(c));
|
||||
// TODO: do this only when the player hasn't issued an order for a while
|
||||
foreach (var automaticOrder in c.Info.Job.Prefab.AutomaticOrders)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == automaticOrder.aiTag);
|
||||
// TODO: Similar code is used in CrewManager:815-> DRY
|
||||
var matchingItems = orderPrefab.ItemIdentifiers.Any() ?
|
||||
Item.ItemList.FindAll(it => orderPrefab.ItemIdentifiers.Contains(it.Prefab.Identifier) || it.HasTag(orderPrefab.ItemIdentifiers)) :
|
||||
Item.ItemList.FindAll(it => it.Components.Any(ic => ic.GetType() == orderPrefab.ItemComponentType));
|
||||
matchingItems.RemoveAll(it => it.Submarine != c.Submarine);
|
||||
var item = matchingItems.GetRandom();
|
||||
var order = new Order(orderPrefab, item ?? c.CurrentHull as Entity, item?.Components.FirstOrDefault(ic => ic.GetType() == orderPrefab.ItemComponentType));
|
||||
SetOrder(order, automaticOrder.option, c, true);
|
||||
|
||||
//#if CLIENT
|
||||
// GameMain.GameSession?.CrewManager?.SetCharacterOrder(c, order, automaticOrder.option, c);
|
||||
//#endif
|
||||
|
||||
// if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.AddOrder(order, order.FadeOutTime))
|
||||
// {
|
||||
// Character.Speak(order.GetChatMessage("", Character.CurrentHull?.RoomName, givingOrderToSelf: true), ChatMessageType.Order);
|
||||
// }
|
||||
}
|
||||
|
||||
updateObjectiveTimer = Rand.Range(0.0f, UpdateObjectiveInterval);
|
||||
|
||||
@@ -267,7 +290,7 @@ namespace Barotrauma
|
||||
if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.AddOrder(newOrder, newOrder.FadeOutTime))
|
||||
{
|
||||
Character.Speak(
|
||||
newOrder.GetChatMessage("", Character.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order);
|
||||
newOrder.GetChatMessage("", Character.CurrentHull?.RoomName, givingOrderToSelf: false), ChatMessageType.Order);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,18 +309,18 @@ namespace Barotrauma
|
||||
|
||||
if (Character.PressureTimer > 50.0f && Character.CurrentHull != null)
|
||||
{
|
||||
Character.Speak(TextManager.Get("DialogPressure").Replace("[roomname]", Character.CurrentHull.DisplayName), null, 0, "pressure", 30.0f);
|
||||
Character.Speak(TextManager.Get("DialogPressure").Replace("[roomname]", Character.CurrentHull.RoomName), null, 0, "pressure", 30.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnAttacked(Character attacker, AttackResult attackResult)
|
||||
{
|
||||
// Damage from falling etc.
|
||||
if (Character.LastDamageSource == null) { return; }
|
||||
float damage = attackResult.Damage;
|
||||
if (damage <= 0) { return; }
|
||||
if (attacker == null || attacker.IsDead || attacker.Removed)
|
||||
{
|
||||
// Ignore damage from falling etc that we shouldn't react to.
|
||||
if (Character.LastDamageSource == null) { return; }
|
||||
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
|
||||
}
|
||||
else if (IsFriendly(attacker))
|
||||
|
||||
@@ -223,10 +223,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
|
||||
|
||||
//only humanoids can climb ladders
|
||||
if (!isDiving && character.AnimController is HumanoidAnimController && IsNextLadderSameAsCurrent)
|
||||
if (character.AnimController is HumanoidAnimController && IsNextLadderSameAsCurrent)
|
||||
{
|
||||
if (character.SelectedConstruction != currentPath.CurrentNode.Ladders.Item &&
|
||||
currentPath.CurrentNode.Ladders.Item.IsInsideTrigger(character.WorldPosition))
|
||||
@@ -236,7 +234,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var collider = character.AnimController.Collider;
|
||||
if (character.IsClimbing && !isDiving)
|
||||
if (character.IsClimbing)
|
||||
{
|
||||
Vector2 diff = currentPath.CurrentNode.SimPosition - pos;
|
||||
bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent;
|
||||
@@ -280,12 +278,6 @@ namespace Barotrauma
|
||||
}
|
||||
else if (character.AnimController.InWater)
|
||||
{
|
||||
// If the character is underwater, we don't need the ladders anymore
|
||||
if (character.IsClimbing && isDiving)
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
if (Vector2.DistanceSquared(pos, currentPath.CurrentNode.SimPosition) < MathUtils.Pow(collider.radius * 3, 2))
|
||||
{
|
||||
currentPath.SkipToNextNode();
|
||||
@@ -396,18 +388,6 @@ namespace Barotrauma
|
||||
buttonPressCooldown = ButtonPressInterval;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!door.HasRequiredItems(character, false) && shouldBeOpen)
|
||||
{
|
||||
currentPath.Unreachable = true;
|
||||
return;
|
||||
}
|
||||
|
||||
door.Item.TryInteract(character, false, true, true);
|
||||
buttonPressCooldown = ButtonPressInterval;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,18 +408,20 @@ namespace Barotrauma
|
||||
//door closed and the character can't open doors -> node can't be traversed
|
||||
if (!canOpenDoors || character.LockHands) { return null; }
|
||||
|
||||
if (!canBreakDoors)
|
||||
{
|
||||
//door closed and the character can't open doors -> node can't be traversed
|
||||
if (!canOpenDoors || character.LockHands) return null;
|
||||
|
||||
var doorButtons = nextNode.Waypoint.ConnectedDoor.Item.GetConnectedComponents<Controller>();
|
||||
if (!doorButtons.Any())
|
||||
{
|
||||
if (!nextNode.Waypoint.ConnectedDoor.HasRequiredItems(character, false)) { return null; }
|
||||
}
|
||||
if (!doorButtons.Any()) return null;
|
||||
|
||||
foreach (Controller button in doorButtons)
|
||||
{
|
||||
if (Math.Sign(button.Item.Position.X - nextNode.Waypoint.Position.X) !=
|
||||
Math.Sign(node.Position.X - nextNode.Position.X)) { continue; }
|
||||
Math.Sign(node.Position.X - nextNode.Position.X)) continue;
|
||||
|
||||
if (!button.HasRequiredItems(character, false)) { return null; }
|
||||
if (!button.HasRequiredItems(character, false)) return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Barotrauma
|
||||
get { return attachJoints.Count > 0; }
|
||||
}
|
||||
|
||||
public bool IsAttachedToSub => IsAttached && (attachTargetBody?.UserData is Submarine || attachTargetBody?.UserData is Entity entity && entity.Submarine != null);
|
||||
public bool IsAttachedToSub => IsAttached && attachTargetBody?.UserData is Entity entity && (entity is Submarine sub || entity?.Submarine != null);
|
||||
|
||||
public LatchOntoAI(XElement element, EnemyAIController enemyAI)
|
||||
{
|
||||
@@ -190,7 +190,7 @@ namespace Barotrauma
|
||||
case AIController.AIState.Attack:
|
||||
if (enemyAI.AttackingLimb != null)
|
||||
{
|
||||
if (attachToSub && !enemyAI.IsSteeringThroughGap && wallAttachPos != Vector2.Zero && attachTargetBody != null)
|
||||
if (attachToSub && wallAttachPos != Vector2.Zero && attachTargetBody != null)
|
||||
{
|
||||
// is not attached or is attached to something else
|
||||
if (!IsAttached || IsAttached && attachJoints[0].BodyB == attachTargetBody)
|
||||
|
||||
@@ -44,7 +44,6 @@ namespace Barotrauma
|
||||
private AIObjectiveContainItem reloadWeaponObjective;
|
||||
private Hull retreatTarget;
|
||||
private AIObjectiveGoTo retreatObjective;
|
||||
private AIObjectiveFindSafety findSafety;
|
||||
|
||||
private float coolDownTimer;
|
||||
|
||||
@@ -61,9 +60,7 @@ namespace Barotrauma
|
||||
{
|
||||
Enemy = enemy;
|
||||
coolDownTimer = CoolDown;
|
||||
findSafety = HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>();
|
||||
findSafety.Priority = 0;
|
||||
findSafety.unreachable.Clear();
|
||||
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 0;
|
||||
Mode = mode;
|
||||
if (Enemy == null)
|
||||
{
|
||||
@@ -178,7 +175,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (retreatTarget == null || (retreatObjective != null && !retreatObjective.CanBeCompleted))
|
||||
{
|
||||
retreatTarget = findSafety.FindBestHull(new List<Hull>() { character.CurrentHull });
|
||||
retreatTarget = HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().FindBestHull(new List<Hull>() { character.CurrentHull });
|
||||
}
|
||||
if (retreatTarget != null)
|
||||
{
|
||||
@@ -280,6 +277,7 @@ namespace Barotrauma
|
||||
{
|
||||
abandon = true;
|
||||
SteeringManager.Reset();
|
||||
//HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
|
||||
public override bool IsCompleted()
|
||||
|
||||
+10
-28
@@ -79,44 +79,26 @@ namespace Barotrauma
|
||||
foreach (FireSource fs in targetHull.FireSources)
|
||||
{
|
||||
bool inRange = fs.IsInDamageRange(character, MathHelper.Clamp(fs.DamageRange * 1.5f, extinguisher.Range * 0.5f, extinguisher.Range));
|
||||
bool move = !inRange;
|
||||
if (inRange || useExtinquisherTimer > 0.0f)
|
||||
if (targetHull == character.CurrentHull && (inRange || useExtinquisherTimer > 0.0f))
|
||||
{
|
||||
useExtinquisherTimer += deltaTime;
|
||||
if (useExtinquisherTimer > 2.0f)
|
||||
{
|
||||
useExtinquisherTimer = 0.0f;
|
||||
}
|
||||
if (useExtinquisherTimer > 2.0f) useExtinquisherTimer = 0.0f;
|
||||
|
||||
character.AIController.SteeringManager.Reset();
|
||||
character.CursorPosition = fs.Position;
|
||||
if (extinguisher.Item.RequireAimToUse)
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
}
|
||||
Limb sightLimb = null;
|
||||
if (character.Inventory.IsInLimbSlot(extinguisherItem, InvSlotType.RightHand))
|
||||
extinguisher.Use(deltaTime, character);
|
||||
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
{
|
||||
sightLimb = character.AnimController.GetLimb(LimbType.RightHand);
|
||||
}
|
||||
else if (character.Inventory.IsInLimbSlot(extinguisherItem, InvSlotType.LeftHand))
|
||||
{
|
||||
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
|
||||
}
|
||||
if (!character.CanSeeTarget(fs, sightLimb))
|
||||
{
|
||||
move = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
move = false;
|
||||
extinguisher.Use(deltaTime, character);
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogPutOutFire").Replace("[roomname]", targetHull.Name), null, 0, "putoutfire", 10.0f);
|
||||
}
|
||||
character.Speak(TextManager.Get("DialogPutOutFire").Replace("[roomname]", targetHull.Name), null, 0, "putoutfire", 10.0f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (move)
|
||||
else
|
||||
{
|
||||
//go to the first firesource
|
||||
if (gotoObjective == null || !gotoObjective.CanBeCompleted || gotoObjective.IsCompleted())
|
||||
@@ -127,8 +109,8 @@ namespace Barotrauma
|
||||
{
|
||||
gotoObjective.TryComplete(deltaTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-17
@@ -18,7 +18,7 @@ namespace Barotrauma
|
||||
const float SearchHullInterval = 3.0f;
|
||||
const float clearUnreachableInterval = 30;
|
||||
|
||||
public readonly List<Hull> unreachable = new List<Hull>();
|
||||
private List<Hull> unreachable = new List<Hull>();
|
||||
|
||||
private float currenthullSafety;
|
||||
private float unreachableClearTimer;
|
||||
@@ -60,16 +60,11 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
divingGearObjective = null;
|
||||
// Reduce the timer so that we get a safe hull target faster.
|
||||
searchHullTimer = Math.Min(1, searchHullTimer);
|
||||
// Reset the timer so that we get a safe hull target.
|
||||
searchHullTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||
{
|
||||
searchHullTimer = Math.Min(1, searchHullTimer);
|
||||
}
|
||||
|
||||
if (unreachableClearTimer > 0)
|
||||
{
|
||||
unreachableClearTimer -= deltaTime;
|
||||
@@ -84,7 +79,7 @@ namespace Barotrauma
|
||||
{
|
||||
searchHullTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
else if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||
{
|
||||
var bestHull = FindBestHull();
|
||||
if (bestHull != null && bestHull != currentHull)
|
||||
@@ -193,17 +188,10 @@ namespace Barotrauma
|
||||
float dist = Math.Abs(character.WorldPosition.X - hull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - hull.WorldPosition.Y) * 2.0f;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.9f, MathUtils.InverseLerp(0, 10000, dist));
|
||||
hullSafety *= distanceFactor;
|
||||
//skip the hull if the safety is already less than the best hull
|
||||
//(no need to do the expensive pathfinding if we already know we're not going to choose this hull)
|
||||
if (hullSafety < bestValue) { continue; }
|
||||
// Each unsafe node reduces the hull safety value.
|
||||
// Ignore current hull, because otherwise the would block all paths from the current hull to the target hull.
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition);
|
||||
if (path.Unreachable)
|
||||
{
|
||||
unreachable.Add(hull);
|
||||
continue;
|
||||
}
|
||||
if (path.Unreachable) { continue; }
|
||||
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
|
||||
hullSafety /= 1 + unsafeNodes;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
@@ -231,6 +219,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Huge preference for closer targets
|
||||
float distance = Vector2.DistanceSquared(character.WorldPosition, hull.WorldPosition);
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, MathUtils.Pow(100000, 2), distance));
|
||||
|
||||
@@ -101,22 +101,10 @@ namespace Barotrauma
|
||||
HumanAIController.AnimController.Crouching = true;
|
||||
}
|
||||
|
||||
//float reach = HumanAIController.AnimController.ArmLength + ConvertUnits.ToSimUnits(repairTool.Range);
|
||||
float reach = ConvertUnits.ToSimUnits(repairTool.Range);
|
||||
bool canReach = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach;
|
||||
if (canReach)
|
||||
{
|
||||
Limb sightLimb = null;
|
||||
if (character.Inventory.IsInLimbSlot(repairTool.Item, InvSlotType.RightHand))
|
||||
{
|
||||
sightLimb = character.AnimController.GetLimb(LimbType.RightHand);
|
||||
}
|
||||
else if (character.Inventory.IsInLimbSlot(repairTool.Item, InvSlotType.LeftHand))
|
||||
{
|
||||
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
|
||||
}
|
||||
canReach = character.CanSeeTarget(leak, sightLimb);
|
||||
}
|
||||
else
|
||||
bool cannotReach = ConvertUnits.ToSimUnits(gapDiff.Length()) > reach;
|
||||
if (cannotReach)
|
||||
{
|
||||
if (gotoObjective != null)
|
||||
{
|
||||
@@ -142,6 +130,7 @@ namespace Barotrauma
|
||||
AddSubObjective(gotoObjective);
|
||||
}
|
||||
}
|
||||
canReach = character.CanSeeTarget(leak, sightLimb);
|
||||
}
|
||||
if (gotoObjective == null || gotoObjective.IsCompleted())
|
||||
{
|
||||
|
||||
@@ -26,9 +26,6 @@ namespace Barotrauma
|
||||
private float standStillTimer;
|
||||
private float walkDuration;
|
||||
|
||||
private readonly List<Hull> targetHulls = new List<Hull>(20);
|
||||
private readonly List<float> hullWeights = new List<float>(20);
|
||||
|
||||
public AIObjectiveIdle(Character character) : base(character, "")
|
||||
{
|
||||
standStillTimer = Rand.Range(-10.0f, 10.0f);
|
||||
@@ -109,7 +106,7 @@ namespace Barotrauma
|
||||
if (isCurrentHullOK)
|
||||
{
|
||||
// Check that there is no unsafe or forbidden hulls on the way to the target
|
||||
// Only do this when the current hull is ok, because otherwise would block all paths from the current hull to the target hull.
|
||||
// Only do this when the current hull is ok, because otherwise the would block all paths from the current hull to the target hull.
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, randomHull.SimPosition);
|
||||
if (path.Unreachable ||
|
||||
path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull)))
|
||||
@@ -131,8 +128,8 @@ namespace Barotrauma
|
||||
character.AIController.SelectTarget(currentTarget.AiTarget);
|
||||
string errorMsg = null;
|
||||
#if DEBUG
|
||||
bool isRoomNameFound = currentTarget.DisplayName != null;
|
||||
errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.DisplayName : currentTarget.ToString()) + ")";
|
||||
bool isRoomNameFound = currentTarget.RoomName != null;
|
||||
errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.RoomName : currentTarget.ToString()) + ")";
|
||||
#endif
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, errorMsg);
|
||||
PathSteering.SetPath(path);
|
||||
@@ -233,9 +230,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Hull> targetHulls = new List<Hull>(20);
|
||||
private readonly List<float> hullWeights = new List<float>(20);
|
||||
|
||||
private void FindTargetHulls()
|
||||
{
|
||||
bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull);
|
||||
|
||||
targetHulls.Clear();
|
||||
hullWeights.Clear();
|
||||
foreach (var hull in Hull.hullList)
|
||||
@@ -265,6 +266,7 @@ namespace Barotrauma
|
||||
hullWeights.Add(weight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private bool IsForbidden(Hull hull)
|
||||
|
||||
+5
-5
@@ -83,12 +83,12 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (repairTool == null)
|
||||
{
|
||||
FindRepairTool();
|
||||
}
|
||||
if (character.CurrentHull == Item.CurrentHull && character.CanInteractWith(Item))
|
||||
if (character.CanInteractWith(Item))
|
||||
{
|
||||
if (repairTool == null)
|
||||
{
|
||||
FindRepairTool();
|
||||
}
|
||||
if (repairTool != null)
|
||||
{
|
||||
OperateRepairTool(deltaTime);
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Barotrauma
|
||||
if (character.SelectedCharacter == null)
|
||||
{
|
||||
character?.Speak(TextManager.Get("DialogFoundUnconsciousTarget")
|
||||
.Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.DisplayName),
|
||||
.Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.RoomName),
|
||||
null, 1.0f,
|
||||
"foundunconscioustarget" + targetCharacter.Name, 60.0f);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
// TODO: Ensure that this works well enough. Consider using AIObjectiveLoop class.
|
||||
class AIObjectiveRescueAll : AIObjective
|
||||
{
|
||||
public override string DebugTag => "rescue all";
|
||||
|
||||
@@ -293,27 +293,6 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
//stop dragging if there's something between the pull limb and the target
|
||||
Vector2 sourceSimPos = mouthLimb.SimPosition;
|
||||
Vector2 targetSimPos = target.SimPosition;
|
||||
if (character.Submarine != null && character.SelectedCharacter.Submarine == null)
|
||||
{
|
||||
targetSimPos -= character.Submarine.SimPosition;
|
||||
}
|
||||
else if (character.Submarine == null && character.SelectedCharacter.Submarine != null)
|
||||
{
|
||||
sourceSimPos -= character.SelectedCharacter.Submarine.SimPosition;
|
||||
}
|
||||
var body = Submarine.CheckVisibility(sourceSimPos, targetSimPos, ignoreSubs: true);
|
||||
if (body != null)
|
||||
{
|
||||
character.DeselectCharacter();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Character targetCharacter = target;
|
||||
float eatSpeed = character.Mass / targetCharacter.Mass * 0.1f;
|
||||
eatTimer += deltaTime * eatSpeed;
|
||||
@@ -609,7 +588,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
float prevWalkPos = WalkPos;
|
||||
WalkPos -= MainLimb.LinearVelocity.X * (CurrentAnimationParams.CycleSpeed / RagdollParams.JointScale / 100.0f);
|
||||
|
||||
Vector2 transformedStepSize = Vector2.Zero;
|
||||
@@ -645,11 +623,6 @@ namespace Barotrauma
|
||||
bool playFootstepSound = false;
|
||||
if (limb.type == LimbType.LeftFoot)
|
||||
{
|
||||
if (Math.Sign(Math.Sin(prevWalkPos)) > 0 && Math.Sign(transformedStepSize.Y) < 0)
|
||||
{
|
||||
playFootstepSound = true;
|
||||
}
|
||||
|
||||
limb.DebugRefPos = footPos + Vector2.UnitX * movement.X * 0.1f;
|
||||
limb.DebugTargetPos = footPos + new Vector2(
|
||||
transformedStepSize.X + movement.X * 0.1f,
|
||||
@@ -658,20 +631,13 @@ namespace Barotrauma
|
||||
}
|
||||
else if (limb.type == LimbType.RightFoot)
|
||||
{
|
||||
if (Math.Sign(Math.Sin(prevWalkPos)) < 0 && Math.Sign(transformedStepSize.Y) > 0)
|
||||
{
|
||||
playFootstepSound = true;
|
||||
}
|
||||
|
||||
limb.DebugRefPos = footPos + Vector2.UnitX * movement.X * 0.1f;
|
||||
limb.DebugTargetPos = footPos + new Vector2(
|
||||
-transformedStepSize.X + movement.X * 0.1f,
|
||||
(-transformedStepSize.Y > 0.0f) ? -transformedStepSize.Y : 0.0f);
|
||||
limb.MoveToPos(limb.DebugTargetPos, FootMoveForce);
|
||||
}
|
||||
#if CLIENT
|
||||
if (playFootstepSound) { PlayImpactSound(limb); }
|
||||
#endif
|
||||
|
||||
if (CurrentGroundedParams.FootAnglesInRadians.ContainsKey(limb.limbParams.ID))
|
||||
{
|
||||
SmoothRotateWithoutWrapping(limb,
|
||||
@@ -751,6 +717,22 @@ namespace Barotrauma
|
||||
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
|
||||
}
|
||||
|
||||
private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque)
|
||||
{
|
||||
//make sure the angle "has the same number of revolutions" as the reference limb
|
||||
//(e.g. we don't want to rotate the legs to 0 if the torso is at 360, because that'd blow up the hip joints)
|
||||
while (referenceLimb.Rotation - angle > MathHelper.TwoPi)
|
||||
{
|
||||
angle += MathHelper.TwoPi;
|
||||
}
|
||||
while (referenceLimb.Rotation - angle < -MathHelper.TwoPi)
|
||||
{
|
||||
angle -= MathHelper.TwoPi;
|
||||
}
|
||||
|
||||
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
|
||||
}
|
||||
|
||||
public override void Flip()
|
||||
{
|
||||
base.Flip();
|
||||
|
||||
@@ -534,12 +534,7 @@ namespace Barotrauma
|
||||
|
||||
Limb leftLeg = GetLimb(LimbType.LeftLeg);
|
||||
Limb rightLeg = GetLimb(LimbType.RightLeg);
|
||||
|
||||
float limpAmount =
|
||||
character.CharacterHealth.GetAfflictionStrength("damage", leftFoot, true) +
|
||||
character.CharacterHealth.GetAfflictionStrength("damage", rightFoot, true);
|
||||
limpAmount = MathHelper.Clamp(limpAmount / 100.0f, 0.0f, 1.0f);
|
||||
|
||||
|
||||
float walkCycleMultiplier = 1.0f;
|
||||
if (Stairs != null)
|
||||
{
|
||||
@@ -560,8 +555,7 @@ namespace Barotrauma
|
||||
//TODO: take into account that the feet aren't necessarily in CurrentHull
|
||||
//full slowdown (1.5f) when water is up to the torso
|
||||
surfaceY = ConvertUnits.ToSimUnits(currentHull.Surface);
|
||||
float bottomPos = Math.Max(colliderPos.Y, currentHull.Rect.Y - currentHull.Rect.Height);
|
||||
slowdownAmount = MathHelper.Clamp((surfaceY - bottomPos) / TorsoPosition.Value, 0.0f, 1.0f) * 1.5f;
|
||||
slowdownAmount = MathHelper.Clamp((surfaceY - colliderPos.Y) / TorsoPosition.Value, 0.0f, 1.0f) * 1.5f;
|
||||
}
|
||||
|
||||
float maxSpeed = Math.Max(TargetMovement.Length() - slowdownAmount, 1.0f);
|
||||
@@ -570,10 +564,11 @@ namespace Barotrauma
|
||||
|
||||
float walkPosX = (float)Math.Cos(WalkPos);
|
||||
float walkPosY = (float)Math.Sin(WalkPos);
|
||||
|
||||
|
||||
|
||||
Vector2 stepSize = StepSize.Value;
|
||||
stepSize.X *= walkPosX;
|
||||
stepSize.Y *= walkPosY;
|
||||
stepSize.Y *= walkPosY;
|
||||
|
||||
float footMid = colliderPos.X;
|
||||
if (limpAmount > 0.0f)
|
||||
@@ -594,7 +589,7 @@ namespace Barotrauma
|
||||
movement.Y = 0.0f;
|
||||
|
||||
if (torso == null) { return; }
|
||||
|
||||
|
||||
bool isNotRemote = true;
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) isNotRemote = !character.IsRemotePlayer;
|
||||
|
||||
@@ -698,7 +693,7 @@ namespace Barotrauma
|
||||
|
||||
//make the character limp if the feet are damaged
|
||||
float footAfflictionStrength = character.CharacterHealth.GetAfflictionStrength("damage", foot, true);
|
||||
footPos.X *= MathHelper.Lerp(1.0f, 0.75f, MathHelper.Clamp(footAfflictionStrength / 50.0f, 0.0f, 1.0f));
|
||||
footPos *= MathHelper.Lerp(1.0f, 0.5f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f));
|
||||
|
||||
if (onSlope && Stairs == null)
|
||||
{
|
||||
@@ -788,19 +783,13 @@ namespace Barotrauma
|
||||
|
||||
//get the upper arm to point downwards
|
||||
var arm = GetLimb(armType);
|
||||
if (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.1f, 0.1f), arm.Mass * 10.0f);
|
||||
|
||||
//get the elbow to a neutral rotation
|
||||
if (Math.Abs(hand.body.AngularVelocity) < 10.0f)
|
||||
{
|
||||
LimbJoint elbow =
|
||||
LimbJoint elbow =
|
||||
GetJointBetweenLimbs(armType, hand.type) ??
|
||||
GetJointBetweenLimbs(armType, foreArmType);
|
||||
hand.body.ApplyTorque(MathHelper.Clamp(-elbow.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * hand.Mass * 10.0f);
|
||||
}
|
||||
hand.body.ApplyTorque(-elbow.JointAngle * hand.Mass * 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1138,12 +1127,12 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
if (Math.Abs(leftFootPos - prevLeftFootPos) > stepHeight && leftFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
|
||||
{
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", leftFoot.WorldPosition, hullGuess: currentHull);
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: leftFoot.WorldPosition);
|
||||
leftFoot.LastImpactSoundTime = (float)Timing.TotalTime;
|
||||
}
|
||||
if (Math.Abs(rightFootPos - prevRightFootPos) > stepHeight && rightFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
|
||||
{
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", rightFoot.WorldPosition, hullGuess: currentHull);
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: rightFoot.WorldPosition);
|
||||
rightFoot.LastImpactSoundTime = (float)Timing.TotalTime;
|
||||
}
|
||||
#endif
|
||||
@@ -1194,8 +1183,7 @@ namespace Barotrauma
|
||||
isClimbing = false;
|
||||
}
|
||||
}
|
||||
else if ((character.IsKeyDown(InputType.Left) || character.IsKeyDown(InputType.Right)) &&
|
||||
(!character.IsKeyDown(InputType.Up) && !character.IsKeyDown(InputType.Down)))
|
||||
else if (character.IsKeyDown(InputType.Left) || character.IsKeyDown(InputType.Right))
|
||||
{
|
||||
isClimbing = false;
|
||||
}
|
||||
|
||||
-1
@@ -84,7 +84,6 @@ namespace Barotrauma
|
||||
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
|
||||
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
|
||||
{
|
||||
//DebugConsole.NewMessage("[RagollParams] Using the default folder.");
|
||||
folder = GetDefaultFolder(speciesName);
|
||||
}
|
||||
return folder;
|
||||
|
||||
@@ -674,6 +674,8 @@ namespace Barotrauma
|
||||
velocity -= ((Submarine)f2.Body.UserData).Velocity;
|
||||
}
|
||||
|
||||
if (character.Submarine == null && f2.Body.UserData is Submarine) velocity -= ((Submarine)f2.Body.UserData).Velocity;
|
||||
|
||||
float impact = Vector2.Dot(velocity, -normal);
|
||||
if (f1.Body == Collider.FarseerBody || !Collider.Enabled)
|
||||
{
|
||||
@@ -692,11 +694,6 @@ namespace Barotrauma
|
||||
character.AddDamage(impactPos, new List<Affliction>() { AfflictionPrefab.InternalDamage.Instantiate((impact - ImpactTolerance) * 10.0f) }, 0.0f, true);
|
||||
strongestImpact = Math.Max(strongestImpact, impact - ImpactTolerance);
|
||||
character.ApplyStatusEffects(ActionType.OnImpact, 1.0f);
|
||||
//briefly disable impact damage
|
||||
//otherwise the character will take damage multiple times when for example falling,
|
||||
//because we use the velocity of the collider to determine the impact
|
||||
//(i.e. the character would take damage until the collider hits the floor and stops)
|
||||
character.DisableImpactDamageTimer = 0.25f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -704,7 +701,7 @@ namespace Barotrauma
|
||||
ImpactProjSpecific(impact, f1.Body);
|
||||
}
|
||||
|
||||
public void SeverLimbJoint(LimbJoint limbJoint, bool playSound = true)
|
||||
public void SeverLimbJoint(LimbJoint limbJoint)
|
||||
{
|
||||
if (!limbJoint.CanBeSevered || limbJoint.IsSevered)
|
||||
{
|
||||
@@ -733,7 +730,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
partial void SeverLimbJointProjSpecific(LimbJoint limbJoint, bool playSound = true);
|
||||
partial void SeverLimbJointProjSpecific(LimbJoint limbJoint);
|
||||
|
||||
private void GetConnectedLimbs(List<Limb> connectedLimbs, List<LimbJoint> checkedJoints, Limb limb)
|
||||
{
|
||||
|
||||
@@ -80,13 +80,13 @@ namespace Barotrauma
|
||||
public HitDetection HitDetectionType { get; private set; }
|
||||
|
||||
[Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")]
|
||||
public AIBehaviorAfterAttack AfterAttack { get; set; }
|
||||
public AIBehaviorAfterAttack AfterAttack { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable(ToolTip = "Should the ai try to reverse when aiming with this attack?")]
|
||||
public bool Reverse { get; private set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target before the AI tries to attack.")]
|
||||
public float Range { get; set; }
|
||||
public float Range { get; private set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target to do damage. In distance based hit detection, the hit will be registered as soon as the target is within the damage range, unless the attack duration has expired.")]
|
||||
public float DamageRange { get; set; }
|
||||
@@ -95,19 +95,19 @@ namespace Barotrauma
|
||||
public float Duration { get; private set; }
|
||||
|
||||
[Serialize(5f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "How long the AI waits between the attacks.")]
|
||||
public float CoolDown { get; set; } = 5;
|
||||
public float CoolDown { get; private set; } = 5;
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "Used as the attack cooldown between different kind of attacks. Does not have effect, if set to 0.")]
|
||||
public float SecondaryCoolDown { get; set; } = 0;
|
||||
public float SecondaryCoolDown { get; private set; } = 0;
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2, ToolTip = "Random factor applied to all cooldowns. Example: 0.1 -> adds a random value between -10% and 10% of the cooldown. Min 0 (default), Max 1 (could disable or double the cooldown in extreme cases).")]
|
||||
public float CoolDownRandomFactor { get; private set; } = 0;
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)]
|
||||
public float StructureDamage { get; set; }
|
||||
public float StructureDamage { get; private set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
public float ItemDamage { get; set; }
|
||||
public float ItemDamage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Legacy support. Use Afflictions.
|
||||
@@ -283,7 +283,6 @@ namespace Barotrauma
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionName + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -293,7 +292,6 @@ namespace Barotrauma
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionIdentifier + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -426,7 +426,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanSpeak;
|
||||
private bool canSpeak;
|
||||
|
||||
private bool speechImpedimentSet;
|
||||
|
||||
@@ -436,7 +436,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!CanSpeak || IsUnconscious || Stun > 0.0f || IsDead) return 100.0f;
|
||||
if (!canSpeak || IsUnconscious || Stun > 0.0f || IsDead) return 100.0f;
|
||||
return speechImpediment;
|
||||
}
|
||||
set
|
||||
@@ -710,7 +710,7 @@ namespace Barotrauma
|
||||
displayName = TextManager.Get($"Character.{Path.GetFileName(Path.GetDirectoryName(file))}", true);
|
||||
|
||||
IsHumanoid = doc.Root.GetAttributeBool("humanoid", false);
|
||||
CanSpeak = doc.Root.GetAttributeBool("canspeak", false);
|
||||
canSpeak = doc.Root.GetAttributeBool("canspeak", false);
|
||||
needsAir = doc.Root.GetAttributeBool("needsair", false);
|
||||
Noise = doc.Root.GetAttributeFloat("noise", 100f);
|
||||
|
||||
@@ -853,7 +853,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (characterConfigFiles == null)
|
||||
{
|
||||
characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character, searchAllContentPackages: true);
|
||||
characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character);
|
||||
}
|
||||
return characterConfigFiles;
|
||||
}
|
||||
@@ -1103,14 +1103,14 @@ namespace Barotrauma
|
||||
if (leftFoot != null)
|
||||
{
|
||||
float footAfflictionStrength = CharacterHealth.GetAfflictionStrength("damage", leftFoot, true);
|
||||
speed *= MathHelper.Lerp(1.0f, 0.4f, MathHelper.Clamp(footAfflictionStrength / 80.0f, 0.0f, 1.0f));
|
||||
speed *= MathHelper.Lerp(1.0f, 0.25f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
var rightFoot = AnimController.GetLimb(LimbType.RightFoot);
|
||||
if (rightFoot != null)
|
||||
{
|
||||
float footAfflictionStrength = CharacterHealth.GetAfflictionStrength("damage", rightFoot, true);
|
||||
speed *= MathHelper.Lerp(1.0f, 0.4f, MathHelper.Clamp(footAfflictionStrength / 80.0f, 0.0f, 1.0f));
|
||||
speed *= MathHelper.Lerp(1.0f, 0.25f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
return speed;
|
||||
@@ -1428,6 +1428,26 @@ namespace Barotrauma
|
||||
return (wall == null || !wall.CastShadow) && (door == null || door.IsOpen);
|
||||
}
|
||||
|
||||
public bool CanSeeCharacter(Character character, Vector2 sourceWorldPos)
|
||||
{
|
||||
Vector2 diff = ConvertUnits.ToSimUnits(character.WorldPosition - sourceWorldPos);
|
||||
|
||||
Body closestBody = null;
|
||||
if (character.Submarine == null)
|
||||
{
|
||||
closestBody = Submarine.CheckVisibility(sourceWorldPos, sourceWorldPos + diff);
|
||||
if (closestBody == null) return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
closestBody = Submarine.CheckVisibility(character.WorldPosition, character.WorldPosition - diff);
|
||||
if (closestBody == null) return true;
|
||||
}
|
||||
|
||||
Structure wall = closestBody.UserData as Structure;
|
||||
return wall == null || !wall.CastShadow;
|
||||
}
|
||||
|
||||
public bool HasEquippedItem(Item item)
|
||||
{
|
||||
for (int i = 0; i < Inventory.Capacity; i++)
|
||||
@@ -1568,8 +1588,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.InteractDistance == 0.0f && !item.Prefab.Triggers.Any()) { return false; }
|
||||
|
||||
|
||||
if (item.InteractDistance == 0.0f && !item.Prefab.Triggers.Any()) return false;
|
||||
|
||||
Pickable pickableComponent = item.GetComponent<Pickable>();
|
||||
if (pickableComponent != null && (pickableComponent.Picker != null && !pickableComponent.Picker.IsDead)) { return false; }
|
||||
@@ -2023,10 +2044,6 @@ namespace Barotrauma
|
||||
{
|
||||
IsRagdolled = IsForceRagdolled;
|
||||
}
|
||||
else if (IsRemotePlayer)
|
||||
{
|
||||
IsRagdolled = IsKeyDown(InputType.Ragdoll);
|
||||
}
|
||||
//Keep us ragdolled if we were forced or we're too speedy to unragdoll
|
||||
else if (allowRagdoll && (!IsRagdolled || AnimController.Collider.LinearVelocity.LengthSquared() < 1f))
|
||||
{
|
||||
@@ -2155,8 +2172,7 @@ namespace Barotrauma
|
||||
|
||||
public void Speak(string message, ChatMessageType? messageType = null, float delay = 0.0f, string identifier = "", float minDurationBetweenSimilar = 0.0f)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (string.IsNullOrEmpty(message)) { return; }
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return;
|
||||
|
||||
//already sent a similar message a moment ago
|
||||
if (!string.IsNullOrEmpty(identifier) && minDurationBetweenSimilar > 0.0f &&
|
||||
@@ -2165,6 +2181,7 @@ namespace Barotrauma
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
aiChatMessageQueue.Add(new AIChatMessage(message, messageType, identifier, delay));
|
||||
}
|
||||
|
||||
@@ -2581,9 +2598,11 @@ namespace Barotrauma
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
CharacterList.Remove(this);
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
if (Controlled == this) { Controlled = null; }
|
||||
CharacterList.Remove(this);
|
||||
|
||||
if (Inventory != null)
|
||||
{
|
||||
|
||||
@@ -399,7 +399,7 @@ namespace Barotrauma
|
||||
{
|
||||
ID = idCounter;
|
||||
idCounter++;
|
||||
Name = element.GetAttributeString("name", "");
|
||||
Name = element.GetAttributeString("name", "unnamed");
|
||||
string genderStr = element.GetAttributeString("gender", "male").ToLowerInvariant();
|
||||
File = element.GetAttributeString("file", "");
|
||||
SourceElement = GetConfig(File).Root;
|
||||
@@ -423,29 +423,6 @@ namespace Barotrauma
|
||||
element.GetAttributeInt("beardindex", -1),
|
||||
element.GetAttributeInt("moustacheindex", -1),
|
||||
element.GetAttributeInt("faceattachmentindex", -1));
|
||||
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
{
|
||||
if (SourceElement.Element("name") != null)
|
||||
{
|
||||
string firstNamePath = SourceElement.Element("name").GetAttributeString("firstname", "");
|
||||
if (firstNamePath != "")
|
||||
{
|
||||
firstNamePath = firstNamePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
|
||||
Name = ToolBox.GetRandomLine(firstNamePath);
|
||||
}
|
||||
|
||||
string lastNamePath = SourceElement.Element("name").GetAttributeString("lastname", "");
|
||||
if (lastNamePath != "")
|
||||
{
|
||||
lastNamePath = lastNamePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
|
||||
if (Name != "") Name += " ";
|
||||
Name += ToolBox.GetRandomLine(lastNamePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StartItemsGiven = element.GetAttributeBool("startitemsgiven", false);
|
||||
string personalityName = element.GetAttributeString("personality", "");
|
||||
ragdollFileName = element.GetAttributeString("ragdoll", string.Empty);
|
||||
@@ -729,8 +706,6 @@ namespace Barotrauma
|
||||
|
||||
partial void LoadAttachmentSprites();
|
||||
|
||||
// TODO: change the formula so that it's not linear and so that it takes into account the usefulness of the skill
|
||||
// -> give a weight to each skill, because some are much more valuable than others?
|
||||
private int CalculateSalary()
|
||||
{
|
||||
if (Name == null || Job == null) return 0;
|
||||
|
||||
@@ -231,8 +231,7 @@ namespace Barotrauma
|
||||
: afflictions.Concat(limbHealths.SelectMany(lh => lh.Afflictions.Where(limbHealthFilter)));
|
||||
}
|
||||
|
||||
private LimbHealth GetMatchingLimbHealth(Limb limb) => limbHealths[limb.HealthIndex];
|
||||
private LimbHealth GetMathingLimbHealth(Affliction affliction) => GetMatchingLimbHealth(Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb));
|
||||
private LimbHealth GetMathingLimbHealth(Affliction affliction) => limbHealths[Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb).HealthIndex];
|
||||
|
||||
/// <summary>
|
||||
/// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb.
|
||||
@@ -266,12 +265,6 @@ namespace Barotrauma
|
||||
|
||||
public Affliction GetAffliction(string afflictionType, Limb limb)
|
||||
{
|
||||
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("Limb health index out of bounds. Character\"" + Character.Name +
|
||||
"\" only has health configured for" + limbHealths.Count + " limbs but the limb " + limb.type + " is targeting index " + limb.HealthIndex);
|
||||
return null;
|
||||
}
|
||||
foreach (Affliction affliction in limbHealths[limb.HealthIndex].Afflictions)
|
||||
{
|
||||
if (affliction.Prefab.AfflictionType == afflictionType) return affliction;
|
||||
@@ -473,13 +466,7 @@ namespace Barotrauma
|
||||
|
||||
private void AddLimbAffliction(Limb limb, Affliction newAffliction)
|
||||
{
|
||||
if (!newAffliction.Prefab.LimbSpecific || limb == null) return;
|
||||
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("Limb health index out of bounds. Character\"" + Character.Name +
|
||||
"\" only has health configured for" + limbHealths.Count + " limbs but the limb " + limb.type + " is targeting index " + limb.HealthIndex);
|
||||
return;
|
||||
}
|
||||
if (!newAffliction.Prefab.LimbSpecific) return;
|
||||
AddLimbAffliction(limbHealths[limb.HealthIndex], newAffliction);
|
||||
}
|
||||
|
||||
@@ -624,12 +611,6 @@ namespace Barotrauma
|
||||
|
||||
partial void UpdateBleedingProjSpecific(AfflictionBleeding affliction, Limb targetLimb, float deltaTime);
|
||||
|
||||
public void SetVitality(float newVitality)
|
||||
{
|
||||
maxVitality = newVitality;
|
||||
CalculateVitality();
|
||||
}
|
||||
|
||||
public void CalculateVitality()
|
||||
{
|
||||
Vitality = MaxVitality;
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class AutomaticOrder
|
||||
{
|
||||
public string aiTag;
|
||||
public string option;
|
||||
public float priority;
|
||||
|
||||
public AutomaticOrder(XElement element)
|
||||
{
|
||||
aiTag = element.GetAttributeString("aitag", null);
|
||||
option = element.GetAttributeString("option", null);
|
||||
priority = element.GetAttributeFloat("priority", 0);
|
||||
}
|
||||
}
|
||||
|
||||
partial class JobPrefab
|
||||
{
|
||||
public static List<JobPrefab> List;
|
||||
|
||||
public readonly XElement Items;
|
||||
public readonly List<string> ItemNames;
|
||||
|
||||
public List<SkillPrefab> Skills;
|
||||
public readonly XElement Items;
|
||||
public readonly List<string> ItemNames = new List<string>();
|
||||
public readonly List<SkillPrefab> Skills = new List<SkillPrefab>();
|
||||
public readonly List<AutomaticOrder> AutomaticOrders = new List<AutomaticOrder>();
|
||||
|
||||
[Serialize("1,1,1,1", false)]
|
||||
public Color UIColor
|
||||
@@ -111,10 +126,6 @@ namespace Barotrauma
|
||||
Name = TextManager.Get("JobName." + Identifier);
|
||||
Description = TextManager.Get("JobDescription." + Identifier);
|
||||
|
||||
ItemNames = new List<string>();
|
||||
|
||||
Skills = new List<SkillPrefab>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -155,7 +166,10 @@ namespace Barotrauma
|
||||
foreach (XElement skillElement in subElement.Elements())
|
||||
{
|
||||
Skills.Add(new SkillPrefab(skillElement));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "automaticorders":
|
||||
subElement.Elements().ForEach(order => AutomaticOrders.Add(new AutomaticOrder(order)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
partial class Limb : ISerializableEntity, ISpatialEntity
|
||||
partial class Limb : ISerializableEntity
|
||||
{
|
||||
// Note: not used
|
||||
private const float LimbDensity = 15;
|
||||
@@ -155,8 +155,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Submarine Submarine => character.Submarine;
|
||||
|
||||
public Vector2 WorldPosition
|
||||
{
|
||||
get { return character.Submarine == null ? Position : Position + character.Submarine.Position; }
|
||||
|
||||
Reference in New Issue
Block a user