Unstable 0.16.1.0
This commit is contained in:
@@ -340,7 +340,7 @@ namespace Barotrauma
|
||||
{
|
||||
targetingTag = "dead";
|
||||
}
|
||||
else if (AIParams.TryGetTarget(targetCharacter.CharacterHealth.GetActiveAfflictionTags(), out CharacterParams.TargetParams tp) && tp.Threshold > Character.GetDamageDoneByAttacker(targetCharacter))
|
||||
else if (AIParams.TryGetTarget(targetCharacter.CharacterHealth.GetActiveAfflictionTags(), out CharacterParams.TargetParams tp) && tp.Threshold >= Character.GetDamageDoneByAttacker(targetCharacter))
|
||||
{
|
||||
targetingTag = tp.Tag;
|
||||
}
|
||||
@@ -678,7 +678,10 @@ namespace Barotrauma
|
||||
return a.Damage >= selectedTargetingParams.Threshold;
|
||||
}
|
||||
Character attacker = targetCharacter.LastAttackers.LastOrDefault(IsValid)?.Character;
|
||||
if (attacker != null)
|
||||
//if the attacker has the same targeting tag as the character we're protecting, we can't change the TargetState
|
||||
//otherwise e.g. a pet that's set to follow humans would start attacking all humans (and other pets, since they're considered part of the same group) when a hostile human attacks it
|
||||
//TODO: a way for pets to differentiate hostile and friendly humans?
|
||||
if (attacker?.AiTarget != null && !targetCharacter.SpeciesName.Equals(GetTargetingTag(attacker.AiTarget), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Attack the character that attacked the target we are protecting
|
||||
ChangeTargetState(attacker, AIState.Attack, selectedTargetingParams.Priority * 2);
|
||||
@@ -1598,7 +1601,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
sweepTimer = Rand.Range(-1000, 1000) * selectedTargetingParams.SweepSpeed;
|
||||
sweepTimer = Rand.Range(-1000f, 1000f) * selectedTargetingParams.SweepSpeed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2305,7 +2308,7 @@ namespace Barotrauma
|
||||
|
||||
if (item.Condition <= 0.0f)
|
||||
{
|
||||
if (!wasBroken) { PetBehavior?.OnEat(item.GetTags(), 1.0f); }
|
||||
if (!wasBroken) { PetBehavior?.OnEat(item); }
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,7 +565,7 @@ namespace Barotrauma
|
||||
Character.AnimController.HeadInWater ||
|
||||
Character.Submarine == null ||
|
||||
(Character.Submarine.TeamID != Character.TeamID && !Character.IsEscorted) ||
|
||||
ObjectiveManager.CurrentOrders.Any(o => o.Objective.KeepDivingGearOn) ||
|
||||
!ObjectiveManager.IsCurrentObjective<AIObjectiveIdle>() && ObjectiveManager.CurrentOrders.Any(o => o.Objective.KeepDivingGearOn) ||
|
||||
ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn) ||
|
||||
Character.CurrentHull.OxygenPercentage < HULL_LOW_OXYGEN_PERCENTAGE + 10;
|
||||
bool IsOrderedToWait() => Character.IsOnPlayerTeam && ObjectiveManager.CurrentOrder is AIObjectiveGoTo goTo && goTo.Target == Character;
|
||||
@@ -1090,7 +1090,6 @@ namespace Barotrauma
|
||||
|
||||
private void RespondToAttack(Character attacker, AttackResult attackResult)
|
||||
{
|
||||
float minorDamageThreshold = 10;
|
||||
float healAmount = 0.0f;
|
||||
if (attacker != null)
|
||||
{
|
||||
@@ -1099,7 +1098,7 @@ namespace Barotrauma
|
||||
// excluding poisons etc
|
||||
float realDamage = attackResult.Damage - healAmount;
|
||||
// including poisons etc
|
||||
float totalDamage = realDamage - healAmount;
|
||||
float totalDamage = realDamage;
|
||||
if (attackResult.Afflictions != null)
|
||||
{
|
||||
foreach (Affliction affliction in attackResult.Afflictions)
|
||||
@@ -1140,6 +1139,13 @@ namespace Barotrauma
|
||||
}
|
||||
bool isAttackerInfected = false;
|
||||
bool isAttackerFightingEnemy = false;
|
||||
float minorDamageThreshold = 1;
|
||||
float majorDamageThreshold = 20;
|
||||
if (attacker.TeamID == Character.TeamID)
|
||||
{
|
||||
minorDamageThreshold = 10;
|
||||
majorDamageThreshold = 40;
|
||||
}
|
||||
if (IsFriendly(attacker))
|
||||
{
|
||||
if (attacker.AnimController.Anim == Barotrauma.AnimController.Animation.CPR && attacker.SelectedCharacter == Character)
|
||||
@@ -1148,11 +1154,11 @@ namespace Barotrauma
|
||||
// Should not cancel any existing ai objectives (so that if the character attacked you and then helped, we still would want to retaliate).
|
||||
return;
|
||||
}
|
||||
float cumulativeDamage = Character.GetDamageDoneByAttacker(attacker);
|
||||
float cumulativeDamage = realDamage + Character.GetDamageDoneByAttacker(attacker);
|
||||
bool isAccidental = attacker.IsBot && !IsMentallyUnstable && !attacker.AIController.IsMentallyUnstable && Character.CombatAction == null;
|
||||
if (isAccidental)
|
||||
{
|
||||
if (!Character.IsSecurity && cumulativeDamage > 1)
|
||||
if (!Character.IsSecurity && cumulativeDamage > minorDamageThreshold)
|
||||
{
|
||||
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, attacker);
|
||||
}
|
||||
@@ -1161,7 +1167,7 @@ namespace Barotrauma
|
||||
{
|
||||
isAttackerInfected = attacker.CharacterHealth.GetAfflictionStrength("alieninfection") > 0;
|
||||
// Inform other NPCs
|
||||
if (isAttackerInfected || cumulativeDamage > 1 || totalDamage >= minorDamageThreshold)
|
||||
if (isAttackerInfected || cumulativeDamage > minorDamageThreshold || totalDamage > minorDamageThreshold)
|
||||
{
|
||||
if (GameMain.IsMultiplayer || !attacker.IsPlayer || Character.TeamID != attacker.TeamID)
|
||||
{
|
||||
@@ -1170,27 +1176,36 @@ namespace Barotrauma
|
||||
}
|
||||
if (Character.IsBot)
|
||||
{
|
||||
if (ObjectiveManager.CurrentObjective is AIObjectiveFightIntruders) { return; }
|
||||
if (attacker.IsPlayer)
|
||||
var combatMode = DetermineCombatMode(Character, cumulativeDamage);
|
||||
if (attacker.IsPlayer && !Character.IsInstigator && !ObjectiveManager.IsCurrentObjective<AIObjectiveCombat>())
|
||||
{
|
||||
if (Character.IsSecurity)
|
||||
switch (combatMode)
|
||||
{
|
||||
if (attacker.TeamID != Character.TeamID && cumulativeDamage > 1 || cumulativeDamage > minorDamageThreshold)
|
||||
{
|
||||
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityarrest"), null, 0.50f, "attackedbyfriendlysecurityarrest", minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityresponse"), null, 0.50f, "attackedbyfriendlysecurityresponse", minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
}
|
||||
else if (!Character.IsInstigator && cumulativeDamage > 1)
|
||||
{
|
||||
Character.Speak(TextManager.Get("DialogAttackedByFriendly"), null, 0.50f, "attackedbyfriendly", minDurationBetweenSimilar: 30.0f);
|
||||
case AIObjectiveCombat.CombatMode.Defensive:
|
||||
case AIObjectiveCombat.CombatMode.Retreat:
|
||||
if (Character.IsSecurity)
|
||||
{
|
||||
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityresponse"), null, 0.5f, "attackedbyfriendlysecurityresponse", minDurationBetweenSimilar: 10.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Character.Speak(TextManager.Get("DialogAttackedByFriendly"), null, 0.5f, "attackedbyfriendly", minDurationBetweenSimilar: 10.0f);
|
||||
}
|
||||
break;
|
||||
case AIObjectiveCombat.CombatMode.Offensive:
|
||||
case AIObjectiveCombat.CombatMode.Arrest:
|
||||
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityarrest"), null, 0.5f, "attackedbyfriendlysecurityarrest", minDurationBetweenSimilar: 10.0f);
|
||||
break;
|
||||
case AIObjectiveCombat.CombatMode.None:
|
||||
if (Character.IsSecurity && realDamage > 1)
|
||||
{
|
||||
Character.Speak(TextManager.Get("dialogattackedbyfriendlysecurityresponse"), null, 0.5f, "attackedbyfriendlysecurityresponse", minDurationBetweenSimilar: 10.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If the attacker is using a low damage and high frequency weapon like a repair tool, we shouldn't use any delay.
|
||||
AddCombatObjective(DetermineCombatMode(Character, cumulativeDamage), attacker, delay: realDamage > 1 ? GetReactionTime() : 0);
|
||||
AddCombatObjective(combatMode, attacker, delay: realDamage > 1 ? GetReactionTime() : 0);
|
||||
}
|
||||
if (!isAttackerFightingEnemy)
|
||||
{
|
||||
@@ -1203,15 +1218,15 @@ namespace Barotrauma
|
||||
if (Character.Submarine != null && Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
|
||||
{
|
||||
// Non-friendly
|
||||
InformOtherNPCs(Character.GetDamageDoneByAttacker(attacker));
|
||||
InformOtherNPCs();
|
||||
}
|
||||
if (Character.IsBot)
|
||||
{
|
||||
AddCombatObjective(DetermineCombatMode(Character, cumulativeDamage: realDamage), attacker);
|
||||
AddCombatObjective(DetermineCombatMode(Character), attacker);
|
||||
}
|
||||
}
|
||||
|
||||
void InformOtherNPCs(float cumulativeDamage)
|
||||
void InformOtherNPCs(float cumulativeDamage = 0)
|
||||
{
|
||||
foreach (Character otherCharacter in Character.CharacterList)
|
||||
{
|
||||
@@ -1238,7 +1253,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
AIObjectiveCombat.CombatMode DetermineCombatMode(Character c, float cumulativeDamage, bool isWitnessing = false)
|
||||
AIObjectiveCombat.CombatMode DetermineCombatMode(Character c, float cumulativeDamage = 0, bool isWitnessing = false)
|
||||
{
|
||||
if (!IsFriendly(attacker))
|
||||
{
|
||||
@@ -1258,7 +1273,6 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
float dmgThreshold = attacker.TeamID == Character.TeamID ? 50 : minorDamageThreshold;
|
||||
if (isAttackerInfected)
|
||||
{
|
||||
cumulativeDamage = 100;
|
||||
@@ -1266,8 +1280,7 @@ namespace Barotrauma
|
||||
if (GameMain.IsSingleplayer && attacker.IsPlayer && Character.TeamID == attacker.TeamID)
|
||||
{
|
||||
// Bots in the player team never act aggressively in single player when attacked by the player
|
||||
dmgThreshold = minorDamageThreshold;
|
||||
return cumulativeDamage > dmgThreshold ? AIObjectiveCombat.CombatMode.Retreat : AIObjectiveCombat.CombatMode.None;
|
||||
return cumulativeDamage > minorDamageThreshold ? AIObjectiveCombat.CombatMode.Retreat : AIObjectiveCombat.CombatMode.None;
|
||||
}
|
||||
if (Character.Submarine == null || !Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
|
||||
{
|
||||
@@ -1308,21 +1321,25 @@ namespace Barotrauma
|
||||
// Already targeting the attacker -> treat as a more serious threat.
|
||||
cumulativeDamage *= 2;
|
||||
}
|
||||
if (cumulativeDamage > dmgThreshold)
|
||||
if (cumulativeDamage > majorDamageThreshold)
|
||||
{
|
||||
if (c.IsSecurity)
|
||||
{
|
||||
return c.IsSecurity ? AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Arrest;
|
||||
return AIObjectiveCombat.CombatMode.Offensive;
|
||||
}
|
||||
else
|
||||
{
|
||||
return c == Character ? AIObjectiveCombat.CombatMode.Defensive : AIObjectiveCombat.CombatMode.Retreat;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (cumulativeDamage > minorDamageThreshold)
|
||||
{
|
||||
return c.IsSecurity ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Retreat;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AIObjectiveCombat.CombatMode.None;
|
||||
}
|
||||
}
|
||||
|
||||
Character FindInstigator()
|
||||
|
||||
+3
-3
@@ -30,7 +30,7 @@ namespace Barotrauma
|
||||
private float holdFireTimer;
|
||||
private bool hasAimed;
|
||||
private bool isLethalWeapon;
|
||||
private bool AllowCoolDown => !IsOffensiveOrArrest || Mode != initialMode;
|
||||
private bool AllowCoolDown => !IsOffensiveOrArrest || Mode != initialMode || character.TeamID == Enemy.TeamID;
|
||||
|
||||
public Character Enemy { get; private set; }
|
||||
public bool HoldPosition { get; set; }
|
||||
@@ -143,7 +143,7 @@ namespace Barotrauma
|
||||
{
|
||||
Mode = CombatMode.Retreat;
|
||||
}
|
||||
spreadTimer = Rand.Range(-10, 10);
|
||||
spreadTimer = Rand.Range(-10f, 10f);
|
||||
HumanAIController.SortTimer = 0;
|
||||
}
|
||||
|
||||
@@ -1177,7 +1177,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private void SpeakNoWeapons() => Speak("dialogcombatnoweapons", delay: 0, minDuration: 30);
|
||||
private void AskHelp() => Speak("dialogcombatretreating", delay: Rand.Range(0, 1), minDuration: 20);
|
||||
private void AskHelp() => Speak("dialogcombatretreating", delay: Rand.Range(0f, 1f), minDuration: 20);
|
||||
|
||||
private void Speak(string textIdentifier, float delay, float minDuration)
|
||||
{
|
||||
|
||||
+7
-16
@@ -43,7 +43,6 @@ namespace Barotrauma
|
||||
private readonly float minDistance = 50;
|
||||
private readonly float seekGapsInterval = 1;
|
||||
private float seekGapsTimer;
|
||||
private bool cannotFollow;
|
||||
|
||||
/// <summary>
|
||||
/// Display units
|
||||
@@ -52,6 +51,11 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsFollowOrderObjective && Target is Character targetCharacter && (targetCharacter.CurrentHull == null) != (character.CurrentHull == null))
|
||||
{
|
||||
// Keep close when the target is going inside/outside
|
||||
return minDistance;
|
||||
}
|
||||
float dist = _closeEnough * CloseEnoughMultiplier;
|
||||
float extraMultiplier = Math.Clamp(CloseEnoughMultiplier * 0.6f, 1, 3);
|
||||
if (character.AnimController.InWater)
|
||||
@@ -288,28 +292,16 @@ namespace Barotrauma
|
||||
{
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit: false, objectiveManager),
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () =>
|
||||
{
|
||||
cannotFollow = false;
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
});
|
||||
onCompleted: () => RemoveSubObjective(ref findDivingGear));
|
||||
}
|
||||
else
|
||||
{
|
||||
TryAddSubObjective(ref findDivingGear, () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager),
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () =>
|
||||
{
|
||||
cannotFollow = false;
|
||||
RemoveSubObjective(ref findDivingGear);
|
||||
});
|
||||
onCompleted: () => RemoveSubObjective(ref findDivingGear));
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
cannotFollow = false;
|
||||
}
|
||||
}
|
||||
if (repeat)
|
||||
{
|
||||
@@ -735,7 +727,6 @@ namespace Barotrauma
|
||||
findDivingGear = null;
|
||||
seekGapsTimer = 0;
|
||||
TargetGap = null;
|
||||
cannotFollow = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -408,7 +408,7 @@ namespace Barotrauma
|
||||
if (orderGiver == null) { return null; }
|
||||
newObjective = new AIObjectiveGoTo(orderGiver, character, this, repeat: true, priorityModifier: priorityModifier)
|
||||
{
|
||||
CloseEnough = Rand.Range(80, 100),
|
||||
CloseEnough = Rand.Range(80f, 100f),
|
||||
CloseEnoughMultiplier = Math.Min(1 + HumanAIController.CountCrew(c => c.ObjectiveManager.HasOrder<AIObjectiveGoTo>(o => o.Target == orderGiver), onlyBots: true) * Rand.Range(0.8f, 1f), 4),
|
||||
ExtraDistanceOutsideSub = 100,
|
||||
ExtraDistanceWhileSwimming = 100,
|
||||
|
||||
@@ -221,6 +221,9 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public int AssignmentPriority { get; }
|
||||
|
||||
public bool ColoredWhenControllingGiver { get; }
|
||||
public bool DisplayGiverInTooltip { get; }
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
Prefabs = new Dictionary<string, Order>();
|
||||
@@ -406,6 +409,8 @@ namespace Barotrauma
|
||||
DrawIconWhenContained = orderElement.GetAttributeBool("displayiconwhencontained", false);
|
||||
AutoDismiss = orderElement.GetAttributeBool("autodismiss", Category == OrderCategory.Movement);
|
||||
AssignmentPriority = Math.Clamp(orderElement.GetAttributeInt("assignmentpriority", 100), 0, 100);
|
||||
ColoredWhenControllingGiver = orderElement.GetAttributeBool("coloredwhencontrollinggiver", false);
|
||||
DisplayGiverInTooltip = orderElement.GetAttributeBool("displaygiverintooltip", false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -441,6 +446,8 @@ namespace Barotrauma
|
||||
Hidden = prefab.Hidden;
|
||||
IgnoreAtOutpost = prefab.IgnoreAtOutpost;
|
||||
AssignmentPriority = prefab.AssignmentPriority;
|
||||
ColoredWhenControllingGiver = prefab.ColoredWhenControllingGiver;
|
||||
DisplayGiverInTooltip = prefab.DisplayGiverInTooltip;
|
||||
|
||||
OrderGiver = orderGiver;
|
||||
TargetEntity = targetEntity;
|
||||
|
||||
@@ -134,6 +134,7 @@ namespace Barotrauma
|
||||
aggregate += Items[i].Commonness;
|
||||
if (aggregate >= r && Items[i].Prefab != null)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":PetProducedItem:" + pet.AiController.Character.SpeciesName + ":" + Items[i].Prefab.Identifier);
|
||||
Entity.Spawner.AddToSpawnQueue(Items[i].Prefab, pet.AiController.Character.WorldPosition);
|
||||
break;
|
||||
}
|
||||
@@ -200,6 +201,8 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":PetSpawned:" + aiController.Character.SpeciesName);
|
||||
}
|
||||
|
||||
public StatusIndicatorType GetCurrentStatusIndicatorType()
|
||||
@@ -210,23 +213,44 @@ namespace Barotrauma
|
||||
return StatusIndicatorType.None;
|
||||
}
|
||||
|
||||
public bool OnEat(IEnumerable<string> tags, float amount)
|
||||
public bool OnEat(Item item)
|
||||
{
|
||||
bool success = OnEat(item.GetTags());
|
||||
if (success)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":PetEat:" + AiController.Character.SpeciesName + ":" + item.prefab.Identifier);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool OnEat(Character character)
|
||||
{
|
||||
if (character == null || !character.IsDead) { return false; }
|
||||
bool success = OnEat("dead");
|
||||
if (success)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":PetEat:" + AiController.Character.SpeciesName + ":" + character.SpeciesName);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool OnEat(IEnumerable<string> tags)
|
||||
{
|
||||
foreach (string tag in tags)
|
||||
{
|
||||
if (OnEat(tag, amount)) { return true; }
|
||||
if (OnEat(tag)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnEat(string tag, float amount)
|
||||
private bool OnEat(string tag)
|
||||
{
|
||||
for (int i = 0; i < foods.Count; i++)
|
||||
{
|
||||
if (tag.Equals(foods[i].Tag, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Hunger += foods[i].Hunger * amount;
|
||||
Happiness += foods[i].Happiness * amount;
|
||||
Hunger += foods[i].Hunger;
|
||||
Happiness += foods[i].Happiness;
|
||||
#if CLIENT
|
||||
AiController.Character.PlaySound(CharacterSound.SoundType.Happy, 0.5f);
|
||||
#endif
|
||||
|
||||
+7
-1
@@ -22,7 +22,13 @@ namespace Barotrauma
|
||||
|
||||
public override void CalculateImportanceSpecific()
|
||||
{
|
||||
if (TargetItemComponent is Turret turret && !turret.HasPowerToShoot()) { return; }
|
||||
if (TargetItemComponent is Turret turret && !turret.HasPowerToShoot())
|
||||
{
|
||||
//operate (= recharge the turrets) with low priority if they're out of power
|
||||
//if something else (issues with reactor or the electrical grid) is preventing them from being charged, fixing those issues should take priority
|
||||
Importance = ShipCommandManager.MinimumIssueThreshold * 1.05f;
|
||||
return;
|
||||
}
|
||||
|
||||
targetingImportances.Clear();
|
||||
foreach (Character character in shipCommandManager.EnemyCharacters)
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Barotrauma
|
||||
private const float RamTimerMax = 17.5f;
|
||||
|
||||
public readonly List<ShipIssueWorker> ShipIssueWorkers = new List<ShipIssueWorker>();
|
||||
private const float MinimumIssueThreshold = 10f;
|
||||
public const float MinimumIssueThreshold = 10f;
|
||||
private const float IssueDevotionBuffer = 5f;
|
||||
|
||||
private float decisionTimer = 6f;
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace Barotrauma
|
||||
|
||||
private void SpawnInitialCells()
|
||||
{
|
||||
int brainRoomCells = Rand.Range(MinCellsPerBrainRoom, MaxCellsPerRoom);
|
||||
int brainRoomCells = Rand.Range(MinCellsPerBrainRoom, MaxCellsPerRoom + 1);
|
||||
if (brain.CurrentHull?.WaterPercentage >= MinWaterLevel)
|
||||
{
|
||||
for (int i = 0; i < brainRoomCells; i++)
|
||||
@@ -262,12 +262,12 @@ namespace Barotrauma
|
||||
if (!TrySpawnCell(out _, brain.CurrentHull)) { break; }
|
||||
}
|
||||
}
|
||||
int cellsInside = Rand.Range(MinCellsInside, MaxCellsInside);
|
||||
int cellsInside = Rand.Range(MinCellsInside, MaxCellsInside + 1);
|
||||
for (int i = 0; i < cellsInside; i++)
|
||||
{
|
||||
if (!TrySpawnCell(out _)) { break; }
|
||||
}
|
||||
int cellsOutside = Rand.Range(MinCellsOutside, MaxCellsOutside);
|
||||
int cellsOutside = Rand.Range(MinCellsOutside, MaxCellsOutside + 1);
|
||||
// If we failed to spawn some of the cells in the brainroom/inside, spawn some extra cells outside.
|
||||
cellsOutside = Math.Clamp(cellsOutside + brainRoomCells + cellsInside - protectiveCells.Count, cellsOutside, MaxCellsOutside);
|
||||
for (int i = 0; i < cellsOutside; i++)
|
||||
|
||||
@@ -420,7 +420,7 @@ namespace Barotrauma
|
||||
|
||||
if (Character.AIController is EnemyAIController enemyAi)
|
||||
{
|
||||
enemyAi.PetBehavior?.OnEat("dead", 1.0f);
|
||||
enemyAi.PetBehavior?.OnEat(target);
|
||||
}
|
||||
|
||||
character.SelectedCharacter = null;
|
||||
|
||||
+11
-11
@@ -533,6 +533,8 @@ namespace Barotrauma
|
||||
|
||||
bool onSlope = Math.Abs(movement.X) > 0.01f && Math.Abs(floorNormal.X) > 0.1f && Math.Sign(floorNormal.X) != Math.Sign(movement.X);
|
||||
|
||||
bool movingHorizontally = !MathUtils.NearlyEqual(targetMovement.X, 0.0f);
|
||||
|
||||
if (Stairs != null || onSlope)
|
||||
{
|
||||
torso.PullJointWorldAnchorB = new Vector2(
|
||||
@@ -562,10 +564,8 @@ namespace Barotrauma
|
||||
|
||||
if (!torso.Disabled)
|
||||
{
|
||||
if (TorsoPosition.HasValue)
|
||||
{
|
||||
y += TorsoPosition.Value;
|
||||
}
|
||||
if (TorsoPosition.HasValue) { y += TorsoPosition.Value; }
|
||||
if (Crouching && !movingHorizontally) { y -= HumanCrouchParams.MoveDownAmountWhenStationary; }
|
||||
torso.PullJointWorldAnchorB =
|
||||
MathUtils.SmoothStep(torso.SimPosition,
|
||||
new Vector2(footMid + movement.X * TorsoLeanAmount, y), getUpForce);
|
||||
@@ -574,10 +574,8 @@ namespace Barotrauma
|
||||
if (!head.Disabled)
|
||||
{
|
||||
y = colliderPos.Y + stepLift * CurrentGroundedParams.StepLiftHeadMultiplier;
|
||||
if (HeadPosition.HasValue)
|
||||
{
|
||||
y += HeadPosition.Value;
|
||||
}
|
||||
if (HeadPosition.HasValue) { y += HeadPosition.Value; }
|
||||
if (Crouching && !movingHorizontally) { y -= HumanCrouchParams.MoveDownAmountWhenStationary; }
|
||||
head.PullJointWorldAnchorB =
|
||||
MathUtils.SmoothStep(head.SimPosition,
|
||||
new Vector2(footMid + movement.X * HeadLeanAmount, y), getUpForce * 1.2f);
|
||||
@@ -593,12 +591,15 @@ namespace Barotrauma
|
||||
{
|
||||
float torsoAngle = TorsoAngle.Value;
|
||||
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
|
||||
if (Crouching && !movingHorizontally) { torsoAngle -= HumanCrouchParams.ExtraTorsoAngleWhenStationary; }
|
||||
torsoAngle -= herpesStrength / 150.0f;
|
||||
torso.body.SmoothRotate(torsoAngle * Dir, CurrentGroundedParams.TorsoTorque);
|
||||
}
|
||||
if (HeadAngle.HasValue)
|
||||
{
|
||||
head.body.SmoothRotate(HeadAngle.Value * Dir, CurrentGroundedParams.HeadTorque);
|
||||
float headAngle = HeadAngle.Value;
|
||||
if (Crouching && !movingHorizontally) { headAngle -= HumanCrouchParams.ExtraHeadAngleWhenStationary; }
|
||||
head.body.SmoothRotate(headAngle * Dir, CurrentGroundedParams.HeadTorque);
|
||||
}
|
||||
|
||||
if (!onGround)
|
||||
@@ -616,8 +617,7 @@ namespace Barotrauma
|
||||
|
||||
Vector2 waistPos = waist != null ? waist.SimPosition : torso.SimPosition;
|
||||
|
||||
//moving horizontally
|
||||
if (TargetMovement.X != 0.0f)
|
||||
if (movingHorizontally)
|
||||
{
|
||||
//progress the walking animation
|
||||
WalkPos -= MathHelper.ToRadians(CurrentAnimationParams.CycleSpeed) * walkCycleMultiplier * movement.X;
|
||||
|
||||
@@ -261,9 +261,16 @@ namespace Barotrauma
|
||||
|
||||
public AttackResult LastDamage;
|
||||
|
||||
public Dictionary<ItemPrefab, double> ItemSelectedDurations
|
||||
{
|
||||
get { return itemSelectedDurations; }
|
||||
}
|
||||
private readonly Dictionary<ItemPrefab, double> itemSelectedDurations = new Dictionary<ItemPrefab, double>();
|
||||
private double itemSelectedTime;
|
||||
|
||||
public float InvisibleTimer;
|
||||
|
||||
private CharacterPrefab prefab;
|
||||
private readonly CharacterPrefab prefab;
|
||||
|
||||
public readonly CharacterParams Params;
|
||||
public string SpeciesName => Params?.SpeciesName ?? "null";
|
||||
@@ -700,7 +707,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!CanSpeak || IsUnconscious || Stun > 0.0f || IsDead) { return 100.0f; }
|
||||
if (!CanSpeak || IsUnconscious || IsKnockedDown) { return 100.0f; }
|
||||
return speechImpediment;
|
||||
}
|
||||
set
|
||||
@@ -737,9 +744,7 @@ namespace Barotrauma
|
||||
get => _selectedConstruction;
|
||||
set
|
||||
{
|
||||
#if CLIENT
|
||||
var prevSelectedConstruction = _selectedConstruction;
|
||||
#endif
|
||||
_selectedConstruction = value;
|
||||
#if CLIENT
|
||||
HintManager.OnSetSelectedConstruction(this, prevSelectedConstruction, _selectedConstruction);
|
||||
@@ -755,6 +760,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (prevSelectedConstruction == null && _selectedConstruction != null)
|
||||
{
|
||||
itemSelectedTime = Timing.TotalTime;
|
||||
}
|
||||
else if (prevSelectedConstruction != null && _selectedConstruction == null && itemSelectedTime > 0)
|
||||
{
|
||||
if (!itemSelectedDurations.ContainsKey(prevSelectedConstruction.Prefab))
|
||||
{
|
||||
itemSelectedDurations.Add(prevSelectedConstruction.Prefab, 0);
|
||||
}
|
||||
itemSelectedDurations[prevSelectedConstruction.Prefab] += Timing.TotalTime - itemSelectedTime;
|
||||
itemSelectedTime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3950,27 +3968,44 @@ namespace Barotrauma
|
||||
|
||||
AnimController.Frozen = false;
|
||||
|
||||
if (GameAnalyticsManager.SendUserStatistics)
|
||||
{
|
||||
string characterType = "Unknown";
|
||||
|
||||
if (this == Controlled)
|
||||
characterType = "Player";
|
||||
else if (IsRemotePlayer)
|
||||
characterType = "RemotePlayer";
|
||||
else if (AIController is EnemyAIController)
|
||||
characterType = "Enemy";
|
||||
else if (AIController is HumanAIController)
|
||||
characterType = "AICrew";
|
||||
|
||||
string causeOfDeathStr = causeOfDeathAffliction == null ?
|
||||
causeOfDeath.ToString() : causeOfDeathAffliction.Prefab.Name.Replace(" ", "");
|
||||
GameAnalyticsManager.AddDesignEvent("Kill:" + characterType + ":" + SpeciesName + ":" + causeOfDeathStr);
|
||||
}
|
||||
|
||||
CauseOfDeath = new CauseOfDeath(
|
||||
causeOfDeath, causeOfDeathAffliction?.Prefab,
|
||||
causeOfDeathAffliction?.Source ?? LastAttacker, LastDamageSource);
|
||||
causeOfDeathAffliction?.Source, LastDamageSource);
|
||||
|
||||
if (GameAnalyticsManager.SendUserStatistics)
|
||||
{
|
||||
string causeOfDeathStr = causeOfDeathAffliction == null ?
|
||||
causeOfDeath.ToString() : causeOfDeathAffliction.Prefab.Identifier.Replace(" ", "");
|
||||
|
||||
string characterType = GetCharacterType(this);
|
||||
GameAnalyticsManager.AddDesignEvent("Kill:" + characterType + ":" + causeOfDeathStr);
|
||||
if (CauseOfDeath.Killer != null)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("Kill:" + characterType + ":Killer:" + GetCharacterType(CauseOfDeath.Killer));
|
||||
}
|
||||
if (CauseOfDeath.DamageSource != null)
|
||||
{
|
||||
string damageSourceStr = CauseOfDeath.DamageSource.ToString();
|
||||
if (CauseOfDeath.DamageSource is Item damageSourceItem) { damageSourceStr = damageSourceItem.ToString(); }
|
||||
GameAnalyticsManager.AddDesignEvent("Kill:" + characterType + ":DamageSource:" + damageSourceStr);
|
||||
}
|
||||
|
||||
static string GetCharacterType(Character character)
|
||||
{
|
||||
if (character.IsPlayer)
|
||||
return "Player";
|
||||
else if (character.AIController is EnemyAIController)
|
||||
return "Enemy" + character.SpeciesName;
|
||||
else if (character.AIController is HumanAIController && character.TeamID == CharacterTeamType.Team2)
|
||||
return "EnemyHuman";
|
||||
else if (character.Info != null && character.TeamID == CharacterTeamType.Team1)
|
||||
return "AICrew";
|
||||
else if (character.Info != null && character.TeamID == CharacterTeamType.FriendlyNPC)
|
||||
return "FriendlyNPC";
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
OnDeath?.Invoke(this, CauseOfDeath);
|
||||
|
||||
var abilityCharacterKiller = new AbilityCharacterKiller(CauseOfDeath.Killer);
|
||||
@@ -4097,7 +4132,7 @@ namespace Barotrauma
|
||||
info?.Remove();
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.KillCharacter(this);
|
||||
GameMain.GameSession?.CrewManager?.KillCharacter(this, resetCrewListIndex: false);
|
||||
#endif
|
||||
|
||||
CharacterList.Remove(this);
|
||||
@@ -4112,6 +4147,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
itemSelectedDurations.Clear();
|
||||
|
||||
DisposeProjSpecific();
|
||||
|
||||
aiTarget?.Remove();
|
||||
|
||||
@@ -1527,7 +1527,7 @@ namespace Barotrauma
|
||||
orderTargetElement.Add(new XAttribute("hullid", (uint)ot.Hull.ID));
|
||||
position -= ot.Hull.WorldPosition;
|
||||
}
|
||||
orderTargetElement.Add(new XAttribute("position", $"{position.X},{position.Y}"));
|
||||
orderTargetElement.Add(new XAttribute("position", XMLExtensions.Vector2ToString(position)));
|
||||
orderElement.Add(orderTargetElement);
|
||||
break;
|
||||
case Order.OrderTargetType.WallSection when targetAvailableInNextLevel && order.TargetEntity is Structure s && order.WallSectionIndex.HasValue:
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (State != InfectionState.Active && stun)
|
||||
{
|
||||
character.SetStun(Rand.Range(2, 4));
|
||||
character.SetStun(Rand.Range(2f, 3f));
|
||||
}
|
||||
State = InfectionState.Active;
|
||||
ActivateHusk();
|
||||
|
||||
+8
@@ -363,7 +363,9 @@ namespace Barotrauma
|
||||
public readonly string Name, Description;
|
||||
public readonly string TranslationOverride;
|
||||
public readonly bool IsBuff;
|
||||
public readonly bool HealableInMedicalClinic;
|
||||
public readonly float HealCostMultiplier;
|
||||
public readonly int BaseHealCost;
|
||||
|
||||
public readonly string CauseOfDeathDescription, SelfCauseOfDeathDescription;
|
||||
|
||||
@@ -656,7 +658,13 @@ namespace Barotrauma
|
||||
Name = TextManager.Get("AfflictionName." + translationId, true) ?? element.GetAttributeString("name", "");
|
||||
Description = TextManager.Get("AfflictionDescription." + translationId, true) ?? element.GetAttributeString("description", "");
|
||||
IsBuff = element.GetAttributeBool("isbuff", false);
|
||||
|
||||
HealableInMedicalClinic = element.GetAttributeBool("healableinmedicalclinic",
|
||||
!IsBuff &&
|
||||
!AfflictionType.Equals("geneticmaterialbuff", StringComparison.OrdinalIgnoreCase) &&
|
||||
!AfflictionType.Equals("geneticmaterialdebuff", StringComparison.OrdinalIgnoreCase));
|
||||
HealCostMultiplier = element.GetAttributeFloat(nameof(HealCostMultiplier).ToLowerInvariant(), 1f);
|
||||
BaseHealCost = element.GetAttributeInt(nameof(BaseHealCost).ToLowerInvariant(), 0);
|
||||
|
||||
if (element.Attribute("nameidentifier") != null)
|
||||
{
|
||||
|
||||
@@ -764,6 +764,7 @@ namespace Barotrauma
|
||||
if (applyAffliction)
|
||||
{
|
||||
afflictionsCopy.Add(newAffliction);
|
||||
newAffliction.Source ??= attacker;
|
||||
}
|
||||
appliedDamageModifiers.AddRange(tempModifiers);
|
||||
}
|
||||
|
||||
+9
@@ -26,6 +26,15 @@ namespace Barotrauma
|
||||
|
||||
class HumanCrouchParams : HumanGroundedParams
|
||||
{
|
||||
[Serialize(0.0f, true, description: "How much lower the character's head and torso move when stationary."), Editable(MinValueFloat = 0, MaxValueFloat = 2, DecimalCount = 2)]
|
||||
public float MoveDownAmountWhenStationary { get; set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable(-360f, 360f)]
|
||||
public float ExtraHeadAngleWhenStationary { get; set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable(-360f, 360f)]
|
||||
public float ExtraTorsoAngleWhenStationary { get; set; }
|
||||
|
||||
public static HumanCrouchParams GetDefaultAnimParams(Character character) => GetDefaultAnimParams<HumanCrouchParams>(character, AnimationType.Crouch);
|
||||
public static HumanCrouchParams GetAnimParams(Character character, string fileName = null)
|
||||
{
|
||||
|
||||
+3
-1
@@ -23,7 +23,9 @@ namespace Barotrauma.Abilities
|
||||
multiplier = 0 + Character.Info.GetSavedStatValue(StatTypes.None, scalingStatIdentifier);
|
||||
}
|
||||
|
||||
targetCharacter.GiveMoney((int)(multiplier * amount));
|
||||
int totalAmount = (int)(multiplier * amount);
|
||||
targetCharacter.GiveMoney(totalAmount);
|
||||
GameAnalyticsManager.AddMoneyGainedEvent(totalAmount, GameAnalyticsManager.MoneySource.Ability, CharacterTalent.Prefab.Identifier);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
|
||||
+3
-1
@@ -16,7 +16,9 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if ((abilityObject as IAbilityCharacter)?.Character is Character character)
|
||||
{
|
||||
Character.GiveMoney((int)(vitalityPercentage * character.MaxVitality));
|
||||
int totalAmount = (int)(vitalityPercentage * character.MaxVitality);
|
||||
Character.GiveMoney(totalAmount);
|
||||
GameAnalyticsManager.AddMoneyGainedEvent(totalAmount, GameAnalyticsManager.MoneySource.Ability, CharacterTalent.Prefab.Identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ namespace Barotrauma.Abilities
|
||||
if (!enemyCharacter.LockHands) { continue; }
|
||||
if (timesGiven > max) { continue; }
|
||||
Character.GiveMoney(moneyAmount);
|
||||
GameAnalyticsManager.AddMoneyGainedEvent(moneyAmount, GameAnalyticsManager.MoneySource.Ability, CharacterTalent.Prefab.Identifier);
|
||||
foreach (Character character in Character.GetFriendlyCrew(Character))
|
||||
{
|
||||
character.Info?.GiveExperience(experienceAmount);
|
||||
|
||||
+3
-3
@@ -12,8 +12,6 @@ namespace Barotrauma.Abilities
|
||||
|
||||
private readonly int moneyPerMission;
|
||||
|
||||
private static List<Client> clientsAlreadyUsed = new List<Client>();
|
||||
|
||||
public CharacterAbilityInsurancePolicy(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
moneyPerMission = abilityElement.GetAttributeInt("moneypermission", 0);
|
||||
@@ -23,7 +21,9 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (Character?.Info is CharacterInfo info)
|
||||
{
|
||||
Character.GiveMoney(moneyPerMission * info.MissionsCompletedSinceDeath);
|
||||
int totalAmount = moneyPerMission * info.MissionsCompletedSinceDeath;
|
||||
Character.GiveMoney(totalAmount);
|
||||
GameAnalyticsManager.AddMoneyGainedEvent(totalAmount, GameAnalyticsManager.MoneySource.Ability, CharacterTalent.Prefab.Identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user